mysql2 0.3.12b4 → 0.3.12b5
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.
- data/CHANGELOG.md +32 -0
- data/README.md +15 -9
- data/ext/mysql2/client.c +146 -74
- data/ext/mysql2/extconf.rb +1 -0
- data/ext/mysql2/mysql2_ext.h +3 -3
- data/ext/mysql2/mysql_enc_name_to_ruby.h +168 -0
- data/ext/mysql2/mysql_enc_to_ruby.h +246 -0
- data/ext/mysql2/result.c +64 -59
- data/lib/mysql2.rb +2 -2
- data/lib/mysql2/client.rb +9 -210
- data/lib/mysql2/version.rb +1 -1
- data/spec/mysql2/client_spec.rb +123 -14
- data/spec/mysql2/error_spec.rb +1 -1
- data/spec/mysql2/result_spec.rb +2 -2
- data/support/mysql_enc_to_ruby.rb +82 -0
- data/support/ruby_enc_to_mysql.rb +61 -0
- metadata +19 -116
- data/.gitignore +0 -14
- data/.rbenv-version +0 -1
- data/.rspec +0 -3
- data/.rvmrc +0 -1
- data/.travis.yml +0 -7
- data/Gemfile +0 -3
- data/Gemfile.lock +0 -61
- data/Rakefile +0 -5
- data/benchmark/active_record.rb +0 -51
- data/benchmark/active_record_threaded.rb +0 -42
- data/benchmark/allocations.rb +0 -33
- data/benchmark/escape.rb +0 -36
- data/benchmark/query_with_mysql_casting.rb +0 -80
- data/benchmark/query_without_mysql_casting.rb +0 -56
- data/benchmark/sequel.rb +0 -37
- data/benchmark/setup_db.rb +0 -119
- data/benchmark/threaded.rb +0 -44
- data/mysql2.gemspec +0 -29
- data/tasks/benchmarks.rake +0 -20
- data/tasks/compile.rake +0 -71
- data/tasks/rspec.rake +0 -26
- data/tasks/vendor_mysql.rake +0 -40
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,37 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.3.12 (not yet released)
|
4
|
+
## 0.3.12b5 (not yet released)
|
5
|
+
* builds on Ruby 2.0-head and Rubinius 2.0-dev
|
6
|
+
* encoding names now stored in a Gperf lookup rather than an array
|
7
|
+
* long-standing bug fix: options set on a single query must not be applied to subsequent queries
|
8
|
+
* add method warning_count
|
9
|
+
* add method abandon_results!
|
10
|
+
* add setter for reconnect option
|
11
|
+
* remove options method (added in 0.3.12b1)
|
12
|
+
* support microsecond Time resolution
|
13
|
+
* several INT / UINT fixes
|
14
|
+
|
15
|
+
## 0.3.12b4 (August 22, 2012)
|
16
|
+
* add write_timeout as well
|
17
|
+
|
18
|
+
## 0.3.12b3 (August 22, 2012)
|
19
|
+
* several INT / LONG fixes
|
20
|
+
* fix linking to MySQL 5.5
|
21
|
+
|
22
|
+
## 0.3.12b2 (August 10, 2012)
|
23
|
+
* more_results is now more_results?
|
24
|
+
|
25
|
+
## 0.3.12b1 (August 8, 2012)
|
26
|
+
* several threading and async bug fixes
|
27
|
+
* better handling of read and write timeouts
|
28
|
+
* add :local_infile connection option
|
29
|
+
* add MULTI_STATEMENTS connection flag and methods store_result, next_result, more_results
|
30
|
+
* add select_db and options methods
|
31
|
+
* add :stream query option
|
32
|
+
* add support for utf8mb4 encoding
|
33
|
+
* deprecation warnings for the :user, :pass, :hostname, :dbname, :db, :sock connection options
|
34
|
+
|
3
35
|
## 0.3.11 (December 6th, 2011)
|
4
36
|
* change mysql error detection strategy from using mysql_field_count to the more explicit mysql_errno
|
5
37
|
* bugfix to avoid race condition with active connections that error out
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Mysql2 - A modern, simple and very fast Mysql library for Ruby - binding to libmysql
|
2
2
|
|
3
|
+
[](https://travis-ci.org/brianmario/mysql2)
|
4
|
+
|
3
5
|
The Mysql2 gem is meant to serve the extremely common use-case of connecting, querying and iterating on results.
|
4
6
|
Some database libraries out there serve as direct 1:1 mappings of the already complex C API's available.
|
5
7
|
This one is not.
|
@@ -18,7 +20,9 @@ Mysql2::Result - returned from issuing a #query on the connection. It includes E
|
|
18
20
|
gem install mysql2
|
19
21
|
```
|
20
22
|
|
21
|
-
You may
|
23
|
+
This gem links against MySQL's `libmysqlclient` C shared library. You may need to install a package such as `libmysqlclient-dev`, `mysql-devel`, or other appropriate package for your system.
|
24
|
+
|
25
|
+
If you have installed MySQL to a non-standard location, add `gem install mysql2 --with-mysql-config=/some/random/path/bin/mysql_config`
|
22
26
|
|
23
27
|
## Usage
|
24
28
|
|
@@ -100,6 +104,7 @@ Mysql2::Client.new(
|
|
100
104
|
:flags = REMEMBER_OPTIONS | LONG_PASSWORD | LONG_FLAG | TRANSACTIONS | PROTOCOL_41 | SECURE_CONNECTION | MULTI_STATEMENTS,
|
101
105
|
:encoding = 'utf8',
|
102
106
|
:read_timeout = seconds,
|
107
|
+
:write_timeout = seconds,
|
103
108
|
:connect_timeout = seconds,
|
104
109
|
:reconnect = true/false,
|
105
110
|
:local_infile = true/false,
|
@@ -120,7 +125,7 @@ while ( client.next_result)
|
|
120
125
|
end
|
121
126
|
```
|
122
127
|
|
123
|
-
See https://gist.github.com/1367987 for using MULTI_STATEMENTS with
|
128
|
+
See https://gist.github.com/1367987 for using MULTI_STATEMENTS with Active Record.
|
124
129
|
|
125
130
|
## Cascading config
|
126
131
|
|
@@ -264,15 +269,15 @@ There are a few things that need to be kept in mind while using streaming:
|
|
264
269
|
|
265
270
|
Read more about the consequences of using `mysql_use_result` (what streaming is implemented with) here: http://dev.mysql.com/doc/refman/5.0/en/mysql-use-result.html.
|
266
271
|
|
267
|
-
##
|
272
|
+
## Active Record
|
268
273
|
|
269
|
-
To use the
|
274
|
+
To use the Active Record driver (with or without rails), all you should need to do is have this gem installed and set the adapter in your database.yml to "mysql2".
|
270
275
|
That was easy right? :)
|
271
276
|
|
272
|
-
NOTE: as of 0.3.0, and
|
277
|
+
NOTE: as of 0.3.0, and Active Record 3.1 - the Active Record adapter has been pulled out of this gem and into Active Record itself. If you need to use mysql2 with
|
273
278
|
Rails versions < 3.1 make sure and specify `gem "mysql2", "~> 0.2.7"` in your Gemfile
|
274
279
|
|
275
|
-
## Asynchronous
|
280
|
+
## Asynchronous Active Record
|
276
281
|
|
277
282
|
Please see the [em-synchrony](https://github.com/igrigorik/em-synchrony) project for details about using EventMachine with mysql2 and Rails.
|
278
283
|
|
@@ -328,7 +333,7 @@ The specs pass on my system (SL 10.6.3, x86_64) in these rubies:
|
|
328
333
|
* ruby-trunk
|
329
334
|
* rbx-head - broken at the moment, working with the rbx team for a solution
|
330
335
|
|
331
|
-
The
|
336
|
+
The Active Record driver should work on 2.3.5 and 3.0
|
332
337
|
|
333
338
|
## Yeah... but why?
|
334
339
|
|
@@ -387,6 +392,7 @@ though.
|
|
387
392
|
## Special Thanks
|
388
393
|
|
389
394
|
* Eric Wong - for the contribution (and the informative explanations) of some thread-safety, non-blocking I/O and cleanup patches. You rock dude
|
390
|
-
* Yury Korolev (http://github.com/yury) - for TONS of help testing the
|
395
|
+
* Yury Korolev (http://github.com/yury) - for TONS of help testing the Active Record adapter
|
391
396
|
* Aaron Patterson (http://github.com/tenderlove) - tons of contributions, suggestions and general badassness
|
392
|
-
* Mike Perham (http://github.com/mperham) - Async
|
397
|
+
* Mike Perham (http://github.com/mperham) - Async Active Record adapter (uses Fibers and EventMachine)
|
398
|
+
* Aaron Stone (http://github.com/sodabrew) - additional client settings, local files, microsecond time, maintenance support.
|
data/ext/mysql2/client.c
CHANGED
@@ -4,13 +4,21 @@
|
|
4
4
|
#ifndef _WIN32
|
5
5
|
#include <sys/socket.h>
|
6
6
|
#endif
|
7
|
+
#include <unistd.h>
|
7
8
|
#include "wait_for_single_fd.h"
|
8
9
|
|
10
|
+
#include "mysql_enc_name_to_ruby.h"
|
11
|
+
|
9
12
|
VALUE cMysql2Client;
|
10
13
|
extern VALUE mMysql2, cMysql2Error;
|
11
|
-
static VALUE intern_encoding_from_charset;
|
12
14
|
static VALUE sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
|
13
|
-
static ID intern_merge, intern_error_number_eql, intern_sql_state_eql;
|
15
|
+
static ID intern_merge, intern_merge_bang, intern_error_number_eql, intern_sql_state_eql;
|
16
|
+
|
17
|
+
#ifndef HAVE_RB_HASH_DUP
|
18
|
+
static VALUE rb_hash_dup(VALUE other) {
|
19
|
+
return rb_funcall(rb_cHash, rb_intern("[]"), 1, other);
|
20
|
+
}
|
21
|
+
#endif
|
14
22
|
|
15
23
|
#define REQUIRE_INITIALIZED(wrapper) \
|
16
24
|
if (!wrapper->initialized) { \
|
@@ -190,12 +198,19 @@ static VALUE allocate(VALUE klass) {
|
|
190
198
|
wrapper->encoding = Qnil;
|
191
199
|
wrapper->active_thread = Qnil;
|
192
200
|
wrapper->reconnect_enabled = 0;
|
193
|
-
wrapper->connected = 0;
|
194
|
-
wrapper->initialized = 0;
|
201
|
+
wrapper->connected = 0; /* means that a database connection is open */
|
202
|
+
wrapper->initialized = 0; /* means that that the wrapper is initialized */
|
195
203
|
wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
|
196
204
|
return obj;
|
197
205
|
}
|
198
206
|
|
207
|
+
/* call-seq:
|
208
|
+
* Mysql2::Client.escape(string)
|
209
|
+
*
|
210
|
+
* Escape +string+ so that it may be used in a SQL statement.
|
211
|
+
* Note that this escape method is not connection encoding aware.
|
212
|
+
* If you need encoding support use Mysql2::Client#escape instead.
|
213
|
+
*/
|
199
214
|
static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
|
200
215
|
unsigned char *newStr;
|
201
216
|
VALUE rb_str;
|
@@ -208,7 +223,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
|
|
208
223
|
|
209
224
|
newLen = mysql_escape_string((char *)newStr, StringValuePtr(str), oldLen);
|
210
225
|
if (newLen == oldLen) {
|
211
|
-
|
226
|
+
/* no need to return a new ruby string if nothing changed */
|
212
227
|
xfree(newStr);
|
213
228
|
return str;
|
214
229
|
} else {
|
@@ -221,6 +236,15 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
|
|
221
236
|
}
|
222
237
|
}
|
223
238
|
|
239
|
+
static VALUE rb_mysql_client_warning_count(VALUE self) {
|
240
|
+
unsigned int warning_count;
|
241
|
+
GET_CLIENT(self);
|
242
|
+
|
243
|
+
warning_count = mysql_warning_count(wrapper->client);
|
244
|
+
|
245
|
+
return UINT2NUM(warning_count);
|
246
|
+
}
|
247
|
+
|
224
248
|
static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
|
225
249
|
struct nogvl_connect_args args;
|
226
250
|
VALUE rv;
|
@@ -283,7 +307,7 @@ static VALUE do_send_query(void *args) {
|
|
283
307
|
struct nogvl_send_query_args *query_args = args;
|
284
308
|
mysql_client_wrapper *wrapper = query_args->wrapper;
|
285
309
|
if (rb_thread_blocking_region(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
|
286
|
-
|
310
|
+
/* an error occurred, we're not active anymore */
|
287
311
|
MARK_CONN_INACTIVE(self);
|
288
312
|
return rb_raise_mysql2_error(wrapper);
|
289
313
|
}
|
@@ -313,8 +337,8 @@ static VALUE nogvl_do_result(void *ptr, char use_result) {
|
|
313
337
|
result = mysql_store_result(wrapper->client);
|
314
338
|
}
|
315
339
|
|
316
|
-
|
317
|
-
|
340
|
+
/* once our result is stored off, this connection is
|
341
|
+
ready for another command to be issued */
|
318
342
|
wrapper->active_thread = Qnil;
|
319
343
|
|
320
344
|
return (VALUE)result;
|
@@ -342,18 +366,18 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
342
366
|
#endif
|
343
367
|
GET_CLIENT(self);
|
344
368
|
|
345
|
-
|
369
|
+
/* if we're not waiting on a result, do nothing */
|
346
370
|
if (NIL_P(wrapper->active_thread))
|
347
371
|
return Qnil;
|
348
372
|
|
349
373
|
REQUIRE_CONNECTED(wrapper);
|
350
374
|
if (rb_thread_blocking_region(nogvl_read_query_result, wrapper->client, RUBY_UBF_IO, 0) == Qfalse) {
|
351
|
-
|
375
|
+
/* an error occurred, mark this connection inactive */
|
352
376
|
MARK_CONN_INACTIVE(self);
|
353
377
|
return rb_raise_mysql2_error(wrapper);
|
354
378
|
}
|
355
379
|
|
356
|
-
VALUE is_streaming = rb_hash_aref(rb_iv_get(self, "@
|
380
|
+
VALUE is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
|
357
381
|
if(is_streaming == Qtrue) {
|
358
382
|
result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
|
359
383
|
} else {
|
@@ -365,13 +389,13 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
365
389
|
MARK_CONN_INACTIVE(self);
|
366
390
|
rb_raise_mysql2_error(wrapper);
|
367
391
|
}
|
368
|
-
|
392
|
+
/* no data and no error, so query was not a SELECT */
|
369
393
|
return Qnil;
|
370
394
|
}
|
371
395
|
|
372
396
|
resultObj = rb_mysql_result_to_obj(result);
|
373
|
-
|
374
|
-
rb_iv_set(resultObj, "@query_options",
|
397
|
+
/* pass-through query options for result construction later */
|
398
|
+
rb_iv_set(resultObj, "@query_options", rb_hash_dup(rb_iv_get(self, "@current_query_options")));
|
375
399
|
|
376
400
|
#ifdef HAVE_RUBY_ENCODING_H
|
377
401
|
GetMysql2Result(resultObj, result_wrapper);
|
@@ -392,9 +416,10 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
|
|
392
416
|
wrapper->active_thread = Qnil;
|
393
417
|
wrapper->connected = 0;
|
394
418
|
|
395
|
-
|
396
|
-
|
397
|
-
|
419
|
+
/* manually close the socket for read/write
|
420
|
+
this feels dirty, but is there another way? */
|
421
|
+
close(wrapper->client->net.fd);
|
422
|
+
wrapper->client->net.fd = -1;
|
398
423
|
|
399
424
|
rb_exc_raise(error);
|
400
425
|
|
@@ -417,8 +442,8 @@ static VALUE do_query(void *args) {
|
|
417
442
|
Check_Type(read_timeout, T_FIXNUM);
|
418
443
|
tvp = &tv;
|
419
444
|
sec = FIX2INT(read_timeout);
|
420
|
-
|
421
|
-
|
445
|
+
/* TODO: support partial seconds?
|
446
|
+
also, this check is here for sanity, we also check up in Ruby */
|
422
447
|
if (sec >= 0) {
|
423
448
|
tvp->tv_sec = sec;
|
424
449
|
} else {
|
@@ -455,9 +480,9 @@ static VALUE finish_and_mark_inactive(void *args) {
|
|
455
480
|
GET_CLIENT(self);
|
456
481
|
|
457
482
|
if (!NIL_P(wrapper->active_thread)) {
|
458
|
-
|
459
|
-
|
460
|
-
|
483
|
+
/* if we got here, the result hasn't been read off the wire yet
|
484
|
+
so lets do that and then throw it away because we have no way
|
485
|
+
of getting it back up to the caller from here */
|
461
486
|
result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
|
462
487
|
mysql_free_result(result);
|
463
488
|
|
@@ -468,6 +493,36 @@ static VALUE finish_and_mark_inactive(void *args) {
|
|
468
493
|
}
|
469
494
|
#endif
|
470
495
|
|
496
|
+
/* call-seq:
|
497
|
+
* client.abandon_results!
|
498
|
+
*
|
499
|
+
* When using MULTI_STATEMENTS support, calling this will throw
|
500
|
+
* away any unprocessed results as fast as it can in order to
|
501
|
+
* put the connection back into a state where queries can be issued
|
502
|
+
* again.
|
503
|
+
*/
|
504
|
+
static VALUE rb_mysql_client_abandon_results(VALUE self) {
|
505
|
+
GET_CLIENT(self);
|
506
|
+
|
507
|
+
MYSQL_RES *result;
|
508
|
+
int ret;
|
509
|
+
|
510
|
+
while (mysql_more_results(wrapper->client) == 1) {
|
511
|
+
ret = mysql_next_result(wrapper->client);
|
512
|
+
if (ret > 0) {
|
513
|
+
rb_raise_mysql2_error(wrapper);
|
514
|
+
}
|
515
|
+
|
516
|
+
result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
|
517
|
+
|
518
|
+
if (result != NULL) {
|
519
|
+
mysql_free_result(result);
|
520
|
+
}
|
521
|
+
}
|
522
|
+
|
523
|
+
return Qnil;
|
524
|
+
}
|
525
|
+
|
471
526
|
/* call-seq:
|
472
527
|
* client.query(sql, options = {})
|
473
528
|
*
|
@@ -480,7 +535,7 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
|
480
535
|
#endif
|
481
536
|
struct nogvl_send_query_args args;
|
482
537
|
int async = 0;
|
483
|
-
VALUE opts,
|
538
|
+
VALUE opts, current;
|
484
539
|
VALUE thread_current = rb_thread_current();
|
485
540
|
#ifdef HAVE_RUBY_ENCODING_H
|
486
541
|
rb_encoding *conn_enc;
|
@@ -490,31 +545,28 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
|
490
545
|
REQUIRE_CONNECTED(wrapper);
|
491
546
|
args.mysql = wrapper->client;
|
492
547
|
|
493
|
-
|
494
|
-
|
548
|
+
rb_iv_set(self, "@current_query_options", rb_hash_dup(rb_iv_get(self, "@query_options")));
|
549
|
+
current = rb_iv_get(self, "@current_query_options");
|
495
550
|
if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
|
496
|
-
opts = rb_funcall(
|
497
|
-
rb_iv_set(self, "@query_options", opts);
|
551
|
+
opts = rb_funcall(current, intern_merge_bang, 1, opts);
|
498
552
|
|
499
|
-
if (rb_hash_aref(
|
553
|
+
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
500
554
|
async = 1;
|
501
555
|
}
|
502
|
-
} else {
|
503
|
-
opts = defaults;
|
504
556
|
}
|
505
557
|
|
506
558
|
Check_Type(args.sql, T_STRING);
|
507
559
|
#ifdef HAVE_RUBY_ENCODING_H
|
508
560
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
509
|
-
|
561
|
+
/* ensure the string is in the encoding the connection is expecting */
|
510
562
|
args.sql = rb_str_export_to_enc(args.sql, conn_enc);
|
511
563
|
#endif
|
512
564
|
args.sql_ptr = StringValuePtr(args.sql);
|
513
565
|
args.sql_len = RSTRING_LEN(args.sql);
|
514
566
|
|
515
|
-
|
567
|
+
/* see if this connection is still waiting on a result from a previous query */
|
516
568
|
if (NIL_P(wrapper->active_thread)) {
|
517
|
-
|
569
|
+
/* mark this connection active */
|
518
570
|
wrapper->active_thread = thread_current;
|
519
571
|
} else if (wrapper->active_thread == thread_current) {
|
520
572
|
rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
|
@@ -544,7 +596,7 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
|
544
596
|
#else
|
545
597
|
do_send_query(&args);
|
546
598
|
|
547
|
-
|
599
|
+
/* this will just block until the result is ready */
|
548
600
|
return rb_ensure(rb_mysql_client_async_result, self, finish_and_mark_inactive, self);
|
549
601
|
#endif
|
550
602
|
}
|
@@ -569,7 +621,7 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
|
|
569
621
|
#ifdef HAVE_RUBY_ENCODING_H
|
570
622
|
default_internal_enc = rb_default_internal_encoding();
|
571
623
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
572
|
-
|
624
|
+
/* ensure the string is in the encoding the connection is expecting */
|
573
625
|
str = rb_str_export_to_enc(str, conn_enc);
|
574
626
|
#endif
|
575
627
|
|
@@ -578,7 +630,7 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
|
|
578
630
|
|
579
631
|
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, StringValuePtr(str), oldLen);
|
580
632
|
if (newLen == oldLen) {
|
581
|
-
|
633
|
+
/* no need to return a new ruby string if nothing changed */
|
582
634
|
xfree(newStr);
|
583
635
|
return str;
|
584
636
|
} else {
|
@@ -639,11 +691,11 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
639
691
|
|
640
692
|
result = mysql_options(wrapper->client, opt, retval);
|
641
693
|
|
642
|
-
|
694
|
+
/* Zero means success */
|
643
695
|
if (result != 0) {
|
644
696
|
rb_warn("%s\n", mysql_error(wrapper->client));
|
645
697
|
} else {
|
646
|
-
|
698
|
+
/* Special case for reconnect, this option is also stored in the wrapper struct */
|
647
699
|
if (opt == MYSQL_OPT_RECONNECT)
|
648
700
|
wrapper->reconnect_enabled = boolval;
|
649
701
|
}
|
@@ -651,12 +703,6 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
651
703
|
return (result == 0) ? Qtrue : Qfalse;
|
652
704
|
}
|
653
705
|
|
654
|
-
static VALUE rb_mysql_client_options(VALUE self, VALUE option, VALUE value) {
|
655
|
-
Check_Type(option, T_FIXNUM);
|
656
|
-
int opt = NUM2INT(option);
|
657
|
-
return _mysql_client_options(self, opt, value);
|
658
|
-
}
|
659
|
-
|
660
706
|
/* call-seq:
|
661
707
|
* client.info
|
662
708
|
*
|
@@ -835,6 +881,11 @@ static VALUE rb_mysql_client_ping(VALUE self) {
|
|
835
881
|
}
|
836
882
|
}
|
837
883
|
|
884
|
+
/* call-seq:
|
885
|
+
* client.more_results?
|
886
|
+
*
|
887
|
+
* Returns true or false if there are more results to process.
|
888
|
+
*/
|
838
889
|
static VALUE rb_mysql_client_more_results(VALUE self)
|
839
890
|
{
|
840
891
|
GET_CLIENT(self);
|
@@ -844,18 +895,33 @@ static VALUE rb_mysql_client_more_results(VALUE self)
|
|
844
895
|
return Qtrue;
|
845
896
|
}
|
846
897
|
|
898
|
+
/* call-seq:
|
899
|
+
* client.next_result
|
900
|
+
*
|
901
|
+
* Fetch the next result set from the server.
|
902
|
+
* Returns nothing.
|
903
|
+
*/
|
847
904
|
static VALUE rb_mysql_client_next_result(VALUE self)
|
848
905
|
{
|
849
906
|
GET_CLIENT(self);
|
850
907
|
int ret;
|
851
908
|
ret = mysql_next_result(wrapper->client);
|
852
|
-
if (ret
|
909
|
+
if (ret > 0) {
|
910
|
+
rb_raise_mysql2_error(wrapper);
|
911
|
+
return Qfalse;
|
912
|
+
} else if (ret == 0) {
|
853
913
|
return Qtrue;
|
854
|
-
else
|
914
|
+
} else {
|
855
915
|
return Qfalse;
|
916
|
+
}
|
856
917
|
}
|
857
918
|
|
858
|
-
|
919
|
+
/* call-seq:
|
920
|
+
* client.store_result
|
921
|
+
*
|
922
|
+
* Return the next result object from a query which
|
923
|
+
* yielded multiple result sets.
|
924
|
+
*/
|
859
925
|
static VALUE rb_mysql_client_store_result(VALUE self)
|
860
926
|
{
|
861
927
|
MYSQL_RES * result;
|
@@ -864,12 +930,7 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
864
930
|
mysql2_result_wrapper * result_wrapper;
|
865
931
|
#endif
|
866
932
|
|
867
|
-
|
868
933
|
GET_CLIENT(self);
|
869
|
-
// MYSQL_RES* res = mysql_store_result(wrapper->client);
|
870
|
-
// if (res == NULL)
|
871
|
-
// mysql_raise(wrapper->client);
|
872
|
-
// return mysqlres2obj(res);
|
873
934
|
|
874
935
|
result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
|
875
936
|
|
@@ -877,13 +938,13 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
877
938
|
if (mysql_errno(wrapper->client) != 0) {
|
878
939
|
rb_raise_mysql2_error(wrapper);
|
879
940
|
}
|
880
|
-
|
941
|
+
/* no data and no error, so query was not a SELECT */
|
881
942
|
return Qnil;
|
882
943
|
}
|
883
944
|
|
884
945
|
resultObj = rb_mysql_result_to_obj(result);
|
885
|
-
|
886
|
-
rb_iv_set(resultObj, "@query_options",
|
946
|
+
/* pass-through query options for result construction later */
|
947
|
+
rb_iv_set(resultObj, "@query_options", rb_hash_dup(rb_iv_get(self, "@current_query_options")));
|
887
948
|
|
888
949
|
#ifdef HAVE_RUBY_ENCODING_H
|
889
950
|
GetMysql2Result(resultObj, result_wrapper);
|
@@ -905,6 +966,13 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
|
|
905
966
|
}
|
906
967
|
#endif
|
907
968
|
|
969
|
+
/* call-seq:
|
970
|
+
* client.reconnect = true
|
971
|
+
*
|
972
|
+
* Enable or disable the automatic reconnect behavior of libmysql.
|
973
|
+
* Read http://dev.mysql.com/doc/refman/5.5/en/auto-reconnect.html
|
974
|
+
* for more information.
|
975
|
+
*/
|
908
976
|
static VALUE set_reconnect(VALUE self, VALUE value) {
|
909
977
|
return _mysql_client_options(self, MYSQL_OPT_RECONNECT, value);
|
910
978
|
}
|
@@ -930,9 +998,9 @@ static VALUE set_read_timeout(VALUE self, VALUE value) {
|
|
930
998
|
if (sec < 0) {
|
931
999
|
rb_raise(cMysql2Error, "read_timeout must be a positive integer, you passed %ld", sec);
|
932
1000
|
}
|
933
|
-
|
934
|
-
|
935
|
-
|
1001
|
+
/* Set the instance variable here even though _mysql_client_options
|
1002
|
+
might not succeed, because the timeout is used in other ways
|
1003
|
+
elsewhere */
|
936
1004
|
rb_iv_set(self, "@read_timeout", value);
|
937
1005
|
return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
|
938
1006
|
}
|
@@ -948,26 +1016,30 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
|
|
948
1016
|
}
|
949
1017
|
|
950
1018
|
static VALUE set_charset_name(VALUE self, VALUE value) {
|
951
|
-
char *
|
1019
|
+
char *charset_name;
|
1020
|
+
size_t charset_name_len;
|
1021
|
+
const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
|
952
1022
|
#ifdef HAVE_RUBY_ENCODING_H
|
953
|
-
|
1023
|
+
rb_encoding *enc;
|
1024
|
+
VALUE rb_enc;
|
954
1025
|
#endif
|
955
1026
|
GET_CLIENT(self);
|
956
1027
|
|
1028
|
+
charset_name = RSTRING_PTR(value);
|
1029
|
+
charset_name_len = RSTRING_LEN(value);
|
1030
|
+
|
957
1031
|
#ifdef HAVE_RUBY_ENCODING_H
|
958
|
-
|
959
|
-
if (
|
1032
|
+
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, charset_name_len);
|
1033
|
+
if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
|
960
1034
|
VALUE inspect = rb_inspect(value);
|
961
1035
|
rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
|
962
1036
|
} else {
|
963
|
-
|
964
|
-
|
965
|
-
|
1037
|
+
enc = rb_enc_find(mysql2rb->rb_name);
|
1038
|
+
rb_enc = rb_enc_from_encoding(enc);
|
1039
|
+
wrapper->encoding = rb_enc;
|
966
1040
|
}
|
967
1041
|
#endif
|
968
1042
|
|
969
|
-
charset_name = StringValuePtr(value);
|
970
|
-
|
971
1043
|
if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
|
972
1044
|
/* TODO: warning - unable to set charset */
|
973
1045
|
rb_warn("%s\n", mysql_error(wrapper->client));
|
@@ -1004,15 +1076,15 @@ static VALUE initialize_ext(VALUE self) {
|
|
1004
1076
|
}
|
1005
1077
|
|
1006
1078
|
void init_mysql2_client() {
|
1007
|
-
|
1008
|
-
|
1079
|
+
/* verify the libmysql we're about to use was the version we were built against
|
1080
|
+
https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
|
1009
1081
|
int i;
|
1010
1082
|
int dots = 0;
|
1011
1083
|
const char *lib = mysql_get_client_info();
|
1012
1084
|
for (i = 0; lib[i] != 0 && MYSQL_SERVER_VERSION[i] != 0; i++) {
|
1013
1085
|
if (lib[i] == '.') {
|
1014
1086
|
dots++;
|
1015
|
-
|
1087
|
+
/* we only compare MAJOR and MINOR */
|
1016
1088
|
if (dots == 2) break;
|
1017
1089
|
}
|
1018
1090
|
if (lib[i] != MYSQL_SERVER_VERSION[i]) {
|
@@ -1032,6 +1104,7 @@ void init_mysql2_client() {
|
|
1032
1104
|
|
1033
1105
|
rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
|
1034
1106
|
rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
|
1107
|
+
rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
|
1035
1108
|
rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
|
1036
1109
|
rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1037
1110
|
rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
|
@@ -1045,12 +1118,12 @@ void init_mysql2_client() {
|
|
1045
1118
|
rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
|
1046
1119
|
rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
|
1047
1120
|
rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
|
1048
|
-
rb_define_method(cMysql2Client, "
|
1121
|
+
rb_define_method(cMysql2Client, "reconnect=", set_reconnect, 1);
|
1122
|
+
rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
|
1049
1123
|
#ifdef HAVE_RUBY_ENCODING_H
|
1050
1124
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
1051
1125
|
#endif
|
1052
1126
|
|
1053
|
-
rb_define_private_method(cMysql2Client, "reconnect=", set_reconnect, 1);
|
1054
1127
|
rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
|
1055
1128
|
rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
|
1056
1129
|
rb_define_private_method(cMysql2Client, "write_timeout=", set_write_timeout, 1);
|
@@ -1060,8 +1133,6 @@ void init_mysql2_client() {
|
|
1060
1133
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1061
1134
|
rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
|
1062
1135
|
|
1063
|
-
intern_encoding_from_charset = rb_intern("encoding_from_charset");
|
1064
|
-
|
1065
1136
|
sym_id = ID2SYM(rb_intern("id"));
|
1066
1137
|
sym_version = ID2SYM(rb_intern("version"));
|
1067
1138
|
sym_async = ID2SYM(rb_intern("async"));
|
@@ -1071,6 +1142,7 @@ void init_mysql2_client() {
|
|
1071
1142
|
sym_stream = ID2SYM(rb_intern("stream"));
|
1072
1143
|
|
1073
1144
|
intern_merge = rb_intern("merge");
|
1145
|
+
intern_merge_bang = rb_intern("merge!");
|
1074
1146
|
intern_error_number_eql = rb_intern("error_number=");
|
1075
1147
|
intern_sql_state_eql = rb_intern("sql_state=");
|
1076
1148
|
|