mysql-pr 2.9.11 → 3.0.1
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 +84 -0
- data/CHANGELOG.md +55 -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 +185 -216
- data/lib/mysql-pr/constants.rb +9 -5
- data/lib/mysql-pr/error.rb +12 -10
- data/lib/mysql-pr/packet.rb +27 -20
- data/lib/mysql-pr/protocol.rb +410 -174
- data/lib/mysql-pr/version.rb +5 -0
- data/lib/mysql-pr.rb +350 -308
- data/mysql-pr.gemspec +35 -0
- metadata +88 -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
|
|
|
@@ -52,7 +54,7 @@ class MysqlPR
|
|
|
52
54
|
# @param [String] str
|
|
53
55
|
# @return [String]
|
|
54
56
|
def escape_string(str)
|
|
55
|
-
str.gsub(/[\0\n\r
|
|
57
|
+
str.gsub(/[\0\n\r\\'"\x1a]/) do |s|
|
|
56
58
|
case s
|
|
57
59
|
when "\0" then "\\0"
|
|
58
60
|
when "\n" then "\\n"
|
|
@@ -64,15 +66,15 @@ 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
|
+
50_000
|
|
76
78
|
end
|
|
77
79
|
alias get_client_version client_version
|
|
78
80
|
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 =
|
|
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,79 @@ 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 [Hash] the options hash
|
|
202
|
+
attr_writer :ssl_options
|
|
203
|
+
|
|
204
|
+
# Check if SSL is enabled for the current connection.
|
|
205
|
+
# @return [Boolean] true if SSL is enabled
|
|
206
|
+
def ssl_enabled?
|
|
207
|
+
@protocol&.ssl_enabled? || false
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Get the SSL cipher being used.
|
|
211
|
+
# @return [Array, nil] cipher info if SSL is enabled, nil otherwise
|
|
212
|
+
def ssl_cipher
|
|
213
|
+
@protocol&.ssl_cipher
|
|
214
|
+
end
|
|
215
|
+
|
|
182
216
|
# 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
217
|
# @param [String] str
|
|
187
|
-
# return [String]
|
|
218
|
+
# @return [String]
|
|
188
219
|
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
|
|
220
|
+
self.class.escape_string(str)
|
|
193
221
|
end
|
|
194
222
|
alias quote escape_string
|
|
195
223
|
|
|
@@ -206,15 +234,15 @@ class MysqlPR
|
|
|
206
234
|
alias get_client_version client_version
|
|
207
235
|
|
|
208
236
|
# Set charset of MySQL connection.
|
|
209
|
-
# @param [String
|
|
237
|
+
# @param [String, MysqlPR::Charset] cs
|
|
238
|
+
# @return [MysqlPR::Charset] the charset
|
|
210
239
|
def charset=(cs)
|
|
211
240
|
charset = cs.is_a?(Charset) ? cs : Charset.by_name(cs)
|
|
212
241
|
if @protocol
|
|
213
242
|
@protocol.charset = charset
|
|
214
|
-
query
|
|
243
|
+
query("SET NAMES #{charset.name}")
|
|
215
244
|
end
|
|
216
245
|
@charset = charset
|
|
217
|
-
cs
|
|
218
246
|
end
|
|
219
247
|
|
|
220
248
|
# @return [String] charset name
|
|
@@ -229,7 +257,7 @@ class MysqlPR
|
|
|
229
257
|
|
|
230
258
|
# @return [String] last error message
|
|
231
259
|
def error
|
|
232
|
-
@last_error
|
|
260
|
+
@last_error&.error
|
|
233
261
|
end
|
|
234
262
|
|
|
235
263
|
# @return [String] sqlstate for last error
|
|
@@ -243,9 +271,7 @@ class MysqlPR
|
|
|
243
271
|
end
|
|
244
272
|
|
|
245
273
|
# @return [String] connection type
|
|
246
|
-
|
|
247
|
-
@host_info
|
|
248
|
-
end
|
|
274
|
+
attr_reader :host_info
|
|
249
275
|
alias get_host_info host_info
|
|
250
276
|
|
|
251
277
|
# @return [Integer] protocol version
|
|
@@ -270,10 +296,10 @@ class MysqlPR
|
|
|
270
296
|
|
|
271
297
|
# @return [String] information for last query
|
|
272
298
|
def info
|
|
273
|
-
@protocol
|
|
299
|
+
@protocol&.message
|
|
274
300
|
end
|
|
275
301
|
|
|
276
|
-
# @return [Integer] number of affected records by insert/update/delete
|
|
302
|
+
# @return [Integer] number of affected records by insert/update/delete
|
|
277
303
|
def affected_rows
|
|
278
304
|
@protocol ? @protocol.affected_rows : 0
|
|
279
305
|
end
|
|
@@ -290,50 +316,48 @@ class MysqlPR
|
|
|
290
316
|
|
|
291
317
|
# Kill query.
|
|
292
318
|
# @param [Integer] pid thread id
|
|
293
|
-
# @return [
|
|
319
|
+
# @return [MysqlPR] self
|
|
294
320
|
def kill(pid)
|
|
295
321
|
check_connection
|
|
296
|
-
@protocol.kill_command
|
|
322
|
+
@protocol.kill_command(pid)
|
|
297
323
|
self
|
|
298
324
|
end
|
|
299
325
|
|
|
300
|
-
#
|
|
301
|
-
# @param [String] db database name that may contain wild card
|
|
326
|
+
# Database list.
|
|
327
|
+
# @param [String] db database name that may contain wild card
|
|
302
328
|
# @return [Array<String>] database list
|
|
303
|
-
def list_dbs(db=nil)
|
|
304
|
-
db &&= db.gsub(/[
|
|
329
|
+
def list_dbs(db = nil)
|
|
330
|
+
db &&= db.gsub(/[\\']/) { "\\#{::Regexp.last_match(0)}" }
|
|
305
331
|
query(db ? "show databases like '#{db}'" : "show databases").map(&:first)
|
|
306
332
|
end
|
|
307
333
|
|
|
308
334
|
# 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 [
|
|
335
|
+
# @param [String] str Query
|
|
336
|
+
# @yield [MysqlPR::Result] evaluated per query
|
|
337
|
+
# @return [MysqlPR::Result] If {#query_with_result} is true and result set exist
|
|
338
|
+
# @return [nil] If {#query_with_result} is true and the query does not return result set
|
|
339
|
+
# @return [MysqlPR] If {#query_with_result} is false or block is specified
|
|
314
340
|
# @example
|
|
315
341
|
# my.query("select 1,NULL,'abc'").fetch # => [1, nil, "abc"]
|
|
316
342
|
def query(str, &block)
|
|
317
343
|
check_connection
|
|
318
344
|
@fields = nil
|
|
319
345
|
begin
|
|
320
|
-
nfields = @protocol.query_command
|
|
346
|
+
nfields = @protocol.query_command(str)
|
|
321
347
|
if nfields
|
|
322
|
-
@fields = @protocol.retr_fields
|
|
348
|
+
@fields = @protocol.retr_fields(nfields)
|
|
323
349
|
@result_exist = true
|
|
324
350
|
end
|
|
325
351
|
if block
|
|
326
|
-
|
|
327
|
-
block.call
|
|
352
|
+
loop do
|
|
353
|
+
block.call(store_result) if @fields
|
|
328
354
|
break unless next_result
|
|
329
355
|
end
|
|
330
356
|
return self
|
|
331
357
|
end
|
|
332
|
-
if @query_with_result
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
return self
|
|
336
|
-
end
|
|
358
|
+
return @fields ? store_result : nil if @query_with_result
|
|
359
|
+
|
|
360
|
+
self
|
|
337
361
|
rescue ServerError => e
|
|
338
362
|
@last_error = e
|
|
339
363
|
@sqlstate = e.sqlstate
|
|
@@ -346,8 +370,9 @@ class MysqlPR
|
|
|
346
370
|
# @return [MysqlPR::Result]
|
|
347
371
|
def store_result
|
|
348
372
|
check_connection
|
|
349
|
-
raise ClientError,
|
|
350
|
-
|
|
373
|
+
raise ClientError, "invalid usage" unless @result_exist
|
|
374
|
+
|
|
375
|
+
res = Result.new(@fields, @protocol)
|
|
351
376
|
@result_exist = false
|
|
352
377
|
res
|
|
353
378
|
end
|
|
@@ -366,59 +391,59 @@ class MysqlPR
|
|
|
366
391
|
|
|
367
392
|
# Set server option.
|
|
368
393
|
# @param [Integer] opt {MysqlPR::OPTION_MULTI_STATEMENTS_ON} or {MysqlPR::OPTION_MULTI_STATEMENTS_OFF}
|
|
369
|
-
# @return [
|
|
394
|
+
# @return [MysqlPR] self
|
|
370
395
|
def set_server_option(opt)
|
|
371
396
|
check_connection
|
|
372
|
-
@protocol.set_option_command
|
|
397
|
+
@protocol.set_option_command(opt)
|
|
373
398
|
self
|
|
374
399
|
end
|
|
375
400
|
|
|
376
|
-
# @return [Boolean] true if multiple queries are specified and unexecuted queries
|
|
401
|
+
# @return [Boolean] true if multiple queries are specified and unexecuted queries exist
|
|
377
402
|
def more_results
|
|
378
|
-
@protocol.server_status & SERVER_MORE_RESULTS_EXISTS != 0
|
|
403
|
+
(@protocol.server_status & SERVER_MORE_RESULTS_EXISTS) != 0
|
|
379
404
|
end
|
|
380
405
|
alias more_results? more_results
|
|
381
406
|
|
|
382
|
-
#
|
|
383
|
-
# @return [Boolean] true if next query exists
|
|
407
|
+
# Execute next query if multiple queries are specified.
|
|
408
|
+
# @return [Boolean] true if next query exists
|
|
384
409
|
def next_result
|
|
385
410
|
return false unless more_results
|
|
411
|
+
|
|
386
412
|
check_connection
|
|
387
413
|
@fields = nil
|
|
388
414
|
nfields = @protocol.get_result
|
|
389
415
|
if nfields
|
|
390
|
-
@fields = @protocol.retr_fields
|
|
416
|
+
@fields = @protocol.retr_fields(nfields)
|
|
391
417
|
@result_exist = true
|
|
392
418
|
end
|
|
393
|
-
|
|
419
|
+
true
|
|
394
420
|
end
|
|
395
421
|
|
|
396
422
|
# Parse prepared-statement.
|
|
397
423
|
# @param [String] str query string
|
|
398
424
|
# @return [MysqlPR::Stmt] Prepared-statement object
|
|
399
425
|
def prepare(str)
|
|
400
|
-
st = Stmt.new
|
|
401
|
-
st.prepare
|
|
426
|
+
st = Stmt.new(@protocol, @charset)
|
|
427
|
+
st.prepare(str)
|
|
402
428
|
st
|
|
403
429
|
end
|
|
404
430
|
|
|
405
|
-
# @private
|
|
406
431
|
# Make empty prepared-statement object.
|
|
407
|
-
# @return [MysqlPR::Stmt]
|
|
432
|
+
# @return [MysqlPR::Stmt]
|
|
408
433
|
def stmt_init
|
|
409
|
-
Stmt.new
|
|
434
|
+
Stmt.new(@protocol, @charset)
|
|
410
435
|
end
|
|
411
436
|
|
|
412
437
|
# Returns MysqlPR::Result object that is empty.
|
|
413
438
|
# 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
|
|
439
|
+
# @param [String] table table name
|
|
440
|
+
# @param [String] field field name that may contain wild card
|
|
416
441
|
# @return [MysqlPR::Result]
|
|
417
|
-
def list_fields(table, field=nil)
|
|
442
|
+
def list_fields(table, field = nil)
|
|
418
443
|
check_connection
|
|
419
444
|
begin
|
|
420
|
-
fields = @protocol.field_list_command
|
|
421
|
-
|
|
445
|
+
fields = @protocol.field_list_command(table, field)
|
|
446
|
+
Result.new(fields)
|
|
422
447
|
rescue ServerError => e
|
|
423
448
|
@last_error = e
|
|
424
449
|
@sqlstate = e.sqlstate
|
|
@@ -434,16 +459,15 @@ class MysqlPR
|
|
|
434
459
|
store_result
|
|
435
460
|
end
|
|
436
461
|
|
|
437
|
-
# @
|
|
438
|
-
# @
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
q = table ? "show tables like '#{quote table}'" : "show tables"
|
|
462
|
+
# @param [String] table database name that may contain wild card
|
|
463
|
+
# @return [Array<String>] list of table name
|
|
464
|
+
def list_tables(table = nil)
|
|
465
|
+
q = table ? "show tables like '#{quote(table)}'" : "show tables"
|
|
442
466
|
query(q).map(&:first)
|
|
443
467
|
end
|
|
444
468
|
|
|
445
|
-
# Check whether the
|
|
446
|
-
# @return [
|
|
469
|
+
# Check whether the connection is available.
|
|
470
|
+
# @return [MysqlPR] self
|
|
447
471
|
def ping
|
|
448
472
|
check_connection
|
|
449
473
|
@protocol.ping_command
|
|
@@ -451,69 +475,68 @@ class MysqlPR
|
|
|
451
475
|
end
|
|
452
476
|
|
|
453
477
|
# Flush tables or caches.
|
|
454
|
-
# @param [Integer] op operation. Use MysqlPR::REFRESH_* value
|
|
455
|
-
# @return [
|
|
478
|
+
# @param [Integer] op operation. Use MysqlPR::REFRESH_* value
|
|
479
|
+
# @return [MysqlPR] self
|
|
456
480
|
def refresh(op)
|
|
457
481
|
check_connection
|
|
458
|
-
@protocol.refresh_command
|
|
482
|
+
@protocol.refresh_command(op)
|
|
459
483
|
self
|
|
460
484
|
end
|
|
461
485
|
|
|
462
486
|
# Reload grant tables.
|
|
463
|
-
# @return [
|
|
487
|
+
# @return [MysqlPR] self
|
|
464
488
|
def reload
|
|
465
|
-
refresh
|
|
489
|
+
refresh(MysqlPR::REFRESH_GRANT)
|
|
466
490
|
end
|
|
467
491
|
|
|
468
492
|
# Select default database
|
|
469
|
-
# @return [
|
|
493
|
+
# @return [MysqlPR] self
|
|
470
494
|
def select_db(db)
|
|
471
|
-
query
|
|
495
|
+
query("use #{db}")
|
|
472
496
|
self
|
|
473
497
|
end
|
|
474
498
|
|
|
475
|
-
#
|
|
476
|
-
# @return [
|
|
477
|
-
def shutdown(level=0)
|
|
499
|
+
# Shutdown server.
|
|
500
|
+
# @return [MysqlPR] self
|
|
501
|
+
def shutdown(level = 0)
|
|
478
502
|
check_connection
|
|
479
|
-
@protocol.shutdown_command
|
|
503
|
+
@protocol.shutdown_command(level)
|
|
480
504
|
self
|
|
481
505
|
end
|
|
482
506
|
|
|
483
507
|
# @return [String] statistics message
|
|
484
508
|
def stat
|
|
485
|
-
@protocol ? @protocol.statistics_command :
|
|
509
|
+
@protocol ? @protocol.statistics_command : "MySQL server has gone away"
|
|
486
510
|
end
|
|
487
511
|
|
|
488
512
|
# Commit transaction
|
|
489
|
-
# @return [
|
|
513
|
+
# @return [MysqlPR] self
|
|
490
514
|
def commit
|
|
491
|
-
query
|
|
515
|
+
query("commit")
|
|
492
516
|
self
|
|
493
517
|
end
|
|
494
518
|
|
|
495
519
|
# Rollback transaction
|
|
496
|
-
# @return [
|
|
520
|
+
# @return [MysqlPR] self
|
|
497
521
|
def rollback
|
|
498
|
-
query
|
|
522
|
+
query("rollback")
|
|
499
523
|
self
|
|
500
524
|
end
|
|
501
525
|
|
|
502
526
|
# Set autocommit mode
|
|
503
527
|
# @param [Boolean] flag
|
|
504
|
-
# @return [
|
|
528
|
+
# @return [MysqlPR] self
|
|
505
529
|
def autocommit(flag)
|
|
506
|
-
query
|
|
530
|
+
query("set autocommit=#{flag ? 1 : 0}")
|
|
507
531
|
self
|
|
508
532
|
end
|
|
509
533
|
|
|
510
534
|
private
|
|
511
535
|
|
|
512
536
|
def check_connection
|
|
513
|
-
raise ClientError::ServerGoneError,
|
|
537
|
+
raise ClientError::ServerGoneError, "The MySQL server has gone away" unless @protocol
|
|
514
538
|
end
|
|
515
539
|
|
|
516
|
-
# @!visibility public
|
|
517
540
|
# Field class
|
|
518
541
|
class Field
|
|
519
542
|
# @return [String] database name
|
|
@@ -536,123 +559,130 @@ class MysqlPR
|
|
|
536
559
|
attr_reader :flags
|
|
537
560
|
# @return [Integer] number of decimals
|
|
538
561
|
attr_reader :decimals
|
|
539
|
-
# @return [String]
|
|
562
|
+
# @return [String] default value
|
|
540
563
|
attr_reader :default
|
|
541
|
-
alias :def :default
|
|
542
564
|
|
|
543
|
-
|
|
565
|
+
alias def default
|
|
566
|
+
|
|
544
567
|
attr_accessor :result
|
|
568
|
+
attr_writer :max_length
|
|
545
569
|
|
|
546
|
-
# @
|
|
570
|
+
# @param [Protocol::FieldPacket] packet
|
|
547
571
|
def initialize(packet)
|
|
548
|
-
@db
|
|
549
|
-
|
|
550
|
-
@
|
|
572
|
+
@db = packet.db
|
|
573
|
+
@table = packet.table
|
|
574
|
+
@org_table = packet.org_table
|
|
575
|
+
@name = packet.name
|
|
576
|
+
@org_name = packet.org_name
|
|
577
|
+
@charsetnr = packet.charsetnr
|
|
578
|
+
@length = packet.length
|
|
579
|
+
@type = packet.type
|
|
580
|
+
@flags = packet.flags
|
|
581
|
+
@decimals = packet.decimals
|
|
582
|
+
@default = packet.default
|
|
583
|
+
@flags |= NUM_FLAG if num_type?
|
|
551
584
|
@max_length = nil
|
|
585
|
+
@result = nil
|
|
552
586
|
end
|
|
553
587
|
|
|
554
588
|
# @return [Hash] field information
|
|
555
|
-
def
|
|
589
|
+
def to_hash
|
|
556
590
|
{
|
|
557
|
-
"name"
|
|
558
|
-
"table"
|
|
559
|
-
"def"
|
|
560
|
-
"type"
|
|
561
|
-
"length"
|
|
591
|
+
"name" => @name,
|
|
592
|
+
"table" => @table,
|
|
593
|
+
"def" => @default,
|
|
594
|
+
"type" => @type,
|
|
595
|
+
"length" => @length,
|
|
562
596
|
"max_length" => max_length,
|
|
563
|
-
"flags"
|
|
564
|
-
"decimals"
|
|
597
|
+
"flags" => @flags,
|
|
598
|
+
"decimals" => @decimals
|
|
565
599
|
}
|
|
566
600
|
end
|
|
601
|
+
alias hash to_hash
|
|
567
602
|
|
|
568
|
-
# @private
|
|
569
603
|
def inspect
|
|
570
604
|
"#<MysqlPR::Field:#{@name}>"
|
|
571
605
|
end
|
|
572
606
|
|
|
573
|
-
# @return [Boolean] true if numeric field
|
|
607
|
+
# @return [Boolean] true if numeric field
|
|
574
608
|
def is_num?
|
|
575
|
-
@flags & NUM_FLAG != 0
|
|
609
|
+
(@flags & NUM_FLAG) != 0
|
|
576
610
|
end
|
|
577
611
|
|
|
578
|
-
# @return [Boolean] true if not null field
|
|
612
|
+
# @return [Boolean] true if not null field
|
|
579
613
|
def is_not_null?
|
|
580
|
-
@flags & NOT_NULL_FLAG != 0
|
|
614
|
+
(@flags & NOT_NULL_FLAG) != 0
|
|
581
615
|
end
|
|
582
616
|
|
|
583
|
-
# @return [Boolean] true if primary key field
|
|
617
|
+
# @return [Boolean] true if primary key field
|
|
584
618
|
def is_pri_key?
|
|
585
|
-
@flags & PRI_KEY_FLAG != 0
|
|
619
|
+
(@flags & PRI_KEY_FLAG) != 0
|
|
586
620
|
end
|
|
587
621
|
|
|
588
622
|
# @return [Integer] maximum width of the field for the result set
|
|
589
623
|
def max_length
|
|
590
624
|
return @max_length if @max_length
|
|
625
|
+
|
|
591
626
|
@max_length = 0
|
|
592
|
-
@result
|
|
627
|
+
@result&.calculate_field_max_length
|
|
593
628
|
@max_length
|
|
594
629
|
end
|
|
595
630
|
|
|
596
|
-
attr_writer :max_length
|
|
597
|
-
|
|
598
631
|
private
|
|
599
632
|
|
|
600
|
-
def
|
|
601
|
-
[TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT,
|
|
633
|
+
def num_type?
|
|
634
|
+
[TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT,
|
|
635
|
+
TYPE_DOUBLE, TYPE_LONGLONG, TYPE_INT24].include?(@type) ||
|
|
636
|
+
(@type == TYPE_TIMESTAMP && (@length == 14 || @length == 8))
|
|
602
637
|
end
|
|
603
|
-
|
|
604
638
|
end
|
|
605
639
|
|
|
606
|
-
#
|
|
607
|
-
# Result set
|
|
640
|
+
# Result set base class
|
|
608
641
|
class ResultBase
|
|
609
642
|
include Enumerable
|
|
610
643
|
|
|
611
644
|
# @return [Array<MysqlPR::Field>] field list
|
|
612
645
|
attr_reader :fields
|
|
613
646
|
|
|
614
|
-
# @param [Array
|
|
647
|
+
# @param [Array<MysqlPR::Field>] fields
|
|
615
648
|
def initialize(fields)
|
|
616
649
|
@fields = fields
|
|
617
|
-
@field_index = 0
|
|
618
|
-
@records = []
|
|
619
|
-
@index = 0
|
|
650
|
+
@field_index = 0
|
|
651
|
+
@records = []
|
|
652
|
+
@index = 0
|
|
620
653
|
@fieldname_with_table = nil
|
|
621
654
|
@fetched_record = nil
|
|
622
655
|
end
|
|
623
656
|
|
|
624
|
-
# ignore
|
|
625
657
|
# @return [void]
|
|
626
|
-
def free
|
|
627
|
-
end
|
|
658
|
+
def free; end
|
|
628
659
|
|
|
629
|
-
# @return [Integer] number of
|
|
660
|
+
# @return [Integer] number of records
|
|
630
661
|
def size
|
|
631
662
|
@records.size
|
|
632
663
|
end
|
|
633
664
|
alias num_rows size
|
|
634
665
|
|
|
635
|
-
# @return [Array] current record data
|
|
666
|
+
# @return [Array, nil] current record data
|
|
636
667
|
def fetch
|
|
637
668
|
@fetched_record = nil
|
|
638
669
|
return nil if @index >= @records.size
|
|
639
|
-
|
|
670
|
+
|
|
671
|
+
@records[@index] = @records[@index].to_a unless @records[@index].is_a?(Array)
|
|
640
672
|
@fetched_record = @records[@index]
|
|
641
673
|
@index += 1
|
|
642
|
-
|
|
674
|
+
@fetched_record
|
|
643
675
|
end
|
|
644
676
|
alias fetch_row fetch
|
|
645
677
|
|
|
646
678
|
# Return data of current record as Hash.
|
|
647
|
-
#
|
|
648
|
-
# @
|
|
649
|
-
|
|
650
|
-
def fetch_hash(with_table=nil)
|
|
679
|
+
# @param [Boolean] with_table if true, hash key is "table_name.field_name"
|
|
680
|
+
# @return [Hash, nil] current record data
|
|
681
|
+
def fetch_hash(with_table = nil)
|
|
651
682
|
row = fetch
|
|
652
683
|
return nil unless row
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
end
|
|
684
|
+
|
|
685
|
+
@fieldname_with_table = @fields.map { |f| "#{f.table}.#{f.name}" } if with_table && @fieldname_with_table.nil?
|
|
656
686
|
ret = {}
|
|
657
687
|
@fields.each_index do |i|
|
|
658
688
|
fname = with_table ? @fieldname_with_table[i] : @fields[i].name
|
|
@@ -663,30 +693,32 @@ class MysqlPR
|
|
|
663
693
|
|
|
664
694
|
# Iterate block with record.
|
|
665
695
|
# @yield [Array] record data
|
|
666
|
-
# @return [self
|
|
696
|
+
# @return [self, Enumerator]
|
|
667
697
|
def each(&block)
|
|
668
698
|
return enum_for(:each) unless block
|
|
669
|
-
|
|
670
|
-
|
|
699
|
+
|
|
700
|
+
while (rec = fetch)
|
|
701
|
+
block.call(rec)
|
|
671
702
|
end
|
|
672
703
|
self
|
|
673
704
|
end
|
|
674
705
|
|
|
675
706
|
# Iterate block with record as Hash.
|
|
676
|
-
# @param [Boolean] with_table if true, hash key is "table_name.field_name"
|
|
707
|
+
# @param [Boolean] with_table if true, hash key is "table_name.field_name"
|
|
677
708
|
# @yield [Hash] record data
|
|
678
|
-
# @return [self
|
|
679
|
-
def each_hash(with_table=nil, &block)
|
|
709
|
+
# @return [self, Enumerator]
|
|
710
|
+
def each_hash(with_table = nil, &block)
|
|
680
711
|
return enum_for(:each_hash, with_table) unless block
|
|
681
|
-
|
|
682
|
-
|
|
712
|
+
|
|
713
|
+
while (rec = fetch_hash(with_table))
|
|
714
|
+
block.call(rec)
|
|
683
715
|
end
|
|
684
716
|
self
|
|
685
717
|
end
|
|
686
718
|
|
|
687
719
|
# Set record position
|
|
688
720
|
# @param [Integer] n record index
|
|
689
|
-
# @return [self]
|
|
721
|
+
# @return [self]
|
|
690
722
|
def data_seek(n)
|
|
691
723
|
@index = n
|
|
692
724
|
self
|
|
@@ -707,27 +739,25 @@ class MysqlPR
|
|
|
707
739
|
end
|
|
708
740
|
end
|
|
709
741
|
|
|
710
|
-
# @!visibility public
|
|
711
742
|
# Result set for simple query
|
|
712
743
|
class Result < ResultBase
|
|
713
|
-
# @private
|
|
714
744
|
# @param [Array<MysqlPR::Field>] fields
|
|
715
|
-
# @param [MysqlPR::Protocol] protocol
|
|
716
|
-
def initialize(fields, protocol=nil)
|
|
717
|
-
super
|
|
745
|
+
# @param [MysqlPR::Protocol, nil] protocol
|
|
746
|
+
def initialize(fields, protocol = nil)
|
|
747
|
+
super(fields)
|
|
718
748
|
return unless protocol
|
|
719
|
-
|
|
720
|
-
|
|
749
|
+
|
|
750
|
+
@records = protocol.retr_all_records(fields.size)
|
|
751
|
+
fields.each { |f| f.result = self }
|
|
721
752
|
end
|
|
722
753
|
|
|
723
|
-
#
|
|
724
|
-
# calculate max_length of all fields
|
|
754
|
+
# Calculate max_length of all fields
|
|
725
755
|
def calculate_field_max_length
|
|
726
756
|
max_length = Array.new(@fields.size, 0)
|
|
727
757
|
@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[
|
|
758
|
+
rec = @records[i] = rec.to_a if rec.is_a?(RawRecord)
|
|
759
|
+
max_length.each_index do |j|
|
|
760
|
+
max_length[j] = rec[j].length if rec[j] && rec[j].length > max_length[j]
|
|
731
761
|
end
|
|
732
762
|
end
|
|
733
763
|
max_length.each_with_index do |len, i|
|
|
@@ -735,9 +765,10 @@ class MysqlPR
|
|
|
735
765
|
end
|
|
736
766
|
end
|
|
737
767
|
|
|
738
|
-
# @return [MysqlPR::Field] current field
|
|
768
|
+
# @return [MysqlPR::Field, nil] current field
|
|
739
769
|
def fetch_field
|
|
740
770
|
return nil if @field_index >= @fields.length
|
|
771
|
+
|
|
741
772
|
ret = @fields[@field_index]
|
|
742
773
|
@field_index += 1
|
|
743
774
|
ret
|
|
@@ -761,7 +792,8 @@ class MysqlPR
|
|
|
761
792
|
# @param [Integer] n field index
|
|
762
793
|
# @return [MysqlPR::Field] field
|
|
763
794
|
def fetch_field_direct(n)
|
|
764
|
-
raise ClientError, "invalid argument: #{n}" if n
|
|
795
|
+
raise ClientError, "invalid argument: #{n}" if n.negative? || n >= @fields.length
|
|
796
|
+
|
|
765
797
|
@fields[n]
|
|
766
798
|
end
|
|
767
799
|
|
|
@@ -770,10 +802,11 @@ class MysqlPR
|
|
|
770
802
|
@fields
|
|
771
803
|
end
|
|
772
804
|
|
|
773
|
-
# @return [Array<Integer
|
|
805
|
+
# @return [Array<Integer>, nil] length of each field
|
|
774
806
|
def fetch_lengths
|
|
775
807
|
return nil unless @fetched_record
|
|
776
|
-
|
|
808
|
+
|
|
809
|
+
@fetched_record.map { |c| c.nil? ? 0 : c.length }
|
|
777
810
|
end
|
|
778
811
|
|
|
779
812
|
# @return [Integer] number of fields
|
|
@@ -782,20 +815,17 @@ class MysqlPR
|
|
|
782
815
|
end
|
|
783
816
|
end
|
|
784
817
|
|
|
785
|
-
# @!visibility private
|
|
786
818
|
# Result set for prepared statement
|
|
787
819
|
class StatementResult < ResultBase
|
|
788
|
-
# @private
|
|
789
820
|
# @param [Array<MysqlPR::Field>] fields
|
|
790
821
|
# @param [MysqlPR::Protocol] protocol
|
|
791
822
|
# @param [MysqlPR::Charset] charset
|
|
792
823
|
def initialize(fields, protocol, charset)
|
|
793
|
-
super
|
|
794
|
-
@records = protocol.stmt_retr_all_records
|
|
824
|
+
super(fields)
|
|
825
|
+
@records = protocol.stmt_retr_all_records(@fields, charset)
|
|
795
826
|
end
|
|
796
827
|
end
|
|
797
828
|
|
|
798
|
-
# @!visibility public
|
|
799
829
|
# Prepared statement
|
|
800
830
|
# @!attribute [r] affected_rows
|
|
801
831
|
# @return [Integer]
|
|
@@ -814,33 +844,35 @@ class MysqlPR
|
|
|
814
844
|
class Stmt
|
|
815
845
|
include Enumerable
|
|
816
846
|
|
|
817
|
-
attr_reader :affected_rows, :insert_id, :server_status, :warning_count
|
|
818
|
-
attr_reader :param_count, :fields, :sqlstate
|
|
847
|
+
attr_reader :affected_rows, :insert_id, :server_status, :warning_count, :param_count, :fields, :sqlstate
|
|
819
848
|
|
|
820
|
-
# @private
|
|
821
849
|
def self.finalizer(protocol, statement_id)
|
|
822
850
|
proc do
|
|
823
|
-
protocol.gc_stmt
|
|
851
|
+
protocol.gc_stmt(statement_id)
|
|
824
852
|
end
|
|
825
853
|
end
|
|
826
854
|
|
|
827
|
-
# @private
|
|
828
855
|
# @param [MysqlPR::Protocol] protocol
|
|
829
856
|
# @param [MysqlPR::Charset] charset
|
|
830
857
|
def initialize(protocol, charset)
|
|
831
858
|
@protocol = protocol
|
|
832
859
|
@charset = charset
|
|
833
860
|
@statement_id = nil
|
|
834
|
-
@affected_rows =
|
|
861
|
+
@affected_rows = 0
|
|
862
|
+
@insert_id = 0
|
|
863
|
+
@server_status = 0
|
|
864
|
+
@warning_count = 0
|
|
835
865
|
@sqlstate = "00000"
|
|
836
866
|
@param_count = nil
|
|
837
867
|
@bind_result = nil
|
|
868
|
+
@result = nil
|
|
869
|
+
@fields = nil
|
|
870
|
+
@last_error = nil
|
|
838
871
|
end
|
|
839
872
|
|
|
840
|
-
#
|
|
841
|
-
# parse prepared-statement and return {MysqlPR::Stmt} object
|
|
873
|
+
# Parse prepared-statement and return {MysqlPR::Stmt} object
|
|
842
874
|
# @param [String] str query string
|
|
843
|
-
# @return self
|
|
875
|
+
# @return [MysqlPR::Stmt] self
|
|
844
876
|
def prepare(str)
|
|
845
877
|
close
|
|
846
878
|
begin
|
|
@@ -861,18 +893,22 @@ class MysqlPR
|
|
|
861
893
|
def execute(*values)
|
|
862
894
|
raise ClientError, "not prepared" unless @param_count
|
|
863
895
|
raise ClientError, "parameter count mismatch" if values.length != @param_count
|
|
864
|
-
|
|
896
|
+
|
|
897
|
+
values = values.map { |v| @charset.convert(v) }
|
|
865
898
|
begin
|
|
866
899
|
@sqlstate = "00000"
|
|
867
|
-
nfields = @protocol.stmt_execute_command
|
|
900
|
+
nfields = @protocol.stmt_execute_command(@statement_id, values)
|
|
868
901
|
if nfields
|
|
869
|
-
@fields = @protocol.retr_fields
|
|
870
|
-
@result = StatementResult.new
|
|
902
|
+
@fields = @protocol.retr_fields(nfields)
|
|
903
|
+
@result = StatementResult.new(@fields, @protocol, @charset)
|
|
871
904
|
else
|
|
872
|
-
@affected_rows
|
|
873
|
-
|
|
905
|
+
@affected_rows = @protocol.affected_rows
|
|
906
|
+
@insert_id = @protocol.insert_id
|
|
907
|
+
@server_status = @protocol.server_status
|
|
908
|
+
@warning_count = @protocol.warning_count
|
|
909
|
+
@info = @protocol.message
|
|
874
910
|
end
|
|
875
|
-
|
|
911
|
+
self
|
|
876
912
|
rescue ServerError => e
|
|
877
913
|
@last_error = e
|
|
878
914
|
@sqlstate = e.sqlstate
|
|
@@ -884,48 +920,26 @@ class MysqlPR
|
|
|
884
920
|
# @return [void]
|
|
885
921
|
def close
|
|
886
922
|
ObjectSpace.undefine_finalizer(self)
|
|
887
|
-
@protocol.stmt_close_command
|
|
923
|
+
@protocol.stmt_close_command(@statement_id) if @statement_id
|
|
888
924
|
@statement_id = nil
|
|
889
925
|
end
|
|
890
926
|
|
|
891
|
-
# @return [Array] current record data
|
|
927
|
+
# @return [Array, nil] current record data
|
|
892
928
|
def fetch
|
|
893
929
|
row = @result.fetch
|
|
894
930
|
return row unless @bind_result
|
|
931
|
+
|
|
895
932
|
row.zip(@bind_result).map do |col, type|
|
|
896
933
|
if col.nil?
|
|
897
934
|
nil
|
|
898
|
-
elsif [Numeric, Integer
|
|
935
|
+
elsif [Numeric, Integer].include?(type)
|
|
899
936
|
col.to_i
|
|
900
937
|
elsif type == String
|
|
901
938
|
col.to_s
|
|
902
939
|
elsif type == Float && !col.is_a?(Float)
|
|
903
940
|
col.to_i.to_f
|
|
904
941
|
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
|
|
942
|
+
parse_time_value(col)
|
|
929
943
|
else
|
|
930
944
|
col
|
|
931
945
|
end
|
|
@@ -933,22 +947,24 @@ class MysqlPR
|
|
|
933
947
|
end
|
|
934
948
|
|
|
935
949
|
# Return data of current record as Hash.
|
|
936
|
-
#
|
|
937
|
-
# @
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
@result.fetch_hash with_table
|
|
950
|
+
# @param [Boolean] with_table if true, hash key is "table_name.field_name"
|
|
951
|
+
# @return [Hash, nil] record data
|
|
952
|
+
def fetch_hash(with_table = nil)
|
|
953
|
+
@result.fetch_hash(with_table)
|
|
941
954
|
end
|
|
942
955
|
|
|
943
956
|
# Set retrieve type of value
|
|
944
|
-
# @param [
|
|
957
|
+
# @param [Class] args value type (Numeric, Integer, Float, String, MysqlPR::Time, or nil)
|
|
945
958
|
# @return [MysqlPR::Stmt] self
|
|
946
959
|
def bind_result(*args)
|
|
947
960
|
if @fields.length != args.length
|
|
948
961
|
raise ClientError, "bind_result: result value count(#{@fields.length}) != number of argument(#{args.length})"
|
|
949
962
|
end
|
|
963
|
+
|
|
950
964
|
args.each do |a|
|
|
951
|
-
|
|
965
|
+
unless [Numeric, Integer, Float, String, MysqlPR::Time, nil].include?(a)
|
|
966
|
+
raise TypeError, "unsupported type: #{a}"
|
|
967
|
+
end
|
|
952
968
|
end
|
|
953
969
|
@bind_result = args
|
|
954
970
|
self
|
|
@@ -956,30 +972,30 @@ class MysqlPR
|
|
|
956
972
|
|
|
957
973
|
# Iterate block with record.
|
|
958
974
|
# @yield [Array] record data
|
|
959
|
-
# @return [MysqlPR::Stmt] self
|
|
960
|
-
# @return [Enumerator] If block is not specified
|
|
975
|
+
# @return [MysqlPR::Stmt, Enumerator] self or Enumerator if block not specified
|
|
961
976
|
def each(&block)
|
|
962
977
|
return enum_for(:each) unless block
|
|
963
|
-
|
|
964
|
-
|
|
978
|
+
|
|
979
|
+
while (rec = fetch)
|
|
980
|
+
block.call(rec)
|
|
965
981
|
end
|
|
966
982
|
self
|
|
967
983
|
end
|
|
968
984
|
|
|
969
985
|
# Iterate block with record as Hash.
|
|
970
|
-
# @param [Boolean] with_table if true, hash key is "table_name.field_name"
|
|
986
|
+
# @param [Boolean] with_table if true, hash key is "table_name.field_name"
|
|
971
987
|
# @yield [Hash] record data
|
|
972
|
-
# @return [MysqlPR::Stmt] self
|
|
973
|
-
|
|
974
|
-
def each_hash(with_table=nil, &block)
|
|
988
|
+
# @return [MysqlPR::Stmt, Enumerator] self or Enumerator if block not specified
|
|
989
|
+
def each_hash(with_table = nil, &block)
|
|
975
990
|
return enum_for(:each_hash, with_table) unless block
|
|
976
|
-
|
|
977
|
-
|
|
991
|
+
|
|
992
|
+
while (rec = fetch_hash(with_table))
|
|
993
|
+
block.call(rec)
|
|
978
994
|
end
|
|
979
995
|
self
|
|
980
996
|
end
|
|
981
997
|
|
|
982
|
-
# @return [Integer] number of
|
|
998
|
+
# @return [Integer] number of records
|
|
983
999
|
def size
|
|
984
1000
|
@result.size
|
|
985
1001
|
end
|
|
@@ -1009,21 +1025,44 @@ class MysqlPR
|
|
|
1009
1025
|
@fields.length
|
|
1010
1026
|
end
|
|
1011
1027
|
|
|
1012
|
-
# ignore
|
|
1013
1028
|
# @return [void]
|
|
1014
|
-
def free_result
|
|
1015
|
-
end
|
|
1029
|
+
def free_result; end
|
|
1016
1030
|
|
|
1017
1031
|
# Returns MysqlPR::Result object that is empty.
|
|
1018
1032
|
# Use fetch_fields to get list of fields.
|
|
1019
|
-
# @return [MysqlPR::Result]
|
|
1033
|
+
# @return [MysqlPR::Result, nil]
|
|
1020
1034
|
def result_metadata
|
|
1021
1035
|
return nil if @fields.empty?
|
|
1022
|
-
|
|
1036
|
+
|
|
1037
|
+
Result.new(@fields)
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
private
|
|
1041
|
+
|
|
1042
|
+
def parse_time_value(col)
|
|
1043
|
+
return MysqlPR::Time.new unless col.to_s =~ /\A\d+\z/
|
|
1044
|
+
|
|
1045
|
+
i = col.to_s.to_i
|
|
1046
|
+
if i < 100_000_000
|
|
1047
|
+
y = i / 10_000
|
|
1048
|
+
m = (i / 100) % 100
|
|
1049
|
+
d = i % 100
|
|
1050
|
+
h = mm = s = 0
|
|
1051
|
+
else
|
|
1052
|
+
y = i / 10_000_000_000
|
|
1053
|
+
m = (i / 100_000_000) % 100
|
|
1054
|
+
d = (i / 1_000_000) % 100
|
|
1055
|
+
h = (i / 10_000) % 100
|
|
1056
|
+
mm = (i / 100) % 100
|
|
1057
|
+
s = i % 100
|
|
1058
|
+
end
|
|
1059
|
+
y += 2000 if y < 70
|
|
1060
|
+
y += 1900 if y >= 70 && y < 100
|
|
1061
|
+
MysqlPR::Time.new(y, m, d, h, mm, s)
|
|
1023
1062
|
end
|
|
1024
1063
|
end
|
|
1025
1064
|
|
|
1026
|
-
#
|
|
1065
|
+
# MySQL Time class
|
|
1027
1066
|
# @!attribute [rw] year
|
|
1028
1067
|
# @return [Integer]
|
|
1029
1068
|
# @!attribute [rw] month
|
|
@@ -1041,6 +1080,12 @@ class MysqlPR
|
|
|
1041
1080
|
# @!attribute [rw] second_part
|
|
1042
1081
|
# @return [Integer]
|
|
1043
1082
|
class Time
|
|
1083
|
+
attr_accessor :year, :month, :day, :hour, :minute, :second, :neg, :second_part
|
|
1084
|
+
|
|
1085
|
+
alias mon month
|
|
1086
|
+
alias min minute
|
|
1087
|
+
alias sec second
|
|
1088
|
+
|
|
1044
1089
|
# @param [Integer] year
|
|
1045
1090
|
# @param [Integer] month
|
|
1046
1091
|
# @param [Integer] day
|
|
@@ -1049,25 +1094,25 @@ class MysqlPR
|
|
|
1049
1094
|
# @param [Integer] second
|
|
1050
1095
|
# @param [Boolean] neg negative flag
|
|
1051
1096
|
# @param [Integer] second_part
|
|
1052
|
-
def initialize(year=0, month=0, day=0, hour=0, minute=0, second=0, neg=false, second_part=0)
|
|
1097
|
+
def initialize(year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, neg = false, second_part = 0)
|
|
1053
1098
|
@date_flag = !(hour && minute && second)
|
|
1054
|
-
@year
|
|
1055
|
-
|
|
1099
|
+
@year = year.to_i
|
|
1100
|
+
@month = month.to_i
|
|
1101
|
+
@day = day.to_i
|
|
1102
|
+
@hour = hour.to_i
|
|
1103
|
+
@minute = minute.to_i
|
|
1104
|
+
@second = second.to_i
|
|
1105
|
+
@neg = neg
|
|
1106
|
+
@second_part = second_part.to_i
|
|
1056
1107
|
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
1108
|
|
|
1062
|
-
# @private
|
|
1063
1109
|
def ==(other)
|
|
1064
1110
|
other.is_a?(MysqlPR::Time) &&
|
|
1065
1111
|
@year == other.year && @month == other.month && @day == other.day &&
|
|
1066
1112
|
@hour == other.hour && @minute == other.minute && @second == other.second &&
|
|
1067
|
-
@neg == neg && @second_part == other.second_part
|
|
1113
|
+
@neg == other.neg && @second_part == other.second_part
|
|
1068
1114
|
end
|
|
1069
1115
|
|
|
1070
|
-
# @private
|
|
1071
1116
|
def eql?(other)
|
|
1072
1117
|
self == other
|
|
1073
1118
|
end
|
|
@@ -1075,25 +1120,22 @@ class MysqlPR
|
|
|
1075
1120
|
# @return [String] "yyyy-mm-dd HH:MM:SS"
|
|
1076
1121
|
def to_s
|
|
1077
1122
|
if @date_flag
|
|
1078
|
-
|
|
1079
|
-
elsif year
|
|
1123
|
+
format("%04d-%02d-%02d", year, mon, day)
|
|
1124
|
+
elsif year.zero? && mon.zero? && day.zero?
|
|
1080
1125
|
h = neg ? hour * -1 : hour
|
|
1081
|
-
|
|
1126
|
+
format("%02d:%02d:%02d", h, min, sec)
|
|
1082
1127
|
else
|
|
1083
|
-
|
|
1128
|
+
format("%04d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec)
|
|
1084
1129
|
end
|
|
1085
1130
|
end
|
|
1086
1131
|
|
|
1087
1132
|
# @return [Integer] yyyymmddHHMMSS
|
|
1088
1133
|
def to_i
|
|
1089
|
-
|
|
1134
|
+
format("%04d%02d%02d%02d%02d%02d", year, mon, day, hour, min, sec).to_i
|
|
1090
1135
|
end
|
|
1091
1136
|
|
|
1092
|
-
# @private
|
|
1093
1137
|
def inspect
|
|
1094
|
-
|
|
1138
|
+
format("#<#{self.class.name}:%04d-%02d-%02d %02d:%02d:%02d>", year, mon, day, hour, min, sec)
|
|
1095
1139
|
end
|
|
1096
|
-
|
|
1097
1140
|
end
|
|
1098
|
-
|
|
1099
1141
|
end
|