mysql2 0.4.4 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +90 -52
- data/examples/eventmachine.rb +0 -2
- data/examples/threaded.rb +2 -4
- data/ext/mysql2/client.c +260 -76
- data/ext/mysql2/client.h +2 -47
- data/ext/mysql2/extconf.rb +44 -18
- data/ext/mysql2/mysql2_ext.c +2 -1
- data/ext/mysql2/mysql2_ext.h +8 -8
- data/ext/mysql2/result.c +23 -74
- data/ext/mysql2/statement.c +139 -63
- data/ext/mysql2/statement.h +0 -2
- data/ext/mysql2/wait_for_single_fd.h +2 -1
- data/lib/mysql2/client.rb +50 -31
- data/lib/mysql2/em.rb +2 -4
- data/lib/mysql2/error.rb +49 -20
- data/lib/mysql2/result.rb +2 -0
- data/lib/mysql2/statement.rb +3 -9
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +14 -15
- data/spec/configuration.yml.example +0 -6
- data/spec/em/em_spec.rb +6 -6
- data/spec/mysql2/client_spec.rb +318 -237
- data/spec/mysql2/error_spec.rb +4 -10
- data/spec/mysql2/result_spec.rb +124 -158
- data/spec/mysql2/statement_spec.rb +185 -180
- data/spec/spec_helper.rb +79 -61
- data/spec/ssl/gen_certs.sh +1 -1
- data/support/5072E1F5.asc +432 -0
- data/support/mysql_enc_to_ruby.rb +2 -2
- data/support/ruby_enc_to_mysql.rb +5 -5
- metadata +16 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 658cf051d498ace207785b75cead2b0f938bba92
|
4
|
+
data.tar.gz: 7f50c5cf9a45656a5347809d4494ee0c17eb8208
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '05994292d6c1de48d3fcc1af99c5c2ec85c09a6bb1111e19129d9434fcdf191d838a91fee9d61d6a67576a6ff316a39b418b9cf13e159d99cbbad2e49eb28e78'
|
7
|
+
data.tar.gz: 5d668a60aa6ed4053787b84c1cb3114604b2c22c225492b53124d7d023fd02f6c9b54f848453fedce72ac64092723b989f3cafb471605ffa5606444cf0a4d26b
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ The Mysql2 gem is meant to serve the extremely common use-case of connecting, qu
|
|
7
7
|
Some database libraries out there serve as direct 1:1 mappings of the already complex C APIs available.
|
8
8
|
This one is not.
|
9
9
|
|
10
|
-
It also forces the use of UTF-8 [or binary] for the connection
|
10
|
+
It also forces the use of UTF-8 [or binary] for the connection and uses encoding-aware MySQL API calls where it can.
|
11
11
|
|
12
12
|
The API consists of three classes:
|
13
13
|
|
@@ -85,6 +85,9 @@ You may use MacPorts, Homebrew, or a native MySQL installer package. The most
|
|
85
85
|
common paths will be automatically searched. If you want to select a specific
|
86
86
|
MySQL directory, use the `--with-mysql-dir` or `--with-mysql-config` options above.
|
87
87
|
|
88
|
+
If you have not done so already, you will need to install the XCode select tools by running
|
89
|
+
`xcode-select --install`.
|
90
|
+
|
88
91
|
### Windows
|
89
92
|
Make sure that you have Ruby and the DevKit compilers installed. We recommend
|
90
93
|
the [Ruby Installer](http://rubyinstaller.org) distribution.
|
@@ -109,7 +112,7 @@ Connect to a database:
|
|
109
112
|
``` ruby
|
110
113
|
# this takes a hash of options, almost all of which map directly
|
111
114
|
# to the familiar database.yml in rails
|
112
|
-
# See http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/
|
115
|
+
# See http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Mysql2Adapter.html
|
113
116
|
client = Mysql2::Client.new(:host => "localhost", :username => "root")
|
114
117
|
```
|
115
118
|
|
@@ -135,7 +138,7 @@ results.each do |row|
|
|
135
138
|
# conveniently, row is a hash
|
136
139
|
# the keys are the fields, as you'd expect
|
137
140
|
# the values are pre-built ruby primitives mapped from their corresponding field types in MySQL
|
138
|
-
puts row["id"] # row["id"].
|
141
|
+
puts row["id"] # row["id"].is_a? Integer
|
139
142
|
if row["dne"] # non-existant hash entry is nil
|
140
143
|
puts row["dne"]
|
141
144
|
end
|
@@ -164,15 +167,16 @@ by the query like this:
|
|
164
167
|
``` ruby
|
165
168
|
headers = results.fields # <= that's an array of field names, in order
|
166
169
|
results.each(:as => :array) do |row|
|
167
|
-
# Each row is an array, ordered the same as the query results
|
168
|
-
# An otter's den is called a "holt" or "couch"
|
170
|
+
# Each row is an array, ordered the same as the query results
|
171
|
+
# An otter's den is called a "holt" or "couch"
|
169
172
|
end
|
170
173
|
```
|
171
174
|
|
172
175
|
Prepared statements are supported, as well. In a prepared statement, use a `?`
|
173
176
|
in place of each value and then execute the statement to retrieve a result set.
|
174
177
|
Pass your arguments to the execute method in the same number and order as the
|
175
|
-
question marks in the statement.
|
178
|
+
question marks in the statement. Query options can be passed as keyword arguments
|
179
|
+
to the execute method.
|
176
180
|
|
177
181
|
``` ruby
|
178
182
|
statement = @client.prepare("SELECT * FROM users WHERE login_count = ?")
|
@@ -181,6 +185,9 @@ result2 = statement.execute(2)
|
|
181
185
|
|
182
186
|
statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND location LIKE ?")
|
183
187
|
result = statement.execute(1, "CA")
|
188
|
+
|
189
|
+
statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND location LIKE ?")
|
190
|
+
result = statement.execute(1, "CA", :as => :array)
|
184
191
|
```
|
185
192
|
|
186
193
|
## Connection options
|
@@ -200,15 +207,29 @@ Mysql2::Client.new(
|
|
200
207
|
:read_timeout = seconds,
|
201
208
|
:write_timeout = seconds,
|
202
209
|
:connect_timeout = seconds,
|
210
|
+
:connect_attrs = {:program_name => $PROGRAM_NAME, ...},
|
203
211
|
:reconnect = true/false,
|
204
212
|
:local_infile = true/false,
|
205
213
|
:secure_auth = true/false,
|
214
|
+
:ssl_mode = :disabled / :preferred / :required / :verify_ca / :verify_identity,
|
206
215
|
:default_file = '/path/to/my.cfg',
|
207
216
|
:default_group = 'my.cfg section',
|
208
217
|
:init_command => sql
|
209
218
|
)
|
210
219
|
```
|
211
220
|
|
221
|
+
### Connecting to MySQL on localhost and elsewhere
|
222
|
+
|
223
|
+
The underlying MySQL client library uses the `:host` parameter to determine the
|
224
|
+
type of connection to make, with special interpretation you should be aware of:
|
225
|
+
|
226
|
+
* An empty value or `"localhost"` will attempt a local connection:
|
227
|
+
* On Unix, connect to the default local socket path. (To set a custom socket
|
228
|
+
path, use the `:socket` parameter).
|
229
|
+
* On Windows, connect using a shared-memory connection, if enabled, or TCP.
|
230
|
+
* A value of `"."` on Windows specifies a named-pipe connection.
|
231
|
+
* An IPv4 or IPv6 address will result in a TCP connection.
|
232
|
+
* Any other value will be looked up as a hostname for a TCP connection.
|
212
233
|
|
213
234
|
### SSL options
|
214
235
|
|
@@ -231,47 +252,6 @@ Mysql2::Client.new(
|
|
231
252
|
)
|
232
253
|
```
|
233
254
|
|
234
|
-
### Multiple result sets
|
235
|
-
|
236
|
-
You can also retrieve multiple result sets. For this to work you need to
|
237
|
-
connect with flags `Mysql2::Client::MULTI_STATEMENTS`. Multiple result sets can
|
238
|
-
be used with stored procedures that return more than one result set, and for
|
239
|
-
bundling several SQL statements into a single call to `client.query`.
|
240
|
-
|
241
|
-
``` ruby
|
242
|
-
client = Mysql2::Client.new(:host => "localhost", :username => "root", :flags => Mysql2::Client::MULTI_STATEMENTS)
|
243
|
-
result = client.query('CALL sp_customer_list( 25, 10 )')
|
244
|
-
# result now contains the first result set
|
245
|
-
while client.next_result
|
246
|
-
result = client.store_result
|
247
|
-
# result now contains the next result set
|
248
|
-
end
|
249
|
-
```
|
250
|
-
|
251
|
-
Repeated calls to `client.next_result` will return true, false, or raise an
|
252
|
-
exception if the respective query erred. When `client.next_result` returns true,
|
253
|
-
call `client.store_result` to retrieve a result object. Exceptions are not
|
254
|
-
raised until `client.next_result` is called to find the status of the respective
|
255
|
-
query. Subsequent queries are not executed if an earlier query raised an
|
256
|
-
exception. Subsequent calls to `client.next_result` will return false.
|
257
|
-
|
258
|
-
``` ruby
|
259
|
-
result = client.query('SELECT 1; SELECT 2; SELECT A; SELECT 3')
|
260
|
-
p result.first
|
261
|
-
|
262
|
-
while client.next_result
|
263
|
-
result = client.store_result
|
264
|
-
p result.first
|
265
|
-
end
|
266
|
-
```
|
267
|
-
|
268
|
-
Yields:
|
269
|
-
```
|
270
|
-
{"1"=>1}
|
271
|
-
{"2"=>2}
|
272
|
-
next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
|
273
|
-
```
|
274
|
-
|
275
255
|
### Secure auth
|
276
256
|
|
277
257
|
Starting wih MySQL 5.6.5, secure_auth is enabled by default on servers (it was disabled by default prior to this).
|
@@ -328,6 +308,47 @@ It is useful if you want to provide session options which survive reconnection.
|
|
328
308
|
Mysql2::Client.new(:init_command => "SET @@SESSION.sql_mode = 'STRICT_ALL_TABLES'")
|
329
309
|
```
|
330
310
|
|
311
|
+
### Multiple result sets
|
312
|
+
|
313
|
+
You can also retrieve multiple result sets. For this to work you need to
|
314
|
+
connect with flags `Mysql2::Client::MULTI_STATEMENTS`. Multiple result sets can
|
315
|
+
be used with stored procedures that return more than one result set, and for
|
316
|
+
bundling several SQL statements into a single call to `client.query`.
|
317
|
+
|
318
|
+
``` ruby
|
319
|
+
client = Mysql2::Client.new(:host => "localhost", :username => "root", :flags => Mysql2::Client::MULTI_STATEMENTS)
|
320
|
+
result = client.query('CALL sp_customer_list( 25, 10 )')
|
321
|
+
# result now contains the first result set
|
322
|
+
while client.next_result
|
323
|
+
result = client.store_result
|
324
|
+
# result now contains the next result set
|
325
|
+
end
|
326
|
+
```
|
327
|
+
|
328
|
+
Repeated calls to `client.next_result` will return true, false, or raise an
|
329
|
+
exception if the respective query erred. When `client.next_result` returns true,
|
330
|
+
call `client.store_result` to retrieve a result object. Exceptions are not
|
331
|
+
raised until `client.next_result` is called to find the status of the respective
|
332
|
+
query. Subsequent queries are not executed if an earlier query raised an
|
333
|
+
exception. Subsequent calls to `client.next_result` will return false.
|
334
|
+
|
335
|
+
``` ruby
|
336
|
+
result = client.query('SELECT 1; SELECT 2; SELECT A; SELECT 3')
|
337
|
+
p result.first
|
338
|
+
|
339
|
+
while client.next_result
|
340
|
+
result = client.store_result
|
341
|
+
p result.first
|
342
|
+
end
|
343
|
+
```
|
344
|
+
|
345
|
+
Yields:
|
346
|
+
```
|
347
|
+
{"1"=>1}
|
348
|
+
{"2"=>2}
|
349
|
+
next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
|
350
|
+
```
|
351
|
+
|
331
352
|
## Cascading config
|
332
353
|
|
333
354
|
The default config hash is at:
|
@@ -365,6 +386,15 @@ c = Mysql2::Client.new
|
|
365
386
|
c.query(sql, :symbolize_keys => true)
|
366
387
|
```
|
367
388
|
|
389
|
+
or
|
390
|
+
|
391
|
+
``` ruby
|
392
|
+
# this will set the options for the Mysql2::Result instance returned from the #execute method
|
393
|
+
c = Mysql2::Client.new
|
394
|
+
s = c.prepare(sql)
|
395
|
+
s.execute(arg1, args2, :symbolize_keys => true)
|
396
|
+
```
|
397
|
+
|
368
398
|
## Result types
|
369
399
|
|
370
400
|
### Array of Arrays
|
@@ -398,6 +428,15 @@ client = Mysql2::Client.new
|
|
398
428
|
result = client.query("SELECT * FROM table_with_boolean_field", :cast_booleans => true)
|
399
429
|
```
|
400
430
|
|
431
|
+
Keep in mind that this works only with fields and not with computed values, e.g. this result will contain `1`, not `true`:
|
432
|
+
|
433
|
+
``` ruby
|
434
|
+
client = Mysql2::Client.new
|
435
|
+
result = client.query("SELECT true", :cast_booleans => true)
|
436
|
+
```
|
437
|
+
|
438
|
+
CAST function wouldn't help here as there's no way to cast to TINYINT(1). Apparently the only way to solve this is to use a stored procedure with return type set to TINYINT(1).
|
439
|
+
|
401
440
|
### Skipping casting
|
402
441
|
|
403
442
|
Mysql2 casting is fast, but not as fast as not casting data. In rare cases where typecasting is not needed, it will be faster to disable it by providing :cast => false. (Note that :cast => false overrides :cast_booleans => true.)
|
@@ -484,15 +523,14 @@ As for field values themselves, I'm workin on it - but expect that soon.
|
|
484
523
|
|
485
524
|
This gem is tested with the following Ruby versions on Linux and Mac OS X:
|
486
525
|
|
487
|
-
* Ruby MRI
|
488
|
-
*
|
489
|
-
* Rubinius 2.x, 3.x
|
526
|
+
* Ruby MRI 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x, 2.5.x, 2.6.x
|
527
|
+
* Rubinius 2.x and 3.x do work but may fail under some workloads
|
490
528
|
|
491
529
|
This gem is tested with the following MySQL and MariaDB versions:
|
492
530
|
|
493
|
-
* MySQL 5.5, 5.6, 5.7
|
531
|
+
* MySQL 5.5, 5.6, 5.7, 8.0
|
494
532
|
* MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
|
495
|
-
* MariaDB 5.5, 10.0, 10.1
|
533
|
+
* MariaDB 5.5, 10.0, 10.1, 10.2, 10.3
|
496
534
|
|
497
535
|
### Ruby on Rails / Active Record
|
498
536
|
|
data/examples/eventmachine.rb
CHANGED
data/examples/threaded.rb
CHANGED
@@ -1,17 +1,15 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
$LOAD_PATH.unshift 'lib'
|
4
2
|
require 'mysql2'
|
5
3
|
require 'timeout'
|
6
4
|
|
7
5
|
# Should never exceed worst case 3.5 secs across all 20 threads
|
8
6
|
Timeout.timeout(3.5) do
|
9
|
-
20
|
7
|
+
Array.new(20) do
|
10
8
|
Thread.new do
|
11
9
|
overhead = rand(3)
|
12
10
|
puts ">> thread #{Thread.current.object_id} query, #{overhead} sec overhead"
|
13
11
|
# 3 second overhead per query
|
14
|
-
Mysql2::Client.new(:
|
12
|
+
Mysql2::Client.new(host: "localhost", username: "root").query("SELECT sleep(#{overhead}) as result")
|
15
13
|
puts "<< thread #{Thread.current.object_id} result, #{overhead} sec overhead"
|
16
14
|
end
|
17
15
|
end.each(&:join)
|