ruby-mysql 2.11.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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)