mysql2 0.3.21 → 0.4.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 +4 -4
- data/CHANGELOG.md +1 -0
- data/README.md +28 -6
- data/examples/eventmachine.rb +1 -1
- data/examples/threaded.rb +4 -6
- data/ext/mysql2/client.c +93 -84
- data/ext/mysql2/client.h +21 -1
- data/ext/mysql2/extconf.rb +60 -41
- 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 +464 -97
- data/ext/mysql2/result.h +11 -4
- data/ext/mysql2/statement.c +491 -0
- data/ext/mysql2/statement.h +18 -0
- data/lib/mysql2/client.rb +32 -24
- 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 +322 -290
- 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: ea76ea0b1ef19163cecb0a38f0a4bcf44b0b32c5
|
4
|
+
data.tar.gz: 472478d5dd515629891b26aca5169d038df0df0e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de4abae3ac4f88dcedf81ab2b4d24b38b22e2006b38476b986a4fe072512d7eb7a7c1c68f569f0ee30b45ffa0e3a727ea626b392e837fcde2444a099a1cb00c5
|
7
|
+
data.tar.gz: 15b876980c214865b1098d4f821647f0c1fd93ab397b020214325984d1e10e36d69297739958773ae158af9ecf2aa932c408b926b18e31a1446a3d8bdfed4de1
|
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
|
@@ -153,6 +155,20 @@ results.each(:as => :array) do |row|
|
|
153
155
|
end
|
154
156
|
```
|
155
157
|
|
158
|
+
Prepared statements are supported, as well. In a prepared statement, use a `?`
|
159
|
+
in place of each value and then execute the statement to retrieve a result set.
|
160
|
+
Pass your arguments to the execute method in the same number and order as the
|
161
|
+
question marks in the statement.
|
162
|
+
|
163
|
+
``` ruby
|
164
|
+
statement = @client.prepare("SELECT * FROM users WHERE login_count = ?")
|
165
|
+
result1 = statement.execute(1)
|
166
|
+
result2 = statement.execute(2)
|
167
|
+
|
168
|
+
statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND location LIKE ?")
|
169
|
+
result = statement.execute(1, "CA")
|
170
|
+
```
|
171
|
+
|
156
172
|
## Connection options
|
157
173
|
|
158
174
|
You may set the following connection options in Mysql2::Client.new(...):
|
@@ -186,7 +202,8 @@ Setting any of the following options will enable an SSL connection, but only if
|
|
186
202
|
your MySQL client library and server have been compiled with SSL support.
|
187
203
|
MySQL client library defaults will be used for any parameters that are left out
|
188
204
|
or set to nil. Relative paths are allowed, and may be required by managed
|
189
|
-
hosting providers such as Heroku.
|
205
|
+
hosting providers such as Heroku. Set `:sslverify => true` to require that the
|
206
|
+
server presents a valid certificate.
|
190
207
|
|
191
208
|
``` ruby
|
192
209
|
Mysql2::Client.new(
|
@@ -195,7 +212,8 @@ Mysql2::Client.new(
|
|
195
212
|
:sslcert => '/path/to/client-cert.pem',
|
196
213
|
:sslca => '/path/to/ca-cert.pem',
|
197
214
|
:sslcapath => '/path/to/cacerts',
|
198
|
-
:sslcipher => 'DHE-RSA-AES256-SHA'
|
215
|
+
:sslcipher => 'DHE-RSA-AES256-SHA',
|
216
|
+
:sslverify => true,
|
199
217
|
)
|
200
218
|
```
|
201
219
|
|
@@ -437,13 +455,13 @@ As for field values themselves, I'm workin on it - but expect that soon.
|
|
437
455
|
|
438
456
|
This gem is tested with the following Ruby versions on Linux and Mac OS X:
|
439
457
|
|
440
|
-
* Ruby MRI 1.8.7, 1.9.
|
458
|
+
* Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x
|
441
459
|
* Ruby Enterprise Edition (based on MRI 1.8.7)
|
442
460
|
* Rubinius 2.x
|
443
461
|
|
444
462
|
This gem is tested with the following MySQL and MariaDB versions:
|
445
463
|
|
446
|
-
* MySQL 5.
|
464
|
+
* MySQL 5.5, 5.7
|
447
465
|
* MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
|
448
466
|
* MariaDB 5.5, 10.0
|
449
467
|
|
@@ -536,4 +554,8 @@ though.
|
|
536
554
|
* Yury Korolev (http://github.com/yury) - for TONS of help testing the Active Record adapter
|
537
555
|
* Aaron Patterson (http://github.com/tenderlove) - tons of contributions, suggestions and general badassness
|
538
556
|
* 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
|
557
|
+
* Aaron Stone (http://github.com/sodabrew) - additional client settings, local files, microsecond time, maintenance support
|
558
|
+
* Kouhei Ueno (https://github.com/nyaxt) - for the original work on Prepared Statements way back in 2012
|
559
|
+
* John Cant (http://github.com/johncant) - polishing and updating Prepared Statements support
|
560
|
+
* Justin Case (http://github.com/justincase) - polishing and updating Prepared Statements support and getting it merged
|
561
|
+
* 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,11 +123,12 @@ 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) {
|
@@ -221,32 +209,19 @@ 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
|
+
wrapper->client = NULL;
|
217
|
+
wrapper->connected = 0;
|
218
|
+
wrapper->active_thread = Qnil;
|
245
219
|
}
|
246
220
|
|
247
221
|
return NULL;
|
248
222
|
}
|
249
223
|
|
224
|
+
/* this is called during GC */
|
250
225
|
static void rb_mysql_client_free(void *ptr) {
|
251
226
|
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
|
252
227
|
decr_mysql2_client(wrapper);
|
@@ -255,7 +230,24 @@ static void rb_mysql_client_free(void *ptr) {
|
|
255
230
|
void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
256
231
|
{
|
257
232
|
wrapper->refcount--;
|
233
|
+
|
258
234
|
if (wrapper->refcount == 0) {
|
235
|
+
#ifndef _WIN32
|
236
|
+
if (wrapper->connected) {
|
237
|
+
/* The client is being garbage collected while connected. Prevent
|
238
|
+
* mysql_close() from sending a mysql-QUIT or from calling shutdown() on
|
239
|
+
* the socket by invalidating it. invalidate_fd() will drop this
|
240
|
+
* process's reference to the socket only, while a QUIT or shutdown()
|
241
|
+
* would render the underlying connection unusable, interrupting other
|
242
|
+
* processes which share this object across a fork().
|
243
|
+
*/
|
244
|
+
if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
|
245
|
+
fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
|
246
|
+
close(wrapper->client->net.fd);
|
247
|
+
}
|
248
|
+
}
|
249
|
+
#endif
|
250
|
+
|
259
251
|
nogvl_close(wrapper);
|
260
252
|
xfree(wrapper->client);
|
261
253
|
xfree(wrapper);
|
@@ -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);
|
@@ -454,7 +449,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
|
|
454
449
|
|
455
450
|
/* once our result is stored off, this connection is
|
456
451
|
ready for another command to be issued */
|
457
|
-
|
452
|
+
MARK_CONN_INACTIVE(self);
|
458
453
|
|
459
454
|
return result;
|
460
455
|
}
|
@@ -507,9 +502,9 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
507
502
|
}
|
508
503
|
|
509
504
|
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
510
|
-
RB_GC_GUARD(current);
|
505
|
+
(void)RB_GC_GUARD(current);
|
511
506
|
Check_Type(current, T_HASH);
|
512
|
-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
507
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
513
508
|
|
514
509
|
return resultObj;
|
515
510
|
}
|
@@ -523,7 +518,7 @@ struct async_query_args {
|
|
523
518
|
static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
524
519
|
GET_CLIENT(self);
|
525
520
|
|
526
|
-
|
521
|
+
MARK_CONN_INACTIVE(self);
|
527
522
|
wrapper->connected = 0;
|
528
523
|
|
529
524
|
/* Invalidate the MySQL socket to prevent further communication.
|
@@ -535,8 +530,6 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
|
535
530
|
}
|
536
531
|
|
537
532
|
rb_exc_raise(error);
|
538
|
-
|
539
|
-
return Qnil;
|
540
533
|
}
|
541
534
|
|
542
535
|
static VALUE do_query(void *args) {
|
@@ -599,13 +592,31 @@ static VALUE finish_and_mark_inactive(void *args) {
|
|
599
592
|
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
|
600
593
|
mysql_free_result(result);
|
601
594
|
|
602
|
-
|
595
|
+
MARK_CONN_INACTIVE(self);
|
603
596
|
}
|
604
597
|
|
605
598
|
return Qnil;
|
606
599
|
}
|
607
600
|
#endif
|
608
601
|
|
602
|
+
void rb_mysql_client_set_active_thread(VALUE self) {
|
603
|
+
VALUE thread_current = rb_thread_current();
|
604
|
+
GET_CLIENT(self);
|
605
|
+
|
606
|
+
// see if this connection is still waiting on a result from a previous query
|
607
|
+
if (NIL_P(wrapper->active_thread)) {
|
608
|
+
// mark this connection active
|
609
|
+
wrapper->active_thread = thread_current;
|
610
|
+
} else if (wrapper->active_thread == thread_current) {
|
611
|
+
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
612
|
+
} else {
|
613
|
+
VALUE inspect = rb_inspect(wrapper->active_thread);
|
614
|
+
const char *thr = StringValueCStr(inspect);
|
615
|
+
|
616
|
+
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
|
617
|
+
}
|
618
|
+
}
|
619
|
+
|
609
620
|
/* call-seq:
|
610
621
|
* client.abandon_results!
|
611
622
|
*
|
@@ -640,20 +651,19 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
|
|
640
651
|
* client.query(sql, options = {})
|
641
652
|
*
|
642
653
|
* Query the database with +sql+, with optional +options+. For the possible
|
643
|
-
* options, see
|
654
|
+
* options, see default_query_options on the Mysql2::Client class.
|
644
655
|
*/
|
645
656
|
static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
646
657
|
#ifndef _WIN32
|
647
658
|
struct async_query_args async_args;
|
648
659
|
#endif
|
649
660
|
struct nogvl_send_query_args args;
|
650
|
-
VALUE thread_current = rb_thread_current();
|
651
661
|
GET_CLIENT(self);
|
652
662
|
|
653
663
|
REQUIRE_CONNECTED(wrapper);
|
654
664
|
args.mysql = wrapper->client;
|
655
665
|
|
656
|
-
RB_GC_GUARD(current);
|
666
|
+
(void)RB_GC_GUARD(current);
|
657
667
|
Check_Type(current, T_HASH);
|
658
668
|
rb_iv_set(self, "@current_query_options", current);
|
659
669
|
|
@@ -666,23 +676,10 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
|
666
676
|
#endif
|
667
677
|
args.sql_ptr = RSTRING_PTR(args.sql);
|
668
678
|
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
679
|
args.wrapper = wrapper;
|
685
680
|
|
681
|
+
rb_mysql_client_set_active_thread(self);
|
682
|
+
|
686
683
|
#ifndef _WIN32
|
687
684
|
rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
688
685
|
|
@@ -899,15 +896,17 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
|
|
899
896
|
*
|
900
897
|
* Return the file descriptor number for this client.
|
901
898
|
*/
|
902
|
-
static VALUE rb_mysql_client_socket(VALUE self) {
|
903
899
|
#ifndef _WIN32
|
900
|
+
static VALUE rb_mysql_client_socket(VALUE self) {
|
904
901
|
GET_CLIENT(self);
|
905
902
|
REQUIRE_CONNECTED(wrapper);
|
906
903
|
return INT2NUM(wrapper->client->net.fd);
|
904
|
+
}
|
907
905
|
#else
|
906
|
+
static VALUE rb_mysql_client_socket(RB_MYSQL_UNUSED VALUE self) {
|
908
907
|
rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
|
909
|
-
#endif
|
910
908
|
}
|
909
|
+
#endif
|
911
910
|
|
912
911
|
/* call-seq:
|
913
912
|
* client.last_id
|
@@ -1067,9 +1066,9 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
1067
1066
|
}
|
1068
1067
|
|
1069
1068
|
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
1070
|
-
RB_GC_GUARD(current);
|
1069
|
+
(void)RB_GC_GUARD(current);
|
1071
1070
|
Check_Type(current, T_HASH);
|
1072
|
-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
1071
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
1073
1072
|
|
1074
1073
|
return resultObj;
|
1075
1074
|
}
|
@@ -1138,7 +1137,6 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
|
|
1138
1137
|
static VALUE set_charset_name(VALUE self, VALUE value) {
|
1139
1138
|
char *charset_name;
|
1140
1139
|
#ifdef HAVE_RUBY_ENCODING_H
|
1141
|
-
size_t charset_name_len;
|
1142
1140
|
const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
|
1143
1141
|
rb_encoding *enc;
|
1144
1142
|
VALUE rb_enc;
|
@@ -1148,8 +1146,7 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
1148
1146
|
charset_name = RSTRING_PTR(value);
|
1149
1147
|
|
1150
1148
|
#ifdef HAVE_RUBY_ENCODING_H
|
1151
|
-
|
1152
|
-
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, charset_name_len);
|
1149
|
+
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
|
1153
1150
|
if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
|
1154
1151
|
VALUE inspect = rb_inspect(value);
|
1155
1152
|
rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
|
@@ -1209,7 +1206,19 @@ static VALUE initialize_ext(VALUE self) {
|
|
1209
1206
|
return self;
|
1210
1207
|
}
|
1211
1208
|
|
1209
|
+
/* call-seq: client.prepare # => Mysql2::Statement
|
1210
|
+
*
|
1211
|
+
* Create a new prepared statement.
|
1212
|
+
*/
|
1213
|
+
static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
|
1214
|
+
GET_CLIENT(self);
|
1215
|
+
REQUIRE_CONNECTED(wrapper);
|
1216
|
+
|
1217
|
+
return rb_mysql_stmt_new(self, sql);
|
1218
|
+
}
|
1219
|
+
|
1212
1220
|
void init_mysql2_client() {
|
1221
|
+
#ifdef _WIN32
|
1213
1222
|
/* verify the libmysql we're about to use was the version we were built against
|
1214
1223
|
https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
|
1215
1224
|
int i;
|
@@ -1224,15 +1233,14 @@ void init_mysql2_client() {
|
|
1224
1233
|
}
|
1225
1234
|
if (lib[i] != MYSQL_LINK_VERSION[i]) {
|
1226
1235
|
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
1236
|
}
|
1229
1237
|
}
|
1238
|
+
#endif
|
1230
1239
|
|
1231
1240
|
/* Initializing mysql library, so different threads could call Client.new */
|
1232
1241
|
/* without race condition in the library */
|
1233
1242
|
if (mysql_library_init(0, NULL, NULL) != 0) {
|
1234
1243
|
rb_raise(rb_eRuntimeError, "Could not initialize MySQL client library");
|
1235
|
-
return;
|
1236
1244
|
}
|
1237
1245
|
|
1238
1246
|
#if 0
|
@@ -1253,6 +1261,7 @@ void init_mysql2_client() {
|
|
1253
1261
|
rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
|
1254
1262
|
rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
|
1255
1263
|
rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
|
1264
|
+
rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
|
1256
1265
|
rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
|
1257
1266
|
rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
|
1258
1267
|
rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
|
@@ -1289,10 +1298,10 @@ void init_mysql2_client() {
|
|
1289
1298
|
sym_array = ID2SYM(rb_intern("array"));
|
1290
1299
|
sym_stream = ID2SYM(rb_intern("stream"));
|
1291
1300
|
|
1301
|
+
intern_brackets = rb_intern("[]");
|
1292
1302
|
intern_merge = rb_intern("merge");
|
1293
1303
|
intern_merge_bang = rb_intern("merge!");
|
1294
|
-
|
1295
|
-
intern_sql_state_eql = rb_intern("sql_state=");
|
1304
|
+
intern_new_with_args = rb_intern("new_with_args");
|
1296
1305
|
|
1297
1306
|
#ifdef CLIENT_LONG_PASSWORD
|
1298
1307
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|