ruby-mysql 2.11.1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 178673e0e53a16263b882308afca1831d674e96057828c2115d34ec3669dc58e
4
- data.tar.gz: 23e9fc852a81f976f1d8130fbcd0c623dc234c8e741e60c0fd58094079fb02bd
3
+ metadata.gz: f95fb29b93c207b23bbc1378ce78b10ab297269f0cf24409116a69502cdf9ae8
4
+ data.tar.gz: 7979ccfd21194e81a4c98f90920b7dc3cdb1165201bdf7bc0d640d16fbe8ebea
5
5
  SHA512:
6
- metadata.gz: dc51341a75ae3373d2b375632781809abc68ac30fbee593761b67a304ef33b3d52538762dfd144f5ddad0b63416b68c0f9ce1f68ab89228f0f5b0e4f82a2332f
7
- data.tar.gz: 8ee78f4d06f1714b2c0cb83537b3efe643e968a212c64c71cd3e19dda422a4f9961a16578dab45d1c43f147a2551d7499447645576a6545698e00419c5cae7c9
6
+ metadata.gz: '068b6f6008f4b77dc6d34b660c8f06b73db2c06e63fc98d69d5455ceb6ab61c3924d158de4398d6b897cc2fa44d460a879b60355dbb16ac90f0af666f750467d'
7
+ data.tar.gz: 769105fbd5f16a4b8ab9c55c4a1243c962db3a238f05baa16d970cb2cbaadba0f0487c2daf194b53ce0bfa48639aac37cb79527da02518607a4a0a861cdd6caf
data/CHANGELOG.md ADDED
@@ -0,0 +1,50 @@
1
+ ## [3.0.0] - 2021-11-16
2
+
3
+ - `Mysql.new` no longer connect. use `Mysql.connect` or `Mysql#connect`.
4
+
5
+ - `Mysql.init` is removed. use `Mysql.new` instead.
6
+
7
+ - `Mysql.new`, `Mysql.conncet` and `Mysql#connect` takes URI object or URI string or Hash object.
8
+ example:
9
+ Mysql.connect('mysql://user:password@hostname:port/dbname?charset=ascii')
10
+ Mysql.connect('mysql://user:password@%2Ftmp%2Fmysql.sock/dbname?charset=ascii') # for UNIX socket
11
+ Mysql.connect('hostname', 'user', 'password', 'dbname')
12
+ Mysql.connect(host: 'hostname', username: 'user', password: 'password', database: 'dbname')
13
+
14
+ - `Mysql.options` is removed. use `Mysql#param = value` instead.
15
+ For example:
16
+ m = Mysql.init
17
+ m.options(Mysql::OPT_LOCAL_INFILE, true)
18
+ m.connect(host, user, passwd)
19
+ change to
20
+ m = Mysql.new
21
+ m.local_infile = true
22
+ m.connect(host, user, passwd)
23
+ or
24
+ m = Mysql.connect(host, user, passwd, local_infile: true)
25
+
26
+ - `Mysql::Time` is removed.
27
+ Instead, `Time` object is returned for the DATE, DATETIME, TIMESTAMP data,
28
+ and `Integer` object is returned for the TIME data.
29
+ If DATE, DATETIME, TIMESTAMP are invalid values for Time, nil is returned.
30
+
31
+ - meaningless methods are removed:
32
+ * `bind_result`
33
+ * `client_info`
34
+ * `client_version`
35
+ * `get_proto_info`
36
+ * `get_server_info`
37
+ * `get_server_version`
38
+ * `proto_info`
39
+ * `query_with_result`
40
+
41
+ - alias method are removed:
42
+ * `get_host_info`: use `host_info`
43
+ * `real_connect`: use `connect`
44
+ * `real_query`: use `query`
45
+
46
+ - methods corresponding to deprecated APIs in MySQL are removed:
47
+ * `list_dbs`: use `SHOW DATABASES`
48
+ * `list_fields`: use `SHOW COLUMNS`
49
+ * `list_processes`: use `SHOW PROCESSLIST`
50
+ * `list_tables`: use `SHOW TABLES`
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # ruby-mysql
2
+
3
+ ## Description
4
+
5
+ MySQL connector for Ruby.
6
+
7
+ ## Installation
8
+
9
+ ```
10
+ gem install ruby-mysql
11
+ ```
12
+
13
+ ## Synopsis
14
+
15
+ ```ruby
16
+ my = Mysql.connect('mysql://username:password@hostname:port/dbname?charset=utf8mb4')
17
+ my.query("select col1, col2 from tblname").each do |col1, col2|
18
+ p col1, col2
19
+ end
20
+ stmt = my.prepare('insert into tblname (col1,col2) values (?,?)')
21
+ stmt.execute 123, 'abc'
22
+ ```
23
+
24
+ ## Copyright
25
+
26
+ * Author: TOMITA Masahiro <tommy@tmtm.org>
27
+ * Copyright: Copyright 2008 TOMITA Masahiro
28
+ * License: MIT
@@ -15,12 +15,10 @@ class Mysql
15
15
  MAX_PACKET_LENGTH = 2**24-1
16
16
 
17
17
  # Convert netdata to Ruby value
18
- # === Argument
19
- # data :: [Packet] packet data
20
- # type :: [Integer] field type
21
- # unsigned :: [true or false] true if value is unsigned
22
- # === Return
23
- # Object :: converted value.
18
+ # @param data [Packet] packet data
19
+ # @param type [Integer] field type
20
+ # @param unsigned [true or false] true if value is unsigned
21
+ # @return [Object] converted value.
24
22
  def self.net2value(pkt, type, unsigned)
25
23
  case type
26
24
  when Field::TYPE_STRING, Field::TYPE_VAR_STRING, Field::TYPE_NEWDECIMAL, Field::TYPE_BLOB, Field::TYPE_JSON
@@ -45,17 +43,18 @@ class Mysql
45
43
  when Field::TYPE_DATE
46
44
  len = pkt.utiny
47
45
  y, m, d = pkt.read(len).unpack("vCC")
48
- t = Mysql::Time.new(y, m, d, nil, nil, nil)
46
+ t = Time.new(y, m, d) rescue nil
49
47
  return t
50
48
  when Field::TYPE_DATETIME, Field::TYPE_TIMESTAMP
51
49
  len = pkt.utiny
52
50
  y, m, d, h, mi, s, sp = pkt.read(len).unpack("vCCCCCV")
53
- return Mysql::Time.new(y, m, d, h, mi, s, false, sp)
51
+ return Time.new(y, m, d, h, mi, Rational((s.to_i*1000000+sp.to_i)/1000000)) rescue nil
54
52
  when Field::TYPE_TIME
55
53
  len = pkt.utiny
56
54
  sign, d, h, mi, s, sp = pkt.read(len).unpack("CVCCCV")
57
- h = d.to_i * 24 + h.to_i
58
- return Mysql::Time.new(0, 0, 0, h, mi, s, sign!=0, sp)
55
+ r = d.to_i*86400 + h.to_i*3600 + mi.to_i*60 + s.to_i + sp.to_f/1000000
56
+ r *= -1 if sign != 0
57
+ return r
59
58
  when Field::TYPE_YEAR
60
59
  return pkt.ushort
61
60
  when Field::TYPE_BIT
@@ -66,13 +65,10 @@ class Mysql
66
65
  end
67
66
 
68
67
  # convert Ruby value to netdata
69
- # === Argument
70
- # v :: [Object] Ruby value.
71
- # === Return
72
- # Integer :: type of column. Field::TYPE_*
73
- # String :: netdata
74
- # === Exception
75
- # ProtocolError :: value too large / value is not supported
68
+ # @param v [Object] Ruby value.
69
+ # @return [Integer] type of column. Field::TYPE_*
70
+ # @return [String] netdata
71
+ # @raise [ProtocolError] value too large / value is not supported
76
72
  def self.value2net(v)
77
73
  case v
78
74
  when nil
@@ -97,12 +93,9 @@ class Mysql
97
93
  when String
98
94
  type = Field::TYPE_STRING
99
95
  val = Packet.lcs(v)
100
- when ::Time
96
+ when Time
101
97
  type = Field::TYPE_DATETIME
102
98
  val = [11, v.year, v.month, v.day, v.hour, v.min, v.sec, v.usec].pack("CvCCCCCV")
103
- when Mysql::Time
104
- type = Field::TYPE_DATETIME
105
- val = [11, v.year, v.month, v.day, v.hour, v.min, v.sec, v.second_part].pack("CvCCCCCV")
106
99
  else
107
100
  raise ProtocolError, "class #{v.class} is not supported"
108
101
  end
@@ -129,30 +122,38 @@ class Mysql
129
122
  # :RESULT :: After retr_fields(), retr_all_records() or stmt_retr_all_records() is needed.
130
123
 
131
124
  # make socket connection to server.
132
- # @param host [String] if "localhost" or "" or nil then use UNIX socket. Otherwise use TCP socket
133
- # @param port [Integer] port number using by TCP socket
134
- # @param socket [String] socket file name using by UNIX socket
135
- # @param [Hash] opts
136
- # @option opts :conn_timeout [Integer] connect timeout (sec).
137
- # @option opts :read_timeout [Integer] read timeout (sec).
138
- # @option opts :write_timeout [Integer] write timeout (sec).
139
- # @option opts :local_infile [String] local infile path
140
- # @option opts :get_server_public_key [Boolean]
125
+ # @param opts [Hash]
126
+ # @option :host [String] hostname mysqld running
127
+ # @option :username [String] username to connect to mysqld
128
+ # @option :password [String] password to connect to mysqld
129
+ # @option :database [String] initial database name
130
+ # @option :port [String] port number (used if host is not 'localhost' or nil)
131
+ # @option :socket [String] socket filename (used if host is 'localhost' or nil)
132
+ # @option :flags [Integer] connection flag. Mysql::CLIENT_* ORed
133
+ # @option :charset [Mysql::Charset] character set
134
+ # @option :connect_timeout [Numeric, nil]
135
+ # @option :read_timeout [Numeric, nil]
136
+ # @option :write_timeout [Numeric, nil]
137
+ # @option :local_infile [Boolean]
138
+ # @option :load_data_local_dir [String]
139
+ # @option :ssl_mode [Integer]
140
+ # @option :get_server_public_key [Boolean]
141
141
  # @raise [ClientError] connection timeout
142
- def initialize(host, port, socket, opts)
142
+ def initialize(opts)
143
143
  @opts = opts
144
+ @charset = Mysql::Charset.by_name("utf8mb4")
144
145
  @insert_id = 0
145
146
  @warning_count = 0
146
147
  @gc_stmt_queue = [] # stmt id list which GC destroy.
147
148
  set_state :INIT
148
149
  @get_server_public_key = @opts[:get_server_public_key]
149
150
  begin
150
- if host.nil? or host.empty? or host == "localhost"
151
- socket ||= ENV["MYSQL_UNIX_PORT"] || MYSQL_UNIX_PORT
151
+ if @opts[:host].nil? or @opts[:host].empty? or @opts[:host] == "localhost"
152
+ socket = @opts[:socket] || ENV["MYSQL_UNIX_PORT"] || MYSQL_UNIX_PORT
152
153
  @socket = Socket.unix(socket)
153
154
  else
154
- port ||= ENV["MYSQL_TCP_PORT"] || (Socket.getservbyname("mysql","tcp") rescue MYSQL_TCP_PORT)
155
- @socket = Socket.tcp(host, port, connect_timeout: @opts[:connect_timeout])
155
+ port = @opts[:port] || ENV["MYSQL_TCP_PORT"] || (Socket.getservbyname("mysql","tcp") rescue MYSQL_TCP_PORT)
156
+ @socket = Socket.tcp(@opts[:host], port, connect_timeout: @opts[:connect_timeout])
156
157
  end
157
158
  rescue Errno::ETIMEDOUT
158
159
  raise ClientError, "connection timeout"
@@ -164,17 +165,10 @@ class Mysql
164
165
  end
165
166
 
166
167
  # initial negotiate and authenticate.
167
- # === Argument
168
- # user :: [String / nil] username
169
- # passwd :: [String / nil] password
170
- # db :: [String / nil] default database name. nil: no default.
171
- # flag :: [Integer] client flag
172
- # charset :: [Mysql::Charset / nil] charset for connection. nil: use server's charset
173
- # === Exception
174
- # ProtocolError :: The old style password is not supported
175
- def authenticate(user, passwd, db, flag, charset)
168
+ # @param charset [Mysql::Charset, nil] charset for connection. nil: use server's charset
169
+ # @raise [ProtocolError] The old style password is not supported
170
+ def authenticate
176
171
  check_state :INIT
177
- @authinfo = [user, passwd, db, flag, charset]
178
172
  reset
179
173
  init_packet = InitialPacket.parse read
180
174
  @server_info = init_packet.server_version
@@ -182,27 +176,28 @@ class Mysql
182
176
  @server_capabilities = init_packet.server_capabilities
183
177
  @thread_id = init_packet.thread_id
184
178
  @client_flags = CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS | CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION | CLIENT_PLUGIN_AUTH
185
- @client_flags |= CLIENT_LOCAL_FILES if @opts[:local_infile]
186
- @client_flags |= CLIENT_CONNECT_WITH_DB if db
187
- @client_flags |= flag
188
- @charset = charset
189
- unless @charset
179
+ @client_flags |= CLIENT_LOCAL_FILES if @opts[:local_infile] || @opts[:load_data_local_dir]
180
+ @client_flags |= CLIENT_CONNECT_WITH_DB if @opts[:database]
181
+ @client_flags |= @opts[:flags]
182
+ if @opts[:charset]
183
+ @charset = @opts[:charset].is_a?(Charset) ? @opts[:charset] : Charset.by_name(@opts[:charset])
184
+ else
190
185
  @charset = Charset.by_number(init_packet.server_charset)
191
186
  @charset.encoding # raise error if unsupported charset
192
187
  end
193
188
  enable_ssl
194
- Authenticator.new(self).authenticate(user, passwd, db, init_packet.scramble_buff, init_packet.auth_plugin)
189
+ Authenticator.new(self).authenticate(@opts[:username], @opts[:password].to_s, @opts[:database], init_packet.scramble_buff, init_packet.auth_plugin)
195
190
  set_state :READY
196
191
  end
197
192
 
198
193
  def enable_ssl
199
194
  case @opts[:ssl_mode]
200
- when SSL_MODE_DISABLED
195
+ when SSL_MODE_DISABLED, '1', 'disabled'
201
196
  return
202
- when SSL_MODE_PREFERRED
197
+ when SSL_MODE_PREFERRED, '2', 'preferred'
203
198
  return if @socket.local_address.unix?
204
199
  return if @server_capabilities & CLIENT_SSL == 0
205
- when SSL_MODE_REQUIRED
200
+ when SSL_MODE_REQUIRED, '3', 'required'
206
201
  if @server_capabilities & CLIENT_SSL == 0
207
202
  raise ClientError::SslConnectionError, "SSL is required but the server doesn't support it"
208
203
  end
@@ -232,10 +227,8 @@ class Mysql
232
227
  end
233
228
 
234
229
  # Query command
235
- # === Argument
236
- # query :: [String] query string
237
- # === Return
238
- # [Integer / nil] number of fields of results. nil if no results.
230
+ # @param query [String] query string
231
+ # @return [Integer, nil] number of fields of results. nil if no results.
239
232
  def query_command(query)
240
233
  check_state :READY
241
234
  begin
@@ -249,8 +242,7 @@ class Mysql
249
242
  end
250
243
 
251
244
  # get result of query.
252
- # === Return
253
- # [integer / nil] number of fields of results. nil if no results.
245
+ # @return [integer, nil] number of fields of results. nil if no results.
254
246
  def get_result
255
247
  begin
256
248
  res_packet = ResultPacket.parse read
@@ -274,7 +266,7 @@ class Mysql
274
266
  # send local file to server
275
267
  def send_local_file(filename)
276
268
  filename = File.absolute_path(filename)
277
- if filename.start_with? @opts[:local_infile]
269
+ if @opts[:local_infile] || @opts[:load_data_local_dir] && filename.start_with?(@opts[:load_data_local_dir])
278
270
  File.open(filename){|f| write f}
279
271
  else
280
272
  raise ClientError::LoadDataLocalInfileRejected, 'LOAD DATA LOCAL INFILE file request rejected due to restrictions on access.'
@@ -285,10 +277,8 @@ class Mysql
285
277
  end
286
278
 
287
279
  # Retrieve n fields
288
- # === Argument
289
- # n :: [Integer] number of fields
290
- # === Return
291
- # [Array of Mysql::Field] field list
280
+ # @param n [Integer] number of fields
281
+ # @return [Array<Mysql::Field>] field list
292
282
  def retr_fields(n)
293
283
  check_state :FIELD
294
284
  begin
@@ -303,10 +293,8 @@ class Mysql
303
293
  end
304
294
 
305
295
  # Retrieve all records for simple query
306
- # === Argument
307
- # fields :: [Array<Mysql::Field>] number of fields
308
- # === Return
309
- # [Array of Array of String] all records
296
+ # @param fields [Array<Mysql::Field>] number of fields
297
+ # @return [Array<Array<String>>] all records
310
298
  def retr_all_records(fields)
311
299
  check_state :RESULT
312
300
  enc = charset.encoding
@@ -323,43 +311,6 @@ class Mysql
323
311
  end
324
312
  end
325
313
 
326
- # Field list command
327
- # === Argument
328
- # table :: [String] table name.
329
- # field :: [String / nil] field name that may contain wild card.
330
- # === Return
331
- # [Array of Field] field list
332
- def field_list_command(table, field)
333
- synchronize do
334
- reset
335
- write [COM_FIELD_LIST, table, 0, field].pack("Ca*Ca*")
336
- fields = []
337
- until (data = read).eof?
338
- fields.push Field.new(FieldPacket.parse(data))
339
- end
340
- return fields
341
- end
342
- end
343
-
344
- # Process info command
345
- # === Return
346
- # [Array of Field] field list
347
- def process_info_command
348
- check_state :READY
349
- begin
350
- reset
351
- write [COM_PROCESS_INFO].pack("C")
352
- field_count = read.lcb
353
- fields = field_count.times.map{Field.new FieldPacket.parse(read)}
354
- read_eof_packet
355
- set_state :RESULT
356
- return fields
357
- rescue
358
- set_state :READY
359
- raise
360
- end
361
- end
362
-
363
314
  # Ping command
364
315
  def ping_command
365
316
  simple_command [COM_PING].pack("C")
@@ -391,12 +342,8 @@ class Mysql
391
342
  end
392
343
 
393
344
  # Stmt prepare command
394
- # === Argument
395
- # stmt :: [String] prepared statement
396
- # === Return
397
- # [Integer] statement id
398
- # [Integer] number of parameters
399
- # [Array of Field] field list
345
+ # @param stmt [String] prepared statement
346
+ # @return [Array<Integer, Integer, Array<Field>>] statement id, number of parameters, field list
400
347
  def stmt_prepare_command(stmt)
401
348
  synchronize do
402
349
  reset
@@ -417,11 +364,9 @@ class Mysql
417
364
  end
418
365
 
419
366
  # Stmt execute command
420
- # === Argument
421
- # stmt_id :: [Integer] statement id
422
- # values :: [Array] parameters
423
- # === Return
424
- # [Integer] number of fields
367
+ # @param stmt_id [Integer] statement id
368
+ # @param values [Array] parameters
369
+ # @return [Integer] number of fields
425
370
  def stmt_execute_command(stmt_id, values)
426
371
  check_state :READY
427
372
  begin
@@ -435,11 +380,9 @@ class Mysql
435
380
  end
436
381
 
437
382
  # Retrieve all records for prepared statement
438
- # === Argument
439
- # fields :: [Array of Mysql::Fields] field list
440
- # charset :: [Mysql::Charset]
441
- # === Return
442
- # [Array of Array of Object] all records
383
+ # @param fields [Array of Mysql::Fields] field list
384
+ # @param charset [Mysql::Charset]
385
+ # @return [Array<Array<Object>>] all records
443
386
  def stmt_retr_all_records(fields, charset)
444
387
  check_state :RESULT
445
388
  enc = charset.encoding
@@ -455,8 +398,7 @@ class Mysql
455
398
  end
456
399
 
457
400
  # Stmt close command
458
- # === Argument
459
- # stmt_id :: [Integer] statement id
401
+ # @param stmt_id [Integer] statement id
460
402
  def stmt_close_command(stmt_id)
461
403
  synchronize do
462
404
  reset
@@ -502,10 +444,8 @@ class Mysql
502
444
  end
503
445
 
504
446
  # Read one packet data
505
- # === Return
506
- # [Packet] packet data
507
- # === Exception
508
- # [ProtocolError] invalid packet sequence number
447
+ # @return [Packet] packet data
448
+ # @rails [ProtocolError] invalid packet sequence number
509
449
  def read
510
450
  data = ''
511
451
  len = nil
@@ -546,9 +486,9 @@ class Mysql
546
486
  def read_timeout(len, timeout)
547
487
  return @socket.read(len) if timeout.nil? || timeout == 0
548
488
  result = ''
549
- e = ::Time.now + timeout
489
+ e = Time.now + timeout
550
490
  while result.size < len
551
- now = ::Time.now
491
+ now = Time.now
552
492
  raise Errno::ETIMEDOUT if now > e
553
493
  r = @socket.read_nonblock(len - result.size, exception: false)
554
494
  case r
@@ -566,8 +506,7 @@ class Mysql
566
506
  end
567
507
 
568
508
  # Write one packet data
569
- # === Argument
570
- # data :: [String / IO] packet data. If data is nil, write empty packet.
509
+ # @param data [String, IO, nil] packet data. If data is nil, write empty packet.
571
510
  def write(data)
572
511
  begin
573
512
  @socket.sync = false
@@ -593,9 +532,9 @@ class Mysql
593
532
  def write_timeout(data, timeout)
594
533
  return @socket.write(data) if timeout.nil? || timeout == 0
595
534
  len = 0
596
- e = ::Time.now + timeout
535
+ e = Time.now + timeout
597
536
  while len < data.size
598
- now = ::Time.now
537
+ now = Time.now
599
538
  raise Errno::ETIMEDOUT if now > e
600
539
  l = @socket.write_nonblock(data[len..-1], exception: false)
601
540
  case l
@@ -611,17 +550,14 @@ class Mysql
611
550
  end
612
551
 
613
552
  # Read EOF packet
614
- # === Exception
615
- # [ProtocolError] packet is not EOF
553
+ # @raise [ProtocolError] packet is not EOF
616
554
  def read_eof_packet
617
555
  raise ProtocolError, "packet is not EOF" unless read.eof?
618
556
  end
619
557
 
620
558
  # Send simple command
621
- # === Argument
622
- # packet :: [String] packet data
623
- # === Return
624
- # [String] received data
559
+ # @param packet :: [String] packet data
560
+ # @return [String] received data
625
561
  def simple_command(packet)
626
562
  synchronize do
627
563
  reset
@@ -831,17 +767,15 @@ class Mysql
831
767
  end
832
768
 
833
769
  class StmtRawRecord
834
- # === Argument
835
- # pkt :: [Packet]
836
- # fields :: [Array of Fields]
837
- # encoding:: [Encoding]
770
+ # @param pkt [Packet]
771
+ # @param fields [Array of Fields]
772
+ # @param encoding [Encoding]
838
773
  def initialize(packet, fields, encoding)
839
774
  @packet, @fields, @encoding = packet, fields, encoding
840
775
  end
841
776
 
842
777
  # Parse statement result packet
843
- # === Return
844
- # [Array of Object] one record
778
+ # @return [Array<Object>] one record
845
779
  def parse_record_packet
846
780
  @packet.utiny # skip first byte
847
781
  null_bit_map = @packet.read((@fields.length+7+2)/8).unpack("b*").first
@@ -851,7 +785,7 @@ class Mysql
851
785
  else
852
786
  unsigned = f.flags & Field::UNSIGNED_FLAG != 0
853
787
  v = Protocol.net2value(@packet, f.type, unsigned)
854
- if v.is_a? Numeric or v.is_a? Mysql::Time
788
+ if v.nil? or v.is_a? Numeric or v.is_a? Time
855
789
  v
856
790
  elsif f.type == Field::TYPE_BIT or f.charsetnr == Charset::BINARY_CHARSET_NUMBER
857
791
  Charset.to_binary(v)