mysql2 0.4.4-x64-mingw32 → 0.4.5-x64-mingw32
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/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:
|