mysql2 0.3.18 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -0
- data/LICENSE +21 -0
- data/README.md +28 -6
- data/examples/threaded.rb +4 -6
- data/ext/mysql2/client.c +124 -126
- data/ext/mysql2/client.h +21 -0
- data/ext/mysql2/extconf.rb +29 -15
- data/ext/mysql2/mysql2_ext.c +1 -0
- data/ext/mysql2/mysql2_ext.h +1 -0
- data/ext/mysql2/mysql_enc_name_to_ruby.h +6 -6
- data/ext/mysql2/result.c +499 -128
- data/ext/mysql2/result.h +9 -3
- data/ext/mysql2/statement.c +454 -0
- data/ext/mysql2/statement.h +22 -0
- data/lib/mysql2/client.rb +24 -1
- data/lib/mysql2/error.rb +9 -21
- data/lib/mysql2/field.rb +4 -0
- data/lib/mysql2/statement.rb +5 -0
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +2 -0
- data/spec/em/em_spec.rb +13 -13
- data/spec/mysql2/client_spec.rb +337 -326
- data/spec/mysql2/error_spec.rb +37 -36
- data/spec/mysql2/result_spec.rb +197 -192
- data/spec/mysql2/statement_spec.rb +598 -0
- data/spec/spec_helper.rb +7 -0
- metadata +15 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a78b4022bc64300ea98eb6609cd7257900bc3245
|
4
|
+
data.tar.gz: e9be4bdd59bb65e1a05631e53c70359476bf2a3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6cd0601f7dbb44e17969a1d818049d4e3d330acce4a491f6079db61a0a8f368b855889984d8e1ab089c8beca5667e2339db7ba50f7e50d4afe92e4783dee044
|
7
|
+
data.tar.gz: 2d012a8b59ea34983f18871ba16176ede502b7bc9938481466112aec34a076cfeebcc7da1d0627cd854279ffacbbf38af8d9c5e56f02175371aa713553c2d45f
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Changes are maintained under [Releases](https://github.com/brianmario/mysql2/releases)
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Brian Lopez
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
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/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
@@ -16,12 +16,13 @@
|
|
16
16
|
|
17
17
|
VALUE cMysql2Client;
|
18
18
|
extern VALUE mMysql2, cMysql2Error;
|
19
|
-
static VALUE sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
|
19
|
+
static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
|
20
20
|
static ID intern_merge, intern_merge_bang, intern_error_number_eql, intern_sql_state_eql;
|
21
|
+
static ID intern_brackets, intern_new;
|
21
22
|
|
22
23
|
#ifndef HAVE_RB_HASH_DUP
|
23
|
-
|
24
|
-
return rb_funcall(rb_cHash,
|
24
|
+
VALUE rb_hash_dup(VALUE other) {
|
25
|
+
return rb_funcall(rb_cHash, intern_brackets, 1, other);
|
25
26
|
}
|
26
27
|
#endif
|
27
28
|
|
@@ -30,25 +31,12 @@ static VALUE rb_hash_dup(VALUE other) {
|
|
30
31
|
rb_raise(cMysql2Error, "MySQL client is not initialized"); \
|
31
32
|
}
|
32
33
|
|
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
34
|
#define REQUIRE_NOT_CONNECTED(wrapper) \
|
40
35
|
REQUIRE_INITIALIZED(wrapper) \
|
41
36
|
if (wrapper->connected) { \
|
42
37
|
rb_raise(cMysql2Error, "MySQL connection is already open"); \
|
43
38
|
}
|
44
39
|
|
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
40
|
/*
|
53
41
|
* compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
|
54
42
|
* variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
|
@@ -136,7 +124,7 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
|
|
136
124
|
rb_enc_associate(rb_sql_state, rb_usascii_encoding());
|
137
125
|
#endif
|
138
126
|
|
139
|
-
e = rb_funcall(cMysql2Error,
|
127
|
+
e = rb_funcall(cMysql2Error, intern_new, 2, rb_error_msg, LONG2FIX(wrapper->server_version));
|
140
128
|
rb_funcall(e, intern_error_number_eql, 1, UINT2NUM(mysql_errno(wrapper->client)));
|
141
129
|
rb_funcall(e, intern_sql_state_eql, 1, rb_sql_state);
|
142
130
|
rb_exc_raise(e);
|
@@ -182,23 +170,31 @@ static void *nogvl_connect(void *ptr) {
|
|
182
170
|
*/
|
183
171
|
static VALUE invalidate_fd(int clientfd)
|
184
172
|
{
|
185
|
-
#ifdef
|
173
|
+
#ifdef O_CLOEXEC
|
186
174
|
/* Atomically set CLOEXEC on the new FD in case another thread forks */
|
187
175
|
int sockfd = open("/dev/null", O_RDWR | O_CLOEXEC);
|
188
|
-
if (sockfd < 0) {
|
189
|
-
/* Maybe SOCK_CLOEXEC is defined but not available on this kernel */
|
190
|
-
int sockfd = open("/dev/null", O_RDWR);
|
191
|
-
fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
192
|
-
}
|
193
176
|
#else
|
194
|
-
/* Well we don't have
|
195
|
-
int sockfd =
|
196
|
-
fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
177
|
+
/* Well we don't have O_CLOEXEC, trigger the fallback code below */
|
178
|
+
int sockfd = -1;
|
197
179
|
#endif
|
198
180
|
|
199
181
|
if (sockfd < 0) {
|
200
|
-
/*
|
201
|
-
*
|
182
|
+
/* Either O_CLOEXEC wasn't defined at compile time, or it was defined at
|
183
|
+
* compile time, but isn't available at run-time. So we'll just be quick
|
184
|
+
* about setting FD_CLOEXEC now.
|
185
|
+
*/
|
186
|
+
int flags;
|
187
|
+
sockfd = open("/dev/null", O_RDWR);
|
188
|
+
flags = fcntl(sockfd, F_GETFD);
|
189
|
+
/* Do the flags dance in case there are more defined flags in the future */
|
190
|
+
if (flags != -1) {
|
191
|
+
flags |= FD_CLOEXEC;
|
192
|
+
fcntl(sockfd, F_SETFD, flags);
|
193
|
+
}
|
194
|
+
}
|
195
|
+
|
196
|
+
if (sockfd < 0) {
|
197
|
+
/* Cannot raise here, because one or both of the following may be true:
|
202
198
|
* a) we have no GVL (in C Ruby)
|
203
199
|
* b) are running as a GC finalizer
|
204
200
|
*/
|
@@ -247,6 +243,7 @@ static void rb_mysql_client_free(void *ptr) {
|
|
247
243
|
void decr_mysql2_client(mysql_client_wrapper *wrapper)
|
248
244
|
{
|
249
245
|
wrapper->refcount--;
|
246
|
+
|
250
247
|
if (wrapper->refcount == 0) {
|
251
248
|
nogvl_close(wrapper);
|
252
249
|
xfree(wrapper->client);
|
@@ -267,6 +264,7 @@ static VALUE allocate(VALUE klass) {
|
|
267
264
|
wrapper->initialized = 0; /* means that that the wrapper is initialized */
|
268
265
|
wrapper->refcount = 1;
|
269
266
|
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
|
267
|
+
|
270
268
|
return obj;
|
271
269
|
}
|
272
270
|
|
@@ -287,7 +285,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
|
|
287
285
|
oldLen = RSTRING_LEN(str);
|
288
286
|
newStr = xmalloc(oldLen*2+1);
|
289
287
|
|
290
|
-
newLen = mysql_escape_string((char *)newStr,
|
288
|
+
newLen = mysql_escape_string((char *)newStr, RSTRING_PTR(str), oldLen);
|
291
289
|
if (newLen == oldLen) {
|
292
290
|
/* no need to return a new ruby string if nothing changed */
|
293
291
|
xfree(newStr);
|
@@ -332,18 +330,17 @@ static VALUE rb_mysql_info(VALUE self) {
|
|
332
330
|
|
333
331
|
static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
|
334
332
|
struct nogvl_connect_args args;
|
335
|
-
time_t start_time, end_time;
|
336
|
-
unsigned int elapsed_time, connect_timeout;
|
333
|
+
time_t start_time, end_time, elapsed_time, connect_timeout;
|
337
334
|
VALUE rv;
|
338
335
|
GET_CLIENT(self);
|
339
336
|
|
340
|
-
args.host
|
341
|
-
args.unix_socket = NIL_P(socket)
|
342
|
-
args.port
|
343
|
-
args.user
|
344
|
-
args.passwd
|
345
|
-
args.db
|
346
|
-
args.mysql
|
337
|
+
args.host = NIL_P(host) ? NULL : StringValueCStr(host);
|
338
|
+
args.unix_socket = NIL_P(socket) ? NULL : StringValueCStr(socket);
|
339
|
+
args.port = NIL_P(port) ? 0 : NUM2INT(port);
|
340
|
+
args.user = NIL_P(user) ? NULL : StringValueCStr(user);
|
341
|
+
args.passwd = NIL_P(pass) ? NULL : StringValueCStr(pass);
|
342
|
+
args.db = NIL_P(database) ? NULL : StringValueCStr(database);
|
343
|
+
args.mysql = wrapper->client;
|
347
344
|
args.client_flag = NUM2ULONG(flags);
|
348
345
|
|
349
346
|
if (wrapper->connect_timeout)
|
@@ -360,7 +357,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
360
357
|
/* avoid an early timeout due to time truncating milliseconds off the start time */
|
361
358
|
if (elapsed_time > 0)
|
362
359
|
elapsed_time--;
|
363
|
-
if (elapsed_time >= wrapper->connect_timeout)
|
360
|
+
if (elapsed_time >= (time_t)wrapper->connect_timeout)
|
364
361
|
break;
|
365
362
|
connect_timeout = wrapper->connect_timeout - elapsed_time;
|
366
363
|
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
|
@@ -415,7 +412,7 @@ static VALUE do_send_query(void *args) {
|
|
415
412
|
mysql_client_wrapper *wrapper = query_args->wrapper;
|
416
413
|
if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
|
417
414
|
/* an error occurred, we're not active anymore */
|
418
|
-
|
415
|
+
wrapper->active_thread = Qnil;
|
419
416
|
return rb_raise_mysql2_error(wrapper);
|
420
417
|
}
|
421
418
|
return Qnil;
|
@@ -499,9 +496,9 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
499
496
|
}
|
500
497
|
|
501
498
|
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
502
|
-
RB_GC_GUARD(current);
|
499
|
+
(void)RB_GC_GUARD(current);
|
503
500
|
Check_Type(current, T_HASH);
|
504
|
-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
501
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
505
502
|
|
506
503
|
return resultObj;
|
507
504
|
}
|
@@ -598,6 +595,25 @@ static VALUE finish_and_mark_inactive(void *args) {
|
|
598
595
|
}
|
599
596
|
#endif
|
600
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
|
+
(void)RB_GC_GUARD(inspect);
|
614
|
+
}
|
615
|
+
}
|
616
|
+
|
601
617
|
/* call-seq:
|
602
618
|
* client.abandon_results!
|
603
619
|
*
|
@@ -634,72 +650,45 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
|
|
634
650
|
* Query the database with +sql+, with optional +options+. For the possible
|
635
651
|
* options, see @@default_query_options on the Mysql2::Client class.
|
636
652
|
*/
|
637
|
-
static VALUE
|
653
|
+
static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
638
654
|
#ifndef _WIN32
|
639
655
|
struct async_query_args async_args;
|
640
656
|
#endif
|
641
657
|
struct nogvl_send_query_args args;
|
642
|
-
int async = 0;
|
643
|
-
VALUE opts, current;
|
644
|
-
VALUE thread_current = rb_thread_current();
|
645
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
646
|
-
rb_encoding *conn_enc;
|
647
|
-
#endif
|
648
658
|
GET_CLIENT(self);
|
649
659
|
|
650
660
|
REQUIRE_CONNECTED(wrapper);
|
651
661
|
args.mysql = wrapper->client;
|
652
662
|
|
653
|
-
|
654
|
-
RB_GC_GUARD(current);
|
663
|
+
(void)RB_GC_GUARD(current);
|
655
664
|
Check_Type(current, T_HASH);
|
656
665
|
rb_iv_set(self, "@current_query_options", current);
|
657
666
|
|
658
|
-
|
659
|
-
rb_funcall(current, intern_merge_bang, 1, opts);
|
660
|
-
|
661
|
-
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
662
|
-
async = 1;
|
663
|
-
}
|
664
|
-
}
|
665
|
-
|
666
|
-
Check_Type(args.sql, T_STRING);
|
667
|
+
Check_Type(sql, T_STRING);
|
667
668
|
#ifdef HAVE_RUBY_ENCODING_H
|
668
|
-
conn_enc = rb_to_encoding(wrapper->encoding);
|
669
669
|
/* ensure the string is in the encoding the connection is expecting */
|
670
|
-
args.sql = rb_str_export_to_enc(
|
670
|
+
args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
|
671
|
+
#else
|
672
|
+
args.sql = sql;
|
671
673
|
#endif
|
672
|
-
args.sql_ptr =
|
674
|
+
args.sql_ptr = RSTRING_PTR(args.sql);
|
673
675
|
args.sql_len = RSTRING_LEN(args.sql);
|
674
|
-
|
675
|
-
/* see if this connection is still waiting on a result from a previous query */
|
676
|
-
if (NIL_P(wrapper->active_thread)) {
|
677
|
-
/* mark this connection active */
|
678
|
-
wrapper->active_thread = thread_current;
|
679
|
-
} else if (wrapper->active_thread == thread_current) {
|
680
|
-
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
681
|
-
} else {
|
682
|
-
VALUE inspect = rb_inspect(wrapper->active_thread);
|
683
|
-
const char *thr = StringValueCStr(inspect);
|
684
|
-
|
685
|
-
rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
|
686
|
-
RB_GC_GUARD(inspect);
|
687
|
-
}
|
688
|
-
|
689
676
|
args.wrapper = wrapper;
|
690
677
|
|
678
|
+
rb_mysql_client_set_active_thread(self);
|
679
|
+
|
691
680
|
#ifndef _WIN32
|
692
681
|
rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
693
682
|
|
694
|
-
if (
|
683
|
+
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
684
|
+
return Qnil;
|
685
|
+
} else {
|
695
686
|
async_args.fd = wrapper->client->net.fd;
|
696
687
|
async_args.self = self;
|
697
688
|
|
698
689
|
rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
699
690
|
|
700
691
|
return rb_mysql_client_async_result(self);
|
701
|
-
} else {
|
702
|
-
return Qnil;
|
703
692
|
}
|
704
693
|
#else
|
705
694
|
do_send_query(&args);
|
@@ -736,9 +725,14 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
|
|
736
725
|
oldLen = RSTRING_LEN(str);
|
737
726
|
newStr = xmalloc(oldLen*2+1);
|
738
727
|
|
739
|
-
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr,
|
728
|
+
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
|
740
729
|
if (newLen == oldLen) {
|
741
730
|
/* no need to return a new ruby string if nothing changed */
|
731
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
732
|
+
if (default_internal_enc) {
|
733
|
+
str = rb_str_export_to_enc(str, default_internal_enc);
|
734
|
+
}
|
735
|
+
#endif
|
742
736
|
xfree(newStr);
|
743
737
|
return str;
|
744
738
|
} else {
|
@@ -800,17 +794,17 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
800
794
|
break;
|
801
795
|
|
802
796
|
case MYSQL_READ_DEFAULT_FILE:
|
803
|
-
charval = (const char *)
|
797
|
+
charval = (const char *)StringValueCStr(value);
|
804
798
|
retval = charval;
|
805
799
|
break;
|
806
800
|
|
807
801
|
case MYSQL_READ_DEFAULT_GROUP:
|
808
|
-
charval = (const char *)
|
802
|
+
charval = (const char *)StringValueCStr(value);
|
809
803
|
retval = charval;
|
810
804
|
break;
|
811
805
|
|
812
806
|
case MYSQL_INIT_COMMAND:
|
813
|
-
charval = (const char *)
|
807
|
+
charval = (const char *)StringValueCStr(value);
|
814
808
|
retval = charval;
|
815
809
|
break;
|
816
810
|
|
@@ -843,30 +837,23 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
843
837
|
*
|
844
838
|
* Returns a string that represents the client library version.
|
845
839
|
*/
|
846
|
-
static VALUE rb_mysql_client_info(VALUE
|
847
|
-
VALUE version,
|
848
|
-
|
849
|
-
rb_encoding *default_internal_enc;
|
850
|
-
rb_encoding *conn_enc;
|
851
|
-
GET_CLIENT(self);
|
852
|
-
#endif
|
853
|
-
version = rb_hash_new();
|
840
|
+
static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
|
841
|
+
VALUE version_info, version, header_version;
|
842
|
+
version_info = rb_hash_new();
|
854
843
|
|
855
|
-
|
856
|
-
|
857
|
-
conn_enc = rb_to_encoding(wrapper->encoding);
|
858
|
-
#endif
|
844
|
+
version = rb_str_new2(mysql_get_client_info());
|
845
|
+
header_version = rb_str_new2(MYSQL_LINK_VERSION);
|
859
846
|
|
860
|
-
rb_hash_aset(version, sym_id, LONG2NUM(mysql_get_client_version()));
|
861
|
-
client_info = rb_str_new2(mysql_get_client_info());
|
862
847
|
#ifdef HAVE_RUBY_ENCODING_H
|
863
|
-
rb_enc_associate(
|
864
|
-
|
865
|
-
client_info = rb_str_export_to_enc(client_info, default_internal_enc);
|
866
|
-
}
|
848
|
+
rb_enc_associate(version, rb_usascii_encoding());
|
849
|
+
rb_enc_associate(header_version, rb_usascii_encoding());
|
867
850
|
#endif
|
868
|
-
|
869
|
-
|
851
|
+
|
852
|
+
rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
|
853
|
+
rb_hash_aset(version_info, sym_version, version);
|
854
|
+
rb_hash_aset(version_info, sym_header_version, header_version);
|
855
|
+
|
856
|
+
return version_info;
|
870
857
|
}
|
871
858
|
|
872
859
|
/* call-seq:
|
@@ -906,19 +893,17 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
|
|
906
893
|
*
|
907
894
|
* Return the file descriptor number for this client.
|
908
895
|
*/
|
896
|
+
#ifndef _WIN32
|
909
897
|
static VALUE rb_mysql_client_socket(VALUE self) {
|
910
898
|
GET_CLIENT(self);
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
REQUIRE_CONNECTED(wrapper);
|
915
|
-
fd_set_fd = wrapper->client->net.fd;
|
916
|
-
return INT2NUM(fd_set_fd);
|
917
|
-
}
|
899
|
+
REQUIRE_CONNECTED(wrapper);
|
900
|
+
return INT2NUM(wrapper->client->net.fd);
|
901
|
+
}
|
918
902
|
#else
|
903
|
+
static VALUE rb_mysql_client_socket(RB_MYSQL_UNUSED VALUE self) {
|
919
904
|
rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
|
920
|
-
#endif
|
921
905
|
}
|
906
|
+
#endif
|
922
907
|
|
923
908
|
/* call-seq:
|
924
909
|
* client.last_id
|
@@ -987,7 +972,7 @@ static VALUE rb_mysql_client_select_db(VALUE self, VALUE db)
|
|
987
972
|
REQUIRE_CONNECTED(wrapper);
|
988
973
|
|
989
974
|
args.mysql = wrapper->client;
|
990
|
-
args.db =
|
975
|
+
args.db = StringValueCStr(db);
|
991
976
|
|
992
977
|
if (rb_thread_call_without_gvl(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse)
|
993
978
|
rb_raise_mysql2_error(wrapper);
|
@@ -1078,9 +1063,9 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
1078
1063
|
}
|
1079
1064
|
|
1080
1065
|
current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
|
1081
|
-
RB_GC_GUARD(current);
|
1066
|
+
(void)RB_GC_GUARD(current);
|
1082
1067
|
Check_Type(current, T_HASH);
|
1083
|
-
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
|
1068
|
+
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
1084
1069
|
|
1085
1070
|
return resultObj;
|
1086
1071
|
}
|
@@ -1149,7 +1134,6 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
|
|
1149
1134
|
static VALUE set_charset_name(VALUE self, VALUE value) {
|
1150
1135
|
char *charset_name;
|
1151
1136
|
#ifdef HAVE_RUBY_ENCODING_H
|
1152
|
-
size_t charset_name_len;
|
1153
1137
|
const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
|
1154
1138
|
rb_encoding *enc;
|
1155
1139
|
VALUE rb_enc;
|
@@ -1159,8 +1143,7 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
1159
1143
|
charset_name = RSTRING_PTR(value);
|
1160
1144
|
|
1161
1145
|
#ifdef HAVE_RUBY_ENCODING_H
|
1162
|
-
|
1163
|
-
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, charset_name_len);
|
1146
|
+
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
|
1164
1147
|
if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
|
1165
1148
|
VALUE inspect = rb_inspect(value);
|
1166
1149
|
rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
|
@@ -1183,11 +1166,11 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
|
|
1183
1166
|
GET_CLIENT(self);
|
1184
1167
|
|
1185
1168
|
mysql_ssl_set(wrapper->client,
|
1186
|
-
NIL_P(key)
|
1187
|
-
NIL_P(cert)
|
1188
|
-
NIL_P(ca)
|
1189
|
-
NIL_P(capath) ? NULL :
|
1190
|
-
NIL_P(cipher) ? NULL :
|
1169
|
+
NIL_P(key) ? NULL : StringValueCStr(key),
|
1170
|
+
NIL_P(cert) ? NULL : StringValueCStr(cert),
|
1171
|
+
NIL_P(ca) ? NULL : StringValueCStr(ca),
|
1172
|
+
NIL_P(capath) ? NULL : StringValueCStr(capath),
|
1173
|
+
NIL_P(cipher) ? NULL : StringValueCStr(cipher));
|
1191
1174
|
|
1192
1175
|
return self;
|
1193
1176
|
}
|
@@ -1220,6 +1203,17 @@ static VALUE initialize_ext(VALUE self) {
|
|
1220
1203
|
return self;
|
1221
1204
|
}
|
1222
1205
|
|
1206
|
+
/* call-seq: client.prepare # => Mysql2::Statement
|
1207
|
+
*
|
1208
|
+
* Create a new prepared statement.
|
1209
|
+
*/
|
1210
|
+
static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
|
1211
|
+
GET_CLIENT(self);
|
1212
|
+
REQUIRE_CONNECTED(wrapper);
|
1213
|
+
|
1214
|
+
return rb_mysql_stmt_new(self, sql);
|
1215
|
+
}
|
1216
|
+
|
1223
1217
|
void init_mysql2_client() {
|
1224
1218
|
/* verify the libmysql we're about to use was the version we were built against
|
1225
1219
|
https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
|
@@ -1254,17 +1248,17 @@ void init_mysql2_client() {
|
|
1254
1248
|
rb_define_alloc_func(cMysql2Client, allocate);
|
1255
1249
|
|
1256
1250
|
rb_define_singleton_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
|
1251
|
+
rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1257
1252
|
|
1258
1253
|
rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
|
1259
|
-
rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
|
1260
1254
|
rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
|
1261
1255
|
rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
|
1262
|
-
rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1263
1256
|
rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
|
1264
1257
|
rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
|
1265
1258
|
rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
|
1266
1259
|
rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
|
1267
1260
|
rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
|
1261
|
+
rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
|
1268
1262
|
rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
|
1269
1263
|
rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
|
1270
1264
|
rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
|
@@ -1290,15 +1284,19 @@ void init_mysql2_client() {
|
|
1290
1284
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
1291
1285
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1292
1286
|
rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
|
1287
|
+
rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
|
1293
1288
|
|
1294
1289
|
sym_id = ID2SYM(rb_intern("id"));
|
1295
1290
|
sym_version = ID2SYM(rb_intern("version"));
|
1291
|
+
sym_header_version = ID2SYM(rb_intern("header_version"));
|
1296
1292
|
sym_async = ID2SYM(rb_intern("async"));
|
1297
1293
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
1298
1294
|
sym_as = ID2SYM(rb_intern("as"));
|
1299
1295
|
sym_array = ID2SYM(rb_intern("array"));
|
1300
1296
|
sym_stream = ID2SYM(rb_intern("stream"));
|
1301
1297
|
|
1298
|
+
intern_brackets = rb_intern("[]");
|
1299
|
+
intern_new = rb_intern("new");
|
1302
1300
|
intern_merge = rb_intern("merge");
|
1303
1301
|
intern_merge_bang = rb_intern("merge!");
|
1304
1302
|
intern_error_number_eql = rb_intern("error_number=");
|
data/ext/mysql2/client.h
CHANGED
@@ -50,7 +50,28 @@ typedef struct {
|
|
50
50
|
MYSQL *client;
|
51
51
|
} mysql_client_wrapper;
|
52
52
|
|
53
|
+
#define REQUIRE_CONNECTED(wrapper) \
|
54
|
+
REQUIRE_INITIALIZED(wrapper) \
|
55
|
+
if (!wrapper->connected && !wrapper->reconnect_enabled) { \
|
56
|
+
rb_raise(cMysql2Error, "closed MySQL connection"); \
|
57
|
+
}
|
58
|
+
|
59
|
+
void rb_mysql_client_set_active_thread(VALUE self);
|
60
|
+
|
61
|
+
#define MARK_CONN_INACTIVE(conn) do {\
|
62
|
+
GET_CLIENT(conn); \
|
63
|
+
wrapper->active_thread = Qnil; \
|
64
|
+
} while(0)
|
65
|
+
|
66
|
+
#define GET_CLIENT(self) \
|
67
|
+
mysql_client_wrapper *wrapper; \
|
68
|
+
Data_Get_Struct(self, mysql_client_wrapper, wrapper);
|
69
|
+
|
53
70
|
void init_mysql2_client();
|
54
71
|
void decr_mysql2_client(mysql_client_wrapper *wrapper);
|
55
72
|
|
56
73
|
#endif
|
74
|
+
|
75
|
+
#ifndef HAVE_RB_HASH_DUP
|
76
|
+
VALUE rb_hash_dup(VALUE other);
|
77
|
+
#endif
|