mysql-pr 2.9.11 → 3.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.
data/lib/mysql-pr.rb CHANGED
@@ -1,47 +1,49 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (C) 2008-2012 TOMITA Masahiro
2
4
  # mailto:tommy@tmtm.org
3
5
 
4
6
  # MySQL connection class.
5
7
  # @example
6
- # my = Mysql.connect('hostname', 'user', 'password', 'dbname')
8
+ # my = MysqlPR.connect('hostname', 'user', 'password', 'dbname')
7
9
  # res = my.query 'select col1,col2 from tbl where id=123'
8
10
  # res.each do |c1, c2|
9
11
  # p c1, c2
10
12
  # end
11
13
  class MysqlPR
12
-
14
+ require "mysql-pr/version"
13
15
  require "mysql-pr/constants"
14
16
  require "mysql-pr/error"
15
17
  require "mysql-pr/charset"
16
18
  require "mysql-pr/protocol"
17
- require "mysql-pr/packet.rb"
18
-
19
- VERSION = 20910 # Version number of this library
20
- MYSQL_UNIX_PORT = "/tmp/mysql.sock" # UNIX domain socket filename
21
- MYSQL_TCP_PORT = 3306 # TCP socket port number
19
+ require "mysql-pr/packet"
20
+
21
+ MYSQL_UNIX_PORT = "/tmp/mysql.sock"
22
+ MYSQL_TCP_PORT = 3306
22
23
 
23
24
  # @return [MysqlPR::Charset] character set of MySQL connection
24
25
  attr_reader :charset
25
- # @private
26
+
27
+ # @return [MysqlPR::Protocol] protocol handler
26
28
  attr_reader :protocol
27
29
 
28
- # @return [Boolean] if true, {#query} return {MysqlPR::Result}.
30
+ # @return [Boolean] if true, {#query} returns {MysqlPR::Result}
29
31
  attr_accessor :query_with_result
30
32
 
31
33
  class << self
32
- # Make Mysql object without connecting.
33
- # @return [Mysql]
34
+ # Make MysqlPR object without connecting.
35
+ # @return [MysqlPR]
34
36
  def init
35
- my = self.allocate
36
- my.instance_eval{initialize}
37
+ my = allocate
38
+ my.send(:initialize)
37
39
  my
38
40
  end
39
41
 
40
- # Make Mysql object and connect to mysqld.
41
- # @param args same as arguments for {#connect}.
42
- # @return [Mysql]
42
+ # Make MysqlPR object and connect to mysqld.
43
+ # @param args same as arguments for {#connect}
44
+ # @return [MysqlPR]
43
45
  def new(*args)
44
- my = self.init
46
+ my = init
45
47
  my.connect(*args)
46
48
  end
47
49
 
@@ -64,13 +66,13 @@ class MysqlPR
64
66
  end
65
67
  alias quote escape_string
66
68
 
67
- # @return [String] client version. This value is dummy for MySQL/Ruby compatibility.
69
+ # @return [String] client version (dummy for MySQL/Ruby compatibility)
68
70
  def client_info
69
71
  "5.0.0"
70
72
  end
71
73
  alias get_client_info client_info
72
74
 
73
- # @return [Integer] client version. This value is dummy for MySQL/Ruby compatibility.
75
+ # @return [Integer] client version (dummy for MySQL/Ruby compatibility)
74
76
  def client_version
75
77
  50000
76
78
  end
@@ -91,49 +93,50 @@ class MysqlPR
91
93
  @last_error = nil
92
94
  @result_exist = false
93
95
  @local_infile = nil
96
+ @ssl_options = nil
94
97
  end
95
98
 
96
99
  # Connect to mysqld.
97
- # @param [String / nil] host hostname mysqld running
98
- # @param [String / nil] user username to connect to mysqld
99
- # @param [String / nil] passwd password to connect to mysqld
100
- # @param [String / nil] db initial database name
101
- # @param [Integer / nil] port port number (used if host is not 'localhost' or nil)
102
- # @param [String / nil] socket socket file name (used if host is 'localhost' or nil)
103
- # @param [Integer / nil] flag connection flag. MysqlPR::CLIENT_* ORed
104
- # @return self
105
- def connect(host=nil, user=nil, passwd=nil, db=nil, port=nil, socket=nil, flag=0)
106
- if flag & CLIENT_COMPRESS != 0
107
- warn 'unsupported flag: CLIENT_COMPRESS'
100
+ # @param [String, nil] host hostname mysqld running
101
+ # @param [String, nil] user username to connect to mysqld
102
+ # @param [String, nil] passwd password to connect to mysqld
103
+ # @param [String, nil] db initial database name
104
+ # @param [Integer, nil] port port number (used if host is not 'localhost' or nil)
105
+ # @param [String, nil] socket socket file name (used if host is 'localhost' or nil)
106
+ # @param [Integer] flag connection flag. MysqlPR::CLIENT_* ORed
107
+ # @return [MysqlPR] self
108
+ def connect(host = nil, user = nil, passwd = nil, db = nil, port = nil, socket = nil, flag = 0)
109
+ if (flag & CLIENT_COMPRESS) != 0
110
+ warn "unsupported flag: CLIENT_COMPRESS"
108
111
  flag &= ~CLIENT_COMPRESS
109
112
  end
110
- @protocol = Protocol.new host, port, socket, @connect_timeout, @read_timeout, @write_timeout
111
- @protocol.authenticate user, passwd, db, (@local_infile ? CLIENT_LOCAL_FILES : 0) | flag, @charset
113
+ @protocol = Protocol.new(host, port, socket, @connect_timeout, @read_timeout, @write_timeout, @ssl_options)
114
+ @protocol.authenticate(user, passwd, db, (@local_infile ? CLIENT_LOCAL_FILES : 0) | flag, @charset)
112
115
  @charset ||= @protocol.charset
113
- @host_info = (host.nil? || host == "localhost") ? 'Localhost via UNIX socket' : "#{host} via TCP/IP"
114
- query @init_command if @init_command
115
- return self
116
+ @host_info = (host.nil? || host == "localhost") ? "Localhost via UNIX socket" : "#{host} via TCP/IP"
117
+ query(@init_command) if @init_command
118
+ self
116
119
  end
117
120
  alias real_connect connect
118
121
 
119
122
  # Disconnect from mysql.
120
- # @return [Mysql] self
123
+ # @return [MysqlPR] self
121
124
  def close
122
125
  if @protocol
123
126
  @protocol.quit_command
124
127
  @protocol = nil
125
128
  end
126
- return self
129
+ self
127
130
  end
128
131
 
129
132
  # Disconnect from mysql without QUIT packet.
130
- # @return [Mysql] self
133
+ # @return [MysqlPR] self
131
134
  def close!
132
135
  if @protocol
133
136
  @protocol.close
134
137
  @protocol = nil
135
138
  end
136
- return self
139
+ self
137
140
  end
138
141
 
139
142
  # Set option for connection.
@@ -142,54 +145,82 @@ class MysqlPR
142
145
  # MysqlPR::INIT_COMMAND, MysqlPR::OPT_CONNECT_TIMEOUT, MysqlPR::OPT_READ_TIMEOUT,
143
146
  # MysqlPR::OPT_WRITE_TIMEOUT, MysqlPR::SET_CHARSET_NAME
144
147
  # @param [Integer] opt option
145
- # @param [Integer] value option value that is depend on opt
146
- # @return [Mysql] self
147
- def options(opt, value=nil)
148
+ # @param [Object] value option value that depends on opt
149
+ # @return [MysqlPR] self
150
+ def options(opt, value = nil)
148
151
  case opt
149
152
  when MysqlPR::INIT_COMMAND
150
153
  @init_command = value.to_s
151
- # when MysqlPR::OPT_COMPRESS
152
154
  when MysqlPR::OPT_CONNECT_TIMEOUT
153
155
  @connect_timeout = value
154
- # when MysqlPR::GUESS_CONNECTION
155
156
  when MysqlPR::OPT_LOCAL_INFILE
156
157
  @local_infile = value
157
- # when MysqlPR::OPT_NAMED_PIPE
158
- # when MysqlPR::OPT_PROTOCOL
159
158
  when MysqlPR::OPT_READ_TIMEOUT
160
159
  @read_timeout = value.to_i
161
- # when MysqlPR::OPT_RECONNECT
162
- # when MysqlPR::SET_CLIENT_IP
163
- # when MysqlPR::OPT_SSL_VERIFY_SERVER_CERT
164
- # when MysqlPR::OPT_USE_EMBEDDED_CONNECTION
165
- # when MysqlPR::OPT_USE_REMOTE_CONNECTION
166
160
  when MysqlPR::OPT_WRITE_TIMEOUT
167
161
  @write_timeout = value.to_i
168
- # when MysqlPR::READ_DEFAULT_FILE
169
- # when MysqlPR::READ_DEFAULT_GROUP
170
- # when MysqlPR::REPORT_DATA_TRUNCATION
171
- # when MysqlPR::SECURE_AUTH
172
- # when MysqlPR::SET_CHARSET_DIR
173
162
  when MysqlPR::SET_CHARSET_NAME
174
- @charset = Charset.by_name value.to_s
175
- # when MysqlPR::SHARED_MEMORY_BASE_NAME
163
+ @charset = Charset.by_name(value.to_s)
176
164
  else
177
165
  warn "option not implemented: #{opt}"
178
166
  end
179
167
  self
180
168
  end
181
169
 
170
+ # Configure SSL options for the connection.
171
+ # Must be called before connect.
172
+ # @param [String, nil] key path to client private key file
173
+ # @param [String, nil] cert path to client certificate file
174
+ # @param [String, nil] ca path to CA certificate file
175
+ # @param [String, nil] ca_path path to directory with CA certificates
176
+ # @param [String, nil] cipher list of allowed ciphers (not currently used)
177
+ # @return [MysqlPR] self
178
+ def ssl_set(key = nil, cert = nil, ca = nil, ca_path = nil, cipher = nil)
179
+ @ssl_options = {
180
+ key: key,
181
+ cert: cert,
182
+ ca: ca,
183
+ ca_path: ca_path,
184
+ cipher: cipher,
185
+ verify: ca || ca_path ? true : false
186
+ }
187
+ self
188
+ end
189
+
190
+ # Configure SSL with additional options.
191
+ # Must be called before connect.
192
+ # @param [Hash] options SSL configuration options
193
+ # @option options [String] :key path to client private key file
194
+ # @option options [String] :cert path to client certificate file
195
+ # @option options [String] :ca path to CA certificate file
196
+ # @option options [String] :ca_path path to directory with CA certificates
197
+ # @option options [Boolean] :verify whether to verify server certificate (default: true if ca provided)
198
+ # @option options [Boolean] :required raise error if server doesn't support SSL
199
+ # @option options [String] :hostname hostname for SNI
200
+ # @option options [Symbol] :min_version minimum TLS version (:TLS1_2, :TLS1_3, etc.)
201
+ # @return [MysqlPR] self
202
+ def ssl_options=(options)
203
+ @ssl_options = options
204
+ self
205
+ end
206
+
207
+ # Check if SSL is enabled for the current connection.
208
+ # @return [Boolean] true if SSL is enabled
209
+ def ssl_enabled?
210
+ @protocol&.ssl_enabled? || false
211
+ end
212
+
213
+ # Get the SSL cipher being used.
214
+ # @return [Array, nil] cipher info if SSL is enabled, nil otherwise
215
+ def ssl_cipher
216
+ @protocol&.ssl_cipher
217
+ end
218
+
182
219
  # Escape special character in MySQL.
183
- #
184
- # In Ruby 1.8, this is not safe for multibyte charset such as 'SJIS'.
185
- # You should use place-holder in prepared-statement.
186
220
  # @param [String] str
187
- # return [String]
221
+ # @return [String]
188
222
  def escape_string(str)
189
- if not defined? Encoding and @charset.unsafe
190
- raise ClientError, 'Mysql#escape_string is called for unsafe multibyte charset'
191
- end
192
- self.class.escape_string str
223
+ self.class.escape_string(str)
193
224
  end
194
225
  alias quote escape_string
195
226
 
@@ -206,12 +237,12 @@ class MysqlPR
206
237
  alias get_client_version client_version
207
238
 
208
239
  # Set charset of MySQL connection.
209
- # @param [String / MysqlPR::Charset] cs
240
+ # @param [String, MysqlPR::Charset] cs
210
241
  def charset=(cs)
211
242
  charset = cs.is_a?(Charset) ? cs : Charset.by_name(cs)
212
243
  if @protocol
213
244
  @protocol.charset = charset
214
- query "SET NAMES #{charset.name}"
245
+ query("SET NAMES #{charset.name}")
215
246
  end
216
247
  @charset = charset
217
248
  cs
@@ -229,7 +260,7 @@ class MysqlPR
229
260
 
230
261
  # @return [String] last error message
231
262
  def error
232
- @last_error && @last_error.error
263
+ @last_error&.error
233
264
  end
234
265
 
235
266
  # @return [String] sqlstate for last error
@@ -270,10 +301,10 @@ class MysqlPR
270
301
 
271
302
  # @return [String] information for last query
272
303
  def info
273
- @protocol && @protocol.message
304
+ @protocol&.message
274
305
  end
275
306
 
276
- # @return [Integer] number of affected records by insert/update/delete.
307
+ # @return [Integer] number of affected records by insert/update/delete
277
308
  def affected_rows
278
309
  @protocol ? @protocol.affected_rows : 0
279
310
  end
@@ -290,41 +321,41 @@ class MysqlPR
290
321
 
291
322
  # Kill query.
292
323
  # @param [Integer] pid thread id
293
- # @return [Mysql] self
324
+ # @return [MysqlPR] self
294
325
  def kill(pid)
295
326
  check_connection
296
- @protocol.kill_command pid
327
+ @protocol.kill_command(pid)
297
328
  self
298
329
  end
299
330
 
300
- # database list.
301
- # @param [String] db database name that may contain wild card.
331
+ # Database list.
332
+ # @param [String] db database name that may contain wild card
302
333
  # @return [Array<String>] database list
303
- def list_dbs(db=nil)
304
- db &&= db.gsub(/[\\\']/){"\\#{$&}"}
334
+ def list_dbs(db = nil)
335
+ db &&= db.gsub(/[\\\']/) { "\\#{$&}" }
305
336
  query(db ? "show databases like '#{db}'" : "show databases").map(&:first)
306
337
  end
307
338
 
308
339
  # Execute query string.
309
- # @param [String] str Query.
310
- # @yield [MysqlPR::Result] evaluated per query.
311
- # @return [MysqlPR::Result] If {#query_with_result} is true and result set exist.
312
- # @return [nil] If {#query_with_result} is true and the query does not return result set.
313
- # @return [Mysql] If {#query_with_result} is false or block is specified
340
+ # @param [String] str Query
341
+ # @yield [MysqlPR::Result] evaluated per query
342
+ # @return [MysqlPR::Result] If {#query_with_result} is true and result set exist
343
+ # @return [nil] If {#query_with_result} is true and the query does not return result set
344
+ # @return [MysqlPR] If {#query_with_result} is false or block is specified
314
345
  # @example
315
346
  # my.query("select 1,NULL,'abc'").fetch # => [1, nil, "abc"]
316
347
  def query(str, &block)
317
348
  check_connection
318
349
  @fields = nil
319
350
  begin
320
- nfields = @protocol.query_command str
351
+ nfields = @protocol.query_command(str)
321
352
  if nfields
322
- @fields = @protocol.retr_fields nfields
353
+ @fields = @protocol.retr_fields(nfields)
323
354
  @result_exist = true
324
355
  end
325
356
  if block
326
- while true
327
- block.call store_result if @fields
357
+ loop do
358
+ block.call(store_result) if @fields
328
359
  break unless next_result
329
360
  end
330
361
  return self
@@ -346,8 +377,9 @@ class MysqlPR
346
377
  # @return [MysqlPR::Result]
347
378
  def store_result
348
379
  check_connection
349
- raise ClientError, 'invalid usage' unless @result_exist
350
- res = Result.new @fields, @protocol
380
+ raise ClientError, "invalid usage" unless @result_exist
381
+
382
+ res = Result.new(@fields, @protocol)
351
383
  @result_exist = false
352
384
  res
353
385
  end
@@ -366,59 +398,59 @@ class MysqlPR
366
398
 
367
399
  # Set server option.
368
400
  # @param [Integer] opt {MysqlPR::OPTION_MULTI_STATEMENTS_ON} or {MysqlPR::OPTION_MULTI_STATEMENTS_OFF}
369
- # @return [Mysql] self
401
+ # @return [MysqlPR] self
370
402
  def set_server_option(opt)
371
403
  check_connection
372
- @protocol.set_option_command opt
404
+ @protocol.set_option_command(opt)
373
405
  self
374
406
  end
375
407
 
376
- # @return [Boolean] true if multiple queries are specified and unexecuted queries exists.
408
+ # @return [Boolean] true if multiple queries are specified and unexecuted queries exist
377
409
  def more_results
378
- @protocol.server_status & SERVER_MORE_RESULTS_EXISTS != 0
410
+ (@protocol.server_status & SERVER_MORE_RESULTS_EXISTS) != 0
379
411
  end
380
412
  alias more_results? more_results
381
413
 
382
- # execute next query if multiple queries are specified.
383
- # @return [Boolean] true if next query exists.
414
+ # Execute next query if multiple queries are specified.
415
+ # @return [Boolean] true if next query exists
384
416
  def next_result
385
417
  return false unless more_results
418
+
386
419
  check_connection
387
420
  @fields = nil
388
421
  nfields = @protocol.get_result
389
422
  if nfields
390
- @fields = @protocol.retr_fields nfields
423
+ @fields = @protocol.retr_fields(nfields)
391
424
  @result_exist = true
392
425
  end
393
- return true
426
+ true
394
427
  end
395
428
 
396
429
  # Parse prepared-statement.
397
430
  # @param [String] str query string
398
431
  # @return [MysqlPR::Stmt] Prepared-statement object
399
432
  def prepare(str)
400
- st = Stmt.new @protocol, @charset
401
- st.prepare str
433
+ st = Stmt.new(@protocol, @charset)
434
+ st.prepare(str)
402
435
  st
403
436
  end
404
437
 
405
- # @private
406
438
  # Make empty prepared-statement object.
407
- # @return [MysqlPR::Stmt] If block is not specified.
439
+ # @return [MysqlPR::Stmt]
408
440
  def stmt_init
409
- Stmt.new @protocol, @charset
441
+ Stmt.new(@protocol, @charset)
410
442
  end
411
443
 
412
444
  # Returns MysqlPR::Result object that is empty.
413
445
  # Use fetch_fields to get list of fields.
414
- # @param [String] table table name.
415
- # @param [String] field field name that may contain wild card.
446
+ # @param [String] table table name
447
+ # @param [String] field field name that may contain wild card
416
448
  # @return [MysqlPR::Result]
417
- def list_fields(table, field=nil)
449
+ def list_fields(table, field = nil)
418
450
  check_connection
419
451
  begin
420
- fields = @protocol.field_list_command table, field
421
- return Result.new fields
452
+ fields = @protocol.field_list_command(table, field)
453
+ Result.new(fields)
422
454
  rescue ServerError => e
423
455
  @last_error = e
424
456
  @sqlstate = e.sqlstate
@@ -434,16 +466,15 @@ class MysqlPR
434
466
  store_result
435
467
  end
436
468
 
437
- # @note for Ruby 1.8: This is not multi-byte safe. Don't use for multi-byte charset such as cp932.
438
- # @param [String] table database name that may contain wild card.
439
- # @return [Array<String>] list of table name.
440
- def list_tables(table=nil)
441
- q = table ? "show tables like '#{quote table}'" : "show tables"
469
+ # @param [String] table database name that may contain wild card
470
+ # @return [Array<String>] list of table name
471
+ def list_tables(table = nil)
472
+ q = table ? "show tables like '#{quote(table)}'" : "show tables"
442
473
  query(q).map(&:first)
443
474
  end
444
475
 
445
- # Check whether the connection is available.
446
- # @return [Mysql] self
476
+ # Check whether the connection is available.
477
+ # @return [MysqlPR] self
447
478
  def ping
448
479
  check_connection
449
480
  @protocol.ping_command
@@ -451,69 +482,68 @@ class MysqlPR
451
482
  end
452
483
 
453
484
  # Flush tables or caches.
454
- # @param [Integer] op operation. Use MysqlPR::REFRESH_* value.
455
- # @return [Mysql] self
485
+ # @param [Integer] op operation. Use MysqlPR::REFRESH_* value
486
+ # @return [MysqlPR] self
456
487
  def refresh(op)
457
488
  check_connection
458
- @protocol.refresh_command op
489
+ @protocol.refresh_command(op)
459
490
  self
460
491
  end
461
492
 
462
493
  # Reload grant tables.
463
- # @return [Mysql] self
494
+ # @return [MysqlPR] self
464
495
  def reload
465
- refresh MysqlPR::REFRESH_GRANT
496
+ refresh(MysqlPR::REFRESH_GRANT)
466
497
  end
467
498
 
468
499
  # Select default database
469
- # @return [Mysql] self
500
+ # @return [MysqlPR] self
470
501
  def select_db(db)
471
- query "use #{db}"
502
+ query("use #{db}")
472
503
  self
473
504
  end
474
505
 
475
- # shutdown server.
476
- # @return [Mysql] self
477
- def shutdown(level=0)
506
+ # Shutdown server.
507
+ # @return [MysqlPR] self
508
+ def shutdown(level = 0)
478
509
  check_connection
479
- @protocol.shutdown_command level
510
+ @protocol.shutdown_command(level)
480
511
  self
481
512
  end
482
513
 
483
514
  # @return [String] statistics message
484
515
  def stat
485
- @protocol ? @protocol.statistics_command : 'MySQL server has gone away'
516
+ @protocol ? @protocol.statistics_command : "MySQL server has gone away"
486
517
  end
487
518
 
488
519
  # Commit transaction
489
- # @return [Mysql] self
520
+ # @return [MysqlPR] self
490
521
  def commit
491
- query 'commit'
522
+ query("commit")
492
523
  self
493
524
  end
494
525
 
495
526
  # Rollback transaction
496
- # @return [Mysql] self
527
+ # @return [MysqlPR] self
497
528
  def rollback
498
- query 'rollback'
529
+ query("rollback")
499
530
  self
500
531
  end
501
532
 
502
533
  # Set autocommit mode
503
534
  # @param [Boolean] flag
504
- # @return [Mysql] self
535
+ # @return [MysqlPR] self
505
536
  def autocommit(flag)
506
- query "set autocommit=#{flag ? 1 : 0}"
537
+ query("set autocommit=#{flag ? 1 : 0}")
507
538
  self
508
539
  end
509
540
 
510
541
  private
511
542
 
512
543
  def check_connection
513
- raise ClientError::ServerGoneError, 'The MySQL server has gone away' unless @protocol
544
+ raise ClientError::ServerGoneError, "The MySQL server has gone away" unless @protocol
514
545
  end
515
546
 
516
- # @!visibility public
517
547
  # Field class
518
548
  class Field
519
549
  # @return [String] database name
@@ -536,23 +566,34 @@ class MysqlPR
536
566
  attr_reader :flags
537
567
  # @return [Integer] number of decimals
538
568
  attr_reader :decimals
539
- # @return [String] defualt value
569
+ # @return [String] default value
540
570
  attr_reader :default
541
- alias :def :default
542
571
 
543
- # @private
572
+ alias def default
573
+
544
574
  attr_accessor :result
575
+ attr_writer :max_length
545
576
 
546
- # @attr [Protocol::FieldPacket] packet
577
+ # @param [Protocol::FieldPacket] packet
547
578
  def initialize(packet)
548
- @db, @table, @org_table, @name, @org_name, @charsetnr, @length, @type, @flags, @decimals, @default =
549
- packet.db, packet.table, packet.org_table, packet.name, packet.org_name, packet.charsetnr, packet.length, packet.type, packet.flags, packet.decimals, packet.default
550
- @flags |= NUM_FLAG if is_num_type?
579
+ @db = packet.db
580
+ @table = packet.table
581
+ @org_table = packet.org_table
582
+ @name = packet.name
583
+ @org_name = packet.org_name
584
+ @charsetnr = packet.charsetnr
585
+ @length = packet.length
586
+ @type = packet.type
587
+ @flags = packet.flags
588
+ @decimals = packet.decimals
589
+ @default = packet.default
590
+ @flags |= NUM_FLAG if num_type?
551
591
  @max_length = nil
592
+ @result = nil
552
593
  end
553
594
 
554
595
  # @return [Hash] field information
555
- def hash
596
+ def to_hash
556
597
  {
557
598
  "name" => @name,
558
599
  "table" => @table,
@@ -564,94 +605,92 @@ class MysqlPR
564
605
  "decimals" => @decimals
565
606
  }
566
607
  end
608
+ alias hash to_hash
567
609
 
568
- # @private
569
610
  def inspect
570
611
  "#<MysqlPR::Field:#{@name}>"
571
612
  end
572
613
 
573
- # @return [Boolean] true if numeric field.
614
+ # @return [Boolean] true if numeric field
574
615
  def is_num?
575
- @flags & NUM_FLAG != 0
616
+ (@flags & NUM_FLAG) != 0
576
617
  end
577
618
 
578
- # @return [Boolean] true if not null field.
619
+ # @return [Boolean] true if not null field
579
620
  def is_not_null?
580
- @flags & NOT_NULL_FLAG != 0
621
+ (@flags & NOT_NULL_FLAG) != 0
581
622
  end
582
623
 
583
- # @return [Boolean] true if primary key field.
624
+ # @return [Boolean] true if primary key field
584
625
  def is_pri_key?
585
- @flags & PRI_KEY_FLAG != 0
626
+ (@flags & PRI_KEY_FLAG) != 0
586
627
  end
587
628
 
588
629
  # @return [Integer] maximum width of the field for the result set
589
630
  def max_length
590
631
  return @max_length if @max_length
632
+
591
633
  @max_length = 0
592
- @result.calculate_field_max_length if @result
634
+ @result&.calculate_field_max_length
593
635
  @max_length
594
636
  end
595
637
 
596
- attr_writer :max_length
597
-
598
638
  private
599
639
 
600
- def is_num_type?
601
- [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))
640
+ def num_type?
641
+ [TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT,
642
+ TYPE_DOUBLE, TYPE_LONGLONG, TYPE_INT24].include?(@type) ||
643
+ (@type == TYPE_TIMESTAMP && (@length == 14 || @length == 8))
602
644
  end
603
-
604
645
  end
605
646
 
606
- # @!visibility public
607
- # Result set
647
+ # Result set base class
608
648
  class ResultBase
609
649
  include Enumerable
610
650
 
611
651
  # @return [Array<MysqlPR::Field>] field list
612
652
  attr_reader :fields
613
653
 
614
- # @param [Array of MysqlPR::Field] fields
654
+ # @param [Array<MysqlPR::Field>] fields
615
655
  def initialize(fields)
616
656
  @fields = fields
617
- @field_index = 0 # index of field
618
- @records = [] # all records
619
- @index = 0 # index of record
657
+ @field_index = 0
658
+ @records = []
659
+ @index = 0
620
660
  @fieldname_with_table = nil
621
661
  @fetched_record = nil
622
662
  end
623
663
 
624
- # ignore
625
664
  # @return [void]
626
- def free
627
- end
665
+ def free; end
628
666
 
629
- # @return [Integer] number of record
667
+ # @return [Integer] number of records
630
668
  def size
631
669
  @records.size
632
670
  end
633
671
  alias num_rows size
634
672
 
635
- # @return [Array] current record data
673
+ # @return [Array, nil] current record data
636
674
  def fetch
637
675
  @fetched_record = nil
638
676
  return nil if @index >= @records.size
639
- @records[@index] = @records[@index].to_a unless @records[@index].is_a? Array
677
+
678
+ @records[@index] = @records[@index].to_a unless @records[@index].is_a?(Array)
640
679
  @fetched_record = @records[@index]
641
680
  @index += 1
642
- return @fetched_record
681
+ @fetched_record
643
682
  end
644
683
  alias fetch_row fetch
645
684
 
646
685
  # Return data of current record as Hash.
647
- # The hash key is field name.
648
- # @param [Boolean] with_table if true, hash key is "table_name.field_name".
649
- # @return [Hash] current record data
650
- def fetch_hash(with_table=nil)
686
+ # @param [Boolean] with_table if true, hash key is "table_name.field_name"
687
+ # @return [Hash, nil] current record data
688
+ def fetch_hash(with_table = nil)
651
689
  row = fetch
652
690
  return nil unless row
653
- if with_table and @fieldname_with_table.nil?
654
- @fieldname_with_table = @fields.map{|f| [f.table, f.name].join(".")}
691
+
692
+ if with_table && @fieldname_with_table.nil?
693
+ @fieldname_with_table = @fields.map { |f| "#{f.table}.#{f.name}" }
655
694
  end
656
695
  ret = {}
657
696
  @fields.each_index do |i|
@@ -663,30 +702,32 @@ class MysqlPR
663
702
 
664
703
  # Iterate block with record.
665
704
  # @yield [Array] record data
666
- # @return [self] self. If block is not specified, this returns Enumerator.
705
+ # @return [self, Enumerator]
667
706
  def each(&block)
668
707
  return enum_for(:each) unless block
669
- while rec = fetch
670
- block.call rec
708
+
709
+ while (rec = fetch)
710
+ block.call(rec)
671
711
  end
672
712
  self
673
713
  end
674
714
 
675
715
  # Iterate block with record as Hash.
676
- # @param [Boolean] with_table if true, hash key is "table_name.field_name".
716
+ # @param [Boolean] with_table if true, hash key is "table_name.field_name"
677
717
  # @yield [Hash] record data
678
- # @return [self] self. If block is not specified, this returns Enumerator.
679
- def each_hash(with_table=nil, &block)
718
+ # @return [self, Enumerator]
719
+ def each_hash(with_table = nil, &block)
680
720
  return enum_for(:each_hash, with_table) unless block
681
- while rec = fetch_hash(with_table)
682
- block.call rec
721
+
722
+ while (rec = fetch_hash(with_table))
723
+ block.call(rec)
683
724
  end
684
725
  self
685
726
  end
686
727
 
687
728
  # Set record position
688
729
  # @param [Integer] n record index
689
- # @return [self] self
730
+ # @return [self]
690
731
  def data_seek(n)
691
732
  @index = n
692
733
  self
@@ -707,27 +748,25 @@ class MysqlPR
707
748
  end
708
749
  end
709
750
 
710
- # @!visibility public
711
751
  # Result set for simple query
712
752
  class Result < ResultBase
713
- # @private
714
753
  # @param [Array<MysqlPR::Field>] fields
715
- # @param [MysqlPR::Protocol] protocol
716
- def initialize(fields, protocol=nil)
717
- super fields
754
+ # @param [MysqlPR::Protocol, nil] protocol
755
+ def initialize(fields, protocol = nil)
756
+ super(fields)
718
757
  return unless protocol
719
- @records = protocol.retr_all_records fields.size
720
- fields.each{|f| f.result = self} # for calculating max_field
758
+
759
+ @records = protocol.retr_all_records(fields.size)
760
+ fields.each { |f| f.result = self }
721
761
  end
722
762
 
723
- # @private
724
- # calculate max_length of all fields
763
+ # Calculate max_length of all fields
725
764
  def calculate_field_max_length
726
765
  max_length = Array.new(@fields.size, 0)
727
766
  @records.each_with_index do |rec, i|
728
- rec = @records[i] = rec.to_a if rec.is_a? RawRecord
729
- max_length.each_index do |i|
730
- max_length[i] = rec[i].length if rec[i] && rec[i].length > max_length[i]
767
+ rec = @records[i] = rec.to_a if rec.is_a?(RawRecord)
768
+ max_length.each_index do |j|
769
+ max_length[j] = rec[j].length if rec[j] && rec[j].length > max_length[j]
731
770
  end
732
771
  end
733
772
  max_length.each_with_index do |len, i|
@@ -735,9 +774,10 @@ class MysqlPR
735
774
  end
736
775
  end
737
776
 
738
- # @return [MysqlPR::Field] current field
777
+ # @return [MysqlPR::Field, nil] current field
739
778
  def fetch_field
740
779
  return nil if @field_index >= @fields.length
780
+
741
781
  ret = @fields[@field_index]
742
782
  @field_index += 1
743
783
  ret
@@ -761,7 +801,8 @@ class MysqlPR
761
801
  # @param [Integer] n field index
762
802
  # @return [MysqlPR::Field] field
763
803
  def fetch_field_direct(n)
764
- raise ClientError, "invalid argument: #{n}" if n < 0 or n >= @fields.length
804
+ raise ClientError, "invalid argument: #{n}" if n.negative? || n >= @fields.length
805
+
765
806
  @fields[n]
766
807
  end
767
808
 
@@ -770,10 +811,11 @@ class MysqlPR
770
811
  @fields
771
812
  end
772
813
 
773
- # @return [Array<Integer>] length of each fields
814
+ # @return [Array<Integer>, nil] length of each field
774
815
  def fetch_lengths
775
816
  return nil unless @fetched_record
776
- @fetched_record.map{|c|c.nil? ? 0 : c.length}
817
+
818
+ @fetched_record.map { |c| c.nil? ? 0 : c.length }
777
819
  end
778
820
 
779
821
  # @return [Integer] number of fields
@@ -782,20 +824,17 @@ class MysqlPR
782
824
  end
783
825
  end
784
826
 
785
- # @!visibility private
786
827
  # Result set for prepared statement
787
828
  class StatementResult < ResultBase
788
- # @private
789
829
  # @param [Array<MysqlPR::Field>] fields
790
830
  # @param [MysqlPR::Protocol] protocol
791
831
  # @param [MysqlPR::Charset] charset
792
832
  def initialize(fields, protocol, charset)
793
- super fields
794
- @records = protocol.stmt_retr_all_records @fields, charset
833
+ super(fields)
834
+ @records = protocol.stmt_retr_all_records(@fields, charset)
795
835
  end
796
836
  end
797
837
 
798
- # @!visibility public
799
838
  # Prepared statement
800
839
  # @!attribute [r] affected_rows
801
840
  # @return [Integer]
@@ -817,30 +856,33 @@ class MysqlPR
817
856
  attr_reader :affected_rows, :insert_id, :server_status, :warning_count
818
857
  attr_reader :param_count, :fields, :sqlstate
819
858
 
820
- # @private
821
859
  def self.finalizer(protocol, statement_id)
822
860
  proc do
823
- protocol.gc_stmt statement_id
861
+ protocol.gc_stmt(statement_id)
824
862
  end
825
863
  end
826
864
 
827
- # @private
828
865
  # @param [MysqlPR::Protocol] protocol
829
866
  # @param [MysqlPR::Charset] charset
830
867
  def initialize(protocol, charset)
831
868
  @protocol = protocol
832
869
  @charset = charset
833
870
  @statement_id = nil
834
- @affected_rows = @insert_id = @server_status = @warning_count = 0
871
+ @affected_rows = 0
872
+ @insert_id = 0
873
+ @server_status = 0
874
+ @warning_count = 0
835
875
  @sqlstate = "00000"
836
876
  @param_count = nil
837
877
  @bind_result = nil
878
+ @result = nil
879
+ @fields = nil
880
+ @last_error = nil
838
881
  end
839
882
 
840
- # @private
841
- # parse prepared-statement and return {MysqlPR::Stmt} object
883
+ # Parse prepared-statement and return {MysqlPR::Stmt} object
842
884
  # @param [String] str query string
843
- # @return self
885
+ # @return [MysqlPR::Stmt] self
844
886
  def prepare(str)
845
887
  close
846
888
  begin
@@ -861,18 +903,22 @@ class MysqlPR
861
903
  def execute(*values)
862
904
  raise ClientError, "not prepared" unless @param_count
863
905
  raise ClientError, "parameter count mismatch" if values.length != @param_count
864
- values = values.map{|v| @charset.convert v}
906
+
907
+ values = values.map { |v| @charset.convert(v) }
865
908
  begin
866
909
  @sqlstate = "00000"
867
- nfields = @protocol.stmt_execute_command @statement_id, values
910
+ nfields = @protocol.stmt_execute_command(@statement_id, values)
868
911
  if nfields
869
- @fields = @protocol.retr_fields nfields
870
- @result = StatementResult.new @fields, @protocol, @charset
912
+ @fields = @protocol.retr_fields(nfields)
913
+ @result = StatementResult.new(@fields, @protocol, @charset)
871
914
  else
872
- @affected_rows, @insert_id, @server_status, @warning_count, @info =
873
- @protocol.affected_rows, @protocol.insert_id, @protocol.server_status, @protocol.warning_count, @protocol.message
915
+ @affected_rows = @protocol.affected_rows
916
+ @insert_id = @protocol.insert_id
917
+ @server_status = @protocol.server_status
918
+ @warning_count = @protocol.warning_count
919
+ @info = @protocol.message
874
920
  end
875
- return self
921
+ self
876
922
  rescue ServerError => e
877
923
  @last_error = e
878
924
  @sqlstate = e.sqlstate
@@ -884,48 +930,26 @@ class MysqlPR
884
930
  # @return [void]
885
931
  def close
886
932
  ObjectSpace.undefine_finalizer(self)
887
- @protocol.stmt_close_command @statement_id if @statement_id
933
+ @protocol.stmt_close_command(@statement_id) if @statement_id
888
934
  @statement_id = nil
889
935
  end
890
936
 
891
- # @return [Array] current record data
937
+ # @return [Array, nil] current record data
892
938
  def fetch
893
939
  row = @result.fetch
894
940
  return row unless @bind_result
941
+
895
942
  row.zip(@bind_result).map do |col, type|
896
943
  if col.nil?
897
944
  nil
898
- elsif [Numeric, Integer, Fixnum].include? type
945
+ elsif [Numeric, Integer].include?(type)
899
946
  col.to_i
900
947
  elsif type == String
901
948
  col.to_s
902
949
  elsif type == Float && !col.is_a?(Float)
903
950
  col.to_i.to_f
904
951
  elsif type == MysqlPR::Time && !col.is_a?(MysqlPR::Time)
905
- if col.to_s =~ /\A\d+\z/
906
- i = col.to_s.to_i
907
- if i < 100000000
908
- y = i/10000
909
- m = i/100%100
910
- d = i%100
911
- h, mm, s = 0
912
- else
913
- y = i/10000000000
914
- m = i/100000000%100
915
- d = i/1000000%100
916
- h = i/10000%100
917
- mm= i/100%100
918
- s = i%100
919
- end
920
- if y < 70
921
- y += 2000
922
- elsif y < 100
923
- y += 1900
924
- end
925
- MysqlPR::Time.new(y, m, d, h, mm, s)
926
- else
927
- MysqlPR::Time.new
928
- end
952
+ parse_time_value(col)
929
953
  else
930
954
  col
931
955
  end
@@ -933,22 +957,24 @@ class MysqlPR
933
957
  end
934
958
 
935
959
  # Return data of current record as Hash.
936
- # The hash key is field name.
937
- # @param [Boolean] with_table if true, hash key is "table_name.field_name".
938
- # @return [Hash] record data
939
- def fetch_hash(with_table=nil)
940
- @result.fetch_hash with_table
960
+ # @param [Boolean] with_table if true, hash key is "table_name.field_name"
961
+ # @return [Hash, nil] record data
962
+ def fetch_hash(with_table = nil)
963
+ @result.fetch_hash(with_table)
941
964
  end
942
965
 
943
966
  # Set retrieve type of value
944
- # @param [Numeric / Fixnum / Integer / Float / String / MysqlPR::Time / nil] args value type
967
+ # @param [Class] args value type (Numeric, Integer, Float, String, MysqlPR::Time, or nil)
945
968
  # @return [MysqlPR::Stmt] self
946
969
  def bind_result(*args)
947
970
  if @fields.length != args.length
948
971
  raise ClientError, "bind_result: result value count(#{@fields.length}) != number of argument(#{args.length})"
949
972
  end
973
+
950
974
  args.each do |a|
951
- raise TypeError unless [Numeric, Fixnum, Integer, Float, String, MysqlPR::Time, nil].include? a
975
+ unless [Numeric, Integer, Float, String, MysqlPR::Time, nil].include?(a)
976
+ raise TypeError, "unsupported type: #{a}"
977
+ end
952
978
  end
953
979
  @bind_result = args
954
980
  self
@@ -956,30 +982,30 @@ class MysqlPR
956
982
 
957
983
  # Iterate block with record.
958
984
  # @yield [Array] record data
959
- # @return [MysqlPR::Stmt] self
960
- # @return [Enumerator] If block is not specified
985
+ # @return [MysqlPR::Stmt, Enumerator] self or Enumerator if block not specified
961
986
  def each(&block)
962
987
  return enum_for(:each) unless block
963
- while rec = fetch
964
- block.call rec
988
+
989
+ while (rec = fetch)
990
+ block.call(rec)
965
991
  end
966
992
  self
967
993
  end
968
994
 
969
995
  # Iterate block with record as Hash.
970
- # @param [Boolean] with_table if true, hash key is "table_name.field_name".
996
+ # @param [Boolean] with_table if true, hash key is "table_name.field_name"
971
997
  # @yield [Hash] record data
972
- # @return [MysqlPR::Stmt] self
973
- # @return [Enumerator] If block is not specified
974
- def each_hash(with_table=nil, &block)
998
+ # @return [MysqlPR::Stmt, Enumerator] self or Enumerator if block not specified
999
+ def each_hash(with_table = nil, &block)
975
1000
  return enum_for(:each_hash, with_table) unless block
976
- while rec = fetch_hash(with_table)
977
- block.call rec
1001
+
1002
+ while (rec = fetch_hash(with_table))
1003
+ block.call(rec)
978
1004
  end
979
1005
  self
980
1006
  end
981
1007
 
982
- # @return [Integer] number of record
1008
+ # @return [Integer] number of records
983
1009
  def size
984
1010
  @result.size
985
1011
  end
@@ -1009,21 +1035,44 @@ class MysqlPR
1009
1035
  @fields.length
1010
1036
  end
1011
1037
 
1012
- # ignore
1013
1038
  # @return [void]
1014
- def free_result
1015
- end
1039
+ def free_result; end
1016
1040
 
1017
1041
  # Returns MysqlPR::Result object that is empty.
1018
1042
  # Use fetch_fields to get list of fields.
1019
- # @return [MysqlPR::Result]
1043
+ # @return [MysqlPR::Result, nil]
1020
1044
  def result_metadata
1021
1045
  return nil if @fields.empty?
1022
- Result.new @fields
1046
+
1047
+ Result.new(@fields)
1048
+ end
1049
+
1050
+ private
1051
+
1052
+ def parse_time_value(col)
1053
+ return MysqlPR::Time.new unless col.to_s =~ /\A\d+\z/
1054
+
1055
+ i = col.to_s.to_i
1056
+ if i < 100_000_000
1057
+ y = i / 10_000
1058
+ m = (i / 100) % 100
1059
+ d = i % 100
1060
+ h = mm = s = 0
1061
+ else
1062
+ y = i / 10_000_000_000
1063
+ m = (i / 100_000_000) % 100
1064
+ d = (i / 1_000_000) % 100
1065
+ h = (i / 10_000) % 100
1066
+ mm = (i / 100) % 100
1067
+ s = i % 100
1068
+ end
1069
+ y += 2000 if y < 70
1070
+ y += 1900 if y >= 70 && y < 100
1071
+ MysqlPR::Time.new(y, m, d, h, mm, s)
1023
1072
  end
1024
1073
  end
1025
1074
 
1026
- # @!visibility public
1075
+ # MySQL Time class
1027
1076
  # @!attribute [rw] year
1028
1077
  # @return [Integer]
1029
1078
  # @!attribute [rw] month
@@ -1041,6 +1090,12 @@ class MysqlPR
1041
1090
  # @!attribute [rw] second_part
1042
1091
  # @return [Integer]
1043
1092
  class Time
1093
+ attr_accessor :year, :month, :day, :hour, :minute, :second, :neg, :second_part
1094
+
1095
+ alias mon month
1096
+ alias min minute
1097
+ alias sec second
1098
+
1044
1099
  # @param [Integer] year
1045
1100
  # @param [Integer] month
1046
1101
  # @param [Integer] day
@@ -1049,25 +1104,25 @@ class MysqlPR
1049
1104
  # @param [Integer] second
1050
1105
  # @param [Boolean] neg negative flag
1051
1106
  # @param [Integer] second_part
1052
- def initialize(year=0, month=0, day=0, hour=0, minute=0, second=0, neg=false, second_part=0)
1107
+ def initialize(year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, neg = false, second_part = 0)
1053
1108
  @date_flag = !(hour && minute && second)
1054
- @year, @month, @day, @hour, @minute, @second, @neg, @second_part =
1055
- year.to_i, month.to_i, day.to_i, hour.to_i, minute.to_i, second.to_i, neg, second_part.to_i
1109
+ @year = year.to_i
1110
+ @month = month.to_i
1111
+ @day = day.to_i
1112
+ @hour = hour.to_i
1113
+ @minute = minute.to_i
1114
+ @second = second.to_i
1115
+ @neg = neg
1116
+ @second_part = second_part.to_i
1056
1117
  end
1057
- attr_accessor :year, :month, :day, :hour, :minute, :second, :neg, :second_part
1058
- alias mon month
1059
- alias min minute
1060
- alias sec second
1061
1118
 
1062
- # @private
1063
1119
  def ==(other)
1064
1120
  other.is_a?(MysqlPR::Time) &&
1065
1121
  @year == other.year && @month == other.month && @day == other.day &&
1066
1122
  @hour == other.hour && @minute == other.minute && @second == other.second &&
1067
- @neg == neg && @second_part == other.second_part
1123
+ @neg == other.neg && @second_part == other.second_part
1068
1124
  end
1069
1125
 
1070
- # @private
1071
1126
  def eql?(other)
1072
1127
  self == other
1073
1128
  end
@@ -1075,25 +1130,22 @@ class MysqlPR
1075
1130
  # @return [String] "yyyy-mm-dd HH:MM:SS"
1076
1131
  def to_s
1077
1132
  if @date_flag
1078
- sprintf "%04d-%02d-%02d", year, mon, day
1079
- elsif year == 0 and mon == 0 and day == 0
1133
+ format("%04d-%02d-%02d", year, mon, day)
1134
+ elsif year.zero? && mon.zero? && day.zero?
1080
1135
  h = neg ? hour * -1 : hour
1081
- sprintf "%02d:%02d:%02d", h, min, sec
1136
+ format("%02d:%02d:%02d", h, min, sec)
1082
1137
  else
1083
- sprintf "%04d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec
1138
+ format("%04d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec)
1084
1139
  end
1085
1140
  end
1086
1141
 
1087
1142
  # @return [Integer] yyyymmddHHMMSS
1088
1143
  def to_i
1089
- sprintf("%04d%02d%02d%02d%02d%02d", year, mon, day, hour, min, sec).to_i
1144
+ format("%04d%02d%02d%02d%02d%02d", year, mon, day, hour, min, sec).to_i
1090
1145
  end
1091
1146
 
1092
- # @private
1093
1147
  def inspect
1094
- sprintf "#<#{self.class.name}:%04d-%02d-%02d %02d:%02d:%02d>", year, mon, day, hour, min, sec
1148
+ format("#<#{self.class.name}:%04d-%02d-%02d %02d:%02d:%02d>", year, mon, day, hour, min, sec)
1095
1149
  end
1096
-
1097
1150
  end
1098
-
1099
1151
  end