mysql2 0.3.12b4 → 0.3.12b5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/brianmario/mysql2.png)](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
|
|