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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 791bc0352c6464ca1765498b0babcedfad3c284e
4
- data.tar.gz: 08b44b761494ec18a0c9903fcb67bfe61718b77b
3
+ metadata.gz: 0e3404975106474ef92ef68d239b4c69901a380e
4
+ data.tar.gz: 77464b6c2940d1b8be408918678f4a6abb47c95f
5
5
  SHA512:
6
- metadata.gz: 5554897f395a4fcd71e26612ef895da5d3e1d528ef29604380974950f7216783bf7636197d04228c39b471a51863f75c56e9140e05bb79410e1582c64842d0dc
7
- data.tar.gz: afad060a7a899d69b75ca2c44aa69a07c743984fc6897cce825c6d9ec3269bbb3733671243afe7fb33a615e61e4aa8c7b9e7863fa18289578ab29abc8409e2e8
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 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)
@@ -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 rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
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
- if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
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(args.sql, conn_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 = StringValuePtr(args.sql);
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 (!async) {
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, StringValuePtr(str), oldLen);
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 *)StringValuePtr(value);
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 *)StringValuePtr(value);
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 *)StringValuePtr(value);
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 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();
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
- #ifdef HAVE_RUBY_ENCODING_H
856
- default_internal_enc = rb_default_internal_encoding();
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(client_info, conn_enc);
864
- if (default_internal_enc) {
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
- rb_hash_aset(version, sym_version, client_info);
869
- return version;
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
- int fd_set_fd;
914
- REQUIRE_CONNECTED(wrapper);
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 = StringValuePtr(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) ? 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));
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"));
@@ -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
- }
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 (rb_hash_aref(opts, sym_stream) == Qtrue) {
483
- streaming = 1;
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->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,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
- rb_mysql_result_free_result(wrapper);
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
- mysql2_result_wrapper *wrapper;
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
- if (wrapper->streamingComplete){
606
- return LONG2NUM(wrapper->numberOfRows);
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
- return INT2FIX(mysql_num_rows(wrapper->result));
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 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.21"
3
3
  end
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
@@ -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
@@ -461,59 +462,39 @@ describe Mysql2::Client do
461
462
  }.should raise_error(Mysql2::Error)
462
463
  end
463
464
 
464
- it "should close the connection when an exception is raised" do
465
- begin
466
- Timeout.timeout(1, Timeout::Error) do
467
- @client.query("SELECT sleep(2)")
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
- lambda {
473
- @client.query("SELECT 1")
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
- it "should handle Timeouts without leaving the connection hanging if reconnect is true" do
478
- client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:reconnect => true))
479
- begin
480
- Timeout.timeout(1, Timeout::Error) do
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
- lambda {
487
- client.query("SELECT 1")
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
- it "should handle Timeouts without leaving the connection hanging if reconnect is set to true after construction true" do
492
- client = Mysql2::Client.new(DatabaseCredentials['root'])
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
- lambda {
501
- client.query("SELECT 1")
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
- client.reconnect = true
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
- begin
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
- 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'))
727
+ context "strings returned by #info" do
728
+ before { pending('Encoding is undefined') unless defined?(Encoding) }
723
729
 
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
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
- 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
736
+ context "strings returned by .info" do
737
+ before { pending('Encoding is undefined') unless defined?(Encoding) }
733
738
 
734
- with_internal_encoding 'us-ascii' do
735
- @client.info[:version].encoding.should eql(Encoding.default_internal)
736
- end
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
 
@@ -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 set the actual count of rows after streaming" do
118
- result = @client.query("SELECT * FROM mysql2_test", :stream => true, :cache_rows => false)
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.each {|r| }
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.18
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: 2015-02-17 00:00:00.000000000 Z
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