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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +68 -0
- data/CHANGELOG.md +48 -0
- data/LICENSE +83 -0
- data/README.md +199 -0
- data/Rakefile +10 -0
- data/docker-compose.yml +25 -0
- data/lib/mysql-pr/charset.rb +91 -120
- data/lib/mysql-pr/constants.rb +4 -0
- data/lib/mysql-pr/error.rb +2 -0
- data/lib/mysql-pr/packet.rb +7 -4
- data/lib/mysql-pr/protocol.rb +237 -21
- data/lib/mysql-pr/version.rb +5 -0
- data/lib/mysql-pr.rb +339 -287
- data/mysql-pr.gemspec +36 -0
- metadata +102 -28
- data/README.rdoc +0 -60
- data/spec/mysql/packet_spec.rb +0 -118
- data/spec/mysql_spec.rb +0 -1701
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 =
|
|
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
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
26
|
+
|
|
27
|
+
# @return [MysqlPR::Protocol] protocol handler
|
|
26
28
|
attr_reader :protocol
|
|
27
29
|
|
|
28
|
-
# @return [Boolean] if true, {#query}
|
|
30
|
+
# @return [Boolean] if true, {#query} returns {MysqlPR::Result}
|
|
29
31
|
attr_accessor :query_with_result
|
|
30
32
|
|
|
31
33
|
class << self
|
|
32
|
-
# Make
|
|
33
|
-
# @return [
|
|
34
|
+
# Make MysqlPR object without connecting.
|
|
35
|
+
# @return [MysqlPR]
|
|
34
36
|
def init
|
|
35
|
-
my =
|
|
36
|
-
my.
|
|
37
|
+
my = allocate
|
|
38
|
+
my.send(:initialize)
|
|
37
39
|
my
|
|
38
40
|
end
|
|
39
41
|
|
|
40
|
-
# Make
|
|
41
|
-
# @param args same as arguments for {#connect}
|
|
42
|
-
# @return [
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
98
|
-
# @param [String
|
|
99
|
-
# @param [String
|
|
100
|
-
# @param [String
|
|
101
|
-
# @param [Integer
|
|
102
|
-
# @param [String
|
|
103
|
-
# @param [Integer
|
|
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
|
|
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
|
|
111
|
-
@protocol.authenticate
|
|
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") ?
|
|
114
|
-
query
|
|
115
|
-
|
|
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 [
|
|
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
|
-
|
|
129
|
+
self
|
|
127
130
|
end
|
|
128
131
|
|
|
129
132
|
# Disconnect from mysql without QUIT packet.
|
|
130
|
-
# @return [
|
|
133
|
+
# @return [MysqlPR] self
|
|
131
134
|
def close!
|
|
132
135
|
if @protocol
|
|
133
136
|
@protocol.close
|
|
134
137
|
@protocol = nil
|
|
135
138
|
end
|
|
136
|
-
|
|
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 [
|
|
146
|
-
# @return [
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 [
|
|
324
|
+
# @return [MysqlPR] self
|
|
294
325
|
def kill(pid)
|
|
295
326
|
check_connection
|
|
296
|
-
@protocol.kill_command
|
|
327
|
+
@protocol.kill_command(pid)
|
|
297
328
|
self
|
|
298
329
|
end
|
|
299
330
|
|
|
300
|
-
#
|
|
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 [
|
|
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
|
|
351
|
+
nfields = @protocol.query_command(str)
|
|
321
352
|
if nfields
|
|
322
|
-
@fields = @protocol.retr_fields
|
|
353
|
+
@fields = @protocol.retr_fields(nfields)
|
|
323
354
|
@result_exist = true
|
|
324
355
|
end
|
|
325
356
|
if block
|
|
326
|
-
|
|
327
|
-
block.call
|
|
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,
|
|
350
|
-
|
|
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 [
|
|
401
|
+
# @return [MysqlPR] self
|
|
370
402
|
def set_server_option(opt)
|
|
371
403
|
check_connection
|
|
372
|
-
@protocol.set_option_command
|
|
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
|
|
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
|
-
#
|
|
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
|
|
423
|
+
@fields = @protocol.retr_fields(nfields)
|
|
391
424
|
@result_exist = true
|
|
392
425
|
end
|
|
393
|
-
|
|
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
|
|
401
|
-
st.prepare
|
|
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]
|
|
439
|
+
# @return [MysqlPR::Stmt]
|
|
408
440
|
def stmt_init
|
|
409
|
-
Stmt.new
|
|
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
|
|
421
|
-
|
|
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
|
-
# @
|
|
438
|
-
# @
|
|
439
|
-
|
|
440
|
-
|
|
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
|
|
446
|
-
# @return [
|
|
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 [
|
|
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
|
|
489
|
+
@protocol.refresh_command(op)
|
|
459
490
|
self
|
|
460
491
|
end
|
|
461
492
|
|
|
462
493
|
# Reload grant tables.
|
|
463
|
-
# @return [
|
|
494
|
+
# @return [MysqlPR] self
|
|
464
495
|
def reload
|
|
465
|
-
refresh
|
|
496
|
+
refresh(MysqlPR::REFRESH_GRANT)
|
|
466
497
|
end
|
|
467
498
|
|
|
468
499
|
# Select default database
|
|
469
|
-
# @return [
|
|
500
|
+
# @return [MysqlPR] self
|
|
470
501
|
def select_db(db)
|
|
471
|
-
query
|
|
502
|
+
query("use #{db}")
|
|
472
503
|
self
|
|
473
504
|
end
|
|
474
505
|
|
|
475
|
-
#
|
|
476
|
-
# @return [
|
|
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
|
|
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 :
|
|
516
|
+
@protocol ? @protocol.statistics_command : "MySQL server has gone away"
|
|
486
517
|
end
|
|
487
518
|
|
|
488
519
|
# Commit transaction
|
|
489
|
-
# @return [
|
|
520
|
+
# @return [MysqlPR] self
|
|
490
521
|
def commit
|
|
491
|
-
query
|
|
522
|
+
query("commit")
|
|
492
523
|
self
|
|
493
524
|
end
|
|
494
525
|
|
|
495
526
|
# Rollback transaction
|
|
496
|
-
# @return [
|
|
527
|
+
# @return [MysqlPR] self
|
|
497
528
|
def rollback
|
|
498
|
-
query
|
|
529
|
+
query("rollback")
|
|
499
530
|
self
|
|
500
531
|
end
|
|
501
532
|
|
|
502
533
|
# Set autocommit mode
|
|
503
534
|
# @param [Boolean] flag
|
|
504
|
-
# @return [
|
|
535
|
+
# @return [MysqlPR] self
|
|
505
536
|
def autocommit(flag)
|
|
506
|
-
query
|
|
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,
|
|
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]
|
|
569
|
+
# @return [String] default value
|
|
540
570
|
attr_reader :default
|
|
541
|
-
alias :def :default
|
|
542
571
|
|
|
543
|
-
|
|
572
|
+
alias def default
|
|
573
|
+
|
|
544
574
|
attr_accessor :result
|
|
575
|
+
attr_writer :max_length
|
|
545
576
|
|
|
546
|
-
# @
|
|
577
|
+
# @param [Protocol::FieldPacket] packet
|
|
547
578
|
def initialize(packet)
|
|
548
|
-
@db
|
|
549
|
-
|
|
550
|
-
@
|
|
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
|
|
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
|
|
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
|
|
601
|
-
[TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT,
|
|
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
|
-
#
|
|
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
|
|
654
|
+
# @param [Array<MysqlPR::Field>] fields
|
|
615
655
|
def initialize(fields)
|
|
616
656
|
@fields = fields
|
|
617
|
-
@field_index = 0
|
|
618
|
-
@records = []
|
|
619
|
-
@index = 0
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
681
|
+
@fetched_record
|
|
643
682
|
end
|
|
644
683
|
alias fetch_row fetch
|
|
645
684
|
|
|
646
685
|
# Return data of current record as Hash.
|
|
647
|
-
#
|
|
648
|
-
# @
|
|
649
|
-
|
|
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
|
-
|
|
654
|
-
|
|
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
|
|
705
|
+
# @return [self, Enumerator]
|
|
667
706
|
def each(&block)
|
|
668
707
|
return enum_for(:each) unless block
|
|
669
|
-
|
|
670
|
-
|
|
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
|
|
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
|
-
|
|
682
|
-
|
|
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]
|
|
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
|
|
754
|
+
# @param [MysqlPR::Protocol, nil] protocol
|
|
755
|
+
def initialize(fields, protocol = nil)
|
|
756
|
+
super(fields)
|
|
718
757
|
return unless protocol
|
|
719
|
-
|
|
720
|
-
|
|
758
|
+
|
|
759
|
+
@records = protocol.retr_all_records(fields.size)
|
|
760
|
+
fields.each { |f| f.result = self }
|
|
721
761
|
end
|
|
722
762
|
|
|
723
|
-
#
|
|
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?
|
|
729
|
-
max_length.each_index do |
|
|
730
|
-
max_length[
|
|
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
|
|
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
|
|
814
|
+
# @return [Array<Integer>, nil] length of each field
|
|
774
815
|
def fetch_lengths
|
|
775
816
|
return nil unless @fetched_record
|
|
776
|
-
|
|
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
|
|
794
|
-
@records = protocol.stmt_retr_all_records
|
|
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
|
|
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 =
|
|
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
|
-
#
|
|
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
|
-
|
|
906
|
+
|
|
907
|
+
values = values.map { |v| @charset.convert(v) }
|
|
865
908
|
begin
|
|
866
909
|
@sqlstate = "00000"
|
|
867
|
-
nfields = @protocol.stmt_execute_command
|
|
910
|
+
nfields = @protocol.stmt_execute_command(@statement_id, values)
|
|
868
911
|
if nfields
|
|
869
|
-
@fields = @protocol.retr_fields
|
|
870
|
-
@result = StatementResult.new
|
|
912
|
+
@fields = @protocol.retr_fields(nfields)
|
|
913
|
+
@result = StatementResult.new(@fields, @protocol, @charset)
|
|
871
914
|
else
|
|
872
|
-
@affected_rows
|
|
873
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
#
|
|
937
|
-
# @
|
|
938
|
-
|
|
939
|
-
|
|
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 [
|
|
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
|
-
|
|
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
|
-
|
|
964
|
-
|
|
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
|
-
|
|
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
|
-
|
|
977
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
|
1055
|
-
|
|
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
|
-
|
|
1079
|
-
elsif year
|
|
1133
|
+
format("%04d-%02d-%02d", year, mon, day)
|
|
1134
|
+
elsif year.zero? && mon.zero? && day.zero?
|
|
1080
1135
|
h = neg ? hour * -1 : hour
|
|
1081
|
-
|
|
1136
|
+
format("%02d:%02d:%02d", h, min, sec)
|
|
1082
1137
|
else
|
|
1083
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|