ruby-mysql 3.0.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +57 -0
- data/README.md +95 -2
- data/lib/mysql/authenticator/caching_sha2_password.rb +3 -2
- data/lib/mysql/authenticator/mysql_native_password.rb +1 -0
- data/lib/mysql/authenticator/sha256_password.rb +1 -0
- data/lib/mysql/authenticator.rb +6 -4
- data/lib/mysql/charset.rb +6 -6
- data/lib/mysql/constants.rb +8 -0
- data/lib/mysql/error.rb +7 -5
- data/lib/mysql/field.rb +95 -0
- data/lib/mysql/packet.rb +6 -5
- data/lib/mysql/protocol.rb +299 -192
- data/lib/mysql/result.rb +212 -0
- data/lib/mysql/stmt.rb +219 -0
- data/lib/mysql.rb +113 -523
- metadata +13 -13
- data/test/test_mysql.rb +0 -1603
- data/test/test_mysql_packet.rb +0 -149
data/lib/mysql.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# coding: ascii-8bit
|
2
|
+
|
2
3
|
# Copyright (C) 2008 TOMITA Masahiro
|
3
4
|
# mailto:tommy@tmtm.org
|
4
5
|
|
@@ -12,15 +13,17 @@ require 'uri'
|
|
12
13
|
# p c1, c2
|
13
14
|
# end
|
14
15
|
class Mysql
|
15
|
-
|
16
|
+
require_relative "mysql/field"
|
17
|
+
require_relative "mysql/result"
|
18
|
+
require_relative "mysql/stmt"
|
16
19
|
require_relative "mysql/constants"
|
17
20
|
require_relative "mysql/error"
|
18
21
|
require_relative "mysql/charset"
|
19
22
|
require_relative "mysql/protocol"
|
20
|
-
require_relative "mysql/packet
|
23
|
+
require_relative "mysql/packet"
|
21
24
|
|
22
|
-
VERSION = '
|
23
|
-
MYSQL_UNIX_PORT = "/tmp/mysql.sock" # UNIX domain socket filename
|
25
|
+
VERSION = -'4.1.0' # Version number of this library
|
26
|
+
MYSQL_UNIX_PORT = -"/tmp/mysql.sock" # UNIX domain socket filename
|
24
27
|
MYSQL_TCP_PORT = 3306 # TCP socket port number
|
25
28
|
|
26
29
|
# @!attribute [rw] host
|
@@ -37,6 +40,8 @@ class Mysql
|
|
37
40
|
# @return [String, nil] socket filename
|
38
41
|
# @!attribute [rw] flags
|
39
42
|
# @return [Integer, nil]
|
43
|
+
# @!attribute [rw] io
|
44
|
+
# @return [[BasicSocket, OpenSSL::SSL::SSLSocket], nil]
|
40
45
|
# @!attribute [rw] connect_timeout
|
41
46
|
# @return [Numeric, nil]
|
42
47
|
# @!attribute [rw] read_timeout
|
@@ -51,8 +56,22 @@ class Mysql
|
|
51
56
|
# @return [String, nil]
|
52
57
|
# @!attribute [rw] ssl_mode
|
53
58
|
# @return [String, Integer] 1 or "disabled" / 2 or "preferred" / 3 or "required"
|
59
|
+
# @!attribute [rw] ssl_context_params
|
60
|
+
# @return [Hash] See OpenSSL::SSL::Context#set_params
|
54
61
|
# @!attribute [rw] get_server_public_key
|
55
62
|
# @return [Boolean]
|
63
|
+
# @!attribute [rw] connect_attrs
|
64
|
+
# @return [Hash]
|
65
|
+
# @!attribute [rw] yield_null_result
|
66
|
+
# @return [Boolean]
|
67
|
+
# @!attribute [rw] return_result
|
68
|
+
# @return [Boolean]
|
69
|
+
# @!attribute [rw] with_table
|
70
|
+
# @return [Boolean]
|
71
|
+
# @!attribute [rw] auto_store_result
|
72
|
+
# @return [Boolean]
|
73
|
+
# @!attribute [rw] cast
|
74
|
+
# @return [Boolean]
|
56
75
|
DEFAULT_OPTS = {
|
57
76
|
host: nil,
|
58
77
|
username: nil,
|
@@ -61,6 +80,7 @@ class Mysql
|
|
61
80
|
port: nil,
|
62
81
|
socket: nil,
|
63
82
|
flags: 0,
|
83
|
+
io: nil,
|
64
84
|
charset: nil,
|
65
85
|
connect_timeout: nil,
|
66
86
|
read_timeout: nil,
|
@@ -69,12 +89,25 @@ class Mysql
|
|
69
89
|
local_infile: nil,
|
70
90
|
load_data_local_dir: nil,
|
71
91
|
ssl_mode: SSL_MODE_PREFERRED,
|
92
|
+
ssl_context_params: {},
|
72
93
|
get_server_public_key: false,
|
94
|
+
connect_attrs: {},
|
95
|
+
yield_null_result: true,
|
96
|
+
return_result: true,
|
97
|
+
with_table: false,
|
98
|
+
auto_store_result: true,
|
99
|
+
cast: true,
|
73
100
|
}.freeze
|
74
101
|
|
75
102
|
# @private
|
76
103
|
attr_reader :protocol
|
77
104
|
|
105
|
+
# @return [Array<Mysql::Field>] fields of result set
|
106
|
+
attr_reader :fields
|
107
|
+
|
108
|
+
# @return [Mysql::Result]
|
109
|
+
attr_reader :result
|
110
|
+
|
78
111
|
class << self
|
79
112
|
# Make Mysql object and connect to mysqld.
|
80
113
|
# parameter is same as arguments for {#initialize}.
|
@@ -87,7 +120,7 @@ class Mysql
|
|
87
120
|
# @param [String] str
|
88
121
|
# @return [String]
|
89
122
|
def escape_string(str)
|
90
|
-
str.gsub(/[\0\n\r
|
123
|
+
str.gsub(/[\0\n\r\\'"\x1a]/) do |s|
|
91
124
|
case s
|
92
125
|
when "\0" then "\\0"
|
93
126
|
when "\n" then "\\n"
|
@@ -98,6 +131,10 @@ class Mysql
|
|
98
131
|
end
|
99
132
|
end
|
100
133
|
alias quote escape_string
|
134
|
+
|
135
|
+
def default_options
|
136
|
+
@default_options ||= DEFAULT_OPTS.dup
|
137
|
+
end
|
101
138
|
end
|
102
139
|
|
103
140
|
# @overload initialize(uri, **opts)
|
@@ -135,15 +172,18 @@ class Mysql
|
|
135
172
|
# @option opts :local_infile [Boolean]
|
136
173
|
# @option opts :load_data_local_dir [String]
|
137
174
|
# @option opts :ssl_mode [Integer]
|
175
|
+
# @option opts :ssl_context_params [Hash<Symbol, String>]
|
138
176
|
# @option opts :get_server_public_key [Boolean]
|
177
|
+
# @option opts :connect_attrs [Hash]
|
178
|
+
# @option opts :io [BasicSocket, OpenSSL::SSL::SSLSocket] Existing socket instance that will be used instead of creating a new socket
|
139
179
|
def initialize(*args, **opts)
|
140
180
|
@fields = nil
|
181
|
+
@result = nil
|
141
182
|
@protocol = nil
|
142
183
|
@sqlstate = "00000"
|
143
184
|
@host_info = nil
|
144
185
|
@last_error = nil
|
145
|
-
@
|
146
|
-
@opts = DEFAULT_OPTS.dup
|
186
|
+
@opts = Mysql.default_options.dup
|
147
187
|
parse_args(args, opts)
|
148
188
|
end
|
149
189
|
|
@@ -164,23 +204,23 @@ class Mysql
|
|
164
204
|
end
|
165
205
|
|
166
206
|
def parse_args(args, opts)
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
207
|
+
unless args.empty?
|
208
|
+
case args[0]
|
209
|
+
when URI
|
210
|
+
uri = args[0]
|
211
|
+
when /\Amysql:\/\//
|
212
|
+
uri = URI.parse(args[0])
|
213
|
+
when String, nil
|
214
|
+
@opts[:host], user, passwd, dbname, port, socket, flags = *args
|
215
|
+
@opts[:username] = user if user
|
216
|
+
@opts[:password] = passwd if passwd
|
217
|
+
@opts[:database] = dbname if dbname
|
218
|
+
@opts[:port] = port if port
|
219
|
+
@opts[:socket] = socket if socket
|
220
|
+
@opts[:flags] = flags if flags
|
221
|
+
when Hash
|
222
|
+
# skip
|
223
|
+
end
|
184
224
|
end
|
185
225
|
if uri
|
186
226
|
host = uri.hostname.to_s
|
@@ -252,7 +292,6 @@ class Mysql
|
|
252
292
|
query "SET NAMES #{charset.name}"
|
253
293
|
end
|
254
294
|
@opts[:charset] = charset
|
255
|
-
cs
|
256
295
|
end
|
257
296
|
|
258
297
|
# @return [String] charset name
|
@@ -267,7 +306,7 @@ class Mysql
|
|
267
306
|
|
268
307
|
# @return [String] last error message
|
269
308
|
def error
|
270
|
-
@last_error
|
309
|
+
@last_error&.error
|
271
310
|
end
|
272
311
|
|
273
312
|
# @return [String] sqlstate for last error
|
@@ -293,13 +332,12 @@ class Mysql
|
|
293
332
|
|
294
333
|
# @return [Integer] server version
|
295
334
|
def server_version
|
296
|
-
|
297
|
-
@protocol.server_version
|
335
|
+
@protocol&.server_version
|
298
336
|
end
|
299
337
|
|
300
338
|
# @return [String] information for last query
|
301
339
|
def info
|
302
|
-
@protocol
|
340
|
+
@protocol&.message
|
303
341
|
end
|
304
342
|
|
305
343
|
# @return [Integer] number of affected records by insert/update/delete.
|
@@ -327,34 +365,33 @@ class Mysql
|
|
327
365
|
end
|
328
366
|
|
329
367
|
# Execute query string.
|
330
|
-
# @
|
331
|
-
#
|
332
|
-
#
|
333
|
-
#
|
334
|
-
# @
|
335
|
-
#
|
336
|
-
# @yield [Mysql::Result] evaluated per query.
|
337
|
-
# @return [self]
|
368
|
+
# @param str [String] Query.
|
369
|
+
# @param return_result [Boolean]
|
370
|
+
# @param yield_null_result [Boolean]
|
371
|
+
# @return [Mysql::Result] if return_result is true and the query returns result set.
|
372
|
+
# @return [nil] if return_result is true and the query does not return result set.
|
373
|
+
# @return [self] if return_result is false or block is specified.
|
338
374
|
# @example
|
339
375
|
# my.query("select 1,NULL,'abc'").fetch # => [1, nil, "abc"]
|
340
376
|
# my.query("select 1,NULL,'abc'"){|res| res.fetch}
|
341
|
-
def query(str, &block)
|
377
|
+
def query(str, **opts, &block)
|
378
|
+
opts = @opts.merge(opts)
|
342
379
|
check_connection
|
343
380
|
@fields = nil
|
344
381
|
begin
|
345
|
-
|
346
|
-
if nfields
|
347
|
-
@fields = @protocol.retr_fields nfields
|
348
|
-
@result_exist = true
|
349
|
-
end
|
382
|
+
@protocol.query_command str
|
350
383
|
if block
|
351
384
|
while true
|
352
|
-
|
353
|
-
|
385
|
+
@protocol.get_result
|
386
|
+
res = store_result(**opts)
|
387
|
+
block.call res if res || opts[:yield_null_result]
|
388
|
+
break unless more_results?
|
354
389
|
end
|
355
390
|
return self
|
356
391
|
end
|
357
|
-
|
392
|
+
@protocol.get_result
|
393
|
+
return self unless opts[:return_result]
|
394
|
+
return store_result(**opts)
|
358
395
|
rescue ServerError => e
|
359
396
|
@last_error = e
|
360
397
|
@sqlstate = e.sqlstate
|
@@ -364,12 +401,12 @@ class Mysql
|
|
364
401
|
|
365
402
|
# Get all data for last query.
|
366
403
|
# @return [Mysql::Result]
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
404
|
+
# @return [nil] if no results
|
405
|
+
def store_result(**opts)
|
406
|
+
return nil if @protocol.field_count.nil? || @protocol.field_count == 0
|
407
|
+
@fields = @protocol.retr_fields
|
408
|
+
opts = @opts.merge(opts)
|
409
|
+
@result = Result.new(@fields, @protocol, **opts)
|
373
410
|
end
|
374
411
|
|
375
412
|
# @return [Integer] Thread ID
|
@@ -388,30 +425,29 @@ class Mysql
|
|
388
425
|
end
|
389
426
|
|
390
427
|
# @return [Boolean] true if multiple queries are specified and unexecuted queries exists.
|
391
|
-
def more_results
|
392
|
-
@protocol.
|
428
|
+
def more_results?
|
429
|
+
@protocol.more_results?
|
393
430
|
end
|
394
|
-
alias more_results? more_results
|
395
431
|
|
396
432
|
# execute next query if multiple queries are specified.
|
397
|
-
# @return [
|
398
|
-
|
399
|
-
|
400
|
-
|
433
|
+
# @return [Mysql::Result] result set of query if return_result is true.
|
434
|
+
# @return [true] if return_result is false and result exists.
|
435
|
+
# @return [nil] query returns no results.
|
436
|
+
def next_result(**opts)
|
437
|
+
return nil unless more_results?
|
438
|
+
opts = @opts.merge(opts)
|
439
|
+
@protocol.get_result
|
401
440
|
@fields = nil
|
402
|
-
|
403
|
-
|
404
|
-
@fields = @protocol.retr_fields nfields
|
405
|
-
@result_exist = true
|
406
|
-
end
|
407
|
-
return true
|
441
|
+
return store_result(**opts) if opts[:return_result]
|
442
|
+
true
|
408
443
|
end
|
409
444
|
|
410
445
|
# Parse prepared-statement.
|
411
446
|
# @param [String] str query string
|
412
447
|
# @return [Mysql::Stmt] Prepared-statement object
|
413
|
-
def prepare(str)
|
414
|
-
|
448
|
+
def prepare(str, **opts)
|
449
|
+
opts = @opts.merge(opts)
|
450
|
+
st = Stmt.new(@protocol, **opts)
|
415
451
|
st.prepare str
|
416
452
|
st
|
417
453
|
end
|
@@ -419,8 +455,9 @@ class Mysql
|
|
419
455
|
# @private
|
420
456
|
# Make empty prepared-statement object.
|
421
457
|
# @return [Mysql::Stmt] If block is not specified.
|
422
|
-
def stmt
|
423
|
-
|
458
|
+
def stmt(**opts)
|
459
|
+
opts = @opts.merge(opts)
|
460
|
+
Stmt.new(@protocol, **opts)
|
424
461
|
end
|
425
462
|
|
426
463
|
# Check whether the connection is available.
|
@@ -488,462 +525,15 @@ class Mysql
|
|
488
525
|
self
|
489
526
|
end
|
490
527
|
|
491
|
-
|
492
|
-
|
493
|
-
def
|
494
|
-
|
495
|
-
end
|
496
|
-
|
497
|
-
# @!visibility public
|
498
|
-
# Field class
|
499
|
-
class Field
|
500
|
-
# @return [String] database name
|
501
|
-
attr_reader :db
|
502
|
-
# @return [String] table name
|
503
|
-
attr_reader :table
|
504
|
-
# @return [String] original table name
|
505
|
-
attr_reader :org_table
|
506
|
-
# @return [String] field name
|
507
|
-
attr_reader :name
|
508
|
-
# @return [String] original field name
|
509
|
-
attr_reader :org_name
|
510
|
-
# @return [Integer] charset id number
|
511
|
-
attr_reader :charsetnr
|
512
|
-
# @return [Integer] field length
|
513
|
-
attr_reader :length
|
514
|
-
# @return [Integer] field type
|
515
|
-
attr_reader :type
|
516
|
-
# @return [Integer] flag
|
517
|
-
attr_reader :flags
|
518
|
-
# @return [Integer] number of decimals
|
519
|
-
attr_reader :decimals
|
520
|
-
# @return [String] defualt value
|
521
|
-
attr_reader :default
|
522
|
-
alias :def :default
|
523
|
-
|
524
|
-
# @private
|
525
|
-
attr_accessor :result
|
526
|
-
|
527
|
-
# @attr [Protocol::FieldPacket] packet
|
528
|
-
def initialize(packet)
|
529
|
-
@db, @table, @org_table, @name, @org_name, @charsetnr, @length, @type, @flags, @decimals, @default =
|
530
|
-
packet.db, packet.table, packet.org_table, packet.name, packet.org_name, packet.charsetnr, packet.length, packet.type, packet.flags, packet.decimals, packet.default
|
531
|
-
@flags |= NUM_FLAG if is_num_type?
|
532
|
-
@max_length = nil
|
533
|
-
end
|
534
|
-
|
535
|
-
# @return [Hash] field information
|
536
|
-
def to_hash
|
537
|
-
{
|
538
|
-
"name" => @name,
|
539
|
-
"table" => @table,
|
540
|
-
"def" => @default,
|
541
|
-
"type" => @type,
|
542
|
-
"length" => @length,
|
543
|
-
"max_length" => max_length,
|
544
|
-
"flags" => @flags,
|
545
|
-
"decimals" => @decimals
|
546
|
-
}
|
547
|
-
end
|
548
|
-
|
549
|
-
# @private
|
550
|
-
def inspect
|
551
|
-
"#<Mysql::Field:#{@name}>"
|
552
|
-
end
|
553
|
-
|
554
|
-
# @return [Boolean] true if numeric field.
|
555
|
-
def is_num?
|
556
|
-
@flags & NUM_FLAG != 0
|
557
|
-
end
|
558
|
-
|
559
|
-
# @return [Boolean] true if not null field.
|
560
|
-
def is_not_null?
|
561
|
-
@flags & NOT_NULL_FLAG != 0
|
562
|
-
end
|
563
|
-
|
564
|
-
# @return [Boolean] true if primary key field.
|
565
|
-
def is_pri_key?
|
566
|
-
@flags & PRI_KEY_FLAG != 0
|
567
|
-
end
|
568
|
-
|
569
|
-
# @return [Integer] maximum width of the field for the result set
|
570
|
-
def max_length
|
571
|
-
return @max_length if @max_length
|
572
|
-
@max_length = 0
|
573
|
-
@result.calculate_field_max_length if @result
|
574
|
-
@max_length
|
575
|
-
end
|
576
|
-
|
577
|
-
attr_writer :max_length
|
578
|
-
|
579
|
-
private
|
580
|
-
|
581
|
-
def is_num_type?
|
582
|
-
[TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT, TYPE_DOUBLE, TYPE_LONGLONG, TYPE_INT24].include?(@type) || (@type == TYPE_TIMESTAMP && (@length == 14 || @length == 8))
|
583
|
-
end
|
584
|
-
|
585
|
-
end
|
586
|
-
|
587
|
-
# @!visibility public
|
588
|
-
# Result set
|
589
|
-
class ResultBase
|
590
|
-
include Enumerable
|
591
|
-
|
592
|
-
# @return [Array<Mysql::Field>] field list
|
593
|
-
attr_reader :fields
|
594
|
-
|
595
|
-
# @param [Array of Mysql::Field] fields
|
596
|
-
def initialize(fields)
|
597
|
-
@fields = fields
|
598
|
-
@field_index = 0 # index of field
|
599
|
-
@records = [] # all records
|
600
|
-
@index = 0 # index of record
|
601
|
-
@fieldname_with_table = nil
|
602
|
-
@fetched_record = nil
|
603
|
-
end
|
604
|
-
|
605
|
-
# ignore
|
606
|
-
# @return [void]
|
607
|
-
def free
|
608
|
-
end
|
609
|
-
|
610
|
-
# @return [Integer] number of record
|
611
|
-
def size
|
612
|
-
@records.size
|
613
|
-
end
|
614
|
-
alias num_rows size
|
615
|
-
|
616
|
-
# @return [Array] current record data
|
617
|
-
def fetch
|
618
|
-
@fetched_record = nil
|
619
|
-
return nil if @index >= @records.size
|
620
|
-
@records[@index] = @records[@index].to_a unless @records[@index].is_a? Array
|
621
|
-
@fetched_record = @records[@index]
|
622
|
-
@index += 1
|
623
|
-
return @fetched_record
|
624
|
-
end
|
625
|
-
alias fetch_row fetch
|
626
|
-
|
627
|
-
# Return data of current record as Hash.
|
628
|
-
# The hash key is field name.
|
629
|
-
# @param [Boolean] with_table if true, hash key is "table_name.field_name".
|
630
|
-
# @return [Hash] current record data
|
631
|
-
def fetch_hash(with_table=nil)
|
632
|
-
row = fetch
|
633
|
-
return nil unless row
|
634
|
-
if with_table and @fieldname_with_table.nil?
|
635
|
-
@fieldname_with_table = @fields.map{|f| [f.table, f.name].join(".")}
|
636
|
-
end
|
637
|
-
ret = {}
|
638
|
-
@fields.each_index do |i|
|
639
|
-
fname = with_table ? @fieldname_with_table[i] : @fields[i].name
|
640
|
-
ret[fname] = row[i]
|
641
|
-
end
|
642
|
-
ret
|
643
|
-
end
|
644
|
-
|
645
|
-
# Iterate block with record.
|
646
|
-
# @yield [Array] record data
|
647
|
-
# @return [self] self. If block is not specified, this returns Enumerator.
|
648
|
-
def each(&block)
|
649
|
-
return enum_for(:each) unless block
|
650
|
-
while rec = fetch
|
651
|
-
block.call rec
|
652
|
-
end
|
653
|
-
self
|
654
|
-
end
|
655
|
-
|
656
|
-
# Iterate block with record as Hash.
|
657
|
-
# @param [Boolean] with_table if true, hash key is "table_name.field_name".
|
658
|
-
# @yield [Hash] record data
|
659
|
-
# @return [self] self. If block is not specified, this returns Enumerator.
|
660
|
-
def each_hash(with_table=nil, &block)
|
661
|
-
return enum_for(:each_hash, with_table) unless block
|
662
|
-
while rec = fetch_hash(with_table)
|
663
|
-
block.call rec
|
664
|
-
end
|
665
|
-
self
|
666
|
-
end
|
667
|
-
|
668
|
-
# Set record position
|
669
|
-
# @param [Integer] n record index
|
670
|
-
# @return [self] self
|
671
|
-
def data_seek(n)
|
672
|
-
@index = n
|
673
|
-
self
|
674
|
-
end
|
675
|
-
|
676
|
-
# @return [Integer] current record position
|
677
|
-
def row_tell
|
678
|
-
@index
|
679
|
-
end
|
680
|
-
|
681
|
-
# Set current position of record
|
682
|
-
# @param [Integer] n record index
|
683
|
-
# @return [Integer] previous position
|
684
|
-
def row_seek(n)
|
685
|
-
ret = @index
|
686
|
-
@index = n
|
687
|
-
ret
|
688
|
-
end
|
689
|
-
end
|
690
|
-
|
691
|
-
# @!visibility public
|
692
|
-
# Result set for simple query
|
693
|
-
class Result < ResultBase
|
694
|
-
# @private
|
695
|
-
# @param [Array<Mysql::Field>] fields
|
696
|
-
# @param [Mysql::Protocol] protocol
|
697
|
-
def initialize(fields, protocol=nil)
|
698
|
-
super fields
|
699
|
-
return unless protocol
|
700
|
-
@records = protocol.retr_all_records fields
|
701
|
-
fields.each{|f| f.result = self} # for calculating max_field
|
702
|
-
end
|
703
|
-
|
704
|
-
# @private
|
705
|
-
# calculate max_length of all fields
|
706
|
-
def calculate_field_max_length
|
707
|
-
max_length = Array.new(@fields.size, 0)
|
708
|
-
@records.each_with_index do |rec, i|
|
709
|
-
rec = @records[i] = rec.to_a if rec.is_a? RawRecord
|
710
|
-
max_length.each_index do |j|
|
711
|
-
max_length[j] = rec[j].length if rec[j] && rec[j].length > max_length[j]
|
712
|
-
end
|
713
|
-
end
|
714
|
-
max_length.each_with_index do |len, i|
|
715
|
-
@fields[i].max_length = len
|
716
|
-
end
|
717
|
-
end
|
718
|
-
|
719
|
-
# @return [Mysql::Field] current field
|
720
|
-
def fetch_field
|
721
|
-
return nil if @field_index >= @fields.length
|
722
|
-
ret = @fields[@field_index]
|
723
|
-
@field_index += 1
|
724
|
-
ret
|
725
|
-
end
|
726
|
-
|
727
|
-
# @return [Integer] current field position
|
728
|
-
def field_tell
|
729
|
-
@field_index
|
730
|
-
end
|
731
|
-
|
732
|
-
# Set field position
|
733
|
-
# @param [Integer] n field index
|
734
|
-
# @return [Integer] previous position
|
735
|
-
def field_seek(n)
|
736
|
-
ret = @field_index
|
737
|
-
@field_index = n
|
738
|
-
ret
|
739
|
-
end
|
740
|
-
|
741
|
-
# Return specified field
|
742
|
-
# @param [Integer] n field index
|
743
|
-
# @return [Mysql::Field] field
|
744
|
-
def fetch_field_direct(n)
|
745
|
-
raise ClientError, "invalid argument: #{n}" if n < 0 or n >= @fields.length
|
746
|
-
@fields[n]
|
747
|
-
end
|
748
|
-
|
749
|
-
# @return [Array<Mysql::Field>] all fields
|
750
|
-
def fetch_fields
|
751
|
-
@fields
|
752
|
-
end
|
753
|
-
|
754
|
-
# @return [Array<Integer>] length of each fields
|
755
|
-
def fetch_lengths
|
756
|
-
return nil unless @fetched_record
|
757
|
-
@fetched_record.map{|c|c.nil? ? 0 : c.length}
|
758
|
-
end
|
759
|
-
|
760
|
-
# @return [Integer] number of fields
|
761
|
-
def num_fields
|
762
|
-
@fields.size
|
763
|
-
end
|
764
|
-
end
|
765
|
-
|
766
|
-
# @!visibility private
|
767
|
-
# Result set for prepared statement
|
768
|
-
class StatementResult < ResultBase
|
769
|
-
# @private
|
770
|
-
# @param [Array<Mysql::Field>] fields
|
771
|
-
# @param [Mysql::Protocol] protocol
|
772
|
-
def initialize(fields, protocol)
|
773
|
-
super fields
|
774
|
-
@records = protocol.stmt_retr_all_records @fields, protocol.charset
|
775
|
-
end
|
528
|
+
# session track
|
529
|
+
# @return [Hash]
|
530
|
+
def session_track
|
531
|
+
@protocol.session_track
|
776
532
|
end
|
777
533
|
|
778
|
-
|
779
|
-
# Prepared statement
|
780
|
-
# @!attribute [r] affected_rows
|
781
|
-
# @return [Integer]
|
782
|
-
# @!attribute [r] insert_id
|
783
|
-
# @return [Integer]
|
784
|
-
# @!attribute [r] server_status
|
785
|
-
# @return [Integer]
|
786
|
-
# @!attribute [r] warning_count
|
787
|
-
# @return [Integer]
|
788
|
-
# @!attribute [r] param_count
|
789
|
-
# @return [Integer]
|
790
|
-
# @!attribute [r] fields
|
791
|
-
# @return [Array<Mysql::Field>]
|
792
|
-
# @!attribute [r] sqlstate
|
793
|
-
# @return [String]
|
794
|
-
class Stmt
|
795
|
-
include Enumerable
|
796
|
-
|
797
|
-
attr_reader :affected_rows, :insert_id, :server_status, :warning_count
|
798
|
-
attr_reader :param_count, :fields, :sqlstate
|
799
|
-
|
800
|
-
# @private
|
801
|
-
def self.finalizer(protocol, statement_id)
|
802
|
-
proc do
|
803
|
-
protocol.gc_stmt statement_id
|
804
|
-
end
|
805
|
-
end
|
806
|
-
|
807
|
-
# @private
|
808
|
-
# @param [Mysql::Protocol] protocol
|
809
|
-
def initialize(protocol)
|
810
|
-
@protocol = protocol
|
811
|
-
@statement_id = nil
|
812
|
-
@affected_rows = @insert_id = @server_status = @warning_count = 0
|
813
|
-
@sqlstate = "00000"
|
814
|
-
@param_count = nil
|
815
|
-
end
|
816
|
-
|
817
|
-
# @private
|
818
|
-
# parse prepared-statement and return {Mysql::Stmt} object
|
819
|
-
# @param [String] str query string
|
820
|
-
# @return self
|
821
|
-
def prepare(str)
|
822
|
-
close
|
823
|
-
begin
|
824
|
-
@sqlstate = "00000"
|
825
|
-
@statement_id, @param_count, @fields = @protocol.stmt_prepare_command(str)
|
826
|
-
rescue ServerError => e
|
827
|
-
@last_error = e
|
828
|
-
@sqlstate = e.sqlstate
|
829
|
-
raise
|
830
|
-
end
|
831
|
-
ObjectSpace.define_finalizer(self, self.class.finalizer(@protocol, @statement_id))
|
832
|
-
self
|
833
|
-
end
|
834
|
-
|
835
|
-
# Execute prepared statement.
|
836
|
-
# @param [Object] values values passed to query
|
837
|
-
# @return [Mysql::Stmt] self
|
838
|
-
def execute(*values)
|
839
|
-
raise ClientError, "not prepared" unless @param_count
|
840
|
-
raise ClientError, "parameter count mismatch" if values.length != @param_count
|
841
|
-
values = values.map{|v| @protocol.charset.convert v}
|
842
|
-
begin
|
843
|
-
@sqlstate = "00000"
|
844
|
-
nfields = @protocol.stmt_execute_command @statement_id, values
|
845
|
-
if nfields
|
846
|
-
@fields = @protocol.retr_fields nfields
|
847
|
-
@result = StatementResult.new @fields, @protocol
|
848
|
-
else
|
849
|
-
@affected_rows, @insert_id, @server_status, @warning_count, @info =
|
850
|
-
@protocol.affected_rows, @protocol.insert_id, @protocol.server_status, @protocol.warning_count, @protocol.message
|
851
|
-
end
|
852
|
-
return self
|
853
|
-
rescue ServerError => e
|
854
|
-
@last_error = e
|
855
|
-
@sqlstate = e.sqlstate
|
856
|
-
raise
|
857
|
-
end
|
858
|
-
end
|
859
|
-
|
860
|
-
# Close prepared statement
|
861
|
-
# @return [void]
|
862
|
-
def close
|
863
|
-
ObjectSpace.undefine_finalizer(self)
|
864
|
-
@protocol.stmt_close_command @statement_id if @statement_id
|
865
|
-
@statement_id = nil
|
866
|
-
end
|
867
|
-
|
868
|
-
# @return [Array] current record data
|
869
|
-
def fetch
|
870
|
-
@result.fetch
|
871
|
-
end
|
872
|
-
|
873
|
-
# Return data of current record as Hash.
|
874
|
-
# The hash key is field name.
|
875
|
-
# @param [Boolean] with_table if true, hash key is "table_name.field_name".
|
876
|
-
# @return [Hash] record data
|
877
|
-
def fetch_hash(with_table=nil)
|
878
|
-
@result.fetch_hash with_table
|
879
|
-
end
|
880
|
-
|
881
|
-
# Iterate block with record.
|
882
|
-
# @yield [Array] record data
|
883
|
-
# @return [Mysql::Stmt] self
|
884
|
-
# @return [Enumerator] If block is not specified
|
885
|
-
def each(&block)
|
886
|
-
return enum_for(:each) unless block
|
887
|
-
while rec = fetch
|
888
|
-
block.call rec
|
889
|
-
end
|
890
|
-
self
|
891
|
-
end
|
892
|
-
|
893
|
-
# Iterate block with record as Hash.
|
894
|
-
# @param [Boolean] with_table if true, hash key is "table_name.field_name".
|
895
|
-
# @yield [Hash] record data
|
896
|
-
# @return [Mysql::Stmt] self
|
897
|
-
# @return [Enumerator] If block is not specified
|
898
|
-
def each_hash(with_table=nil, &block)
|
899
|
-
return enum_for(:each_hash, with_table) unless block
|
900
|
-
while rec = fetch_hash(with_table)
|
901
|
-
block.call rec
|
902
|
-
end
|
903
|
-
self
|
904
|
-
end
|
905
|
-
|
906
|
-
# @return [Integer] number of record
|
907
|
-
def size
|
908
|
-
@result.size
|
909
|
-
end
|
910
|
-
alias num_rows size
|
911
|
-
|
912
|
-
# Set record position
|
913
|
-
# @param [Integer] n record index
|
914
|
-
# @return [void]
|
915
|
-
def data_seek(n)
|
916
|
-
@result.data_seek(n)
|
917
|
-
end
|
918
|
-
|
919
|
-
# @return [Integer] current record position
|
920
|
-
def row_tell
|
921
|
-
@result.row_tell
|
922
|
-
end
|
923
|
-
|
924
|
-
# Set current position of record
|
925
|
-
# @param [Integer] n record index
|
926
|
-
# @return [Integer] previous position
|
927
|
-
def row_seek(n)
|
928
|
-
@result.row_seek(n)
|
929
|
-
end
|
930
|
-
|
931
|
-
# @return [Integer] number of columns for last query
|
932
|
-
def field_count
|
933
|
-
@fields.length
|
934
|
-
end
|
935
|
-
|
936
|
-
# ignore
|
937
|
-
# @return [void]
|
938
|
-
def free_result
|
939
|
-
end
|
534
|
+
private
|
940
535
|
|
941
|
-
|
942
|
-
|
943
|
-
# @return [Mysql::Result]
|
944
|
-
def result_metadata
|
945
|
-
return nil if @fields.empty?
|
946
|
-
Result.new @fields
|
947
|
-
end
|
536
|
+
def check_connection
|
537
|
+
raise ClientError, 'MySQL client is not connected' unless @protocol
|
948
538
|
end
|
949
539
|
end
|