mysql2 0.3.18 → 0.3.20

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 791bc0352c6464ca1765498b0babcedfad3c284e
4
- data.tar.gz: 08b44b761494ec18a0c9903fcb67bfe61718b77b
3
+ metadata.gz: 6261e72195a2e1281c59ad35c44fe9edefc69241
4
+ data.tar.gz: 5dd5b66d3bc11fcd978d4a2d491ac47e3a005510
5
5
  SHA512:
6
- metadata.gz: 5554897f395a4fcd71e26612ef895da5d3e1d528ef29604380974950f7216783bf7636197d04228c39b471a51863f75c56e9140e05bb79410e1582c64842d0dc
7
- data.tar.gz: afad060a7a899d69b75ca2c44aa69a07c743984fc6897cce825c6d9ec3269bbb3733671243afe7fb33a615e61e4aa8c7b9e7863fa18289578ab29abc8409e2e8
6
+ metadata.gz: 9a1dc5ee4f77d51c5042b5eb8153a18c2d199506c5514a5064c818fe5f800fcbd32dae5a73c29eec268b8f96e1f31c358f87e9306e50fbee7836f7a3a88c5393
7
+ data.tar.gz: fa5d2a4b7815f4b834598e647e0aecb328e8997638ea7c73ad313cfa6b9eba5099a329c402071fb08a8a09b99e5a99784191bf7ff5722f0707a69a9c5d041d62
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 SOCK_CLOEXEC
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 SOCK_CLOEXEC, so just set FD_CLOEXEC quickly */
195
- int sockfd = open("/dev/null", O_RDWR);
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
- * Cannot raise here, because one or both of the following may be true:
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, StringValuePtr(str), oldLen);
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 = NIL_P(host) ? NULL : StringValuePtr(host);
341
- args.unix_socket = NIL_P(socket) ? NULL : StringValuePtr(socket);
342
- args.port = NIL_P(port) ? 0 : NUM2INT(port);
343
- args.user = NIL_P(user) ? NULL : StringValuePtr(user);
344
- args.passwd = NIL_P(pass) ? NULL : StringValuePtr(pass);
345
- args.db = NIL_P(database) ? NULL : StringValuePtr(database);
346
- args.mysql = wrapper->client;
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)
@@ -669,7 +677,7 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
669
677
  /* ensure the string is in the encoding the connection is expecting */
670
678
  args.sql = rb_str_export_to_enc(args.sql, conn_enc);
671
679
  #endif
672
- args.sql_ptr = StringValuePtr(args.sql);
680
+ args.sql_ptr = RSTRING_PTR(args.sql);
673
681
  args.sql_len = RSTRING_LEN(args.sql);
674
682
 
675
683
  /* see if this connection is still waiting on a result from a previous query */
@@ -736,9 +744,14 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
736
744
  oldLen = RSTRING_LEN(str);
737
745
  newStr = xmalloc(oldLen*2+1);
738
746
 
739
- newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, StringValuePtr(str), oldLen);
747
+ newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
740
748
  if (newLen == oldLen) {
741
749
  /* no need to return a new ruby string if nothing changed */
750
+ #ifdef HAVE_RUBY_ENCODING_H
751
+ if (default_internal_enc) {
752
+ str = rb_str_export_to_enc(str, default_internal_enc);
753
+ }
754
+ #endif
742
755
  xfree(newStr);
743
756
  return str;
744
757
  } else {
@@ -800,17 +813,17 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
800
813
  break;
801
814
 
802
815
  case MYSQL_READ_DEFAULT_FILE:
803
- charval = (const char *)StringValuePtr(value);
816
+ charval = (const char *)StringValueCStr(value);
804
817
  retval = charval;
805
818
  break;
806
819
 
807
820
  case MYSQL_READ_DEFAULT_GROUP:
808
- charval = (const char *)StringValuePtr(value);
821
+ charval = (const char *)StringValueCStr(value);
809
822
  retval = charval;
810
823
  break;
811
824
 
812
825
  case MYSQL_INIT_COMMAND:
813
- charval = (const char *)StringValuePtr(value);
826
+ charval = (const char *)StringValueCStr(value);
814
827
  retval = charval;
815
828
  break;
816
829
 
@@ -843,30 +856,23 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
843
856
  *
844
857
  * Returns a string that represents the client library version.
845
858
  */
846
- static VALUE rb_mysql_client_info(VALUE self) {
847
- VALUE version, client_info;
848
- #ifdef HAVE_RUBY_ENCODING_H
849
- rb_encoding *default_internal_enc;
850
- rb_encoding *conn_enc;
851
- GET_CLIENT(self);
852
- #endif
853
- version = rb_hash_new();
859
+ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
860
+ VALUE version_info, version, header_version;
861
+ version_info = rb_hash_new();
854
862
 
855
- #ifdef HAVE_RUBY_ENCODING_H
856
- default_internal_enc = rb_default_internal_encoding();
857
- conn_enc = rb_to_encoding(wrapper->encoding);
858
- #endif
863
+ version = rb_str_new2(mysql_get_client_info());
864
+ header_version = rb_str_new2(MYSQL_LINK_VERSION);
859
865
 
860
- rb_hash_aset(version, sym_id, LONG2NUM(mysql_get_client_version()));
861
- client_info = rb_str_new2(mysql_get_client_info());
862
866
  #ifdef HAVE_RUBY_ENCODING_H
863
- rb_enc_associate(client_info, conn_enc);
864
- if (default_internal_enc) {
865
- client_info = rb_str_export_to_enc(client_info, default_internal_enc);
866
- }
867
+ rb_enc_associate(version, rb_usascii_encoding());
868
+ rb_enc_associate(header_version, rb_usascii_encoding());
867
869
  #endif
868
- rb_hash_aset(version, sym_version, client_info);
869
- return version;
870
+
871
+ rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
872
+ rb_hash_aset(version_info, sym_version, version);
873
+ rb_hash_aset(version_info, sym_header_version, header_version);
874
+
875
+ return version_info;
870
876
  }
871
877
 
872
878
  /* call-seq:
@@ -907,14 +913,10 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
907
913
  * Return the file descriptor number for this client.
908
914
  */
909
915
  static VALUE rb_mysql_client_socket(VALUE self) {
910
- GET_CLIENT(self);
911
916
  #ifndef _WIN32
912
- {
913
- int fd_set_fd;
914
- REQUIRE_CONNECTED(wrapper);
915
- fd_set_fd = wrapper->client->net.fd;
916
- return INT2NUM(fd_set_fd);
917
- }
917
+ GET_CLIENT(self);
918
+ REQUIRE_CONNECTED(wrapper);
919
+ return INT2NUM(wrapper->client->net.fd);
918
920
  #else
919
921
  rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
920
922
  #endif
@@ -987,7 +989,7 @@ static VALUE rb_mysql_client_select_db(VALUE self, VALUE db)
987
989
  REQUIRE_CONNECTED(wrapper);
988
990
 
989
991
  args.mysql = wrapper->client;
990
- args.db = StringValuePtr(db);
992
+ args.db = StringValueCStr(db);
991
993
 
992
994
  if (rb_thread_call_without_gvl(nogvl_select_db, &args, RUBY_UBF_IO, 0) == Qfalse)
993
995
  rb_raise_mysql2_error(wrapper);
@@ -1183,11 +1185,11 @@ static VALUE set_ssl_options(VALUE self, VALUE key, VALUE cert, VALUE ca, VALUE
1183
1185
  GET_CLIENT(self);
1184
1186
 
1185
1187
  mysql_ssl_set(wrapper->client,
1186
- NIL_P(key) ? NULL : StringValuePtr(key),
1187
- NIL_P(cert) ? NULL : StringValuePtr(cert),
1188
- NIL_P(ca) ? NULL : StringValuePtr(ca),
1189
- NIL_P(capath) ? NULL : StringValuePtr(capath),
1190
- NIL_P(cipher) ? NULL : StringValuePtr(cipher));
1188
+ NIL_P(key) ? NULL : StringValueCStr(key),
1189
+ NIL_P(cert) ? NULL : StringValueCStr(cert),
1190
+ NIL_P(ca) ? NULL : StringValueCStr(ca),
1191
+ NIL_P(capath) ? NULL : StringValueCStr(capath),
1192
+ NIL_P(cipher) ? NULL : StringValueCStr(cipher));
1191
1193
 
1192
1194
  return self;
1193
1195
  }
@@ -1254,12 +1256,12 @@ void init_mysql2_client() {
1254
1256
  rb_define_alloc_func(cMysql2Client, allocate);
1255
1257
 
1256
1258
  rb_define_singleton_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
1259
+ rb_define_singleton_method(cMysql2Client, "info", rb_mysql_client_info, 0);
1257
1260
 
1258
1261
  rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
1259
1262
  rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
1260
1263
  rb_define_method(cMysql2Client, "abandon_results!", rb_mysql_client_abandon_results, 0);
1261
1264
  rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
1262
- rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
1263
1265
  rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
1264
1266
  rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
1265
1267
  rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
@@ -1293,6 +1295,7 @@ void init_mysql2_client() {
1293
1295
 
1294
1296
  sym_id = ID2SYM(rb_intern("id"));
1295
1297
  sym_version = ID2SYM(rb_intern("version"));
1298
+ sym_header_version = ID2SYM(rb_intern("header_version"));
1296
1299
  sym_async = ID2SYM(rb_intern("async"));
1297
1300
  sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
1298
1301
  sym_as = ID2SYM(rb_intern("as"));
@@ -2,7 +2,13 @@
2
2
  require 'mkmf'
3
3
 
4
4
  def asplode lib
5
- abort "-----\n#{lib} is missing. please check your installation of mysql and try again.\n-----"
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
- libs = ['m', 'z', 'socket', 'nsl', 'mygcc']
70
- while not find_library('mysqlclient', 'mysql_query', lib, "#{lib}/mysql") do
71
- exit 1 if libs.empty?
72
- have_library(libs.shift)
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
- GetMysql2Result(self, wrapper);
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
- GetMysql2Result(self, wrapper);
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
- GetMysql2Result(self, wrapper);
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 = 0, asArray = 0, castBool = 0, cacheRows = 1, cast = 1, streaming = 0;
449
+ int symbolizeKeys, asArray, castBool, cacheRows, cast;
450
450
  MYSQL_FIELD * fields = NULL;
451
451
 
452
- GetMysql2Result(self, wrapper);
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
- if (rb_hash_aref(opts, sym_symbolize_keys) == Qtrue) {
463
- symbolizeKeys = 1;
464
- }
465
-
466
- if (rb_hash_aref(opts, sym_as) == sym_array) {
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
- }
481
-
482
- if (rb_hash_aref(opts, sym_stream) == Qtrue) {
483
- streaming = 1;
484
- }
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));
485
467
 
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->lastRowProcessed == 0) {
512
- if (streaming) {
513
- /* We can't get number of rows if we're streaming, */
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
- if (block != Qnil && row != Qnil) {
537
- rb_yield(row);
538
- wrapper->lastRowProcessed++;
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,15 @@ 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
+ }
535
+
557
536
  if (cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
558
537
  /* we've already read the entire dataset from the C result into our */
559
538
  /* internal array. Lets hand that over to the user since it's ready to go */
@@ -598,17 +577,19 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
598
577
  }
599
578
 
600
579
  static VALUE rb_mysql_result_count(VALUE self) {
601
- mysql2_result_wrapper *wrapper;
580
+ GET_RESULT(self);
581
+
582
+ if (wrapper->is_streaming) {
583
+ /* This is an unsigned long per result.h */
584
+ return ULONG2NUM(wrapper->numberOfRows);
585
+ }
602
586
 
603
- GetMysql2Result(self, wrapper);
604
587
  if (wrapper->resultFreed) {
605
- if (wrapper->streamingComplete){
606
- return LONG2NUM(wrapper->numberOfRows);
607
- } else {
608
- return LONG2NUM(RARRAY_LEN(wrapper->rows));
609
- }
588
+ /* Ruby arrays have platform signed long length */
589
+ return LONG2NUM(RARRAY_LEN(wrapper->rows));
610
590
  } else {
611
- return INT2FIX(mysql_num_rows(wrapper->result));
591
+ /* MySQL returns an unsigned 64-bit long here */
592
+ return ULL2NUM(mysql_num_rows(wrapper->result));
612
593
  }
613
594
  }
614
595
 
@@ -616,6 +597,7 @@ static VALUE rb_mysql_result_count(VALUE self) {
616
597
  VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_RES *r) {
617
598
  VALUE obj;
618
599
  mysql2_result_wrapper * wrapper;
600
+
619
601
  obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
620
602
  wrapper->numberOfFields = 0;
621
603
  wrapper->numberOfRows = 0;
@@ -634,6 +616,10 @@ VALUE rb_mysql_result_to_obj(VALUE client, VALUE encoding, VALUE options, MYSQL_
634
616
 
635
617
  rb_iv_set(obj, "@query_options", options);
636
618
 
619
+ /* Options that cannot be changed in results.each(...) { |row| }
620
+ * should be processed here. */
621
+ wrapper->is_streaming = (rb_hash_aref(options, sym_stream) == Qtrue ? 1 : 0);
622
+
637
623
  return obj;
638
624
  }
639
625
 
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
@@ -82,6 +82,10 @@ module Mysql2
82
82
  info_hash
83
83
  end
84
84
 
85
+ def info
86
+ self.class.info
87
+ end
88
+
85
89
  private
86
90
  def self.local_offset
87
91
  ::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 contetx.
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.
@@ -1,3 +1,3 @@
1
1
  module Mysql2
2
- VERSION = "0.3.18"
2
+ VERSION = "0.3.20"
3
3
  end
@@ -66,12 +66,13 @@ describe Mysql2::Client do
66
66
  end
67
67
  end
68
68
  client = klient.new
69
- (client.connect_args.last[6] & (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)).should be_true
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
@@ -614,6 +615,21 @@ describe Mysql2::Client do
614
615
 
615
616
  @multi_client.more_results?.should be_false
616
617
  end
618
+
619
+ it "#more_results? should work with stored procedures" do
620
+ @multi_client.query("DROP PROCEDURE IF EXISTS test_proc")
621
+ @multi_client.query("CREATE PROCEDURE test_proc() BEGIN SELECT 1 AS 'set_1'; SELECT 2 AS 'set_2'; END")
622
+ @multi_client.query("CALL test_proc()").first.should eql({ 'set_1' => 1 })
623
+ @multi_client.more_results?.should be_true
624
+
625
+ @multi_client.next_result
626
+ @multi_client.store_result.first.should eql({ 'set_2' => 2 })
627
+
628
+ @multi_client.next_result
629
+ @multi_client.store_result.should be_nil # this is the result from CALL itself
630
+
631
+ @multi_client.more_results?.should be_false
632
+ end
617
633
  end
618
634
  end
619
635
 
@@ -700,6 +716,19 @@ describe Mysql2::Client do
700
716
  @client.escape ""
701
717
  }.should raise_error(Mysql2::Error)
702
718
  end
719
+
720
+ context 'when mysql encoding is not utf8' do
721
+ before { pending('Encoding is undefined') unless defined?(Encoding) }
722
+
723
+ let(:client) { Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "ujis")) }
724
+
725
+ it 'should return a internal encoding string if Encoding.default_internal is set' do
726
+ with_internal_encoding Encoding::UTF_8 do
727
+ client.escape("\u{30C6}\u{30B9}\u{30C8}").should eql("\u{30C6}\u{30B9}\u{30C8}")
728
+ client.escape("\u{30C6}'\u{30B9}\"\u{30C8}").should eql("\u{30C6}\\'\u{30B9}\\\"\u{30C8}")
729
+ end
730
+ end
731
+ end
703
732
  end
704
733
 
705
734
  it "should respond to #info" do
@@ -715,26 +744,21 @@ describe Mysql2::Client do
715
744
  info[:version].class.should eql(String)
716
745
  end
717
746
 
718
- if defined? Encoding
719
- context "strings returned by #info" do
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'))
747
+ context "strings returned by #info" do
748
+ before { pending('Encoding is undefined') unless defined?(Encoding) }
723
749
 
724
- client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
725
- client2.info[:version].encoding.should eql(Encoding.find('us-ascii'))
726
- end
727
- end
750
+ it "should be tagged as ascii" do
751
+ @client.info[:version].encoding.should eql(Encoding::US_ASCII)
752
+ @client.info[:header_version].encoding.should eql(Encoding::US_ASCII)
753
+ end
754
+ end
728
755
 
729
- it "should use Encoding.default_internal" do
730
- with_internal_encoding 'utf-8' do
731
- @client.info[:version].encoding.should eql(Encoding.default_internal)
732
- end
756
+ context "strings returned by .info" do
757
+ before { pending('Encoding is undefined') unless defined?(Encoding) }
733
758
 
734
- with_internal_encoding 'us-ascii' do
735
- @client.info[:version].encoding.should eql(Encoding.default_internal)
736
- end
737
- end
759
+ it "should be tagged as ascii" do
760
+ Mysql2::Client.info[:version].encoding.should eql(Encoding::US_ASCII)
761
+ Mysql2::Client.info[:header_version].encoding.should eql(Encoding::US_ASCII)
738
762
  end
739
763
  end
740
764
 
@@ -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
@@ -107,18 +115,19 @@ describe Mysql2::Result do
107
115
 
108
116
  context "streaming" do
109
117
  it "should maintain a count while streaming" do
110
- result = @client.query('SELECT 1')
111
-
112
- result.count.should eql(1)
118
+ result = @client.query('SELECT 1', :stream => true, :cache_rows => false)
119
+ result.count.should eql(0)
113
120
  result.each.to_a
114
121
  result.count.should eql(1)
115
122
  end
116
123
 
117
- it "should set the actual count of rows after streaming" do
118
- result = @client.query("SELECT * FROM mysql2_test", :stream => true, :cache_rows => false)
124
+ it "should retain the count when mixing first and each" do
125
+ result = @client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false)
119
126
  result.count.should eql(0)
120
- result.each {|r| }
127
+ result.first
121
128
  result.count.should eql(1)
129
+ result.each.to_a
130
+ result.count.should eql(2)
122
131
  end
123
132
 
124
133
  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.18
4
+ version: 0.3.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Lopez
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-17 00:00:00.000000000 Z
11
+ date: 2015-08-25 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