mysql2 0.3.21 → 0.4.2
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 +4 -4
- data/CHANGELOG.md +1 -0
- data/README.md +63 -12
- data/examples/eventmachine.rb +1 -1
- data/examples/threaded.rb +4 -6
- data/ext/mysql2/client.c +100 -95
- data/ext/mysql2/client.h +21 -1
- data/ext/mysql2/extconf.rb +86 -43
- data/ext/mysql2/infile.c +2 -2
- data/ext/mysql2/mysql2_ext.c +1 -0
- data/ext/mysql2/mysql2_ext.h +5 -6
- data/ext/mysql2/mysql_enc_name_to_ruby.h +2 -2
- data/ext/mysql2/mysql_enc_to_ruby.h +25 -22
- data/ext/mysql2/result.c +469 -99
- data/ext/mysql2/result.h +11 -4
- data/ext/mysql2/statement.c +494 -0
- data/ext/mysql2/statement.h +19 -0
- data/lib/mysql2/client.rb +52 -22
- data/lib/mysql2/console.rb +1 -1
- data/lib/mysql2/em.rb +5 -6
- data/lib/mysql2/error.rb +17 -26
- data/lib/mysql2/field.rb +3 -0
- data/lib/mysql2/statement.rb +17 -0
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +37 -35
- data/spec/em/em_spec.rb +21 -21
- data/spec/mysql2/client_spec.rb +340 -302
- data/spec/mysql2/error_spec.rb +37 -36
- data/spec/mysql2/result_spec.rb +200 -209
- data/spec/mysql2/statement_spec.rb +684 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/ssl/ca-cert.pem +17 -0
- data/spec/ssl/ca-key.pem +27 -0
- data/spec/ssl/ca.cnf +22 -0
- data/spec/ssl/cert.cnf +22 -0
- data/spec/ssl/client-cert.pem +17 -0
- data/spec/ssl/client-key.pem +27 -0
- data/spec/ssl/client-req.pem +15 -0
- data/spec/ssl/gen_certs.sh +48 -0
- data/spec/ssl/pkcs8-client-key.pem +28 -0
- data/spec/ssl/pkcs8-server-key.pem +28 -0
- data/spec/ssl/server-cert.pem +17 -0
- data/spec/ssl/server-key.pem +27 -0
- data/spec/ssl/server-req.pem +15 -0
- data/support/mysql_enc_to_ruby.rb +7 -8
- data/support/ruby_enc_to_mysql.rb +1 -1
- metadata +41 -47
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bfe50d56c4b02379320402f6c9a50e1c004855b1
|
|
4
|
+
data.tar.gz: 495a7a40b1a1fc006e8f3816b0b215c8a82f4585
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 076e0ae48e790418cc2272bf14f3f93859b751452eeaaefd43537a22266bf5cab6d443645b06d2e3bcaab3be2768e65bc5b699bb5e100e10e5a35911d4e96c33
|
|
7
|
+
data.tar.gz: b921cb3b96ef7428db4870ca82719a52b7921d24fed356ba6765ff1890a98ae8c0c1351e8c158fa098d8652aebf4b958ec1cb3eeb46ff2e8c2b9e58d69dde1d7
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Changes are maintained under [Releases](https://github.com/brianmario/mysql2/releases)
|
data/README.md
CHANGED
|
@@ -9,12 +9,14 @@ This one is not.
|
|
|
9
9
|
|
|
10
10
|
It also forces the use of UTF-8 [or binary] for the connection [and all strings in 1.9, unless Encoding.default_internal is set then it'll convert from UTF-8 to that encoding] and uses encoding-aware MySQL API calls where it can.
|
|
11
11
|
|
|
12
|
-
The API consists of
|
|
12
|
+
The API consists of three classes:
|
|
13
13
|
|
|
14
14
|
`Mysql2::Client` - your connection to the database.
|
|
15
15
|
|
|
16
16
|
`Mysql2::Result` - returned from issuing a #query on the connection. It includes Enumerable.
|
|
17
17
|
|
|
18
|
+
`Mysql2::Statement` - returned from issuing a #prepare on the connection. Execute the statement to get a Result.
|
|
19
|
+
|
|
18
20
|
## Installing
|
|
19
21
|
### General Instructions
|
|
20
22
|
``` sh
|
|
@@ -56,6 +58,20 @@ This may be needed if you deploy to a system where these libraries
|
|
|
56
58
|
are located somewhere different than on your build system.
|
|
57
59
|
This overrides any rpath calculated by default or by the options above.
|
|
58
60
|
|
|
61
|
+
* `--with-sanitize[=address,cfi,integer,memory,thread,undefined]` -
|
|
62
|
+
Enable sanitizers for Clang / GCC. If no argument is given, try to enable
|
|
63
|
+
all sanitizers or fail if none are available. If a command-separated list of
|
|
64
|
+
specific sanitizers is given, configure will fail unless they all are available.
|
|
65
|
+
Note that the some sanitizers may incur a performance penalty, and the Address
|
|
66
|
+
Sanitizer may require a runtime library.
|
|
67
|
+
To see line numbers in backtraces, declare these environment variables
|
|
68
|
+
(adjust the llvm-symbolizer path as needed for your system):
|
|
69
|
+
|
|
70
|
+
``` sh
|
|
71
|
+
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-3.4
|
|
72
|
+
export ASAN_OPTIONS=symbolize=1
|
|
73
|
+
```
|
|
74
|
+
|
|
59
75
|
### Linux and other Unixes
|
|
60
76
|
|
|
61
77
|
You may need to install a package such as `libmysqlclient-dev` or `mysql-devel`;
|
|
@@ -153,6 +169,20 @@ results.each(:as => :array) do |row|
|
|
|
153
169
|
end
|
|
154
170
|
```
|
|
155
171
|
|
|
172
|
+
Prepared statements are supported, as well. In a prepared statement, use a `?`
|
|
173
|
+
in place of each value and then execute the statement to retrieve a result set.
|
|
174
|
+
Pass your arguments to the execute method in the same number and order as the
|
|
175
|
+
question marks in the statement.
|
|
176
|
+
|
|
177
|
+
``` ruby
|
|
178
|
+
statement = @client.prepare("SELECT * FROM users WHERE login_count = ?")
|
|
179
|
+
result1 = statement.execute(1)
|
|
180
|
+
result2 = statement.execute(2)
|
|
181
|
+
|
|
182
|
+
statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND location LIKE ?")
|
|
183
|
+
result = statement.execute(1, "CA")
|
|
184
|
+
```
|
|
185
|
+
|
|
156
186
|
## Connection options
|
|
157
187
|
|
|
158
188
|
You may set the following connection options in Mysql2::Client.new(...):
|
|
@@ -186,7 +216,8 @@ Setting any of the following options will enable an SSL connection, but only if
|
|
|
186
216
|
your MySQL client library and server have been compiled with SSL support.
|
|
187
217
|
MySQL client library defaults will be used for any parameters that are left out
|
|
188
218
|
or set to nil. Relative paths are allowed, and may be required by managed
|
|
189
|
-
hosting providers such as Heroku.
|
|
219
|
+
hosting providers such as Heroku. Set `:sslverify => true` to require that the
|
|
220
|
+
server presents a valid certificate.
|
|
190
221
|
|
|
191
222
|
``` ruby
|
|
192
223
|
Mysql2::Client.new(
|
|
@@ -195,7 +226,8 @@ Mysql2::Client.new(
|
|
|
195
226
|
:sslcert => '/path/to/client-cert.pem',
|
|
196
227
|
:sslca => '/path/to/ca-cert.pem',
|
|
197
228
|
:sslcapath => '/path/to/cacerts',
|
|
198
|
-
:sslcipher => 'DHE-RSA-AES256-SHA'
|
|
229
|
+
:sslcipher => 'DHE-RSA-AES256-SHA',
|
|
230
|
+
:sslverify => true,
|
|
199
231
|
)
|
|
200
232
|
```
|
|
201
233
|
|
|
@@ -240,15 +272,26 @@ Yields:
|
|
|
240
272
|
next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
|
|
241
273
|
```
|
|
242
274
|
|
|
243
|
-
See https://gist.github.com/1367987 for using MULTI_STATEMENTS with Active Record.
|
|
244
|
-
|
|
245
275
|
### Secure auth
|
|
246
276
|
|
|
247
277
|
Starting wih MySQL 5.6.5, secure_auth is enabled by default on servers (it was disabled by default prior to this).
|
|
248
278
|
When secure_auth is enabled, the server will refuse a connection if the account password is stored in old pre-MySQL 4.1 format.
|
|
249
279
|
The MySQL 5.6.5 client library may also refuse to attempt a connection if provided an older format password.
|
|
250
|
-
To bypass this restriction in the client, pass the option
|
|
251
|
-
|
|
280
|
+
To bypass this restriction in the client, pass the option `:secure_auth => false` to Mysql2::Client.new().
|
|
281
|
+
|
|
282
|
+
### Flags option parsing
|
|
283
|
+
|
|
284
|
+
The `:flags` parameter accepts an integer, a string, or an array. The integer
|
|
285
|
+
form allows the client to assemble flags from constants defined under
|
|
286
|
+
`Mysql2::Client` such as `Mysql2::Client::FOUND_ROWS`. Use a bitwise `|` (OR)
|
|
287
|
+
to specify several flags.
|
|
288
|
+
|
|
289
|
+
The string form will be split on whitespace and parsed as with the array form:
|
|
290
|
+
Plain flags are added to the default flags, while flags prefixed with `-`
|
|
291
|
+
(minus) are removed from the default flags.
|
|
292
|
+
|
|
293
|
+
This allows easier use with ActiveRecord's database.yml, avoiding the need for magic flag numbers.
|
|
294
|
+
For example, to disable protocol compression, and enable multiple statements and result sets:
|
|
252
295
|
|
|
253
296
|
``` yaml
|
|
254
297
|
development:
|
|
@@ -259,13 +302,17 @@ development:
|
|
|
259
302
|
password: my_password
|
|
260
303
|
host: 127.0.0.1
|
|
261
304
|
port: 3306
|
|
305
|
+
flags:
|
|
306
|
+
- -COMPRESS
|
|
307
|
+
- FOUND_ROWS
|
|
308
|
+
- MULTI_STATEMENTS
|
|
262
309
|
secure_auth: false
|
|
263
310
|
```
|
|
264
311
|
|
|
265
312
|
### Reading a MySQL config file
|
|
266
313
|
|
|
267
314
|
You may read configuration options from a MySQL configuration file by passing
|
|
268
|
-
the `:default_file` and `:default_group`
|
|
315
|
+
the `:default_file` and `:default_group` parameters. For example:
|
|
269
316
|
|
|
270
317
|
``` ruby
|
|
271
318
|
Mysql2::Client.new(:default_file => '/user/.my.cnf', :default_group => 'client')
|
|
@@ -273,7 +320,7 @@ Mysql2::Client.new(:default_file => '/user/.my.cnf', :default_group => 'client')
|
|
|
273
320
|
|
|
274
321
|
### Initial command on connect and reconnect
|
|
275
322
|
|
|
276
|
-
If you specify the init_command option, the SQL string you provide will be executed after the connection is established.
|
|
323
|
+
If you specify the `:init_command` option, the SQL string you provide will be executed after the connection is established.
|
|
277
324
|
If `:reconnect` is set to `true`, init_command will also be executed after a successful reconnect.
|
|
278
325
|
It is useful if you want to provide session options which survive reconnection.
|
|
279
326
|
|
|
@@ -437,13 +484,13 @@ As for field values themselves, I'm workin on it - but expect that soon.
|
|
|
437
484
|
|
|
438
485
|
This gem is tested with the following Ruby versions on Linux and Mac OS X:
|
|
439
486
|
|
|
440
|
-
* Ruby MRI 1.8.7, 1.9.
|
|
487
|
+
* Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x
|
|
441
488
|
* Ruby Enterprise Edition (based on MRI 1.8.7)
|
|
442
489
|
* Rubinius 2.x
|
|
443
490
|
|
|
444
491
|
This gem is tested with the following MySQL and MariaDB versions:
|
|
445
492
|
|
|
446
|
-
* MySQL 5.
|
|
493
|
+
* MySQL 5.5, 5.7
|
|
447
494
|
* MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
|
|
448
495
|
* MariaDB 5.5, 10.0
|
|
449
496
|
|
|
@@ -536,4 +583,8 @@ though.
|
|
|
536
583
|
* Yury Korolev (http://github.com/yury) - for TONS of help testing the Active Record adapter
|
|
537
584
|
* Aaron Patterson (http://github.com/tenderlove) - tons of contributions, suggestions and general badassness
|
|
538
585
|
* Mike Perham (http://github.com/mperham) - Async Active Record adapter (uses Fibers and EventMachine)
|
|
539
|
-
* Aaron Stone (http://github.com/sodabrew) - additional client settings, local files, microsecond time, maintenance support
|
|
586
|
+
* Aaron Stone (http://github.com/sodabrew) - additional client settings, local files, microsecond time, maintenance support
|
|
587
|
+
* Kouhei Ueno (https://github.com/nyaxt) - for the original work on Prepared Statements way back in 2012
|
|
588
|
+
* John Cant (http://github.com/johncant) - polishing and updating Prepared Statements support
|
|
589
|
+
* Justin Case (http://github.com/justincase) - polishing and updating Prepared Statements support and getting it merged
|
|
590
|
+
* Tamir Duberstein (http://github.com/tamird) - for help with timeouts and all around updates and cleanups
|
data/examples/eventmachine.rb
CHANGED
data/examples/threaded.rb
CHANGED
|
@@ -4,17 +4,15 @@ $LOAD_PATH.unshift 'lib'
|
|
|
4
4
|
require 'mysql2'
|
|
5
5
|
require 'timeout'
|
|
6
6
|
|
|
7
|
-
threads = []
|
|
8
7
|
# Should never exceed worst case 3.5 secs across all 20 threads
|
|
9
8
|
Timeout.timeout(3.5) do
|
|
10
|
-
20.times do
|
|
11
|
-
|
|
9
|
+
20.times.map do
|
|
10
|
+
Thread.new do
|
|
12
11
|
overhead = rand(3)
|
|
13
12
|
puts ">> thread #{Thread.current.object_id} query, #{overhead} sec overhead"
|
|
14
13
|
# 3 second overhead per query
|
|
15
14
|
Mysql2::Client.new(:host => "localhost", :username => "root").query("SELECT sleep(#{overhead}) as result")
|
|
16
15
|
puts "<< thread #{Thread.current.object_id} result, #{overhead} sec overhead"
|
|
17
16
|
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
end
|
|
17
|
+
end.each(&:join)
|
|
18
|
+
end
|
data/ext/mysql2/client.c
CHANGED
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
VALUE cMysql2Client;
|
|
18
18
|
extern VALUE mMysql2, cMysql2Error;
|
|
19
19
|
static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
|
|
20
|
-
static ID intern_merge, intern_merge_bang,
|
|
20
|
+
static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args;
|
|
21
21
|
|
|
22
22
|
#ifndef HAVE_RB_HASH_DUP
|
|
23
|
-
|
|
24
|
-
return rb_funcall(rb_cHash,
|
|
23
|
+
VALUE rb_hash_dup(VALUE other) {
|
|
24
|
+
return rb_funcall(rb_cHash, intern_brackets, 1, other);
|
|
25
25
|
}
|
|
26
26
|
#endif
|
|
27
27
|
|
|
@@ -30,25 +30,12 @@ static VALUE rb_hash_dup(VALUE other) {
|
|
|
30
30
|
rb_raise(cMysql2Error, "MySQL client is not initialized"); \
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
#define REQUIRE_CONNECTED(wrapper) \
|
|
34
|
-
REQUIRE_INITIALIZED(wrapper) \
|
|
35
|
-
if (!wrapper->connected && !wrapper->reconnect_enabled) { \
|
|
36
|
-
rb_raise(cMysql2Error, "closed MySQL connection"); \
|
|
37
|
-
}
|
|
38
|
-
|
|
39
33
|
#define REQUIRE_NOT_CONNECTED(wrapper) \
|
|
40
34
|
REQUIRE_INITIALIZED(wrapper) \
|
|
41
35
|
if (wrapper->connected) { \
|
|
42
36
|
rb_raise(cMysql2Error, "MySQL connection is already open"); \
|
|
43
37
|
}
|
|
44
38
|
|
|
45
|
-
#define MARK_CONN_INACTIVE(conn) \
|
|
46
|
-
wrapper->active_thread = Qnil;
|
|
47
|
-
|
|
48
|
-
#define GET_CLIENT(self) \
|
|
49
|
-
mysql_client_wrapper *wrapper; \
|
|
50
|
-
Data_Get_Struct(self, mysql_client_wrapper, wrapper)
|
|
51
|
-
|
|
52
39
|
/*
|
|
53
40
|
* compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
|
|
54
41
|
* variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
|
|
@@ -136,16 +123,17 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
|
|
|
136
123
|
rb_enc_associate(rb_sql_state, rb_usascii_encoding());
|
|
137
124
|
#endif
|
|
138
125
|
|
|
139
|
-
e = rb_funcall(cMysql2Error,
|
|
140
|
-
|
|
141
|
-
|
|
126
|
+
e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
|
|
127
|
+
rb_error_msg,
|
|
128
|
+
LONG2FIX(wrapper->server_version),
|
|
129
|
+
UINT2NUM(mysql_errno(wrapper->client)),
|
|
130
|
+
rb_sql_state);
|
|
142
131
|
rb_exc_raise(e);
|
|
143
|
-
return Qnil;
|
|
144
132
|
}
|
|
145
133
|
|
|
146
134
|
static void *nogvl_init(void *ptr) {
|
|
147
135
|
MYSQL *client;
|
|
148
|
-
mysql_client_wrapper *wrapper =
|
|
136
|
+
mysql_client_wrapper *wrapper = ptr;
|
|
149
137
|
|
|
150
138
|
/* may initialize embedded server and read /etc/services off disk */
|
|
151
139
|
client = mysql_init(wrapper->client);
|
|
@@ -221,43 +209,47 @@ static VALUE invalidate_fd(int clientfd)
|
|
|
221
209
|
#endif /* _WIN32 */
|
|
222
210
|
|
|
223
211
|
static void *nogvl_close(void *ptr) {
|
|
224
|
-
mysql_client_wrapper *wrapper;
|
|
225
|
-
wrapper = ptr;
|
|
226
|
-
if (wrapper->connected) {
|
|
227
|
-
wrapper->active_thread = Qnil;
|
|
228
|
-
wrapper->connected = 0;
|
|
229
|
-
#ifndef _WIN32
|
|
230
|
-
/* Invalidate the socket before calling mysql_close(). This prevents
|
|
231
|
-
* mysql_close() from sending a mysql-QUIT or from calling shutdown() on
|
|
232
|
-
* the socket. The difference is that invalidate_fd will drop this
|
|
233
|
-
* process's reference to the socket only, while a QUIT or shutdown()
|
|
234
|
-
* would render the underlying connection unusable, interrupting other
|
|
235
|
-
* processes which share this object across a fork().
|
|
236
|
-
*/
|
|
237
|
-
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
|
238
|
-
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, leaking some memory\n");
|
|
239
|
-
close(wrapper->client->net.fd);
|
|
240
|
-
return NULL;
|
|
241
|
-
}
|
|
242
|
-
#endif
|
|
212
|
+
mysql_client_wrapper *wrapper = ptr;
|
|
243
213
|
|
|
244
|
-
|
|
214
|
+
if (wrapper->client) {
|
|
215
|
+
mysql_close(wrapper->client);
|
|
216
|
+
xfree(wrapper->client);
|
|
217
|
+
wrapper->client = NULL;
|
|
218
|
+
wrapper->connected = 0;
|
|
219
|
+
wrapper->active_thread = Qnil;
|
|
245
220
|
}
|
|
246
221
|
|
|
247
222
|
return NULL;
|
|
248
223
|
}
|
|
249
224
|
|
|
225
|
+
/* this is called during GC */
|
|
250
226
|
static void rb_mysql_client_free(void *ptr) {
|
|
251
|
-
mysql_client_wrapper *wrapper =
|
|
227
|
+
mysql_client_wrapper *wrapper = ptr;
|
|
252
228
|
decr_mysql2_client(wrapper);
|
|
253
229
|
}
|
|
254
230
|
|
|
255
231
|
void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
|
256
232
|
{
|
|
257
233
|
wrapper->refcount--;
|
|
234
|
+
|
|
258
235
|
if (wrapper->refcount == 0) {
|
|
236
|
+
#ifndef _WIN32
|
|
237
|
+
if (wrapper->connected) {
|
|
238
|
+
/* The client is being garbage collected while connected. Prevent
|
|
239
|
+
* mysql_close() from sending a mysql-QUIT or from calling shutdown() on
|
|
240
|
+
* the socket by invalidating it. invalidate_fd() will drop this
|
|
241
|
+
* process's reference to the socket only, while a QUIT or shutdown()
|
|
242
|
+
* would render the underlying connection unusable, interrupting other
|
|
243
|
+
* processes which share this object across a fork().
|
|
244
|
+
*/
|
|
245
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
|
246
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
|
|
247
|
+
close(wrapper->client->net.fd);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
#endif
|
|
251
|
+
|
|
259
252
|
nogvl_close(wrapper);
|
|
260
|
-
xfree(wrapper->client);
|
|
261
253
|
xfree(wrapper);
|
|
262
254
|
}
|
|
263
255
|
}
|
|
@@ -267,7 +259,7 @@ static VALUE allocate(VALUE klass) {
|
|
|
267
259
|
mysql_client_wrapper * wrapper;
|
|
268
260
|
obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
|
|
269
261
|
wrapper->encoding = Qnil;
|
|
270
|
-
|
|
262
|
+
MARK_CONN_INACTIVE(self);
|
|
271
263
|
wrapper->server_version = 0;
|
|
272
264
|
wrapper->reconnect_enabled = 0;
|
|
273
265
|
wrapper->connect_timeout = 0;
|
|
@@ -275,6 +267,7 @@ static VALUE allocate(VALUE klass) {
|
|
|
275
267
|
wrapper->initialized = 0; /* means that that the wrapper is initialized */
|
|
276
268
|
wrapper->refcount = 1;
|
|
277
269
|
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
|
|
270
|
+
|
|
278
271
|
return obj;
|
|
279
272
|
}
|
|
280
273
|
|
|
@@ -340,8 +333,7 @@ static VALUE rb_mysql_info(VALUE self) {
|
|
|
340
333
|
|
|
341
334
|
static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
|
|
342
335
|
struct nogvl_connect_args args;
|
|
343
|
-
time_t start_time, end_time;
|
|
344
|
-
unsigned int elapsed_time, connect_timeout;
|
|
336
|
+
time_t start_time, end_time, elapsed_time, connect_timeout;
|
|
345
337
|
VALUE rv;
|
|
346
338
|
GET_CLIENT(self);
|
|
347
339
|
|
|
@@ -368,7 +360,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
|
368
360
|
/* avoid an early timeout due to time truncating milliseconds off the start time */
|
|
369
361
|
if (elapsed_time > 0)
|
|
370
362
|
elapsed_time--;
|
|
371
|
-
if (elapsed_time >= wrapper->connect_timeout)
|
|
363
|
+
if (elapsed_time >= (time_t)wrapper->connect_timeout)
|
|
372
364
|
break;
|
|
373
365
|
connect_timeout = wrapper->connect_timeout - elapsed_time;
|
|
374
366
|
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
|
|
@@ -389,10 +381,13 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
|
389
381
|
}
|
|
390
382
|
|
|
391
383
|
/*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
384
|
+
* Terminate the connection; call this when the connection is no longer needed.
|
|
385
|
+
* The garbage collector can close the connection, but doing so emits an
|
|
386
|
+
* "Aborted connection" error on the server and increments the Aborted_clients
|
|
387
|
+
* status variable.
|
|
388
|
+
*
|
|
389
|
+
* @see http://dev.mysql.com/doc/en/communication-errors.html
|
|
390
|
+
* @return [void]
|
|
396
391
|
*/
|
|
397
392
|
static VALUE rb_mysql_client_close(VALUE self) {
|
|
398
393
|
GET_CLIENT(self);
|
|
@@ -442,10 +437,9 @@ static void *nogvl_read_query_result(void *ptr) {
|
|
|
442
437
|
}
|
|
443
438
|
|
|
444
439
|
static void *nogvl_do_result(void *ptr, char use_result) {
|
|
445
|
-
mysql_client_wrapper *wrapper;
|
|
440
|
+
mysql_client_wrapper *wrapper = ptr;
|
|
446
441
|
MYSQL_RES *result;
|
|
447
442
|
|
|
448
|
-
wrapper = (mysql_client_wrapper *)ptr;
|
|
449
443
|
if (use_result) {
|
|
450
444
|
result = mysql_use_result(wrapper->client);
|
|
451
445
|
} else {
|
|
@@ -454,7 +448,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
|
|
|
454
448
|
|
|
455
449
|
/* once our result is stored off, this connection is
|
|
456
450
|
ready for another command to be issued */
|
|
457
|
-
|
|
451
|
+
MARK_CONN_INACTIVE(self);
|
|
458
452
|
|
|
459
453
|
return result;
|
|
460
454
|
}
|
|
@@ -507,9 +501,9 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
|
507
501
|
}
|
|
508
502
|
|
|
509
503
|
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
|
510
|
-
RB_GC_GUARD(current);
|
|
504
|
+
(void)RB_GC_GUARD(current);
|
|
511
505
|
Check_Type(current, T_HASH);
|
|
512
|
-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
|
506
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
|
513
507
|
|
|
514
508
|
return resultObj;
|
|
515
509
|
}
|
|
@@ -523,7 +517,7 @@ struct async_query_args {
|
|
|
523
517
|
static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
|
524
518
|
GET_CLIENT(self);
|
|
525
519
|
|
|
526
|
-
|
|
520
|
+
MARK_CONN_INACTIVE(self);
|
|
527
521
|
wrapper->connected = 0;
|
|
528
522
|
|
|
529
523
|
/* Invalidate the MySQL socket to prevent further communication.
|
|
@@ -535,19 +529,16 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
|
|
535
529
|
}
|
|
536
530
|
|
|
537
531
|
rb_exc_raise(error);
|
|
538
|
-
|
|
539
|
-
return Qnil;
|
|
540
532
|
}
|
|
541
533
|
|
|
542
534
|
static VALUE do_query(void *args) {
|
|
543
|
-
struct async_query_args *async_args;
|
|
535
|
+
struct async_query_args *async_args = args;
|
|
544
536
|
struct timeval tv;
|
|
545
|
-
struct timeval*
|
|
537
|
+
struct timeval *tvp;
|
|
546
538
|
long int sec;
|
|
547
539
|
int retval;
|
|
548
540
|
VALUE read_timeout;
|
|
549
541
|
|
|
550
|
-
async_args = (struct async_query_args *)args;
|
|
551
542
|
read_timeout = rb_iv_get(async_args->self, "@read_timeout");
|
|
552
543
|
|
|
553
544
|
tvp = NULL;
|
|
@@ -585,11 +576,9 @@ static VALUE do_query(void *args) {
|
|
|
585
576
|
}
|
|
586
577
|
#else
|
|
587
578
|
static VALUE finish_and_mark_inactive(void *args) {
|
|
588
|
-
VALUE self;
|
|
579
|
+
VALUE self = args;
|
|
589
580
|
MYSQL_RES *result;
|
|
590
581
|
|
|
591
|
-
self = (VALUE)args;
|
|
592
|
-
|
|
593
582
|
GET_CLIENT(self);
|
|
594
583
|
|
|
595
584
|
if (!NIL_P(wrapper->active_thread)) {
|
|
@@ -599,13 +588,31 @@ static VALUE finish_and_mark_inactive(void *args) {
|
|
|
599
588
|
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
|
|
600
589
|
mysql_free_result(result);
|
|
601
590
|
|
|
602
|
-
|
|
591
|
+
MARK_CONN_INACTIVE(self);
|
|
603
592
|
}
|
|
604
593
|
|
|
605
594
|
return Qnil;
|
|
606
595
|
}
|
|
607
596
|
#endif
|
|
608
597
|
|
|
598
|
+
void rb_mysql_client_set_active_thread(VALUE self) {
|
|
599
|
+
VALUE thread_current = rb_thread_current();
|
|
600
|
+
GET_CLIENT(self);
|
|
601
|
+
|
|
602
|
+
// see if this connection is still waiting on a result from a previous query
|
|
603
|
+
if (NIL_P(wrapper->active_thread)) {
|
|
604
|
+
// mark this connection active
|
|
605
|
+
wrapper->active_thread = thread_current;
|
|
606
|
+
} else if (wrapper->active_thread == thread_current) {
|
|
607
|
+
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
|
608
|
+
} else {
|
|
609
|
+
VALUE inspect = rb_inspect(wrapper->active_thread);
|
|
610
|
+
const char *thr = StringValueCStr(inspect);
|
|
611
|
+
|
|
612
|
+
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
609
616
|
/* call-seq:
|
|
610
617
|
* client.abandon_results!
|
|
611
618
|
*
|
|
@@ -640,20 +647,19 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
|
|
|
640
647
|
* client.query(sql, options = {})
|
|
641
648
|
*
|
|
642
649
|
* Query the database with +sql+, with optional +options+. For the possible
|
|
643
|
-
* options, see
|
|
650
|
+
* options, see default_query_options on the Mysql2::Client class.
|
|
644
651
|
*/
|
|
645
652
|
static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
|
646
653
|
#ifndef _WIN32
|
|
647
654
|
struct async_query_args async_args;
|
|
648
655
|
#endif
|
|
649
656
|
struct nogvl_send_query_args args;
|
|
650
|
-
VALUE thread_current = rb_thread_current();
|
|
651
657
|
GET_CLIENT(self);
|
|
652
658
|
|
|
653
659
|
REQUIRE_CONNECTED(wrapper);
|
|
654
660
|
args.mysql = wrapper->client;
|
|
655
661
|
|
|
656
|
-
RB_GC_GUARD(current);
|
|
662
|
+
(void)RB_GC_GUARD(current);
|
|
657
663
|
Check_Type(current, T_HASH);
|
|
658
664
|
rb_iv_set(self, "@current_query_options", current);
|
|
659
665
|
|
|
@@ -666,23 +672,10 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
|
|
666
672
|
#endif
|
|
667
673
|
args.sql_ptr = RSTRING_PTR(args.sql);
|
|
668
674
|
args.sql_len = RSTRING_LEN(args.sql);
|
|
669
|
-
|
|
670
|
-
/* see if this connection is still waiting on a result from a previous query */
|
|
671
|
-
if (NIL_P(wrapper->active_thread)) {
|
|
672
|
-
/* mark this connection active */
|
|
673
|
-
wrapper->active_thread = thread_current;
|
|
674
|
-
} else if (wrapper->active_thread == thread_current) {
|
|
675
|
-
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
|
676
|
-
} else {
|
|
677
|
-
VALUE inspect = rb_inspect(wrapper->active_thread);
|
|
678
|
-
const char *thr = StringValueCStr(inspect);
|
|
679
|
-
|
|
680
|
-
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
|
|
681
|
-
RB_GC_GUARD(inspect);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
675
|
args.wrapper = wrapper;
|
|
685
676
|
|
|
677
|
+
rb_mysql_client_set_active_thread(self);
|
|
678
|
+
|
|
686
679
|
#ifndef _WIN32
|
|
687
680
|
rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
|
688
681
|
|
|
@@ -899,15 +892,17 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
|
|
|
899
892
|
*
|
|
900
893
|
* Return the file descriptor number for this client.
|
|
901
894
|
*/
|
|
902
|
-
static VALUE rb_mysql_client_socket(VALUE self) {
|
|
903
895
|
#ifndef _WIN32
|
|
896
|
+
static VALUE rb_mysql_client_socket(VALUE self) {
|
|
904
897
|
GET_CLIENT(self);
|
|
905
898
|
REQUIRE_CONNECTED(wrapper);
|
|
906
899
|
return INT2NUM(wrapper->client->net.fd);
|
|
900
|
+
}
|
|
907
901
|
#else
|
|
902
|
+
static VALUE rb_mysql_client_socket(RB_MYSQL_UNUSED VALUE self) {
|
|
908
903
|
rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
|
|
909
|
-
#endif
|
|
910
904
|
}
|
|
905
|
+
#endif
|
|
911
906
|
|
|
912
907
|
/* call-seq:
|
|
913
908
|
* client.last_id
|
|
@@ -1067,9 +1062,9 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
|
1067
1062
|
}
|
|
1068
1063
|
|
|
1069
1064
|
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
|
1070
|
-
RB_GC_GUARD(current);
|
|
1065
|
+
(void)RB_GC_GUARD(current);
|
|
1071
1066
|
Check_Type(current, T_HASH);
|
|
1072
|
-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
|
1067
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
|
1073
1068
|
|
|
1074
1069
|
return resultObj;
|
|
1075
1070
|
}
|
|
@@ -1138,7 +1133,6 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
|
|
|
1138
1133
|
static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
1139
1134
|
char *charset_name;
|
|
1140
1135
|
#ifdef HAVE_RUBY_ENCODING_H
|
|
1141
|
-
size_t charset_name_len;
|
|
1142
1136
|
const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
|
|
1143
1137
|
rb_encoding *enc;
|
|
1144
1138
|
VALUE rb_enc;
|
|
@@ -1148,8 +1142,7 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
|
1148
1142
|
charset_name = RSTRING_PTR(value);
|
|
1149
1143
|
|
|
1150
1144
|
#ifdef HAVE_RUBY_ENCODING_H
|
|
1151
|
-
|
|
1152
|
-
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, charset_name_len);
|
|
1145
|
+
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
|
|
1153
1146
|
if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
|
|
1154
1147
|
VALUE inspect = rb_inspect(value);
|
|
1155
1148
|
rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
|
|
@@ -1209,7 +1202,19 @@ static VALUE initialize_ext(VALUE self) {
|
|
|
1209
1202
|
return self;
|
|
1210
1203
|
}
|
|
1211
1204
|
|
|
1205
|
+
/* call-seq: client.prepare # => Mysql2::Statement
|
|
1206
|
+
*
|
|
1207
|
+
* Create a new prepared statement.
|
|
1208
|
+
*/
|
|
1209
|
+
static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
|
|
1210
|
+
GET_CLIENT(self);
|
|
1211
|
+
REQUIRE_CONNECTED(wrapper);
|
|
1212
|
+
|
|
1213
|
+
return rb_mysql_stmt_new(self, sql);
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1212
1216
|
void init_mysql2_client() {
|
|
1217
|
+
#ifdef _WIN32
|
|
1213
1218
|
/* verify the libmysql we're about to use was the version we were built against
|
|
1214
1219
|
https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
|
|
1215
1220
|
int i;
|
|
@@ -1224,15 +1229,14 @@ void init_mysql2_client() {
|
|
|
1224
1229
|
}
|
|
1225
1230
|
if (lib[i] != MYSQL_LINK_VERSION[i]) {
|
|
1226
1231
|
rb_raise(rb_eRuntimeError, "Incorrect MySQL client library version! This gem was compiled for %s but the client library is %s.", MYSQL_LINK_VERSION, lib);
|
|
1227
|
-
return;
|
|
1228
1232
|
}
|
|
1229
1233
|
}
|
|
1234
|
+
#endif
|
|
1230
1235
|
|
|
1231
1236
|
/* Initializing mysql library, so different threads could call Client.new */
|
|
1232
1237
|
/* without race condition in the library */
|
|
1233
1238
|
if (mysql_library_init(0, NULL, NULL) != 0) {
|
|
1234
1239
|
rb_raise(rb_eRuntimeError, "Could not initialize MySQL client library");
|
|
1235
|
-
return;
|
|
1236
1240
|
}
|
|
1237
1241
|
|
|
1238
1242
|
#if 0
|
|
@@ -1253,6 +1257,7 @@ void init_mysql2_client() {
|
|
|
1253
1257
|
rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
|
|
1254
1258
|
rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
|
|
1255
1259
|
rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
|
|
1260
|
+
rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
|
|
1256
1261
|
rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
|
|
1257
1262
|
rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
|
|
1258
1263
|
rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
|
|
@@ -1289,10 +1294,10 @@ void init_mysql2_client() {
|
|
|
1289
1294
|
sym_array = ID2SYM(rb_intern("array"));
|
|
1290
1295
|
sym_stream = ID2SYM(rb_intern("stream"));
|
|
1291
1296
|
|
|
1297
|
+
intern_brackets = rb_intern("[]");
|
|
1292
1298
|
intern_merge = rb_intern("merge");
|
|
1293
1299
|
intern_merge_bang = rb_intern("merge!");
|
|
1294
|
-
|
|
1295
|
-
intern_sql_state_eql = rb_intern("sql_state=");
|
|
1300
|
+
intern_new_with_args = rb_intern("new_with_args");
|
|
1296
1301
|
|
|
1297
1302
|
#ifdef CLIENT_LONG_PASSWORD
|
|
1298
1303
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|