mysql2 0.4.4-x64-mingw32 → 0.4.5-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +15 -5
- data/ext/mysql2/client.c +82 -0
- data/ext/mysql2/client.h +0 -6
- data/ext/mysql2/extconf.rb +20 -0
- data/ext/mysql2/result.c +3 -0
- data/ext/mysql2/statement.c +82 -12
- data/lib/mysql2/2.0/mysql2.so +0 -0
- data/lib/mysql2/2.1/mysql2.so +0 -0
- data/lib/mysql2/2.2/mysql2.so +0 -0
- data/lib/mysql2/2.3/mysql2.so +0 -0
- data/lib/mysql2/client.rb +14 -1
- data/lib/mysql2/version.rb +1 -1
- data/spec/configuration.yml.example +0 -6
- data/spec/mysql2/client_spec.rb +27 -22
- data/spec/mysql2/error_spec.rb +1 -1
- data/spec/mysql2/statement_spec.rb +62 -6
- data/spec/ssl/gen_certs.sh +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1b0efca37f8ff5a3ac50230e15463915e04b36d
|
4
|
+
data.tar.gz: d2701aa6fc62b6b94f1041c097aeeaa2e9aa2d46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f50def5ea6bac5087889b85e2bd1aa21358d94f8a38cc573ef517035b7ff05c5cb957e9742e5a1297fcf61c728fc3389575f7815559a21841970c8fc5bf5df9
|
7
|
+
data.tar.gz: 5ad524efa103b43485fb523c8f08608e20331ded371ba6e757e459ec528fe149aec909c03439e8ca080337bd1c84b710c90a4fda070b2eb06673979dec6cd8ff
|
data/README.md
CHANGED
@@ -164,8 +164,8 @@ by the query like this:
|
|
164
164
|
``` ruby
|
165
165
|
headers = results.fields # <= that's an array of field names, in order
|
166
166
|
results.each(:as => :array) do |row|
|
167
|
-
# Each row is an array, ordered the same as the query results
|
168
|
-
# An otter's den is called a "holt" or "couch"
|
167
|
+
# Each row is an array, ordered the same as the query results
|
168
|
+
# An otter's den is called a "holt" or "couch"
|
169
169
|
end
|
170
170
|
```
|
171
171
|
|
@@ -203,6 +203,7 @@ Mysql2::Client.new(
|
|
203
203
|
:reconnect = true/false,
|
204
204
|
:local_infile = true/false,
|
205
205
|
:secure_auth = true/false,
|
206
|
+
:ssl_mode = :disabled / :preferred / :required / :verify_ca / :verify_identity,
|
206
207
|
:default_file = '/path/to/my.cfg',
|
207
208
|
:default_group = 'my.cfg section',
|
208
209
|
:init_command => sql
|
@@ -398,6 +399,15 @@ client = Mysql2::Client.new
|
|
398
399
|
result = client.query("SELECT * FROM table_with_boolean_field", :cast_booleans => true)
|
399
400
|
```
|
400
401
|
|
402
|
+
Keep in mind that this works only with fields and not with computed values, e.g. this result will contain `1`, not `true`:
|
403
|
+
|
404
|
+
``` ruby
|
405
|
+
client = Mysql2::Client.new
|
406
|
+
result = client.query("SELECT true", :cast_booleans => true)
|
407
|
+
```
|
408
|
+
|
409
|
+
CAST function wouldn't help here as there's no way to cast to TINYINT(1). Apparently the only way to solve this is to use a stored procedure with return type set to TINYINT(1).
|
410
|
+
|
401
411
|
### Skipping casting
|
402
412
|
|
403
413
|
Mysql2 casting is fast, but not as fast as not casting data. In rare cases where typecasting is not needed, it will be faster to disable it by providing :cast => false. (Note that :cast => false overrides :cast_booleans => true.)
|
@@ -484,13 +494,13 @@ As for field values themselves, I'm workin on it - but expect that soon.
|
|
484
494
|
|
485
495
|
This gem is tested with the following Ruby versions on Linux and Mac OS X:
|
486
496
|
|
487
|
-
* Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x, 2.3.x
|
497
|
+
* Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x
|
488
498
|
* Ruby Enterprise Edition (based on MRI 1.8.7)
|
489
|
-
* Rubinius 2.x
|
499
|
+
* Rubinius 2.x and 3.x do work but may fail under some workloads
|
490
500
|
|
491
501
|
This gem is tested with the following MySQL and MariaDB versions:
|
492
502
|
|
493
|
-
* MySQL 5.5, 5.6, 5.7
|
503
|
+
* MySQL 5.5, 5.6, 5.7, 8.0
|
494
504
|
* MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
|
495
505
|
* MariaDB 5.5, 10.0, 10.1
|
496
506
|
|
data/ext/mysql2/client.c
CHANGED
@@ -30,6 +30,12 @@ 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, "MySQL client is not connected"); \
|
37
|
+
}
|
38
|
+
|
33
39
|
#define REQUIRE_NOT_CONNECTED(wrapper) \
|
34
40
|
REQUIRE_INITIALIZED(wrapper) \
|
35
41
|
if (wrapper->connected) { \
|
@@ -47,6 +53,16 @@ VALUE rb_hash_dup(VALUE other) {
|
|
47
53
|
#define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
|
48
54
|
#endif
|
49
55
|
|
56
|
+
/*
|
57
|
+
* compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
|
58
|
+
*/
|
59
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
60
|
+
#define SSL_MODE_DISABLED 1
|
61
|
+
#define SSL_MODE_REQUIRED 3
|
62
|
+
#define HAVE_CONST_SSL_MODE_DISABLED
|
63
|
+
#define HAVE_CONST_SSL_MODE_REQUIRED
|
64
|
+
#endif
|
65
|
+
|
50
66
|
/*
|
51
67
|
* used to pass all arguments to mysql_real_connect while inside
|
52
68
|
* rb_thread_call_without_gvl
|
@@ -83,6 +99,43 @@ struct nogvl_select_db_args {
|
|
83
99
|
char *db;
|
84
100
|
};
|
85
101
|
|
102
|
+
static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
|
103
|
+
unsigned long version = mysql_get_client_version();
|
104
|
+
|
105
|
+
if (version < 50703) {
|
106
|
+
rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
|
107
|
+
return Qnil;
|
108
|
+
}
|
109
|
+
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
110
|
+
GET_CLIENT(self);
|
111
|
+
int val = NUM2INT( setting );
|
112
|
+
if (version >= 50703 && version < 50711) {
|
113
|
+
if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
|
114
|
+
my_bool b = ( val == SSL_MODE_REQUIRED );
|
115
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
|
116
|
+
return INT2NUM(result);
|
117
|
+
|
118
|
+
} else {
|
119
|
+
rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
|
120
|
+
return Qnil;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
#endif
|
124
|
+
#ifdef FULL_SSL_MODE_SUPPORT
|
125
|
+
GET_CLIENT(self);
|
126
|
+
int val = NUM2INT( setting );
|
127
|
+
|
128
|
+
if (val != SSL_MODE_DISABLED && val != SSL_MODE_PREFERRED && val != SSL_MODE_REQUIRED && val != SSL_MODE_VERIFY_CA && val != SSL_MODE_VERIFY_IDENTITY) {
|
129
|
+
rb_raise(cMysql2Error, "ssl_mode= takes DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY, you passed: %d", val );
|
130
|
+
}
|
131
|
+
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_MODE, &val );
|
132
|
+
|
133
|
+
return INT2NUM(result);
|
134
|
+
#endif
|
135
|
+
#ifdef NO_SSL_MODE_SUPPORT
|
136
|
+
return Qnil;
|
137
|
+
#endif
|
138
|
+
}
|
86
139
|
/*
|
87
140
|
* non-blocking mysql_*() functions that we won't be wrapping since
|
88
141
|
* they do not appear to hit the network nor issue any interruptible
|
@@ -1192,6 +1245,7 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
1192
1245
|
#endif
|
1193
1246
|
GET_CLIENT(self);
|
1194
1247
|
|
1248
|
+
Check_Type(value, T_STRING);
|
1195
1249
|
charset_name = RSTRING_PTR(value);
|
1196
1250
|
|
1197
1251
|
#ifdef HAVE_RUBY_ENCODING_H
|
@@ -1337,6 +1391,7 @@ void init_mysql2_client() {
|
|
1337
1391
|
rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
|
1338
1392
|
rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
|
1339
1393
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
1394
|
+
rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
|
1340
1395
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1341
1396
|
rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
|
1342
1397
|
rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
|
@@ -1464,4 +1519,31 @@ void init_mysql2_client() {
|
|
1464
1519
|
rb_const_set(cMysql2Client, rb_intern("BASIC_FLAGS"),
|
1465
1520
|
LONG2NUM(CLIENT_BASIC_FLAGS));
|
1466
1521
|
#endif
|
1522
|
+
|
1523
|
+
#if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
|
1524
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1525
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
|
1526
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1527
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
|
1528
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
|
1529
|
+
#elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
|
1530
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1531
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
|
1532
|
+
#endif
|
1533
|
+
|
1534
|
+
#ifndef HAVE_CONST_SSL_MODE_DISABLED
|
1535
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));
|
1536
|
+
#endif
|
1537
|
+
#ifndef HAVE_CONST_SSL_MODE_PREFERRED
|
1538
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(0));
|
1539
|
+
#endif
|
1540
|
+
#ifndef HAVE_CONST_SSL_MODE_REQUIRED
|
1541
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(0));
|
1542
|
+
#endif
|
1543
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_CA
|
1544
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(0));
|
1545
|
+
#endif
|
1546
|
+
#ifndef HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
|
1547
|
+
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
|
1548
|
+
#endif
|
1467
1549
|
}
|
data/ext/mysql2/client.h
CHANGED
@@ -51,12 +51,6 @@ typedef struct {
|
|
51
51
|
MYSQL *client;
|
52
52
|
} mysql_client_wrapper;
|
53
53
|
|
54
|
-
#define REQUIRE_CONNECTED(wrapper) \
|
55
|
-
REQUIRE_INITIALIZED(wrapper) \
|
56
|
-
if (!wrapper->connected && !wrapper->reconnect_enabled) { \
|
57
|
-
rb_raise(cMysql2Error, "closed MySQL connection"); \
|
58
|
-
}
|
59
|
-
|
60
54
|
void rb_mysql_client_set_active_thread(VALUE self);
|
61
55
|
|
62
56
|
#define GET_CLIENT(self) \
|
data/ext/mysql2/extconf.rb
CHANGED
@@ -12,6 +12,20 @@ def asplode(lib)
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
def add_ssl_defines(header)
|
16
|
+
all_modes_found = %w(SSL_MODE_DISABLED SSL_MODE_PREFERRED SSL_MODE_REQUIRED SSL_MODE_VERIFY_CA SSL_MODE_VERIFY_IDENTITY).inject(true) do |m, ssl_mode|
|
17
|
+
m && have_const(ssl_mode, header)
|
18
|
+
end
|
19
|
+
$CFLAGS << ' -DFULL_SSL_MODE_SUPPORT' if all_modes_found
|
20
|
+
# if we only have ssl toggle (--ssl,--disable-ssl) from 5.7.3 to 5.7.10
|
21
|
+
has_no_support = all_modes_found ? false : !have_const('MYSQL_OPT_SSL_ENFORCE', header)
|
22
|
+
$CFLAGS << ' -DNO_SSL_MODE_SUPPORT' if has_no_support
|
23
|
+
end
|
24
|
+
|
25
|
+
# 2.1+
|
26
|
+
have_func('rb_absint_size')
|
27
|
+
have_func('rb_absint_singlebit_p')
|
28
|
+
|
15
29
|
# 2.0-only
|
16
30
|
have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h')
|
17
31
|
|
@@ -20,6 +34,7 @@ have_func('rb_thread_blocking_region')
|
|
20
34
|
have_func('rb_wait_for_single_fd')
|
21
35
|
have_func('rb_hash_dup')
|
22
36
|
have_func('rb_intern3')
|
37
|
+
have_func('rb_big_cmp')
|
23
38
|
|
24
39
|
# borrowed from mysqlplus
|
25
40
|
# http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
|
@@ -37,6 +52,9 @@ dirs = ENV.fetch('PATH').split(File::PATH_SEPARATOR) + %w(
|
|
37
52
|
/usr/local/opt/mysql5*
|
38
53
|
).map { |dir| dir << '/bin' }
|
39
54
|
|
55
|
+
# For those without HOMEBREW_ROOT in PATH
|
56
|
+
dirs << "#{ENV['HOMEBREW_ROOT']}/bin" if ENV['HOMEBREW_ROOT']
|
57
|
+
|
40
58
|
GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5,mariadb_config}"
|
41
59
|
|
42
60
|
# If the user has provided a --with-mysql-dir argument, we must respect it or fail.
|
@@ -87,6 +105,8 @@ else
|
|
87
105
|
asplode 'mysql.h'
|
88
106
|
end
|
89
107
|
|
108
|
+
add_ssl_defines([prefix, 'mysql.h'].compact.join('/'))
|
109
|
+
|
90
110
|
%w(errmsg.h mysqld_error.h).each do |h|
|
91
111
|
header = [prefix, h].compact.join '/'
|
92
112
|
asplode h unless have_header header
|
data/ext/mysql2/result.c
CHANGED
@@ -920,6 +920,9 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
920
920
|
wrapper->numberOfRows = wrapper->stmt_wrapper ? mysql_stmt_num_rows(wrapper->stmt_wrapper->stmt) : mysql_num_rows(wrapper->result);
|
921
921
|
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
922
922
|
} else if (wrapper->rows && !cacheRows) {
|
923
|
+
if (wrapper->resultFreed) {
|
924
|
+
rb_raise(cMysql2Error, "Result set has already been freed");
|
925
|
+
}
|
923
926
|
mysql_data_seek(wrapper->result, 0);
|
924
927
|
wrapper->lastRowProcessed = 0;
|
925
928
|
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
data/ext/mysql2/statement.c
CHANGED
@@ -2,8 +2,11 @@
|
|
2
2
|
|
3
3
|
VALUE cMysql2Statement;
|
4
4
|
extern VALUE mMysql2, cMysql2Error, cBigDecimal, cDateTime, cDate;
|
5
|
-
static VALUE sym_stream, intern_new_with_args, intern_each;
|
6
|
-
static VALUE intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year
|
5
|
+
static VALUE sym_stream, intern_new_with_args, intern_each, intern_to_s;
|
6
|
+
static VALUE intern_sec_fraction, intern_usec, intern_sec, intern_min, intern_hour, intern_day, intern_month, intern_year;
|
7
|
+
#ifndef HAVE_RB_BIG_CMP
|
8
|
+
static ID id_cmp;
|
9
|
+
#endif
|
7
10
|
|
8
11
|
#define GET_STATEMENT(self) \
|
9
12
|
mysql_stmt_wrapper *stmt_wrapper; \
|
@@ -180,7 +183,6 @@ static void *nogvl_execute(void *ptr) {
|
|
180
183
|
static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length_buffer, VALUE string) {
|
181
184
|
unsigned long length;
|
182
185
|
|
183
|
-
bind_buffer->buffer_type = MYSQL_TYPE_STRING;
|
184
186
|
bind_buffer->buffer = RSTRING_PTR(string);
|
185
187
|
|
186
188
|
length = RSTRING_LEN(string);
|
@@ -204,6 +206,50 @@ static void set_buffer_for_string(MYSQL_BIND* bind_buffer, unsigned long *length
|
|
204
206
|
xfree(length_buffers); \
|
205
207
|
}
|
206
208
|
|
209
|
+
/* return 0 if the given bignum can cast as LONG_LONG, otherwise 1 */
|
210
|
+
static int my_big2ll(VALUE bignum, LONG_LONG *ptr)
|
211
|
+
{
|
212
|
+
unsigned LONG_LONG num;
|
213
|
+
size_t len;
|
214
|
+
#ifdef HAVE_RB_ABSINT_SIZE
|
215
|
+
int nlz_bits = 0;
|
216
|
+
len = rb_absint_size(bignum, &nlz_bits);
|
217
|
+
#else
|
218
|
+
len = RBIGNUM_LEN(bignum) * SIZEOF_BDIGITS;
|
219
|
+
#endif
|
220
|
+
if (len > sizeof(LONG_LONG)) goto overflow;
|
221
|
+
if (RBIGNUM_POSITIVE_P(bignum)) {
|
222
|
+
num = rb_big2ull(bignum);
|
223
|
+
if (num > LLONG_MAX)
|
224
|
+
goto overflow;
|
225
|
+
*ptr = num;
|
226
|
+
}
|
227
|
+
else {
|
228
|
+
if (len == 8 &&
|
229
|
+
#ifdef HAVE_RB_ABSINT_SIZE
|
230
|
+
nlz_bits == 0 &&
|
231
|
+
#endif
|
232
|
+
#if defined(HAVE_RB_ABSINT_SIZE) && defined(HAVE_RB_ABSINT_SINGLEBIT_P)
|
233
|
+
/* Optimized to avoid object allocation for Ruby 2.1+
|
234
|
+
* only -0x8000000000000000 is safe if `len == 8 && nlz_bits == 0`
|
235
|
+
*/
|
236
|
+
!rb_absint_singlebit_p(bignum)
|
237
|
+
#elif defined(HAVE_RB_BIG_CMP)
|
238
|
+
rb_big_cmp(bignum, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
|
239
|
+
#else
|
240
|
+
/* Ruby 1.8.7 and REE doesn't have rb_big_cmp */
|
241
|
+
rb_funcall(bignum, id_cmp, 1, LL2NUM(LLONG_MIN)) == INT2FIX(-1)
|
242
|
+
#endif
|
243
|
+
) {
|
244
|
+
goto overflow;
|
245
|
+
}
|
246
|
+
*ptr = rb_big2ll(bignum);
|
247
|
+
}
|
248
|
+
return 0;
|
249
|
+
overflow:
|
250
|
+
return 1;
|
251
|
+
}
|
252
|
+
|
207
253
|
/* call-seq: stmt.execute
|
208
254
|
*
|
209
255
|
* Executes the current prepared statement, returns +result+.
|
@@ -265,9 +311,23 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
265
311
|
#endif
|
266
312
|
break;
|
267
313
|
case T_BIGNUM:
|
268
|
-
|
269
|
-
|
270
|
-
|
314
|
+
{
|
315
|
+
LONG_LONG num;
|
316
|
+
if (my_big2ll(argv[i], &num) == 0) {
|
317
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_LONGLONG;
|
318
|
+
bind_buffers[i].buffer = xmalloc(sizeof(long long int));
|
319
|
+
*(LONG_LONG*)(bind_buffers[i].buffer) = num;
|
320
|
+
} else {
|
321
|
+
/* The bignum was larger than we can fit in LONG_LONG, send it as a string */
|
322
|
+
VALUE rb_val_as_string = rb_big2str(argv[i], 10);
|
323
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_NEWDECIMAL;
|
324
|
+
params_enc[i] = rb_val_as_string;
|
325
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
326
|
+
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
327
|
+
#endif
|
328
|
+
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
329
|
+
}
|
330
|
+
}
|
271
331
|
break;
|
272
332
|
case T_FLOAT:
|
273
333
|
bind_buffers[i].buffer_type = MYSQL_TYPE_DOUBLE;
|
@@ -275,13 +335,13 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
275
335
|
*(double*)(bind_buffers[i].buffer) = NUM2DBL(argv[i]);
|
276
336
|
break;
|
277
337
|
case T_STRING:
|
278
|
-
|
279
|
-
|
338
|
+
bind_buffers[i].buffer_type = MYSQL_TYPE_STRING;
|
339
|
+
|
340
|
+
params_enc[i] = argv[i];
|
280
341
|
#ifdef HAVE_RUBY_ENCODING_H
|
281
|
-
|
342
|
+
params_enc[i] = rb_str_export_to_enc(params_enc[i], conn_enc);
|
282
343
|
#endif
|
283
|
-
|
284
|
-
}
|
344
|
+
set_buffer_for_string(&bind_buffers[i], &length_buffers[i], params_enc[i]);
|
285
345
|
break;
|
286
346
|
default:
|
287
347
|
// TODO: what Ruby type should support MYSQL_TYPE_TIME
|
@@ -294,7 +354,13 @@ static VALUE execute(int argc, VALUE *argv, VALUE self) {
|
|
294
354
|
|
295
355
|
memset(&t, 0, sizeof(MYSQL_TIME));
|
296
356
|
t.neg = 0;
|
297
|
-
|
357
|
+
|
358
|
+
if (CLASS_OF(argv[i]) == rb_cTime) {
|
359
|
+
t.second_part = FIX2INT(rb_funcall(rb_time, intern_usec, 0));
|
360
|
+
} else if (CLASS_OF(argv[i]) == cDateTime) {
|
361
|
+
t.second_part = NUM2DBL(rb_funcall(rb_time, intern_sec_fraction, 0)) * 1000000;
|
362
|
+
}
|
363
|
+
|
298
364
|
t.second = FIX2INT(rb_funcall(rb_time, intern_sec, 0));
|
299
365
|
t.minute = FIX2INT(rb_funcall(rb_time, intern_min, 0));
|
300
366
|
t.hour = FIX2INT(rb_funcall(rb_time, intern_hour, 0));
|
@@ -492,6 +558,7 @@ void init_mysql2_statement() {
|
|
492
558
|
intern_new_with_args = rb_intern("new_with_args");
|
493
559
|
intern_each = rb_intern("each");
|
494
560
|
|
561
|
+
intern_sec_fraction = rb_intern("sec_fraction");
|
495
562
|
intern_usec = rb_intern("usec");
|
496
563
|
intern_sec = rb_intern("sec");
|
497
564
|
intern_min = rb_intern("min");
|
@@ -501,4 +568,7 @@ void init_mysql2_statement() {
|
|
501
568
|
intern_year = rb_intern("year");
|
502
569
|
|
503
570
|
intern_to_s = rb_intern("to_s");
|
571
|
+
#ifndef HAVE_RB_BIG_CMP
|
572
|
+
id_cmp = rb_intern("<=>");
|
573
|
+
#endif
|
504
574
|
}
|
data/lib/mysql2/2.0/mysql2.so
CHANGED
Binary file
|
data/lib/mysql2/2.1/mysql2.so
CHANGED
Binary file
|
data/lib/mysql2/2.2/mysql2.so
CHANGED
Binary file
|
data/lib/mysql2/2.3/mysql2.so
CHANGED
Binary file
|
data/lib/mysql2/client.rb
CHANGED
@@ -19,6 +19,7 @@ module Mysql2
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def initialize(opts = {})
|
22
|
+
fail Mysql2::Error, "Options parameter must be a Hash" unless opts.is_a? Hash
|
22
23
|
opts = Mysql2::Util.key_hash_as_symbols(opts)
|
23
24
|
@read_timeout = nil
|
24
25
|
@query_options = self.class.default_query_options.dup
|
@@ -36,7 +37,7 @@ module Mysql2
|
|
36
37
|
when :reconnect, :local_infile, :secure_auth, :automatic_close
|
37
38
|
send(:"#{key}=", !!opts[key]) # rubocop:disable Style/DoubleNegation
|
38
39
|
when :connect_timeout, :read_timeout, :write_timeout
|
39
|
-
send(:"#{key}=", opts[key]) unless opts[key].nil?
|
40
|
+
send(:"#{key}=", opts[key].to_i) unless opts[key].nil?
|
40
41
|
else
|
41
42
|
send(:"#{key}=", opts[key])
|
42
43
|
end
|
@@ -47,6 +48,7 @@ module Mysql2
|
|
47
48
|
|
48
49
|
ssl_options = opts.values_at(:sslkey, :sslcert, :sslca, :sslcapath, :sslcipher)
|
49
50
|
ssl_set(*ssl_options) if ssl_options.any?
|
51
|
+
self.ssl_mode = parse_ssl_mode(opts[:ssl_mode]) if opts[:ssl_mode]
|
50
52
|
|
51
53
|
case opts[:flags]
|
52
54
|
when Array
|
@@ -87,6 +89,17 @@ module Mysql2
|
|
87
89
|
connect user, pass, host, port, database, socket, flags
|
88
90
|
end
|
89
91
|
|
92
|
+
def parse_ssl_mode(mode)
|
93
|
+
m = mode.to_s.upcase
|
94
|
+
if m.start_with?('SSL_MODE_')
|
95
|
+
return Mysql2::Client.const_get(m) if Mysql2::Client.const_defined?(m)
|
96
|
+
else
|
97
|
+
x = 'SSL_MODE_' + m
|
98
|
+
return Mysql2::Client.const_get(x) if Mysql2::Client.const_defined?(x)
|
99
|
+
end
|
100
|
+
warn "Unknown MySQL ssl_mode flag: #{mode}"
|
101
|
+
end
|
102
|
+
|
90
103
|
def parse_flags_array(flags, initial = 0)
|
91
104
|
flags.reduce(initial) do |memo, f|
|
92
105
|
fneg = f.start_with?('-') ? f[1..-1] : nil
|
data/lib/mysql2/version.rb
CHANGED
data/spec/mysql2/client_spec.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
require 'spec_helper'
|
3
|
-
require 'stringio'
|
4
3
|
|
5
4
|
RSpec.describe Mysql2::Client do
|
6
5
|
context "using defaults file" do
|
@@ -34,6 +33,12 @@ RSpec.describe Mysql2::Client do
|
|
34
33
|
}.to raise_error(Mysql2::Error)
|
35
34
|
end
|
36
35
|
|
36
|
+
it "should raise an exception on non-string encodings" do
|
37
|
+
expect {
|
38
|
+
Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => :fake))
|
39
|
+
}.to raise_error(TypeError)
|
40
|
+
end
|
41
|
+
|
37
42
|
it "should not raise an exception on create for a valid encoding" do
|
38
43
|
expect {
|
39
44
|
Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "utf8"))
|
@@ -200,22 +205,21 @@ RSpec.describe Mysql2::Client do
|
|
200
205
|
|
201
206
|
if RUBY_PLATFORM =~ /mingw|mswin/
|
202
207
|
it "cannot be disabled" do
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
client
|
208
|
+
expect do
|
209
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => false))
|
210
|
+
expect(client.automatic_close?).to be(true)
|
211
|
+
end.to output(/always closed by garbage collector/).to_stderr
|
212
|
+
|
213
|
+
expect do
|
214
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => true))
|
215
|
+
expect(client.automatic_close?).to be(true)
|
216
|
+
end.to_not output(/always closed by garbage collector/).to_stderr
|
217
|
+
|
218
|
+
expect do
|
219
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:automatic_close => true))
|
211
220
|
client.automatic_close = false
|
212
|
-
expect(
|
213
|
-
|
214
|
-
|
215
|
-
expect { client.automatic_close = true }.to_not change { $stderr.string }
|
216
|
-
ensure
|
217
|
-
$stderr = stderr
|
218
|
-
end
|
221
|
+
expect(client.automatic_close?).to be(true)
|
222
|
+
end.to output(/always closed by garbage collector/).to_stderr
|
219
223
|
end
|
220
224
|
else
|
221
225
|
it "can be configured" do
|
@@ -263,13 +267,14 @@ RSpec.describe Mysql2::Client do
|
|
263
267
|
end
|
264
268
|
|
265
269
|
it "should be able to connect to database with numeric-only name" do
|
266
|
-
|
267
|
-
@client.query "CREATE DATABASE IF NOT EXISTS `#{
|
268
|
-
@client.query "GRANT ALL ON `#{creds['database']}`.* TO #{creds['username']}@`#{creds['host']}`"
|
270
|
+
database = 1235
|
271
|
+
@client.query "CREATE DATABASE IF NOT EXISTS `#{database}`"
|
269
272
|
|
270
|
-
expect {
|
273
|
+
expect {
|
274
|
+
Mysql2::Client.new(DatabaseCredentials['root'].merge('database' => database))
|
275
|
+
}.not_to raise_error
|
271
276
|
|
272
|
-
@client.query "DROP DATABASE IF EXISTS `#{
|
277
|
+
@client.query "DROP DATABASE IF EXISTS `#{database}`"
|
273
278
|
end
|
274
279
|
|
275
280
|
it "should respond to #close" do
|
@@ -559,7 +564,7 @@ RSpec.describe Mysql2::Client do
|
|
559
564
|
context 'when a non-standard exception class is raised' do
|
560
565
|
it "should close the connection when an exception is raised" do
|
561
566
|
expect { Timeout.timeout(0.1, ArgumentError) { @client.query('SELECT SLEEP(1)') } }.to raise_error(ArgumentError)
|
562
|
-
expect { @client.query('SELECT 1') }.to raise_error(Mysql2::Error, '
|
567
|
+
expect { @client.query('SELECT 1') }.to raise_error(Mysql2::Error, 'MySQL client is not connected')
|
563
568
|
end
|
564
569
|
|
565
570
|
it "should handle Timeouts without leaving the connection hanging if reconnect is true" do
|
data/spec/mysql2/error_spec.rb
CHANGED
@@ -6,11 +6,13 @@ RSpec.describe Mysql2::Statement do
|
|
6
6
|
@client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "utf8"))
|
7
7
|
end
|
8
8
|
|
9
|
+
def stmt_count
|
10
|
+
@client.query("SHOW STATUS LIKE 'Prepared_stmt_count'").first['Value'].to_i
|
11
|
+
end
|
12
|
+
|
9
13
|
it "should create a statement" do
|
10
14
|
statement = nil
|
11
|
-
expect { statement = @client.prepare 'SELECT 1' }.to change
|
12
|
-
@client.query("SHOW STATUS LIKE 'Prepared_stmt_count'").first['Value'].to_i
|
13
|
-
}.by(1)
|
15
|
+
expect { statement = @client.prepare 'SELECT 1' }.to change(&method(:stmt_count)).by(1)
|
14
16
|
expect(statement).to be_an_instance_of(Mysql2::Statement)
|
15
17
|
end
|
16
18
|
|
@@ -59,6 +61,26 @@ RSpec.describe Mysql2::Statement do
|
|
59
61
|
expect(rows).to eq([{ "1" => 1 }])
|
60
62
|
end
|
61
63
|
|
64
|
+
it "should handle bignum but in int64_t" do
|
65
|
+
stmt = @client.prepare('SELECT ? AS max, ? AS min')
|
66
|
+
int64_max = (1 << 63) - 1
|
67
|
+
int64_min = -(1 << 63)
|
68
|
+
result = stmt.execute(int64_max, int64_min)
|
69
|
+
expect(result.to_a).to eq(['max' => int64_max, 'min' => int64_min])
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should handle bignum but beyond int64_t" do
|
73
|
+
stmt = @client.prepare('SELECT ? AS max1, ? AS max2, ? AS max3, ? AS min1, ? AS min2, ? AS min3')
|
74
|
+
int64_max1 = (1 << 63)
|
75
|
+
int64_max2 = (1 << 64) - 1
|
76
|
+
int64_max3 = 1 << 64
|
77
|
+
int64_min1 = -(1 << 63) - 1
|
78
|
+
int64_min2 = -(1 << 64) + 1
|
79
|
+
int64_min3 = -0xC000000000000000
|
80
|
+
result = stmt.execute(int64_max1, int64_max2, int64_max3, int64_min1, int64_min2, int64_min3)
|
81
|
+
expect(result.to_a).to eq(['max1' => int64_max1, 'max2' => int64_max2, 'max3' => int64_max3, 'min1' => int64_min1, 'min2' => int64_min2, 'min3' => int64_min3])
|
82
|
+
end
|
83
|
+
|
62
84
|
it "should keep its result after other query" do
|
63
85
|
@client.query 'USE test'
|
64
86
|
@client.query 'CREATE TABLE IF NOT EXISTS mysql2_stmt_q(a int)'
|
@@ -108,6 +130,35 @@ RSpec.describe Mysql2::Statement do
|
|
108
130
|
expect(result.first.first[1]).to be_an_instance_of(Time)
|
109
131
|
end
|
110
132
|
|
133
|
+
it "should prepare Date values" do
|
134
|
+
now = Date.today
|
135
|
+
statement = @client.prepare('SELECT ? AS a')
|
136
|
+
result = statement.execute(now)
|
137
|
+
expect(result.first['a'].to_s).to eql(now.strftime('%F'))
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should prepare Time values with microseconds" do
|
141
|
+
now = Time.now
|
142
|
+
statement = @client.prepare('SELECT ? AS a')
|
143
|
+
result = statement.execute(now)
|
144
|
+
if RUBY_VERSION =~ /1.8/
|
145
|
+
expect(result.first['a'].strftime('%F %T %z')).to eql(now.strftime('%F %T %z'))
|
146
|
+
else
|
147
|
+
expect(result.first['a'].strftime('%F %T.%6N %z')).to eql(now.strftime('%F %T.%6N %z'))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should prepare DateTime values with microseconds" do
|
152
|
+
now = DateTime.now
|
153
|
+
statement = @client.prepare('SELECT ? AS a')
|
154
|
+
result = statement.execute(now)
|
155
|
+
if RUBY_VERSION =~ /1.8/
|
156
|
+
expect(result.first['a'].strftime('%F %T %z')).to eql(now.strftime('%F %T %z'))
|
157
|
+
else
|
158
|
+
expect(result.first['a'].strftime('%F %T.%6N %z')).to eql(now.strftime('%F %T.%6N %z'))
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
111
162
|
it "should tell us about the fields" do
|
112
163
|
statement = @client.prepare 'SELECT 1 as foo, 2'
|
113
164
|
statement.execute
|
@@ -117,6 +168,13 @@ RSpec.describe Mysql2::Statement do
|
|
117
168
|
expect(list[1]).to eq('2')
|
118
169
|
end
|
119
170
|
|
171
|
+
it "should handle as a decimal binding a BigDecimal" do
|
172
|
+
stmt = @client.prepare('SELECT ? AS decimal_test')
|
173
|
+
test_result = stmt.execute(BigDecimal.new("123.45")).first
|
174
|
+
expect(test_result['decimal_test']).to be_an_instance_of(BigDecimal)
|
175
|
+
expect(test_result['decimal_test']).to eql(123.45)
|
176
|
+
end
|
177
|
+
|
120
178
|
it "should update a DECIMAL value passing a BigDecimal" do
|
121
179
|
@client.query 'USE test'
|
122
180
|
@client.query 'DROP TABLE IF EXISTS mysql2_stmt_decimal_test'
|
@@ -689,9 +747,7 @@ RSpec.describe Mysql2::Statement do
|
|
689
747
|
context 'close' do
|
690
748
|
it 'should free server resources' do
|
691
749
|
stmt = @client.prepare 'SELECT 1'
|
692
|
-
expect { stmt.close }.to change
|
693
|
-
@client.query("SHOW STATUS LIKE 'Prepared_stmt_count'").first['Value'].to_i
|
694
|
-
}.by(-1)
|
750
|
+
expect { stmt.close }.to change(&method(:stmt_count)).by(-1)
|
695
751
|
end
|
696
752
|
|
697
753
|
it 'should raise an error on subsequent execution' do
|
data/spec/ssl/gen_certs.sh
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mysql2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.5
|
5
5
|
platform: x64-mingw32
|
6
6
|
authors:
|
7
7
|
- Brian Lopez
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-10-22 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description:
|
15
15
|
email:
|