mysql2 0.3.18 → 0.3.21
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/LICENSE +21 -0
- data/ext/mysql2/client.c +71 -81
- data/ext/mysql2/extconf.rb +23 -6
- data/ext/mysql2/result.c +56 -64
- data/ext/mysql2/result.h +1 -2
- data/lib/mysql2/client.rb +16 -0
- data/lib/mysql2/error.rb +1 -1
- data/lib/mysql2/version.rb +1 -1
- data/lib/mysql2.rb +18 -0
- data/spec/mysql2/client_spec.rb +69 -65
- data/spec/mysql2/result_spec.rb +20 -6
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e3404975106474ef92ef68d239b4c69901a380e
|
4
|
+
data.tar.gz: 77464b6c2940d1b8be408918678f4a6abb47c95f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e4e545f707900e4338c9965d068d81260b63cf3d7501926b5d2bce98415a1f589d88a541b256dc81f2e8519869103894303357f44dbc14b132fb3cfeca0d0f1d
|
7
|
+
data.tar.gz: 0e3d0943c65f739827fd394e471fa30e04a9c0a9499ed896e944f1b513de4c4c066b0c8e168d4bf66ebbc6905b0df9ffe7fe904094ff49ebff29abdb5c6c6ffc
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Brian Lopez
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/ext/mysql2/client.c
CHANGED
@@ -16,7 +16,7 @@
|
|
16
16
|
|
17
17
|
VALUE cMysql2Client;
|
18
18
|
extern VALUE mMysql2, cMysql2Error;
|
19
|
-
static VALUE sym_id, sym_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
|
19
|
+
static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
|
20
20
|
static ID intern_merge, intern_merge_bang, intern_error_number_eql, intern_sql_state_eql;
|
21
21
|
|
22
22
|
#ifndef HAVE_RB_HASH_DUP
|
@@ -182,23 +182,31 @@ static void *nogvl_connect(void *ptr) {
|
|
182
182
|
*/
|
183
183
|
static VALUE invalidate_fd(int clientfd)
|
184
184
|
{
|
185
|
-
#ifdef
|
185
|
+
#ifdef O_CLOEXEC
|
186
186
|
/* Atomically set CLOEXEC on the new FD in case another thread forks */
|
187
187
|
int sockfd = open("/dev/null", O_RDWR | O_CLOEXEC);
|
188
|
-
if (sockfd < 0) {
|
189
|
-
/* Maybe SOCK_CLOEXEC is defined but not available on this kernel */
|
190
|
-
int sockfd = open("/dev/null", O_RDWR);
|
191
|
-
fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
192
|
-
}
|
193
188
|
#else
|
194
|
-
/* Well we don't have
|
195
|
-
int sockfd =
|
196
|
-
fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
189
|
+
/* Well we don't have O_CLOEXEC, trigger the fallback code below */
|
190
|
+
int sockfd = -1;
|
197
191
|
#endif
|
198
192
|
|
199
193
|
if (sockfd < 0) {
|
200
|
-
/*
|
201
|
-
*
|
194
|
+
/* Either O_CLOEXEC wasn't defined at compile time, or it was defined at
|
195
|
+
* compile time, but isn't available at run-time. So we'll just be quick
|
196
|
+
* about setting FD_CLOEXEC now.
|
197
|
+
*/
|
198
|
+
int flags;
|
199
|
+
sockfd = open("/dev/null", O_RDWR);
|
200
|
+
flags = fcntl(sockfd, F_GETFD);
|
201
|
+
/* Do the flags dance in case there are more defined flags in the future */
|
202
|
+
if (flags != -1) {
|
203
|
+
flags |= FD_CLOEXEC;
|
204
|
+
fcntl(sockfd, F_SETFD, flags);
|
205
|
+
}
|
206
|
+
}
|
207
|
+
|
208
|
+
if (sockfd < 0) {
|
209
|
+
/* Cannot raise here, because one or both of the following may be true:
|
202
210
|
* a) we have no GVL (in C Ruby)
|
203
211
|
* b) are running as a GC finalizer
|
204
212
|
*/
|
@@ -287,7 +295,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
|
|
287
295
|
oldLen = RSTRING_LEN(str);
|
288
296
|
newStr = xmalloc(oldLen*2+1);
|
289
297
|
|
290
|
-
newLen = mysql_escape_string((char *)newStr,
|
298
|
+
newLen = mysql_escape_string((char *)newStr, RSTRING_PTR(str), oldLen);
|
291
299
|
if (newLen == oldLen) {
|
292
300
|
/* no need to return a new ruby string if nothing changed */
|
293
301
|
xfree(newStr);
|
@@ -337,13 +345,13 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
337
345
|
VALUE rv;
|
338
346
|
GET_CLIENT(self);
|
339
347
|
|
340
|
-
args.host
|
341
|
-
args.unix_socket = NIL_P(socket)
|
342
|
-
args.port
|
343
|
-
args.user
|
344
|
-
args.passwd
|
345
|
-
args.db
|
346
|
-
args.mysql
|
348
|
+
args.host = NIL_P(host) ? NULL : StringValueCStr(host);
|
349
|
+
args.unix_socket = NIL_P(socket) ? NULL : StringValueCStr(socket);
|
350
|
+
args.port = NIL_P(port) ? 0 : NUM2INT(port);
|
351
|
+
args.user = NIL_P(user) ? NULL : StringValueCStr(user);
|
352
|
+
args.passwd = NIL_P(pass) ? NULL : StringValueCStr(pass);
|
353
|
+
args.db = NIL_P(database) ? NULL : StringValueCStr(database);
|
354
|
+
args.mysql = wrapper->client;
|
347
355
|
args.client_flag = NUM2ULONG(flags);
|
348
356
|
|
349
357
|
if (wrapper->connect_timeout)
|
@@ -634,42 +642,29 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
|
|
634
642
|
* Query the database with +sql+, with optional +options+. For the possible
|
635
643
|
* options, see @@default_query_options on the Mysql2::Client class.
|
636
644
|
*/
|
637
|
-
static VALUE
|
645
|
+
static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
638
646
|
#ifndef _WIN32
|
639
647
|
struct async_query_args async_args;
|
640
648
|
#endif
|
641
649
|
struct nogvl_send_query_args args;
|
642
|
-
int async = 0;
|
643
|
-
VALUE opts, current;
|
644
650
|
VALUE thread_current = rb_thread_current();
|
645
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
646
|
-
rb_encoding *conn_enc;
|
647
|
-
#endif
|
648
651
|
GET_CLIENT(self);
|
649
652
|
|
650
653
|
REQUIRE_CONNECTED(wrapper);
|
651
654
|
args.mysql = wrapper->client;
|
652
655
|
|
653
|
-
current = rb_hash_dup(rb_iv_get(self, "@query_options"));
|
654
656
|
RB_GC_GUARD(current);
|
655
657
|
Check_Type(current, T_HASH);
|
656
658
|
rb_iv_set(self, "@current_query_options", current);
|
657
659
|
|
658
|
-
|
659
|
-
rb_funcall(current, intern_merge_bang, 1, opts);
|
660
|
-
|
661
|
-
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
662
|
-
async = 1;
|
663
|
-
}
|
664
|
-
}
|
665
|
-
|
666
|
-
Check_Type(args.sql, T_STRING);
|
660
|
+
Check_Type(sql, T_STRING);
|
667
661
|
#ifdef HAVE_RUBY_ENCODING_H
|
668
|
-
conn_enc = rb_to_encoding(wrapper->encoding);
|
669
662
|
/* ensure the string is in the encoding the connection is expecting */
|
670
|
-
args.sql = rb_str_export_to_enc(
|
663
|
+
args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
|
664
|
+
#else
|
665
|
+
args.sql = sql;
|
671
666
|
#endif
|
672
|
-
args.sql_ptr =
|
667
|
+
args.sql_ptr = RSTRING_PTR(args.sql);
|
673
668
|
args.sql_len = RSTRING_LEN(args.sql);
|
674
669
|
|
675
670
|
/* see if this connection is still waiting on a result from a previous query */
|
@@ -691,15 +686,15 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
|
691
686
|
#ifndef _WIN32
|
692
687
|
rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
693
688
|
|
694
|
-
if (
|
689
|
+
if (rb_hash_aref(current, sym_async) == Qtrue) {
|
690
|
+
return Qnil;
|
691
|
+
} else {
|
695
692
|
async_args.fd = wrapper->client->net.fd;
|
696
693
|
async_args.self = self;
|
697
694
|
|
698
695
|
rb_rescue2(do_query, (VALUE)&async_args, disconnect_and_raise, self, rb_eException, (VALUE)0);
|
699
696
|
|
700
697
|
return rb_mysql_client_async_result(self);
|
701
|
-
} else {
|
702
|
-
return Qnil;
|
703
698
|
}
|
704
699
|
#else
|
705
700
|
do_send_query(&args);
|
@@ -736,9 +731,14 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
|
|
736
731
|
oldLen = RSTRING_LEN(str);
|
737
732
|
newStr = xmalloc(oldLen*2+1);
|
738
733
|
|
739
|
-
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr,
|
734
|
+
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
|
740
735
|
if (newLen == oldLen) {
|
741
736
|
/* no need to return a new ruby string if nothing changed */
|
737
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
738
|
+
if (default_internal_enc) {
|
739
|
+
str = rb_str_export_to_enc(str, default_internal_enc);
|
740
|
+
}
|
741
|
+
#endif
|
742
742
|
xfree(newStr);
|
743
743
|
return str;
|
744
744
|
} else {
|
@@ -800,17 +800,17 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
800
800
|
break;
|
801
801
|
|
802
802
|
case MYSQL_READ_DEFAULT_FILE:
|
803
|
-
charval = (const char *)
|
803
|
+
charval = (const char *)StringValueCStr(value);
|
804
804
|
retval = charval;
|
805
805
|
break;
|
806
806
|
|
807
807
|
case MYSQL_READ_DEFAULT_GROUP:
|
808
|
-
charval = (const char *)
|
808
|
+
charval = (const char *)StringValueCStr(value);
|
809
809
|
retval = charval;
|
810
810
|
break;
|
811
811
|
|
812
812
|
case MYSQL_INIT_COMMAND:
|
813
|
-
charval = (const char *)
|
813
|
+
charval = (const char *)StringValueCStr(value);
|
814
814
|
retval = charval;
|
815
815
|
break;
|
816
816
|
|
@@ -843,30 +843,23 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
843
843
|
*
|
844
844
|
* Returns a string that represents the client library version.
|
845
845
|
*/
|
846
|
-
static VALUE rb_mysql_client_info(VALUE
|
847
|
-
VALUE version,
|
848
|
-
|
849
|
-
rb_encoding *default_internal_enc;
|
850
|
-
rb_encoding *conn_enc;
|
851
|
-
GET_CLIENT(self);
|
852
|
-
#endif
|
853
|
-
version = rb_hash_new();
|
846
|
+
static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
|
847
|
+
VALUE version_info, version, header_version;
|
848
|
+
version_info = rb_hash_new();
|
854
849
|
|
855
|
-
|
856
|
-
|
857
|
-
conn_enc = rb_to_encoding(wrapper->encoding);
|
858
|
-
#endif
|
850
|
+
version = rb_str_new2(mysql_get_client_info());
|
851
|
+
header_version = rb_str_new2(MYSQL_LINK_VERSION);
|
859
852
|
|
860
|
-
rb_hash_aset(version, sym_id, LONG2NUM(mysql_get_client_version()));
|
861
|
-
client_info = rb_str_new2(mysql_get_client_info());
|
862
853
|
#ifdef HAVE_RUBY_ENCODING_H
|
863
|
-
rb_enc_associate(
|
864
|
-
|
865
|
-
client_info = rb_str_export_to_enc(client_info, default_internal_enc);
|
866
|
-
}
|
854
|
+
rb_enc_associate(version, rb_usascii_encoding());
|
855
|
+
rb_enc_associate(header_version, rb_usascii_encoding());
|
867
856
|
#endif
|
868
|
-
|
869
|
-
|
857
|
+
|
858
|
+
rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
|
859
|
+
rb_hash_aset(version_info, sym_version, version);
|
860
|
+
rb_hash_aset(version_info, sym_header_version, header_version);
|
861
|
+
|
862
|
+
return version_info;
|
870
863
|
}
|
871
864
|
|
872
865
|
/* call-seq:
|
@@ -907,14 +900,10 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
|
|
907
900
|
* Return the file descriptor number for this client.
|
908
901
|
*/
|
909
902
|
static VALUE rb_mysql_client_socket(VALUE self) {
|
910
|
-
GET_CLIENT(self);
|
911
903
|
#ifndef _WIN32
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
fd_set_fd = wrapper->client->net.fd;
|
916
|
-
return INT2NUM(fd_set_fd);
|
917
|
-
}
|
904
|
+
GET_CLIENT(self);
|
905
|
+
REQUIRE_CONNECTED(wrapper);
|
906
|
+
return INT2NUM(wrapper->client->net.fd);
|
918
907
|
#else
|
919
908
|
rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
|
920
909
|
#endif
|
@@ -987,7 +976,7 @@ static VALUE rb_mysql_client_select_db(VALUE self, VALUE db)
|
|
987
976
|
REQUIRE_CONNECTED(wrapper);
|
988
977
|
|
989
978
|
args.mysql = wrapper->client;
|
990
|
-
args.db =
|
979
|
+
args.db = StringValueCStr(db);
|
991
980
|
|
992
981
|
if (rb_thread_call_without_gvl(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse)
|
993
982
|
rb_raise_mysql2_error(wrapper);
|
@@ -1183,11 +1172,11 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
|
|
1183
1172
|
GET_CLIENT(self);
|
1184
1173
|
|
1185
1174
|
mysql_ssl_set(wrapper->client,
|
1186
|
-
NIL_P(key)
|
1187
|
-
NIL_P(cert)
|
1188
|
-
NIL_P(ca)
|
1189
|
-
NIL_P(capath) ? NULL :
|
1190
|
-
NIL_P(cipher) ? NULL :
|
1175
|
+
NIL_P(key) ? NULL : StringValueCStr(key),
|
1176
|
+
NIL_P(cert) ? NULL : StringValueCStr(cert),
|
1177
|
+
NIL_P(ca) ? NULL : StringValueCStr(ca),
|
1178
|
+
NIL_P(capath) ? NULL : StringValueCStr(capath),
|
1179
|
+
NIL_P(cipher) ? NULL : StringValueCStr(cipher));
|
1191
1180
|
|
1192
1181
|
return self;
|
1193
1182
|
}
|
@@ -1254,12 +1243,11 @@ void init_mysql2_client() {
|
|
1254
1243
|
rb_define_alloc_func(cMysql2Client, allocate);
|
1255
1244
|
|
1256
1245
|
rb_define_singleton_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
|
1246
|
+
rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1257
1247
|
|
1258
1248
|
rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
|
1259
|
-
rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
|
1260
1249
|
rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
|
1261
1250
|
rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
|
1262
|
-
rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
1263
1251
|
rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
|
1264
1252
|
rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
|
1265
1253
|
rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
|
@@ -1290,9 +1278,11 @@ void init_mysql2_client() {
|
|
1290
1278
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
1291
1279
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1292
1280
|
rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
|
1281
|
+
rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
|
1293
1282
|
|
1294
1283
|
sym_id = ID2SYM(rb_intern("id"));
|
1295
1284
|
sym_version = ID2SYM(rb_intern("version"));
|
1285
|
+
sym_header_version = ID2SYM(rb_intern("header_version"));
|
1296
1286
|
sym_async = ID2SYM(rb_intern("async"));
|
1297
1287
|
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
1298
1288
|
sym_as = ID2SYM(rb_intern("as"));
|
data/ext/mysql2/extconf.rb
CHANGED
@@ -2,7 +2,13 @@
|
|
2
2
|
require 'mkmf'
|
3
3
|
|
4
4
|
def asplode lib
|
5
|
-
|
5
|
+
if RUBY_PLATFORM =~ /mingw|mswin/
|
6
|
+
abort "-----\n#{lib} is missing. Check your installation of MySQL or Connector/C, and try again.\n-----"
|
7
|
+
elsif RUBY_PLATFORM =~ /darwin/
|
8
|
+
abort "-----\n#{lib} is missing. You may need to 'brew install mysql' or 'port install mysql', and try again.\n-----"
|
9
|
+
else
|
10
|
+
abort "-----\n#{lib} is missing. You may need to 'apt-get install libmysqlclient-dev' or 'yum install mysql-devel', and try again.\n-----"
|
11
|
+
end
|
6
12
|
end
|
7
13
|
|
8
14
|
# 2.0-only
|
@@ -29,7 +35,7 @@ dirs = ENV['PATH'].split(File::PATH_SEPARATOR) + %w[
|
|
29
35
|
/usr/local/lib/mysql5*
|
30
36
|
].map{|dir| "#{dir}/bin" }
|
31
37
|
|
32
|
-
GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5}"
|
38
|
+
GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5,mariadb_config}"
|
33
39
|
|
34
40
|
# If the user has provided a --with-mysql-dir argument, we must respect it or fail.
|
35
41
|
inc, lib = dir_config('mysql')
|
@@ -66,11 +72,22 @@ elsif mc = (with_config('mysql-config') || Dir[GLOB].first)
|
|
66
72
|
rpath_dir = libs
|
67
73
|
else
|
68
74
|
inc, lib = dir_config('mysql', '/usr/local')
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
75
|
+
unless find_library('mysqlclient', 'mysql_query', lib, "#{lib}/mysql")
|
76
|
+
found = false
|
77
|
+
# For some systems and some versions of libmysqlclient, there were extra
|
78
|
+
# libraries needed to link. Try each typical extra library, add it to the
|
79
|
+
# global compile flags, and see if that allows us to link libmysqlclient.
|
80
|
+
warn "-----\nlibmysqlclient is missing. Trying again with extra runtime libraries...\n-----"
|
81
|
+
|
82
|
+
%w{ m z socket nsl mygcc }.each do |extra_lib|
|
83
|
+
if have_library(extra_lib) && find_library('mysqlclient', 'mysql_query', lib, "#{lib}/mysql")
|
84
|
+
found = true
|
85
|
+
break
|
86
|
+
end
|
87
|
+
end
|
88
|
+
asplode('libmysqlclient') unless found
|
73
89
|
end
|
90
|
+
|
74
91
|
rpath_dir = lib
|
75
92
|
end
|
76
93
|
|
data/ext/mysql2/result.c
CHANGED
@@ -50,6 +50,10 @@ static rb_encoding *binaryEncoding;
|
|
50
50
|
#define MYSQL2_MIN_TIME 62171150401ULL
|
51
51
|
#endif
|
52
52
|
|
53
|
+
#define GET_RESULT(obj) \
|
54
|
+
mysql2_result_wrapper *wrapper; \
|
55
|
+
Data_Get_Struct(self, mysql2_result_wrapper, wrapper);
|
56
|
+
|
53
57
|
static VALUE cMysql2Result;
|
54
58
|
static VALUE cBigDecimal, cDate, cDateTime;
|
55
59
|
static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
|
@@ -103,9 +107,8 @@ static void *nogvl_fetch_row(void *ptr) {
|
|
103
107
|
}
|
104
108
|
|
105
109
|
static VALUE rb_mysql_result_fetch_field(VALUE self, unsigned int idx, short int symbolize_keys) {
|
106
|
-
mysql2_result_wrapper * wrapper;
|
107
110
|
VALUE rb_field;
|
108
|
-
|
111
|
+
GET_RESULT(self);
|
109
112
|
|
110
113
|
if (wrapper->fields == Qnil) {
|
111
114
|
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
@@ -193,7 +196,6 @@ static unsigned int msec_char_to_uint(char *msec_char, size_t len)
|
|
193
196
|
|
194
197
|
static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezone, int symbolizeKeys, int asArray, int castBool, int cast, MYSQL_FIELD * fields) {
|
195
198
|
VALUE rowVal;
|
196
|
-
mysql2_result_wrapper * wrapper;
|
197
199
|
MYSQL_ROW row;
|
198
200
|
unsigned int i = 0;
|
199
201
|
unsigned long * fieldLengths;
|
@@ -202,7 +204,7 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
202
204
|
rb_encoding *default_internal_enc;
|
203
205
|
rb_encoding *conn_enc;
|
204
206
|
#endif
|
205
|
-
|
207
|
+
GET_RESULT(self);
|
206
208
|
|
207
209
|
#ifdef HAVE_RUBY_ENCODING_H
|
208
210
|
default_internal_enc = rb_default_internal_encoding();
|
@@ -413,12 +415,11 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
|
|
413
415
|
}
|
414
416
|
|
415
417
|
static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
416
|
-
mysql2_result_wrapper * wrapper;
|
417
418
|
unsigned int i = 0;
|
418
419
|
short int symbolizeKeys = 0;
|
419
420
|
VALUE defaults;
|
420
421
|
|
421
|
-
|
422
|
+
GET_RESULT(self);
|
422
423
|
|
423
424
|
defaults = rb_iv_get(self, "@query_options");
|
424
425
|
Check_Type(defaults, T_HASH);
|
@@ -443,13 +444,12 @@ static VALUE rb_mysql_result_fetch_fields(VALUE self) {
|
|
443
444
|
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
444
445
|
VALUE defaults, opts, block;
|
445
446
|
ID db_timezone, app_timezone, dbTz, appTz;
|
446
|
-
mysql2_result_wrapper * wrapper;
|
447
447
|
unsigned long i;
|
448
448
|
const char * errstr;
|
449
|
-
int symbolizeKeys
|
449
|
+
int symbolizeKeys, asArray, castBool, cacheRows, cast;
|
450
450
|
MYSQL_FIELD * fields = NULL;
|
451
451
|
|
452
|
-
|
452
|
+
GET_RESULT(self);
|
453
453
|
|
454
454
|
defaults = rb_iv_get(self, "@query_options");
|
455
455
|
Check_Type(defaults, T_HASH);
|
@@ -459,32 +459,14 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
459
459
|
opts = defaults;
|
460
460
|
}
|
461
461
|
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
asArray = 1;
|
468
|
-
}
|
469
|
-
|
470
|
-
if (rb_hash_aref(opts, sym_cast_booleans) == Qtrue) {
|
471
|
-
castBool = 1;
|
472
|
-
}
|
473
|
-
|
474
|
-
if (rb_hash_aref(opts, sym_cache_rows) == Qfalse) {
|
475
|
-
cacheRows = 0;
|
476
|
-
}
|
477
|
-
|
478
|
-
if (rb_hash_aref(opts, sym_cast) == Qfalse) {
|
479
|
-
cast = 0;
|
480
|
-
}
|
462
|
+
symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys));
|
463
|
+
asArray = rb_hash_aref(opts, sym_as) == sym_array;
|
464
|
+
castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans));
|
465
|
+
cacheRows = RTEST(rb_hash_aref(opts, sym_cache_rows));
|
466
|
+
cast = RTEST(rb_hash_aref(opts, sym_cast));
|
481
467
|
|
482
|
-
if (
|
483
|
-
|
484
|
-
}
|
485
|
-
|
486
|
-
if (streaming && cacheRows) {
|
487
|
-
rb_warn("cacheRows is ignored if streaming is true");
|
468
|
+
if (wrapper->is_streaming && cacheRows) {
|
469
|
+
rb_warn(":cache_rows is ignored if :stream is true");
|
488
470
|
}
|
489
471
|
|
490
472
|
dbTz = rb_hash_aref(opts, sym_database_timezone);
|
@@ -508,23 +490,12 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
508
490
|
app_timezone = Qnil;
|
509
491
|
}
|
510
492
|
|
511
|
-
if (wrapper->
|
512
|
-
|
513
|
-
|
514
|
-
/* until we've finished fetching all rows */
|
515
|
-
wrapper->numberOfRows = 0;
|
493
|
+
if (wrapper->is_streaming) {
|
494
|
+
/* When streaming, we will only yield rows, not return them. */
|
495
|
+
if (wrapper->rows == Qnil) {
|
516
496
|
wrapper->rows = rb_ary_new();
|
517
|
-
} else {
|
518
|
-
wrapper->numberOfRows = mysql_num_rows(wrapper->result);
|
519
|
-
if (wrapper->numberOfRows == 0) {
|
520
|
-
wrapper->rows = rb_ary_new();
|
521
|
-
return wrapper->rows;
|
522
|
-
}
|
523
|
-
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
524
497
|
}
|
525
|
-
}
|
526
498
|
|
527
|
-
if (streaming) {
|
528
499
|
if (!wrapper->streamingComplete) {
|
529
500
|
VALUE row;
|
530
501
|
|
@@ -532,16 +503,15 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
532
503
|
|
533
504
|
do {
|
534
505
|
row = rb_mysql_result_fetch_row(self, db_timezone, app_timezone, symbolizeKeys, asArray, castBool, cast, fields);
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
506
|
+
if (row != Qnil) {
|
507
|
+
wrapper->numberOfRows++;
|
508
|
+
if (block != Qnil) {
|
509
|
+
rb_yield(row);
|
510
|
+
}
|
539
511
|
}
|
540
512
|
} while(row != Qnil);
|
541
513
|
|
542
514
|
rb_mysql_result_free_result(wrapper);
|
543
|
-
|
544
|
-
wrapper->numberOfRows = wrapper->lastRowProcessed;
|
545
515
|
wrapper->streamingComplete = 1;
|
546
516
|
|
547
517
|
// Check for errors, the connection might have gone out from under us
|
@@ -554,6 +524,19 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
554
524
|
rb_raise(cMysql2Error, "You have already fetched all the rows for this query and streaming is true. (to reiterate you must requery).");
|
555
525
|
}
|
556
526
|
} else {
|
527
|
+
if (wrapper->lastRowProcessed == 0) {
|
528
|
+
wrapper->numberOfRows = mysql_num_rows(wrapper->result);
|
529
|
+
if (wrapper->numberOfRows == 0) {
|
530
|
+
wrapper->rows = rb_ary_new();
|
531
|
+
return wrapper->rows;
|
532
|
+
}
|
533
|
+
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
534
|
+
} else if (!cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
535
|
+
mysql_data_seek(wrapper->result, 0);
|
536
|
+
wrapper->lastRowProcessed = 0;
|
537
|
+
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
538
|
+
}
|
539
|
+
|
557
540
|
if (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
558
541
|
/* we've already read the entire dataset from the C result into our */
|
559
542
|
/* internal array. Lets hand that over to the user since it's ready to go */
|
@@ -579,7 +562,9 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
579
562
|
|
580
563
|
if (row == Qnil) {
|
581
564
|
/* we don't need the mysql C dataset around anymore, peace it */
|
582
|
-
|
565
|
+
if (cacheRows) {
|
566
|
+
rb_mysql_result_free_result(wrapper);
|
567
|
+
}
|
583
568
|
return Qnil;
|
584
569
|
}
|
585
570
|
|
@@ -587,7 +572,7 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
587
572
|
rb_yield(row);
|
588
573
|
}
|
589
574
|
}
|
590
|
-
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
575
|
+
if (wrapper->lastRowProcessed == wrapper->numberOfRows && cacheRows) {
|
591
576
|
/* we don't need the mysql C dataset around anymore, peace it */
|
592
577
|
rb_mysql_result_free_result(wrapper);
|
593
578
|
}
|
@@ -598,17 +583,19 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
598
583
|
}
|
599
584
|
|
600
585
|
static VALUE rb_mysql_result_count(VALUE self) {
|
601
|
-
|
586
|
+
GET_RESULT(self);
|
587
|
+
|
588
|
+
if (wrapper->is_streaming) {
|
589
|
+
/* This is an unsigned long per result.h */
|
590
|
+
return ULONG2NUM(wrapper->numberOfRows);
|
591
|
+
}
|
602
592
|
|
603
|
-
GetMysql2Result(self, wrapper);
|
604
593
|
if (wrapper->resultFreed) {
|
605
|
-
|
606
|
-
|
607
|
-
} else {
|
608
|
-
return LONG2NUM(RARRAY_LEN(wrapper->rows));
|
609
|
-
}
|
594
|
+
/* Ruby arrays have platform signed long length */
|
595
|
+
return LONG2NUM(RARRAY_LEN(wrapper->rows));
|
610
596
|
} else {
|
611
|
-
|
597
|
+
/* MySQL returns an unsigned 64-bit long here */
|
598
|
+
return ULL2NUM(mysql_num_rows(wrapper->result));
|
612
599
|
}
|
613
600
|
}
|
614
601
|
|
@@ -616,6 +603,7 @@ static VALUE rb_mysql_result_count(VALUE self) {
|
|
616
603
|
VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r) {
|
617
604
|
VALUE obj;
|
618
605
|
mysql2_result_wrapper * wrapper;
|
606
|
+
|
619
607
|
obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
|
620
608
|
wrapper->numberOfFields = 0;
|
621
609
|
wrapper->numberOfRows = 0;
|
@@ -634,6 +622,10 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
|
|
634
622
|
|
635
623
|
rb_iv_set(obj, "@query_options", options);
|
636
624
|
|
625
|
+
/* Options that cannot be changed in results.each(...) { |row| }
|
626
|
+
* should be processed here. */
|
627
|
+
wrapper->is_streaming = (rb_hash_aref(options, sym_stream) == Qtrue ? 1 : 0);
|
628
|
+
|
637
629
|
return obj;
|
638
630
|
}
|
639
631
|
|
data/ext/mysql2/result.h
CHANGED
@@ -12,12 +12,11 @@ typedef struct {
|
|
12
12
|
unsigned int numberOfFields;
|
13
13
|
unsigned long numberOfRows;
|
14
14
|
unsigned long lastRowProcessed;
|
15
|
+
char is_streaming;
|
15
16
|
char streamingComplete;
|
16
17
|
char resultFreed;
|
17
18
|
MYSQL_RES *result;
|
18
19
|
mysql_client_wrapper *client_wrapper;
|
19
20
|
} mysql2_result_wrapper;
|
20
21
|
|
21
|
-
#define GetMysql2Result(obj, sval) (sval = (mysql2_result_wrapper*)DATA_PTR(obj));
|
22
|
-
|
23
22
|
#endif
|
data/lib/mysql2/client.rb
CHANGED
@@ -74,6 +74,18 @@ module Mysql2
|
|
74
74
|
@@default_query_options
|
75
75
|
end
|
76
76
|
|
77
|
+
if Thread.respond_to?(:handle_interrupt)
|
78
|
+
def query(sql, options = {})
|
79
|
+
Thread.handle_interrupt(::Mysql2::Util::TimeoutError => :never) do
|
80
|
+
_query(sql, @query_options.merge(options))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
else
|
84
|
+
def query(sql, options = {})
|
85
|
+
_query(sql, @query_options.merge(options))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
77
89
|
def query_info
|
78
90
|
info = query_info_string
|
79
91
|
return {} unless info
|
@@ -82,6 +94,10 @@ module Mysql2
|
|
82
94
|
info_hash
|
83
95
|
end
|
84
96
|
|
97
|
+
def info
|
98
|
+
self.class.info
|
99
|
+
end
|
100
|
+
|
85
101
|
private
|
86
102
|
def self.local_offset
|
87
103
|
::Time.local(2010).utc_offset.to_r / 86400
|
data/lib/mysql2/error.rb
CHANGED
@@ -30,7 +30,7 @@ module Mysql2
|
|
30
30
|
# variable.
|
31
31
|
#
|
32
32
|
# See http://dev.mysql.com/doc/refman/5.5/en/charset-errors.html for
|
33
|
-
# more
|
33
|
+
# more context.
|
34
34
|
#
|
35
35
|
# Before MySQL 5.5 error message template strings are in whatever encoding
|
36
36
|
# is associated with the error message language.
|
data/lib/mysql2/version.rb
CHANGED
data/lib/mysql2.rb
CHANGED
@@ -61,4 +61,22 @@ module Mysql2::Util
|
|
61
61
|
Hash[hash.map { |k,v| [k.to_sym, v] }]
|
62
62
|
end
|
63
63
|
|
64
|
+
#
|
65
|
+
# In Mysql2::Client#query and Mysql2::Statement#execute,
|
66
|
+
# Thread#handle_interrupt is used to prevent Timeout#timeout
|
67
|
+
# from interrupting query execution.
|
68
|
+
#
|
69
|
+
# Timeout::ExitException was removed in Ruby 2.3.0, 2.2.3, and 2.1.8,
|
70
|
+
# but is present in earlier 2.1.x and 2.2.x, so we provide a shim.
|
71
|
+
#
|
72
|
+
if Thread.respond_to?(:handle_interrupt)
|
73
|
+
require 'timeout'
|
74
|
+
# rubocop:disable Style/ConstantName
|
75
|
+
TimeoutError = if defined?(::Timeout::ExitException)
|
76
|
+
::Timeout::ExitException
|
77
|
+
else
|
78
|
+
::Timeout::Error
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
64
82
|
end
|
data/spec/mysql2/client_spec.rb
CHANGED
@@ -66,12 +66,13 @@ describe Mysql2::Client do
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
client = klient.new
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
69
|
+
client_flags = Mysql2::Client::REMEMBER_OPTIONS |
|
70
|
+
Mysql2::Client::LONG_PASSWORD |
|
71
|
+
Mysql2::Client::LONG_FLAG |
|
72
|
+
Mysql2::Client::TRANSACTIONS |
|
73
|
+
Mysql2::Client::PROTOCOL_41 |
|
74
|
+
Mysql2::Client::SECURE_CONNECTION
|
75
|
+
client.connect_args.last[6].should eql(client_flags)
|
75
76
|
end
|
76
77
|
|
77
78
|
it "should execute init command" do
|
@@ -461,59 +462,39 @@ describe Mysql2::Client do
|
|
461
462
|
}.should raise_error(Mysql2::Error)
|
462
463
|
end
|
463
464
|
|
464
|
-
it
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
end
|
469
|
-
rescue Timeout::Error
|
470
|
-
end
|
465
|
+
it 'should be impervious to connection-corrupting timeouts in #query' do
|
466
|
+
pending('`Thread.handle_interrupt` is not defined') unless Thread.respond_to?(:handle_interrupt)
|
467
|
+
# attempt to break the connection
|
468
|
+
expect { Timeout.timeout(0.1) { @client.query('SELECT SLEEP(1)') } }.to raise_error(Timeout::Error)
|
471
469
|
|
472
|
-
|
473
|
-
|
474
|
-
}.should raise_error(Mysql2::Error, 'closed MySQL connection')
|
470
|
+
# expect the connection to not be broken
|
471
|
+
expect { @client.query('SELECT 1') }.to_not raise_error
|
475
472
|
end
|
476
473
|
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
client.query("SELECT sleep(2)")
|
482
|
-
end
|
483
|
-
rescue Timeout::Error
|
474
|
+
context 'when a non-standard exception class is raised' do
|
475
|
+
it "should close the connection when an exception is raised" do
|
476
|
+
expect { Timeout.timeout(0.1, ArgumentError) { @client.query('SELECT SLEEP(1)') } }.to raise_error(ArgumentError)
|
477
|
+
expect { @client.query('SELECT 1') }.to raise_error(Mysql2::Error, 'closed MySQL connection')
|
484
478
|
end
|
485
479
|
|
486
|
-
|
487
|
-
client.
|
488
|
-
}.should_not raise_error(Mysql2::Error)
|
489
|
-
end
|
480
|
+
it "should handle Timeouts without leaving the connection hanging if reconnect is true" do
|
481
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:reconnect => true))
|
490
482
|
|
491
|
-
|
492
|
-
|
493
|
-
begin
|
494
|
-
Timeout.timeout(1, Timeout::Error) do
|
495
|
-
client.query("SELECT sleep(2)")
|
496
|
-
end
|
497
|
-
rescue Timeout::Error
|
483
|
+
expect { Timeout.timeout(0.1, ArgumentError) { client.query('SELECT SLEEP(1)') } }.to raise_error(ArgumentError)
|
484
|
+
expect { client.query('SELECT 1') }.to_not raise_error
|
498
485
|
end
|
499
486
|
|
500
|
-
|
501
|
-
client.
|
502
|
-
}.should raise_error(Mysql2::Error)
|
487
|
+
it "should handle Timeouts without leaving the connection hanging if reconnect is set to true after construction true" do
|
488
|
+
client = Mysql2::Client.new(DatabaseCredentials['root'])
|
503
489
|
|
504
|
-
|
490
|
+
expect { Timeout.timeout(0.1, ArgumentError) { client.query('SELECT SLEEP(1)') } }.to raise_error(ArgumentError)
|
491
|
+
expect { client.query('SELECT 1') }.to raise_error(Mysql2::Error)
|
505
492
|
|
506
|
-
|
507
|
-
Timeout.timeout(1, Timeout::Error) do
|
508
|
-
client.query("SELECT sleep(2)")
|
509
|
-
end
|
510
|
-
rescue Timeout::Error
|
511
|
-
end
|
512
|
-
|
513
|
-
lambda {
|
514
|
-
client.query("SELECT 1")
|
515
|
-
}.should_not raise_error(Mysql2::Error)
|
493
|
+
client.reconnect = true
|
516
494
|
|
495
|
+
expect { Timeout.timeout(0.1, ArgumentError) { client.query('SELECT SLEEP(1)') } }.to raise_error(ArgumentError)
|
496
|
+
expect { client.query('SELECT 1') }.to_not raise_error
|
497
|
+
end
|
517
498
|
end
|
518
499
|
|
519
500
|
it "threaded queries should be supported" do
|
@@ -614,6 +595,21 @@ describe Mysql2::Client do
|
|
614
595
|
|
615
596
|
@multi_client.more_results?.should be_false
|
616
597
|
end
|
598
|
+
|
599
|
+
it "#more_results? should work with stored procedures" do
|
600
|
+
@multi_client.query("DROP PROCEDURE IF EXISTS test_proc")
|
601
|
+
@multi_client.query("CREATE PROCEDURE test_proc() BEGIN SELECT 1 AS 'set_1'; SELECT 2 AS 'set_2'; END")
|
602
|
+
@multi_client.query("CALL test_proc()").first.should eql({ 'set_1' => 1 })
|
603
|
+
@multi_client.more_results?.should be_true
|
604
|
+
|
605
|
+
@multi_client.next_result
|
606
|
+
@multi_client.store_result.first.should eql({ 'set_2' => 2 })
|
607
|
+
|
608
|
+
@multi_client.next_result
|
609
|
+
@multi_client.store_result.should be_nil # this is the result from CALL itself
|
610
|
+
|
611
|
+
@multi_client.more_results?.should be_false
|
612
|
+
end
|
617
613
|
end
|
618
614
|
end
|
619
615
|
|
@@ -700,6 +696,19 @@ describe Mysql2::Client do
|
|
700
696
|
@client.escape ""
|
701
697
|
}.should raise_error(Mysql2::Error)
|
702
698
|
end
|
699
|
+
|
700
|
+
context 'when mysql encoding is not utf8' do
|
701
|
+
before { pending('Encoding is undefined') unless defined?(Encoding) }
|
702
|
+
|
703
|
+
let(:client) { Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "ujis")) }
|
704
|
+
|
705
|
+
it 'should return a internal encoding string if Encoding.default_internal is set' do
|
706
|
+
with_internal_encoding Encoding::UTF_8 do
|
707
|
+
client.escape("\u{30C6}\u{30B9}\u{30C8}").should eql("\u{30C6}\u{30B9}\u{30C8}")
|
708
|
+
client.escape("\u{30C6}'\u{30B9}\"\u{30C8}").should eql("\u{30C6}\\'\u{30B9}\\\"\u{30C8}")
|
709
|
+
end
|
710
|
+
end
|
711
|
+
end
|
703
712
|
end
|
704
713
|
|
705
714
|
it "should respond to #info" do
|
@@ -715,26 +724,21 @@ describe Mysql2::Client do
|
|
715
724
|
info[:version].class.should eql(String)
|
716
725
|
end
|
717
726
|
|
718
|
-
|
719
|
-
|
720
|
-
it "should default to the connection's encoding if Encoding.default_internal is nil" do
|
721
|
-
with_internal_encoding nil do
|
722
|
-
@client.info[:version].encoding.should eql(Encoding.find('utf-8'))
|
727
|
+
context "strings returned by #info" do
|
728
|
+
before { pending('Encoding is undefined') unless defined?(Encoding) }
|
723
729
|
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
730
|
+
it "should be tagged as ascii" do
|
731
|
+
@client.info[:version].encoding.should eql(Encoding::US_ASCII)
|
732
|
+
@client.info[:header_version].encoding.should eql(Encoding::US_ASCII)
|
733
|
+
end
|
734
|
+
end
|
728
735
|
|
729
|
-
|
730
|
-
|
731
|
-
@client.info[:version].encoding.should eql(Encoding.default_internal)
|
732
|
-
end
|
736
|
+
context "strings returned by .info" do
|
737
|
+
before { pending('Encoding is undefined') unless defined?(Encoding) }
|
733
738
|
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
end
|
739
|
+
it "should be tagged as ascii" do
|
740
|
+
Mysql2::Client.info[:version].encoding.should eql(Encoding::US_ASCII)
|
741
|
+
Mysql2::Client.info[:header_version].encoding.should eql(Encoding::US_ASCII)
|
738
742
|
end
|
739
743
|
end
|
740
744
|
|
data/spec/mysql2/result_spec.rb
CHANGED
@@ -6,6 +6,14 @@ describe Mysql2::Result do
|
|
6
6
|
@result = @client.query "SELECT 1"
|
7
7
|
end
|
8
8
|
|
9
|
+
it "should raise a TypeError exception when it doesn't wrap a result set" do
|
10
|
+
r = Mysql2::Result.new
|
11
|
+
expect { r.count }.to raise_error(TypeError)
|
12
|
+
expect { r.fields }.to raise_error(TypeError)
|
13
|
+
expect { r.size }.to raise_error(TypeError)
|
14
|
+
expect { r.each }.to raise_error(TypeError)
|
15
|
+
end
|
16
|
+
|
9
17
|
it "should have included Enumerable" do
|
10
18
|
Mysql2::Result.ancestors.include?(Enumerable).should be_true
|
11
19
|
end
|
@@ -70,6 +78,11 @@ describe Mysql2::Result do
|
|
70
78
|
result.first.object_id.should_not eql(result.first.object_id)
|
71
79
|
end
|
72
80
|
|
81
|
+
it "should be able to iterate a second time even if cache_rows is disabled" do
|
82
|
+
result = @client.query "SELECT 1 UNION SELECT 2", :cache_rows => false
|
83
|
+
result.to_a.should eql(result.to_a)
|
84
|
+
end
|
85
|
+
|
73
86
|
it "should yield different value for #first if streaming" do
|
74
87
|
result = @client.query "SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false
|
75
88
|
result.first.should_not eql(result.first)
|
@@ -107,18 +120,19 @@ describe Mysql2::Result do
|
|
107
120
|
|
108
121
|
context "streaming" do
|
109
122
|
it "should maintain a count while streaming" do
|
110
|
-
result = @client.query('SELECT 1')
|
111
|
-
|
112
|
-
result.count.should eql(1)
|
123
|
+
result = @client.query('SELECT 1', :stream => true, :cache_rows => false)
|
124
|
+
result.count.should eql(0)
|
113
125
|
result.each.to_a
|
114
126
|
result.count.should eql(1)
|
115
127
|
end
|
116
128
|
|
117
|
-
it "should
|
118
|
-
result = @client.query("SELECT
|
129
|
+
it "should retain the count when mixing first and each" do
|
130
|
+
result = @client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false)
|
119
131
|
result.count.should eql(0)
|
120
|
-
result.
|
132
|
+
result.first
|
121
133
|
result.count.should eql(1)
|
134
|
+
result.each.to_a
|
135
|
+
result.count.should eql(2)
|
122
136
|
end
|
123
137
|
|
124
138
|
it "should not yield nil at the end of streaming" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mysql2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.21
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Lopez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-05-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -59,6 +59,7 @@ extensions:
|
|
59
59
|
- ext/mysql2/extconf.rb
|
60
60
|
extra_rdoc_files: []
|
61
61
|
files:
|
62
|
+
- LICENSE
|
62
63
|
- README.md
|
63
64
|
- ext/mysql2/client.c
|
64
65
|
- ext/mysql2/client.h
|
@@ -114,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
115
|
version: '0'
|
115
116
|
requirements: []
|
116
117
|
rubyforge_project:
|
117
|
-
rubygems_version: 2.0.14
|
118
|
+
rubygems_version: 2.0.14.1
|
118
119
|
signing_key:
|
119
120
|
specification_version: 4
|
120
121
|
summary: A simple, fast Mysql library for Ruby, binding to libmysql
|