mysql2 0.3.21 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -0
  3. data/README.md +28 -6
  4. data/examples/eventmachine.rb +1 -1
  5. data/examples/threaded.rb +4 -6
  6. data/ext/mysql2/client.c +93 -84
  7. data/ext/mysql2/client.h +21 -1
  8. data/ext/mysql2/extconf.rb +60 -41
  9. data/ext/mysql2/infile.c +2 -2
  10. data/ext/mysql2/mysql2_ext.c +1 -0
  11. data/ext/mysql2/mysql2_ext.h +5 -6
  12. data/ext/mysql2/mysql_enc_name_to_ruby.h +2 -2
  13. data/ext/mysql2/mysql_enc_to_ruby.h +25 -22
  14. data/ext/mysql2/result.c +464 -97
  15. data/ext/mysql2/result.h +11 -4
  16. data/ext/mysql2/statement.c +491 -0
  17. data/ext/mysql2/statement.h +18 -0
  18. data/lib/mysql2/client.rb +32 -24
  19. data/lib/mysql2/console.rb +1 -1
  20. data/lib/mysql2/em.rb +5 -6
  21. data/lib/mysql2/error.rb +17 -26
  22. data/lib/mysql2/field.rb +3 -0
  23. data/lib/mysql2/statement.rb +17 -0
  24. data/lib/mysql2/version.rb +1 -1
  25. data/lib/mysql2.rb +37 -35
  26. data/spec/em/em_spec.rb +21 -21
  27. data/spec/mysql2/client_spec.rb +322 -290
  28. data/spec/mysql2/error_spec.rb +37 -36
  29. data/spec/mysql2/result_spec.rb +200 -209
  30. data/spec/mysql2/statement_spec.rb +684 -0
  31. data/spec/spec_helper.rb +7 -0
  32. data/spec/ssl/ca-cert.pem +17 -0
  33. data/spec/ssl/ca-key.pem +27 -0
  34. data/spec/ssl/ca.cnf +22 -0
  35. data/spec/ssl/cert.cnf +22 -0
  36. data/spec/ssl/client-cert.pem +17 -0
  37. data/spec/ssl/client-key.pem +27 -0
  38. data/spec/ssl/client-req.pem +15 -0
  39. data/spec/ssl/gen_certs.sh +48 -0
  40. data/spec/ssl/pkcs8-client-key.pem +28 -0
  41. data/spec/ssl/pkcs8-server-key.pem +28 -0
  42. data/spec/ssl/server-cert.pem +17 -0
  43. data/spec/ssl/server-key.pem +27 -0
  44. data/spec/ssl/server-req.pem +15 -0
  45. data/support/mysql_enc_to_ruby.rb +7 -8
  46. data/support/ruby_enc_to_mysql.rb +1 -1
  47. metadata +41 -47
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0e3404975106474ef92ef68d239b4c69901a380e
4
- data.tar.gz: 77464b6c2940d1b8be408918678f4a6abb47c95f
3
+ metadata.gz: ea76ea0b1ef19163cecb0a38f0a4bcf44b0b32c5
4
+ data.tar.gz: 472478d5dd515629891b26aca5169d038df0df0e
5
5
  SHA512:
6
- metadata.gz: e4e545f707900e4338c9965d068d81260b63cf3d7501926b5d2bce98415a1f589d88a541b256dc81f2e8519869103894303357f44dbc14b132fb3cfeca0d0f1d
7
- data.tar.gz: 0e3d0943c65f739827fd394e471fa30e04a9c0a9499ed896e944f1b513de4c4c066b0c8e168d4bf66ebbc6905b0df9ffe7fe904094ff49ebff29abdb5c6c6ffc
6
+ metadata.gz: de4abae3ac4f88dcedf81ab2b4d24b38b22e2006b38476b986a4fe072512d7eb7a7c1c68f569f0ee30b45ffa0e3a727ea626b392e837fcde2444a099a1cb00c5
7
+ data.tar.gz: 15b876980c214865b1098d4f821647f0c1fd93ab397b020214325984d1e10e36d69297739958773ae158af9ecf2aa932c408b926b18e31a1446a3d8bdfed4de1
data/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+ Changes are maintained under [Releases](https://github.com/brianmario/mysql2/releases)
data/README.md CHANGED
@@ -9,12 +9,14 @@ This one is not.
9
9
 
10
10
  It also forces the use of UTF-8 [or binary] for the connection [and all strings in 1.9, unless Encoding.default_internal is set then it'll convert from UTF-8 to that encoding] and uses encoding-aware MySQL API calls where it can.
11
11
 
12
- The API consists of two classes:
12
+ The API consists of three classes:
13
13
 
14
14
  `Mysql2::Client` - your connection to the database.
15
15
 
16
16
  `Mysql2::Result` - returned from issuing a #query on the connection. It includes Enumerable.
17
17
 
18
+ `Mysql2::Statement` - returned from issuing a #prepare on the connection. Execute the statement to get a Result.
19
+
18
20
  ## Installing
19
21
  ### General Instructions
20
22
  ``` sh
@@ -153,6 +155,20 @@ results.each(:as => :array) do |row|
153
155
  end
154
156
  ```
155
157
 
158
+ Prepared statements are supported, as well. In a prepared statement, use a `?`
159
+ in place of each value and then execute the statement to retrieve a result set.
160
+ Pass your arguments to the execute method in the same number and order as the
161
+ question marks in the statement.
162
+
163
+ ``` ruby
164
+ statement = @client.prepare("SELECT * FROM users WHERE login_count = ?")
165
+ result1 = statement.execute(1)
166
+ result2 = statement.execute(2)
167
+
168
+ statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND location LIKE ?")
169
+ result = statement.execute(1, "CA")
170
+ ```
171
+
156
172
  ## Connection options
157
173
 
158
174
  You may set the following connection options in Mysql2::Client.new(...):
@@ -186,7 +202,8 @@ Setting any of the following options will enable an SSL connection, but only if
186
202
  your MySQL client library and server have been compiled with SSL support.
187
203
  MySQL client library defaults will be used for any parameters that are left out
188
204
  or set to nil. Relative paths are allowed, and may be required by managed
189
- hosting providers such as Heroku.
205
+ hosting providers such as Heroku. Set `:sslverify => true` to require that the
206
+ server presents a valid certificate.
190
207
 
191
208
  ``` ruby
192
209
  Mysql2::Client.new(
@@ -195,7 +212,8 @@ Mysql2::Client.new(
195
212
  :sslcert => '/path/to/client-cert.pem',
196
213
  :sslca => '/path/to/ca-cert.pem',
197
214
  :sslcapath => '/path/to/cacerts',
198
- :sslcipher => 'DHE-RSA-AES256-SHA'
215
+ :sslcipher => 'DHE-RSA-AES256-SHA',
216
+ :sslverify => true,
199
217
  )
200
218
  ```
201
219
 
@@ -437,13 +455,13 @@ As for field values themselves, I'm workin on it - but expect that soon.
437
455
 
438
456
  This gem is tested with the following Ruby versions on Linux and Mac OS X:
439
457
 
440
- * Ruby MRI 1.8.7, 1.9.2, 1.9.3, 2.0.0, 2.1.x, 2.2.x (ongoing patch releases)
458
+ * Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x
441
459
  * Ruby Enterprise Edition (based on MRI 1.8.7)
442
460
  * Rubinius 2.x
443
461
 
444
462
  This gem is tested with the following MySQL and MariaDB versions:
445
463
 
446
- * MySQL 5.0, 5.1, 5.5, 5.6, 5.7
464
+ * MySQL 5.5, 5.7
447
465
  * MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
448
466
  * MariaDB 5.5, 10.0
449
467
 
@@ -536,4 +554,8 @@ though.
536
554
  * Yury Korolev (http://github.com/yury) - for TONS of help testing the Active Record adapter
537
555
  * Aaron Patterson (http://github.com/tenderlove) - tons of contributions, suggestions and general badassness
538
556
  * Mike Perham (http://github.com/mperham) - Async Active Record adapter (uses Fibers and EventMachine)
539
- * Aaron Stone (http://github.com/sodabrew) - additional client settings, local files, microsecond time, maintenance support.
557
+ * Aaron Stone (http://github.com/sodabrew) - additional client settings, local files, microsecond time, maintenance support
558
+ * Kouhei Ueno (https://github.com/nyaxt) - for the original work on Prepared Statements way back in 2012
559
+ * John Cant (http://github.com/johncant) - polishing and updating Prepared Statements support
560
+ * Justin Case (http://github.com/justincase) - polishing and updating Prepared Statements support and getting it merged
561
+ * Tamir Duberstein (http://github.com/tamird) - for help with timeouts and all around updates and cleanups
@@ -18,4 +18,4 @@ EM.run do
18
18
  defer2.callback do |result|
19
19
  puts "Result: #{result.to_a.inspect}"
20
20
  end
21
- end
21
+ end
data/examples/threaded.rb CHANGED
@@ -4,17 +4,15 @@ $LOAD_PATH.unshift 'lib'
4
4
  require 'mysql2'
5
5
  require 'timeout'
6
6
 
7
- threads = []
8
7
  # Should never exceed worst case 3.5 secs across all 20 threads
9
8
  Timeout.timeout(3.5) do
10
- 20.times do
11
- threads << Thread.new do
9
+ 20.times.map do
10
+ Thread.new do
12
11
  overhead = rand(3)
13
12
  puts ">> thread #{Thread.current.object_id} query, #{overhead} sec overhead"
14
13
  # 3 second overhead per query
15
14
  Mysql2::Client.new(:host => "localhost", :username => "root").query("SELECT sleep(#{overhead}) as result")
16
15
  puts "<< thread #{Thread.current.object_id} result, #{overhead} sec overhead"
17
16
  end
18
- end
19
- threads.each{|t| t.join }
20
- end
17
+ end.each(&:join)
18
+ end
data/ext/mysql2/client.c CHANGED
@@ -17,11 +17,11 @@
17
17
  VALUE cMysql2Client;
18
18
  extern VALUE mMysql2, cMysql2Error;
19
19
  static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
20
- static ID intern_merge, intern_merge_bang, intern_error_number_eql, intern_sql_state_eql;
20
+ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args;
21
21
 
22
22
  #ifndef HAVE_RB_HASH_DUP
23
- static VALUE rb_hash_dup(VALUE other) {
24
- return rb_funcall(rb_cHash, rb_intern("[]"), 1, other);
23
+ VALUE rb_hash_dup(VALUE other) {
24
+ return rb_funcall(rb_cHash, intern_brackets, 1, other);
25
25
  }
26
26
  #endif
27
27
 
@@ -30,25 +30,12 @@ static VALUE rb_hash_dup(VALUE other) {
30
30
  rb_raise(cMysql2Error, "MySQL client is not initialized"); \
31
31
  }
32
32
 
33
- #define REQUIRE_CONNECTED(wrapper) \
34
- REQUIRE_INITIALIZED(wrapper) \
35
- if (!wrapper->connected && !wrapper->reconnect_enabled) { \
36
- rb_raise(cMysql2Error, "closed MySQL connection"); \
37
- }
38
-
39
33
  #define REQUIRE_NOT_CONNECTED(wrapper) \
40
34
  REQUIRE_INITIALIZED(wrapper) \
41
35
  if (wrapper->connected) { \
42
36
  rb_raise(cMysql2Error, "MySQL connection is already open"); \
43
37
  }
44
38
 
45
- #define MARK_CONN_INACTIVE(conn) \
46
- wrapper->active_thread = Qnil;
47
-
48
- #define GET_CLIENT(self) \
49
- mysql_client_wrapper *wrapper; \
50
- Data_Get_Struct(self, mysql_client_wrapper, wrapper)
51
-
52
39
  /*
53
40
  * compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
54
41
  * variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
@@ -136,11 +123,12 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
136
123
  rb_enc_associate(rb_sql_state, rb_usascii_encoding());
137
124
  #endif
138
125
 
139
- e = rb_funcall(cMysql2Error, rb_intern("new"), 2, rb_error_msg, LONG2FIX(wrapper->server_version));
140
- rb_funcall(e, intern_error_number_eql, 1, UINT2NUM(mysql_errno(wrapper->client)));
141
- rb_funcall(e, intern_sql_state_eql, 1, rb_sql_state);
126
+ e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
127
+ rb_error_msg,
128
+ LONG2FIX(wrapper->server_version),
129
+ UINT2NUM(mysql_errno(wrapper->client)),
130
+ rb_sql_state);
142
131
  rb_exc_raise(e);
143
- return Qnil;
144
132
  }
145
133
 
146
134
  static void *nogvl_init(void *ptr) {
@@ -221,32 +209,19 @@ static VALUE invalidate_fd(int clientfd)
221
209
  #endif /* _WIN32 */
222
210
 
223
211
  static void *nogvl_close(void *ptr) {
224
- mysql_client_wrapper *wrapper;
225
- wrapper = ptr;
226
- if (wrapper->connected) {
227
- wrapper->active_thread = Qnil;
228
- wrapper->connected = 0;
229
- #ifndef _WIN32
230
- /* Invalidate the socket before calling mysql_close(). This prevents
231
- * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
232
- * the socket. The difference is that invalidate_fd will drop this
233
- * process's reference to the socket only, while a QUIT or shutdown()
234
- * would render the underlying connection unusable, interrupting other
235
- * processes which share this object across a fork().
236
- */
237
- if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
238
- fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely, leaking some memory\n");
239
- close(wrapper->client->net.fd);
240
- return NULL;
241
- }
242
- #endif
212
+ mysql_client_wrapper *wrapper = ptr;
243
213
 
244
- mysql_close(wrapper->client); /* only used to free memory at this point */
214
+ if (wrapper->client) {
215
+ mysql_close(wrapper->client);
216
+ wrapper->client = NULL;
217
+ wrapper->connected = 0;
218
+ wrapper->active_thread = Qnil;
245
219
  }
246
220
 
247
221
  return NULL;
248
222
  }
249
223
 
224
+ /* this is called during GC */
250
225
  static void rb_mysql_client_free(void *ptr) {
251
226
  mysql_client_wrapper *wrapper = (mysql_client_wrapper *)ptr;
252
227
  decr_mysql2_client(wrapper);
@@ -255,7 +230,24 @@ static void rb_mysql_client_free(void *ptr) {
255
230
  void decr_mysql2_client(mysql_client_wrapper *wrapper)
256
231
  {
257
232
  wrapper->refcount--;
233
+
258
234
  if (wrapper->refcount == 0) {
235
+ #ifndef _WIN32
236
+ if (wrapper->connected) {
237
+ /* The client is being garbage collected while connected. Prevent
238
+ * mysql_close() from sending a mysql-QUIT or from calling shutdown() on
239
+ * the socket by invalidating it. invalidate_fd() will drop this
240
+ * process's reference to the socket only, while a QUIT or shutdown()
241
+ * would render the underlying connection unusable, interrupting other
242
+ * processes which share this object across a fork().
243
+ */
244
+ if (invalidate_fd(wrapper->client->net.fd) == Qfalse) {
245
+ fprintf(stderr, "[WARN] mysql2 failed to invalidate FD safely\n");
246
+ close(wrapper->client->net.fd);
247
+ }
248
+ }
249
+ #endif
250
+
259
251
  nogvl_close(wrapper);
260
252
  xfree(wrapper->client);
261
253
  xfree(wrapper);
@@ -267,7 +259,7 @@ static VALUE allocate(VALUE klass) {
267
259
  mysql_client_wrapper * wrapper;
268
260
  obj = Data_Make_Struct(klass, mysql_client_wrapper, rb_mysql_client_mark, rb_mysql_client_free, wrapper);
269
261
  wrapper->encoding = Qnil;
270
- wrapper->active_thread = Qnil;
262
+ MARK_CONN_INACTIVE(self);
271
263
  wrapper->server_version = 0;
272
264
  wrapper->reconnect_enabled = 0;
273
265
  wrapper->connect_timeout = 0;
@@ -275,6 +267,7 @@ static VALUE allocate(VALUE klass) {
275
267
  wrapper->initialized = 0; /* means that that the wrapper is initialized */
276
268
  wrapper->refcount = 1;
277
269
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
270
+
278
271
  return obj;
279
272
  }
280
273
 
@@ -340,8 +333,7 @@ static VALUE rb_mysql_info(VALUE self) {
340
333
 
341
334
  static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
342
335
  struct nogvl_connect_args args;
343
- time_t start_time, end_time;
344
- unsigned int elapsed_time, connect_timeout;
336
+ time_t start_time, end_time, elapsed_time, connect_timeout;
345
337
  VALUE rv;
346
338
  GET_CLIENT(self);
347
339
 
@@ -368,7 +360,7 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
368
360
  /* avoid an early timeout due to time truncating milliseconds off the start time */
369
361
  if (elapsed_time > 0)
370
362
  elapsed_time--;
371
- if (elapsed_time >= wrapper->connect_timeout)
363
+ if (elapsed_time >= (time_t)wrapper->connect_timeout)
372
364
  break;
373
365
  connect_timeout = wrapper->connect_timeout - elapsed_time;
374
366
  mysql_options(wrapper->client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout);
@@ -389,10 +381,13 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
389
381
  }
390
382
 
391
383
  /*
392
- * Immediately disconnect from the server, normally the garbage collector
393
- * will disconnect automatically when a connection is no longer needed.
394
- * Explicitly closing this will free up server resources sooner than waiting
395
- * for the garbage collector.
384
+ * Terminate the connection; call this when the connection is no longer needed.
385
+ * The garbage collector can close the connection, but doing so emits an
386
+ * "Aborted connection" error on the server and increments the Aborted_clients
387
+ * status variable.
388
+ *
389
+ * @see http://dev.mysql.com/doc/en/communication-errors.html
390
+ * @return [void]
396
391
  */
397
392
  static VALUE rb_mysql_client_close(VALUE self) {
398
393
  GET_CLIENT(self);
@@ -454,7 +449,7 @@ static void *nogvl_do_result(void *ptr, char use_result) {
454
449
 
455
450
  /* once our result is stored off, this connection is
456
451
  ready for another command to be issued */
457
- wrapper->active_thread = Qnil;
452
+ MARK_CONN_INACTIVE(self);
458
453
 
459
454
  return result;
460
455
  }
@@ -507,9 +502,9 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
507
502
  }
508
503
 
509
504
  current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
510
- RB_GC_GUARD(current);
505
+ (void)RB_GC_GUARD(current);
511
506
  Check_Type(current, T_HASH);
512
- resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
507
+ resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
513
508
 
514
509
  return resultObj;
515
510
  }
@@ -523,7 +518,7 @@ struct async_query_args {
523
518
  static VALUE disconnect_and_raise(VALUE self, VALUE error) {
524
519
  GET_CLIENT(self);
525
520
 
526
- wrapper->active_thread = Qnil;
521
+ MARK_CONN_INACTIVE(self);
527
522
  wrapper->connected = 0;
528
523
 
529
524
  /* Invalidate the MySQL socket to prevent further communication.
@@ -535,8 +530,6 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
535
530
  }
536
531
 
537
532
  rb_exc_raise(error);
538
-
539
- return Qnil;
540
533
  }
541
534
 
542
535
  static VALUE do_query(void *args) {
@@ -599,13 +592,31 @@ static VALUE finish_and_mark_inactive(void *args) {
599
592
  result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
600
593
  mysql_free_result(result);
601
594
 
602
- wrapper->active_thread = Qnil;
595
+ MARK_CONN_INACTIVE(self);
603
596
  }
604
597
 
605
598
  return Qnil;
606
599
  }
607
600
  #endif
608
601
 
602
+ void rb_mysql_client_set_active_thread(VALUE self) {
603
+ VALUE thread_current = rb_thread_current();
604
+ GET_CLIENT(self);
605
+
606
+ // see if this connection is still waiting on a result from a previous query
607
+ if (NIL_P(wrapper->active_thread)) {
608
+ // mark this connection active
609
+ wrapper->active_thread = thread_current;
610
+ } else if (wrapper->active_thread == thread_current) {
611
+ rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
612
+ } else {
613
+ VALUE inspect = rb_inspect(wrapper->active_thread);
614
+ const char *thr = StringValueCStr(inspect);
615
+
616
+ rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
617
+ }
618
+ }
619
+
609
620
  /* call-seq:
610
621
  * client.abandon_results!
611
622
  *
@@ -640,20 +651,19 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
640
651
  * client.query(sql, options = {})
641
652
  *
642
653
  * Query the database with +sql+, with optional +options+. For the possible
643
- * options, see @@default_query_options on the Mysql2::Client class.
654
+ * options, see default_query_options on the Mysql2::Client class.
644
655
  */
645
656
  static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
646
657
  #ifndef _WIN32
647
658
  struct async_query_args async_args;
648
659
  #endif
649
660
  struct nogvl_send_query_args args;
650
- VALUE thread_current = rb_thread_current();
651
661
  GET_CLIENT(self);
652
662
 
653
663
  REQUIRE_CONNECTED(wrapper);
654
664
  args.mysql = wrapper->client;
655
665
 
656
- RB_GC_GUARD(current);
666
+ (void)RB_GC_GUARD(current);
657
667
  Check_Type(current, T_HASH);
658
668
  rb_iv_set(self, "@current_query_options", current);
659
669
 
@@ -666,23 +676,10 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
666
676
  #endif
667
677
  args.sql_ptr = RSTRING_PTR(args.sql);
668
678
  args.sql_len = RSTRING_LEN(args.sql);
669
-
670
- /* see if this connection is still waiting on a result from a previous query */
671
- if (NIL_P(wrapper->active_thread)) {
672
- /* mark this connection active */
673
- wrapper->active_thread = thread_current;
674
- } else if (wrapper->active_thread == thread_current) {
675
- rb_raise(cMysql2Error, "This connection is still waiting for a result, try again once you have the result");
676
- } else {
677
- VALUE inspect = rb_inspect(wrapper->active_thread);
678
- const char *thr = StringValueCStr(inspect);
679
-
680
- rb_raise(cMysql2Error, "This connection is in use by: %s", thr);
681
- RB_GC_GUARD(inspect);
682
- }
683
-
684
679
  args.wrapper = wrapper;
685
680
 
681
+ rb_mysql_client_set_active_thread(self);
682
+
686
683
  #ifndef _WIN32
687
684
  rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
688
685
 
@@ -899,15 +896,17 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
899
896
  *
900
897
  * Return the file descriptor number for this client.
901
898
  */
902
- static VALUE rb_mysql_client_socket(VALUE self) {
903
899
  #ifndef _WIN32
900
+ static VALUE rb_mysql_client_socket(VALUE self) {
904
901
  GET_CLIENT(self);
905
902
  REQUIRE_CONNECTED(wrapper);
906
903
  return INT2NUM(wrapper->client->net.fd);
904
+ }
907
905
  #else
906
+ static VALUE rb_mysql_client_socket(RB_MYSQL_UNUSED VALUE self) {
908
907
  rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
909
- #endif
910
908
  }
909
+ #endif
911
910
 
912
911
  /* call-seq:
913
912
  * client.last_id
@@ -1067,9 +1066,9 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1067
1066
  }
1068
1067
 
1069
1068
  current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
1070
- RB_GC_GUARD(current);
1069
+ (void)RB_GC_GUARD(current);
1071
1070
  Check_Type(current, T_HASH);
1072
- resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result);
1071
+ resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
1073
1072
 
1074
1073
  return resultObj;
1075
1074
  }
@@ -1138,7 +1137,6 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
1138
1137
  static VALUE set_charset_name(VALUE self, VALUE value) {
1139
1138
  char *charset_name;
1140
1139
  #ifdef HAVE_RUBY_ENCODING_H
1141
- size_t charset_name_len;
1142
1140
  const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
1143
1141
  rb_encoding *enc;
1144
1142
  VALUE rb_enc;
@@ -1148,8 +1146,7 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
1148
1146
  charset_name = RSTRING_PTR(value);
1149
1147
 
1150
1148
  #ifdef HAVE_RUBY_ENCODING_H
1151
- charset_name_len = RSTRING_LEN(value);
1152
- mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, charset_name_len);
1149
+ mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
1153
1150
  if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
1154
1151
  VALUE inspect = rb_inspect(value);
1155
1152
  rb_raise(cMysql2Error, "Unsupported charset: '%s'", RSTRING_PTR(inspect));
@@ -1209,7 +1206,19 @@ static VALUE initialize_ext(VALUE self) {
1209
1206
  return self;
1210
1207
  }
1211
1208
 
1209
+ /* call-seq: client.prepare # => Mysql2::Statement
1210
+ *
1211
+ * Create a new prepared statement.
1212
+ */
1213
+ static VALUE rb_mysql_client_prepare_statement(VALUE self, VALUE sql) {
1214
+ GET_CLIENT(self);
1215
+ REQUIRE_CONNECTED(wrapper);
1216
+
1217
+ return rb_mysql_stmt_new(self, sql);
1218
+ }
1219
+
1212
1220
  void init_mysql2_client() {
1221
+ #ifdef _WIN32
1213
1222
  /* verify the libmysql we're about to use was the version we were built against
1214
1223
  https://github.com/luislavena/mysql-gem/commit/a600a9c459597da0712f70f43736e24b484f8a99 */
1215
1224
  int i;
@@ -1224,15 +1233,14 @@ void init_mysql2_client() {
1224
1233
  }
1225
1234
  if (lib[i] != MYSQL_LINK_VERSION[i]) {
1226
1235
  rb_raise(rb_eRuntimeError, "Incorrect MySQL client library version! This gem was compiled for %s but the client library is %s.", MYSQL_LINK_VERSION, lib);
1227
- return;
1228
1236
  }
1229
1237
  }
1238
+ #endif
1230
1239
 
1231
1240
  /* Initializing mysql library, so different threads could call Client.new */
1232
1241
  /* without race condition in the library */
1233
1242
  if (mysql_library_init(0, NULL, NULL) != 0) {
1234
1243
  rb_raise(rb_eRuntimeError, "Could not initialize MySQL client library");
1235
- return;
1236
1244
  }
1237
1245
 
1238
1246
  #if 0
@@ -1253,6 +1261,7 @@ void init_mysql2_client() {
1253
1261
  rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
1254
1262
  rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
1255
1263
  rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
1264
+ rb_define_method(cMysql2Client, "prepare", rb_mysql_client_prepare_statement, 1);
1256
1265
  rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
1257
1266
  rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
1258
1267
  rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
@@ -1289,10 +1298,10 @@ void init_mysql2_client() {
1289
1298
  sym_array = ID2SYM(rb_intern("array"));
1290
1299
  sym_stream = ID2SYM(rb_intern("stream"));
1291
1300
 
1301
+ intern_brackets = rb_intern("[]");
1292
1302
  intern_merge = rb_intern("merge");
1293
1303
  intern_merge_bang = rb_intern("merge!");
1294
- intern_error_number_eql = rb_intern("error_number=");
1295
- intern_sql_state_eql = rb_intern("sql_state=");
1304
+ intern_new_with_args = rb_intern("new_with_args");
1296
1305
 
1297
1306
  #ifdef CLIENT_LONG_PASSWORD
1298
1307
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),