ruby-mysql 3.0.1 → 4.0.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 +50 -0
- 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 +294 -190
- data/lib/mysql/result.rb +212 -0
- data/lib/mysql/stmt.rb +219 -0
- data/lib/mysql.rb +109 -523
- metadata +56 -14
- 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.0.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
|
@@ -51,8 +54,22 @@ class Mysql
|
|
51
54
|
# @return [String, nil]
|
52
55
|
# @!attribute [rw] ssl_mode
|
53
56
|
# @return [String, Integer] 1 or "disabled" / 2 or "preferred" / 3 or "required"
|
57
|
+
# @!attribute [rw] ssl_context_params
|
58
|
+
# @return [Hash] See OpenSSL::SSL::Context#set_params
|
54
59
|
# @!attribute [rw] get_server_public_key
|
55
60
|
# @return [Boolean]
|
61
|
+
# @!attribute [rw] connect_attrs
|
62
|
+
# @return [Hash]
|
63
|
+
# @!attribute [rw] yield_null_result
|
64
|
+
# @return [Boolean]
|
65
|
+
# @!attribute [rw] return_result
|
66
|
+
# @return [Boolean]
|
67
|
+
# @!attribute [rw] with_table
|
68
|
+
# @return [Boolean]
|
69
|
+
# @!attribute [rw] auto_store_result
|
70
|
+
# @return [Boolean]
|
71
|
+
# @!attribute [rw] cast
|
72
|
+
# @return [Boolean]
|
56
73
|
DEFAULT_OPTS = {
|
57
74
|
host: nil,
|
58
75
|
username: nil,
|
@@ -69,12 +86,25 @@ class Mysql
|
|
69
86
|
local_infile: nil,
|
70
87
|
load_data_local_dir: nil,
|
71
88
|
ssl_mode: SSL_MODE_PREFERRED,
|
89
|
+
ssl_context_params: {},
|
72
90
|
get_server_public_key: false,
|
91
|
+
connect_attrs: {},
|
92
|
+
yield_null_result: true,
|
93
|
+
return_result: true,
|
94
|
+
with_table: false,
|
95
|
+
auto_store_result: true,
|
96
|
+
cast: true,
|
73
97
|
}.freeze
|
74
98
|
|
75
99
|
# @private
|
76
100
|
attr_reader :protocol
|
77
101
|
|
102
|
+
# @return [Array<Mysql::Field>] fields of result set
|
103
|
+
attr_reader :fields
|
104
|
+
|
105
|
+
# @return [Mysql::Result]
|
106
|
+
attr_reader :result
|
107
|
+
|
78
108
|
class << self
|
79
109
|
# Make Mysql object and connect to mysqld.
|
80
110
|
# parameter is same as arguments for {#initialize}.
|
@@ -87,7 +117,7 @@ class Mysql
|
|
87
117
|
# @param [String] str
|
88
118
|
# @return [String]
|
89
119
|
def escape_string(str)
|
90
|
-
str.gsub(/[\0\n\r
|
120
|
+
str.gsub(/[\0\n\r\\'"\x1a]/) do |s|
|
91
121
|
case s
|
92
122
|
when "\0" then "\\0"
|
93
123
|
when "\n" then "\\n"
|
@@ -98,6 +128,10 @@ class Mysql
|
|
98
128
|
end
|
99
129
|
end
|
100
130
|
alias quote escape_string
|
131
|
+
|
132
|
+
def default_options
|
133
|
+
@default_options ||= DEFAULT_OPTS.dup
|
134
|
+
end
|
101
135
|
end
|
102
136
|
|
103
137
|
# @overload initialize(uri, **opts)
|
@@ -135,15 +169,17 @@ class Mysql
|
|
135
169
|
# @option opts :local_infile [Boolean]
|
136
170
|
# @option opts :load_data_local_dir [String]
|
137
171
|
# @option opts :ssl_mode [Integer]
|
172
|
+
# @option opts :ssl_context_params [Hash<Symbol, String>]
|
138
173
|
# @option opts :get_server_public_key [Boolean]
|
174
|
+
# @option opts :connect_attrs [Hash]
|
139
175
|
def initialize(*args, **opts)
|
140
176
|
@fields = nil
|
177
|
+
@result = nil
|
141
178
|
@protocol = nil
|
142
179
|
@sqlstate = "00000"
|
143
180
|
@host_info = nil
|
144
181
|
@last_error = nil
|
145
|
-
@
|
146
|
-
@opts = DEFAULT_OPTS.dup
|
182
|
+
@opts = Mysql.default_options.dup
|
147
183
|
parse_args(args, opts)
|
148
184
|
end
|
149
185
|
|
@@ -164,23 +200,23 @@ class Mysql
|
|
164
200
|
end
|
165
201
|
|
166
202
|
def parse_args(args, opts)
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
203
|
+
unless args.empty?
|
204
|
+
case args[0]
|
205
|
+
when URI
|
206
|
+
uri = args[0]
|
207
|
+
when /\Amysql:\/\//
|
208
|
+
uri = URI.parse(args[0])
|
209
|
+
when String, nil
|
210
|
+
@opts[:host], user, passwd, dbname, port, socket, flags = *args
|
211
|
+
@opts[:username] = user if user
|
212
|
+
@opts[:password] = passwd if passwd
|
213
|
+
@opts[:database] = dbname if dbname
|
214
|
+
@opts[:port] = port if port
|
215
|
+
@opts[:socket] = socket if socket
|
216
|
+
@opts[:flags] = flags if flags
|
217
|
+
when Hash
|
218
|
+
# skip
|
219
|
+
end
|
184
220
|
end
|
185
221
|
if uri
|
186
222
|
host = uri.hostname.to_s
|
@@ -252,7 +288,6 @@ class Mysql
|
|
252
288
|
query "SET NAMES #{charset.name}"
|
253
289
|
end
|
254
290
|
@opts[:charset] = charset
|
255
|
-
cs
|
256
291
|
end
|
257
292
|
|
258
293
|
# @return [String] charset name
|
@@ -267,7 +302,7 @@ class Mysql
|
|
267
302
|
|
268
303
|
# @return [String] last error message
|
269
304
|
def error
|
270
|
-
@last_error
|
305
|
+
@last_error&.error
|
271
306
|
end
|
272
307
|
|
273
308
|
# @return [String] sqlstate for last error
|
@@ -293,13 +328,12 @@ class Mysql
|
|
293
328
|
|
294
329
|
# @return [Integer] server version
|
295
330
|
def server_version
|
296
|
-
|
297
|
-
@protocol.server_version
|
331
|
+
@protocol&.server_version
|
298
332
|
end
|
299
333
|
|
300
334
|
# @return [String] information for last query
|
301
335
|
def info
|
302
|
-
@protocol
|
336
|
+
@protocol&.message
|
303
337
|
end
|
304
338
|
|
305
339
|
# @return [Integer] number of affected records by insert/update/delete.
|
@@ -327,34 +361,33 @@ class Mysql
|
|
327
361
|
end
|
328
362
|
|
329
363
|
# Execute query string.
|
330
|
-
# @
|
331
|
-
#
|
332
|
-
#
|
333
|
-
#
|
334
|
-
# @
|
335
|
-
#
|
336
|
-
# @yield [Mysql::Result] evaluated per query.
|
337
|
-
# @return [self]
|
364
|
+
# @param str [String] Query.
|
365
|
+
# @param return_result [Boolean]
|
366
|
+
# @param yield_null_result [Boolean]
|
367
|
+
# @return [Mysql::Result] if return_result is true and the query returns result set.
|
368
|
+
# @return [nil] if return_result is true and the query does not return result set.
|
369
|
+
# @return [self] if return_result is false or block is specified.
|
338
370
|
# @example
|
339
371
|
# my.query("select 1,NULL,'abc'").fetch # => [1, nil, "abc"]
|
340
372
|
# my.query("select 1,NULL,'abc'"){|res| res.fetch}
|
341
|
-
def query(str, &block)
|
373
|
+
def query(str, **opts, &block)
|
374
|
+
opts = @opts.merge(opts)
|
342
375
|
check_connection
|
343
376
|
@fields = nil
|
344
377
|
begin
|
345
|
-
|
346
|
-
if nfields
|
347
|
-
@fields = @protocol.retr_fields nfields
|
348
|
-
@result_exist = true
|
349
|
-
end
|
378
|
+
@protocol.query_command str
|
350
379
|
if block
|
351
380
|
while true
|
352
|
-
|
353
|
-
|
381
|
+
@protocol.get_result
|
382
|
+
res = store_result(**opts)
|
383
|
+
block.call res if res || opts[:yield_null_result]
|
384
|
+
break unless more_results?
|
354
385
|
end
|
355
386
|
return self
|
356
387
|
end
|
357
|
-
|
388
|
+
@protocol.get_result
|
389
|
+
return self unless opts[:return_result]
|
390
|
+
return store_result(**opts)
|
358
391
|
rescue ServerError => e
|
359
392
|
@last_error = e
|
360
393
|
@sqlstate = e.sqlstate
|
@@ -364,12 +397,12 @@ class Mysql
|
|
364
397
|
|
365
398
|
# Get all data for last query.
|
366
399
|
# @return [Mysql::Result]
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
400
|
+
# @return [nil] if no results
|
401
|
+
def store_result(**opts)
|
402
|
+
return nil if @protocol.field_count.nil? || @protocol.field_count == 0
|
403
|
+
@fields = @protocol.retr_fields
|
404
|
+
opts = @opts.merge(opts)
|
405
|
+
@result = Result.new(@fields, @protocol, **opts)
|
373
406
|
end
|
374
407
|
|
375
408
|
# @return [Integer] Thread ID
|
@@ -388,30 +421,29 @@ class Mysql
|
|
388
421
|
end
|
389
422
|
|
390
423
|
# @return [Boolean] true if multiple queries are specified and unexecuted queries exists.
|
391
|
-
def more_results
|
392
|
-
@protocol.
|
424
|
+
def more_results?
|
425
|
+
@protocol.more_results?
|
393
426
|
end
|
394
|
-
alias more_results? more_results
|
395
427
|
|
396
428
|
# execute next query if multiple queries are specified.
|
397
|
-
# @return [
|
398
|
-
|
399
|
-
|
400
|
-
|
429
|
+
# @return [Mysql::Result] result set of query if return_result is true.
|
430
|
+
# @return [true] if return_result is false and result exists.
|
431
|
+
# @return [nil] query returns no results.
|
432
|
+
def next_result(**opts)
|
433
|
+
return nil unless more_results?
|
434
|
+
opts = @opts.merge(opts)
|
435
|
+
@protocol.get_result
|
401
436
|
@fields = nil
|
402
|
-
|
403
|
-
|
404
|
-
@fields = @protocol.retr_fields nfields
|
405
|
-
@result_exist = true
|
406
|
-
end
|
407
|
-
return true
|
437
|
+
return store_result(**opts) if opts[:return_result]
|
438
|
+
true
|
408
439
|
end
|
409
440
|
|
410
441
|
# Parse prepared-statement.
|
411
442
|
# @param [String] str query string
|
412
443
|
# @return [Mysql::Stmt] Prepared-statement object
|
413
|
-
def prepare(str)
|
414
|
-
|
444
|
+
def prepare(str, **opts)
|
445
|
+
opts = @opts.merge(opts)
|
446
|
+
st = Stmt.new(@protocol, **opts)
|
415
447
|
st.prepare str
|
416
448
|
st
|
417
449
|
end
|
@@ -419,8 +451,9 @@ class Mysql
|
|
419
451
|
# @private
|
420
452
|
# Make empty prepared-statement object.
|
421
453
|
# @return [Mysql::Stmt] If block is not specified.
|
422
|
-
def stmt
|
423
|
-
|
454
|
+
def stmt(**opts)
|
455
|
+
opts = @opts.merge(opts)
|
456
|
+
Stmt.new(@protocol, **opts)
|
424
457
|
end
|
425
458
|
|
426
459
|
# Check whether the connection is available.
|
@@ -488,462 +521,15 @@ class Mysql
|
|
488
521
|
self
|
489
522
|
end
|
490
523
|
|
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
|
524
|
+
# session track
|
525
|
+
# @return [Hash]
|
526
|
+
def session_track
|
527
|
+
@protocol.session_track
|
776
528
|
end
|
777
529
|
|
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
|
530
|
+
private
|
940
531
|
|
941
|
-
|
942
|
-
|
943
|
-
# @return [Mysql::Result]
|
944
|
-
def result_metadata
|
945
|
-
return nil if @fields.empty?
|
946
|
-
Result.new @fields
|
947
|
-
end
|
532
|
+
def check_connection
|
533
|
+
raise ClientError, 'MySQL client is not connected' unless @protocol
|
948
534
|
end
|
949
535
|
end
|