mysql2 0.4.10 → 0.5.3

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.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +66 -32
  3. data/ext/mysql2/client.c +125 -59
  4. data/ext/mysql2/client.h +1 -39
  5. data/ext/mysql2/extconf.rb +24 -21
  6. data/ext/mysql2/mysql2_ext.c +2 -1
  7. data/ext/mysql2/mysql2_ext.h +8 -4
  8. data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -56
  9. data/ext/mysql2/mysql_enc_to_ruby.h +64 -3
  10. data/ext/mysql2/result.c +32 -85
  11. data/ext/mysql2/result.h +2 -3
  12. data/ext/mysql2/statement.c +81 -72
  13. data/ext/mysql2/statement.h +0 -2
  14. data/ext/mysql2/wait_for_single_fd.h +2 -1
  15. data/lib/mysql2.rb +17 -15
  16. data/lib/mysql2/client.rb +33 -27
  17. data/lib/mysql2/em.rb +2 -4
  18. data/lib/mysql2/error.rb +51 -22
  19. data/lib/mysql2/result.rb +2 -0
  20. data/lib/mysql2/statement.rb +3 -9
  21. data/lib/mysql2/version.rb +1 -1
  22. data/support/5072E1F5.asc +5 -5
  23. data/support/mysql_enc_to_ruby.rb +8 -3
  24. data/support/ruby_enc_to_mysql.rb +7 -5
  25. metadata +8 -58
  26. data/examples/eventmachine.rb +0 -21
  27. data/examples/threaded.rb +0 -18
  28. data/spec/configuration.yml.example +0 -11
  29. data/spec/em/em_spec.rb +0 -136
  30. data/spec/my.cnf.example +0 -9
  31. data/spec/mysql2/client_spec.rb +0 -1039
  32. data/spec/mysql2/error_spec.rb +0 -82
  33. data/spec/mysql2/result_spec.rb +0 -545
  34. data/spec/mysql2/statement_spec.rb +0 -776
  35. data/spec/rcov.opts +0 -3
  36. data/spec/spec_helper.rb +0 -108
  37. data/spec/ssl/ca-cert.pem +0 -17
  38. data/spec/ssl/ca-key.pem +0 -27
  39. data/spec/ssl/ca.cnf +0 -22
  40. data/spec/ssl/cert.cnf +0 -22
  41. data/spec/ssl/client-cert.pem +0 -17
  42. data/spec/ssl/client-key.pem +0 -27
  43. data/spec/ssl/client-req.pem +0 -15
  44. data/spec/ssl/gen_certs.sh +0 -48
  45. data/spec/ssl/pkcs8-client-key.pem +0 -28
  46. data/spec/ssl/pkcs8-server-key.pem +0 -28
  47. data/spec/ssl/server-cert.pem +0 -17
  48. data/spec/ssl/server-key.pem +0 -27
  49. data/spec/ssl/server-req.pem +0 -15
  50. data/spec/test_data +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 75b3d925930b92cf7b1a36fa196d334e245919ac
4
- data.tar.gz: 2bbe0a78b156f8c5b59643c4d57a7ce19b764bcc
2
+ SHA256:
3
+ metadata.gz: 8be4e0f4afbea38c5460544e39c9e0d987dd040c9de5ebdbc4c1e200102ca8e4
4
+ data.tar.gz: 996e05358297b03bf8f06abffd37d83a590912246ec607d2c3ee53b325c43022
5
5
  SHA512:
6
- metadata.gz: 602f336b5ed83421862b9dec36a9ddbd477dcbacc3ef16d58d5252072dba0bc7f7a955000482414eda1d104bda72ded87f3f4c795f8b4b4d36999bc6ee171e4b
7
- data.tar.gz: 20281fda66cf4595edc05ac6a933d5f641c2f9f87771e8ace1e9de00902ecea54ddbc2d1b743c3dbd97b48c795d0ad32f9ab785e5848a4f3de92c7ddebeef659
6
+ metadata.gz: daf37441bcb29366453963529b5a144df0d599b79b7a0b9d90b2ae5ed3dee97c1945a58111d6fe6bc2f1c20f75c9960c3cc1cd988a0e5d1be5ce44b7dcdf3633
7
+ data.tar.gz: 421df08c45f4257c2f04f3dc0a0e1c1557c695598c4e1a80236d618d1a79510459b8919b753ad9af2d46d21fa19ba060d5b53da2f54a00ff90ca45a4534c9108
data/README.md CHANGED
@@ -7,7 +7,7 @@ The Mysql2 gem is meant to serve the extremely common use-case of connecting, qu
7
7
  Some database libraries out there serve as direct 1:1 mappings of the already complex C APIs available.
8
8
  This one is not.
9
9
 
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.
10
+ It also forces the use of UTF-8 [or binary] for the connection and uses encoding-aware MySQL API calls where it can.
11
11
 
12
12
  The API consists of three classes:
13
13
 
@@ -18,16 +18,18 @@ The API consists of three classes:
18
18
  `Mysql2::Statement` - returned from issuing a #prepare on the connection. Execute the statement to get a Result.
19
19
 
20
20
  ## Installing
21
+
21
22
  ### General Instructions
23
+
22
24
  ``` sh
23
25
  gem install mysql2
24
26
  ```
25
27
 
26
28
  This gem links against MySQL's `libmysqlclient` library or `Connector/C`
27
29
  library, and compatible alternatives such as MariaDB.
28
- You may need to install a package such as `libmysqlclient-dev`, `mysql-devel`,
29
- or other appropriate package for your system. See below for system-specific
30
- instructions.
30
+ You may need to install a package such as `libmariadb-dev`, `libmysqlclient-dev`,
31
+ `mysql-devel`, or other appropriate package for your system. See below for
32
+ system-specific instructions.
31
33
 
32
34
  By default, the mysql2 gem will try to find a copy of MySQL in this order:
33
35
 
@@ -74,10 +76,11 @@ To see line numbers in backtraces, declare these environment variables
74
76
 
75
77
  ### Linux and other Unixes
76
78
 
77
- You may need to install a package such as `libmysqlclient-dev` or `mysql-devel`;
78
- refer to your distribution's package guide to find the particular package.
79
- The most common issue we see is a user who has the library file `libmysqlclient.so` but is
80
- missing the header file `mysql.h` -- double check that you have the _-dev_ packages installed.
79
+ You may need to install a package such as `libmariadb-dev`, `libmysqlclient-dev`,
80
+ `mysql-devel`, or `default-libmysqlclient-dev`; refer to your distribution's package guide to
81
+ find the particular package. The most common issue we see is a user who has
82
+ the library file `libmysqlclient.so` but is missing the header file `mysql.h`
83
+ -- double check that you have the _-dev_ packages installed.
81
84
 
82
85
  ### Mac OS X
83
86
 
@@ -89,6 +92,7 @@ If you have not done so already, you will need to install the XCode select tools
89
92
  `xcode-select --install`.
90
93
 
91
94
  ### Windows
95
+
92
96
  Make sure that you have Ruby and the DevKit compilers installed. We recommend
93
97
  the [Ruby Installer](http://rubyinstaller.org) distribution.
94
98
 
@@ -138,7 +142,7 @@ results.each do |row|
138
142
  # conveniently, row is a hash
139
143
  # the keys are the fields, as you'd expect
140
144
  # the values are pre-built ruby primitives mapped from their corresponding field types in MySQL
141
- puts row["id"] # row["id"].class == Fixnum
145
+ puts row["id"] # row["id"].is_a? Integer
142
146
  if row["dne"] # non-existant hash entry is nil
143
147
  puts row["dne"]
144
148
  end
@@ -156,7 +160,7 @@ end
156
160
  How about with symbolized keys?
157
161
 
158
162
  ``` ruby
159
- client.query("SELECT * FROM users WHERE group='githubbers'", :symbolize_keys => true) do |row|
163
+ client.query("SELECT * FROM users WHERE group='githubbers'", :symbolize_keys => true).each do |row|
160
164
  # do something with row, it's ready to rock
161
165
  end
162
166
  ```
@@ -175,7 +179,11 @@ end
175
179
  Prepared statements are supported, as well. In a prepared statement, use a `?`
176
180
  in place of each value and then execute the statement to retrieve a result set.
177
181
  Pass your arguments to the execute method in the same number and order as the
178
- question marks in the statement.
182
+ question marks in the statement. Query options can be passed as keyword arguments
183
+ to the execute method.
184
+
185
+ Be sure to read about the known limitations of prepared statements at
186
+ [https://dev.mysql.com/doc/refman/5.6/en/c-api-prepared-statement-problems.html](https://dev.mysql.com/doc/refman/5.6/en/c-api-prepared-statement-problems.html)
179
187
 
180
188
  ``` ruby
181
189
  statement = @client.prepare("SELECT * FROM users WHERE login_count = ?")
@@ -184,6 +192,9 @@ result2 = statement.execute(2)
184
192
 
185
193
  statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND location LIKE ?")
186
194
  result = statement.execute(1, "CA")
195
+
196
+ statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND location LIKE ?")
197
+ result = statement.execute(1, "CA", :as => :array)
187
198
  ```
188
199
 
189
200
  ## Connection options
@@ -203,12 +214,14 @@ Mysql2::Client.new(
203
214
  :read_timeout = seconds,
204
215
  :write_timeout = seconds,
205
216
  :connect_timeout = seconds,
217
+ :connect_attrs = {:program_name => $PROGRAM_NAME, ...},
206
218
  :reconnect = true/false,
207
219
  :local_infile = true/false,
208
220
  :secure_auth = true/false,
209
221
  :ssl_mode = :disabled / :preferred / :required / :verify_ca / :verify_identity,
210
222
  :default_file = '/path/to/my.cfg',
211
223
  :default_group = 'my.cfg section',
224
+ :default_auth = 'authentication_windows_client'
212
225
  :init_command => sql
213
226
  )
214
227
  ```
@@ -265,8 +278,10 @@ The string form will be split on whitespace and parsed as with the array form:
265
278
  Plain flags are added to the default flags, while flags prefixed with `-`
266
279
  (minus) are removed from the default flags.
267
280
 
268
- This allows easier use with ActiveRecord's database.yml, avoiding the need for magic flag numbers.
269
- For example, to disable protocol compression, and enable multiple statements and result sets:
281
+ ### Using Active Record's database.yml
282
+
283
+ Active Record typically reads its configuration from a file named `database.yml` or an environment variable `DATABASE_URL`.
284
+ Use the value `mysql2` as the adapter name. For example:
270
285
 
271
286
  ``` yaml
272
287
  development:
@@ -284,6 +299,15 @@ development:
284
299
  secure_auth: false
285
300
  ```
286
301
 
302
+ ### Using Active Record's DATABASE_URL
303
+
304
+ Active Record typically reads its configuration from a file named `database.yml` or an environment variable `DATABASE_URL`.
305
+ Use the value `mysql2` as the protocol name. For example:
306
+
307
+ ``` shell
308
+ DATABASE_URL=mysql2://sql_user:sql_pass@sql_host_name:port/sql_db_name?option1=value1&option2=value2
309
+ ```
310
+
287
311
  ### Reading a MySQL config file
288
312
 
289
313
  You may read configuration options from a MySQL configuration file by passing
@@ -338,7 +362,8 @@ end
338
362
  ```
339
363
 
340
364
  Yields:
341
- ```
365
+
366
+ ```ruby
342
367
  {"1"=>1}
343
368
  {"2"=>2}
344
369
  next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
@@ -381,6 +406,15 @@ c = Mysql2::Client.new
381
406
  c.query(sql, :symbolize_keys => true)
382
407
  ```
383
408
 
409
+ or
410
+
411
+ ``` ruby
412
+ # this will set the options for the Mysql2::Result instance returned from the #execute method
413
+ c = Mysql2::Client.new
414
+ s = c.prepare(sql)
415
+ s.execute(arg1, args2, :symbolize_keys => true)
416
+ ```
417
+
384
418
  ## Result types
385
419
 
386
420
  ### Array of Arrays
@@ -488,7 +522,7 @@ There are a few things that need to be kept in mind while using streaming:
488
522
  * `:cache_rows` is ignored currently. (if you want to use `:cache_rows` you probably don't want to be using `:stream`)
489
523
  * You must fetch all rows in the result set of your query before you can make new queries. (i.e. with `Mysql2::Result#each`)
490
524
 
491
- Read more about the consequences of using `mysql_use_result` (what streaming is implemented with) here: http://dev.mysql.com/doc/refman/5.0/en/mysql-use-result.html.
525
+ Read more about the consequences of using `mysql_use_result` (what streaming is implemented with) here: [http://dev.mysql.com/doc/refman/5.0/en/mysql-use-result.html](http://dev.mysql.com/doc/refman/5.0/en/mysql-use-result.html).
492
526
 
493
527
  ### Lazy Everything
494
528
 
@@ -509,21 +543,21 @@ As for field values themselves, I'm workin on it - but expect that soon.
509
543
 
510
544
  This gem is tested with the following Ruby versions on Linux and Mac OS X:
511
545
 
512
- * Ruby MRI 1.8.7, 1.9.3, 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x
513
- * Ruby Enterprise Edition (based on MRI 1.8.7)
514
- * Rubinius 2.x and 3.x do work but may fail under some workloads
546
+ * Ruby MRI 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x, 2.5.x, 2.6.x
547
+ * Rubinius 2.x and 3.x do work but may fail under some workloads
515
548
 
516
549
  This gem is tested with the following MySQL and MariaDB versions:
517
550
 
518
- * MySQL 5.5, 5.6, 5.7, 8.0
519
- * MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
520
- * MariaDB 5.5, 10.0, 10.1
551
+ * MySQL 5.5, 5.6, 5.7, 8.0
552
+ * MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
553
+ * MariaDB 5.5, 10.0, 10.1, 10.2, 10.3
521
554
 
522
555
  ### Ruby on Rails / Active Record
523
556
 
524
- * mysql2 0.4.x works with Rails / Active Record 4.2.5 - 5.0 and higher.
525
- * mysql2 0.3.x works with Rails / Active Record 3.1, 3.2, 4.x, 5.0.
526
- * mysql2 0.2.x works with Rails / Active Record 2.3 - 3.0.
557
+ * mysql2 0.5.x works with Rails / Active Record 5.0.7, 5.1.6, and higher.
558
+ * mysql2 0.4.x works with Rails / Active Record 4.2.5 - 5.0 and higher.
559
+ * mysql2 0.3.x works with Rails / Active Record 3.1, 3.2, 4.x, 5.0.
560
+ * mysql2 0.2.x works with Rails / Active Record 2.3 - 3.0.
527
561
 
528
562
  ### Asynchronous Active Record
529
563
 
@@ -606,11 +640,11 @@ though.
606
640
  ## Special Thanks
607
641
 
608
642
  * Eric Wong - for the contribution (and the informative explanations) of some thread-safety, non-blocking I/O and cleanup patches. You rock dude
609
- * Yury Korolev (http://github.com/yury) - for TONS of help testing the Active Record adapter
610
- * Aaron Patterson (http://github.com/tenderlove) - tons of contributions, suggestions and general badassness
611
- * Mike Perham (http://github.com/mperham) - Async Active Record adapter (uses Fibers and EventMachine)
612
- * Aaron Stone (http://github.com/sodabrew) - additional client settings, local files, microsecond time, maintenance support
613
- * Kouhei Ueno (https://github.com/nyaxt) - for the original work on Prepared Statements way back in 2012
614
- * John Cant (http://github.com/johncant) - polishing and updating Prepared Statements support
615
- * Justin Case (http://github.com/justincase) - polishing and updating Prepared Statements support and getting it merged
616
- * Tamir Duberstein (http://github.com/tamird) - for help with timeouts and all around updates and cleanups
643
+ * [Yury Korolev](http://github.com/yury) - for TONS of help testing the Active Record adapter
644
+ * [Aaron Patterson](http://github.com/tenderlove) - tons of contributions, suggestions and general badassness
645
+ * [Mike Perham](http://github.com/mperham) - Async Active Record adapter (uses Fibers and EventMachine)
646
+ * [Aaron Stone](http://github.com/sodabrew) - additional client settings, local files, microsecond time, maintenance support
647
+ * [Kouhei Ueno](https://github.com/nyaxt) - for the original work on Prepared Statements way back in 2012
648
+ * [John Cant](http://github.com/johncant) - polishing and updating Prepared Statements support
649
+ * [Justin Case](http://github.com/justincase) - polishing and updating Prepared Statements support and getting it merged
650
+ * [Tamir Duberstein](http://github.com/tamird) - for help with timeouts and all around updates and cleanups
@@ -15,15 +15,11 @@
15
15
  #include "mysql_enc_name_to_ruby.h"
16
16
 
17
17
  VALUE cMysql2Client;
18
- extern VALUE mMysql2, cMysql2Error;
18
+ extern VALUE mMysql2, cMysql2Error, cMysql2TimeoutError;
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_brackets, intern_merge, intern_merge_bang, intern_new_with_args;
21
-
22
- #ifndef HAVE_RB_HASH_DUP
23
- VALUE rb_hash_dup(VALUE other) {
24
- return rb_funcall(rb_cHash, intern_brackets, 1, other);
25
- }
26
- #endif
20
+ static VALUE sym_no_good_index_used, sym_no_index_used, sym_query_was_slow;
21
+ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args,
22
+ intern_current_query_options, intern_read_timeout;
27
23
 
28
24
  #define REQUIRE_INITIALIZED(wrapper) \
29
25
  if (!wrapper->initialized) { \
@@ -117,15 +113,19 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
117
113
  #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
118
114
  GET_CLIENT(self);
119
115
  int val = NUM2INT( setting );
120
- if (version >= 50703 && version < 50711) {
116
+ // Either MySQL 5.7.3 - 5.7.10, or Connector/C 6.1.3 - 6.1.x
117
+ if ((version >= 50703 && version < 50711) || (version >= 60103 && version < 60200)) {
121
118
  if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
122
- bool b = ( val == SSL_MODE_REQUIRED );
119
+ my_bool b = ( val == SSL_MODE_REQUIRED );
123
120
  int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
124
121
  return INT2NUM(result);
125
122
  } else {
126
123
  rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
127
124
  return Qnil;
128
125
  }
126
+ } else {
127
+ rb_warn( "Your mysql client library does not support ssl_mode as expected." );
128
+ return Qnil;
129
129
  }
130
130
  #endif
131
131
  #ifdef FULL_SSL_MODE_SUPPORT
@@ -178,10 +178,8 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
178
178
  VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
179
179
  VALUE e;
180
180
 
181
- #ifdef HAVE_RUBY_ENCODING_H
182
181
  rb_enc_associate(rb_error_msg, rb_utf8_encoding());
183
182
  rb_enc_associate(rb_sql_state, rb_usascii_encoding());
184
- #endif
185
183
 
186
184
  e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
187
185
  rb_error_msg,
@@ -357,9 +355,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
357
355
  return str;
358
356
  } else {
359
357
  rb_str = rb_str_new((const char*)newStr, newLen);
360
- #ifdef HAVE_RUBY_ENCODING_H
361
358
  rb_enc_copy(rb_str, str);
362
- #endif
363
359
  xfree(newStr);
364
360
  return rb_str;
365
361
  }
@@ -386,9 +382,7 @@ static VALUE rb_mysql_info(VALUE self) {
386
382
  }
387
383
 
388
384
  rb_str = rb_str_new2(info);
389
- #ifdef HAVE_RUBY_ENCODING_H
390
385
  rb_enc_associate(rb_str, rb_utf8_encoding());
391
- #endif
392
386
 
393
387
  return rb_str;
394
388
  }
@@ -406,14 +400,25 @@ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
406
400
  }
407
401
 
408
402
  rb_str = rb_str_new2(cipher);
409
- #ifdef HAVE_RUBY_ENCODING_H
410
403
  rb_enc_associate(rb_str, rb_utf8_encoding());
411
- #endif
412
404
 
413
405
  return rb_str;
414
406
  }
415
407
 
416
- static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
408
+ #ifdef CLIENT_CONNECT_ATTRS
409
+ static int opt_connect_attr_add_i(VALUE key, VALUE value, VALUE arg)
410
+ {
411
+ mysql_client_wrapper *wrapper = (mysql_client_wrapper *)arg;
412
+ rb_encoding *enc = rb_to_encoding(wrapper->encoding);
413
+ key = rb_str_export_to_enc(key, enc);
414
+ value = rb_str_export_to_enc(value, enc);
415
+
416
+ mysql_options4(wrapper->client, MYSQL_OPT_CONNECT_ATTR_ADD, StringValueCStr(key), StringValueCStr(value));
417
+ return ST_CONTINUE;
418
+ }
419
+ #endif
420
+
421
+ static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags, VALUE conn_attrs) {
417
422
  struct nogvl_connect_args args;
418
423
  time_t start_time, end_time, elapsed_time, connect_timeout;
419
424
  VALUE rv;
@@ -428,6 +433,11 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
428
433
  args.mysql = wrapper->client;
429
434
  args.client_flag = NUM2ULONG(flags);
430
435
 
436
+ #ifdef CLIENT_CONNECT_ATTRS
437
+ mysql_options(wrapper->client, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
438
+ rb_hash_foreach(conn_attrs, opt_connect_attr_add_i, (VALUE)wrapper);
439
+ #endif
440
+
431
441
  if (wrapper->connect_timeout)
432
442
  time(&start_time);
433
443
  rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
@@ -521,7 +531,7 @@ static VALUE do_send_query(void *args) {
521
531
  */
522
532
  static void *nogvl_read_query_result(void *ptr) {
523
533
  MYSQL * client = ptr;
524
- bool res = mysql_read_query_result(client);
534
+ my_bool res = mysql_read_query_result(client);
525
535
 
526
536
  return (void *)(res == 0 ? Qtrue : Qfalse);
527
537
  }
@@ -574,7 +584,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
574
584
  rb_raise_mysql2_error(wrapper);
575
585
  }
576
586
 
577
- is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
587
+ is_streaming = rb_hash_aref(rb_ivar_get(self, intern_current_query_options), sym_stream);
578
588
  if (is_streaming == Qtrue) {
579
589
  result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
580
590
  } else {
@@ -590,11 +600,14 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
590
600
  return Qnil;
591
601
  }
592
602
 
593
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
603
+ // Duplicate the options hash and put the copy in the Result object
604
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
594
605
  (void)RB_GC_GUARD(current);
595
606
  Check_Type(current, T_HASH);
596
607
  resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
597
608
 
609
+ rb_mysql_set_server_query_flags(wrapper->client, resultObj);
610
+
598
611
  return resultObj;
599
612
  }
600
613
 
@@ -631,7 +644,7 @@ static VALUE do_query(void *args) {
631
644
  int retval;
632
645
  VALUE read_timeout;
633
646
 
634
- read_timeout = rb_iv_get(async_args->self, "@read_timeout");
647
+ read_timeout = rb_ivar_get(async_args->self, intern_read_timeout);
635
648
 
636
649
  tvp = NULL;
637
650
  if (!NIL_P(read_timeout)) {
@@ -652,7 +665,7 @@ static VALUE do_query(void *args) {
652
665
  retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp);
653
666
 
654
667
  if (retval == 0) {
655
- rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
668
+ rb_raise(cMysql2TimeoutError, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
656
669
  }
657
670
 
658
671
  if (retval < 0) {
@@ -747,7 +760,7 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
747
760
  * Query the database with +sql+, with optional +options+. For the possible
748
761
  * options, see default_query_options on the Mysql2::Client class.
749
762
  */
750
- static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
763
+ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
751
764
  #ifndef _WIN32
752
765
  struct async_query_args async_args;
753
766
  #endif
@@ -759,15 +772,11 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
759
772
 
760
773
  (void)RB_GC_GUARD(current);
761
774
  Check_Type(current, T_HASH);
762
- rb_iv_set(self, "@current_query_options", current);
775
+ rb_ivar_set(self, intern_current_query_options, current);
763
776
 
764
777
  Check_Type(sql, T_STRING);
765
- #ifdef HAVE_RUBY_ENCODING_H
766
778
  /* ensure the string is in the encoding the connection is expecting */
767
779
  args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
768
- #else
769
- args.sql = sql;
770
- #endif
771
780
  args.sql_ptr = RSTRING_PTR(args.sql);
772
781
  args.sql_len = RSTRING_LEN(args.sql);
773
782
  args.wrapper = wrapper;
@@ -804,20 +813,16 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
804
813
  unsigned char *newStr;
805
814
  VALUE rb_str;
806
815
  unsigned long newLen, oldLen;
807
- #ifdef HAVE_RUBY_ENCODING_H
808
816
  rb_encoding *default_internal_enc;
809
817
  rb_encoding *conn_enc;
810
- #endif
811
818
  GET_CLIENT(self);
812
819
 
813
820
  REQUIRE_CONNECTED(wrapper);
814
821
  Check_Type(str, T_STRING);
815
- #ifdef HAVE_RUBY_ENCODING_H
816
822
  default_internal_enc = rb_default_internal_encoding();
817
823
  conn_enc = rb_to_encoding(wrapper->encoding);
818
824
  /* ensure the string is in the encoding the connection is expecting */
819
825
  str = rb_str_export_to_enc(str, conn_enc);
820
- #endif
821
826
 
822
827
  oldLen = RSTRING_LEN(str);
823
828
  newStr = xmalloc(oldLen*2+1);
@@ -825,21 +830,17 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
825
830
  newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
826
831
  if (newLen == oldLen) {
827
832
  /* no need to return a new ruby string if nothing changed */
828
- #ifdef HAVE_RUBY_ENCODING_H
829
833
  if (default_internal_enc) {
830
834
  str = rb_str_export_to_enc(str, default_internal_enc);
831
835
  }
832
- #endif
833
836
  xfree(newStr);
834
837
  return str;
835
838
  } else {
836
839
  rb_str = rb_str_new((const char*)newStr, newLen);
837
- #ifdef HAVE_RUBY_ENCODING_H
838
840
  rb_enc_associate(rb_str, conn_enc);
839
841
  if (default_internal_enc) {
840
842
  rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
841
843
  }
842
- #endif
843
844
  xfree(newStr);
844
845
  return rb_str;
845
846
  }
@@ -850,7 +851,7 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
850
851
  const void *retval = NULL;
851
852
  unsigned int intval = 0;
852
853
  const char * charval = NULL;
853
- bool boolval;
854
+ my_bool boolval;
854
855
 
855
856
  GET_CLIENT(self);
856
857
 
@@ -907,6 +908,11 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
907
908
  retval = charval;
908
909
  break;
909
910
 
911
+ case MYSQL_DEFAULT_AUTH:
912
+ charval = (const char *)StringValueCStr(value);
913
+ retval = charval;
914
+ break;
915
+
910
916
  #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
911
917
  case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
912
918
  boolval = (value == Qfalse ? 0 : 1);
@@ -950,10 +956,8 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
950
956
  version = rb_str_new2(mysql_get_client_info());
951
957
  header_version = rb_str_new2(MYSQL_LINK_VERSION);
952
958
 
953
- #ifdef HAVE_RUBY_ENCODING_H
954
959
  rb_enc_associate(version, rb_usascii_encoding());
955
960
  rb_enc_associate(header_version, rb_usascii_encoding());
956
- #endif
957
961
 
958
962
  rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
959
963
  rb_hash_aset(version_info, sym_version, version);
@@ -969,27 +973,21 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
969
973
  */
970
974
  static VALUE rb_mysql_client_server_info(VALUE self) {
971
975
  VALUE version, server_info;
972
- #ifdef HAVE_RUBY_ENCODING_H
973
976
  rb_encoding *default_internal_enc;
974
977
  rb_encoding *conn_enc;
975
- #endif
976
978
  GET_CLIENT(self);
977
979
 
978
980
  REQUIRE_CONNECTED(wrapper);
979
- #ifdef HAVE_RUBY_ENCODING_H
980
981
  default_internal_enc = rb_default_internal_encoding();
981
982
  conn_enc = rb_to_encoding(wrapper->encoding);
982
- #endif
983
983
 
984
984
  version = rb_hash_new();
985
985
  rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
986
986
  server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
987
- #ifdef HAVE_RUBY_ENCODING_H
988
987
  rb_enc_associate(server_info, conn_enc);
989
988
  if (default_internal_enc) {
990
989
  server_info = rb_str_export_to_enc(server_info, default_internal_enc);
991
990
  }
992
- #endif
993
991
  rb_hash_aset(version, sym_version, server_info);
994
992
  return version;
995
993
  }
@@ -1110,6 +1108,23 @@ static VALUE rb_mysql_client_ping(VALUE self) {
1110
1108
  }
1111
1109
  }
1112
1110
 
1111
+ /* call-seq:
1112
+ * client.set_server_option(value)
1113
+ *
1114
+ * Enables or disables an option for the connection.
1115
+ * Read https://dev.mysql.com/doc/refman/5.7/en/mysql-set-server-option.html
1116
+ * for more information.
1117
+ */
1118
+ static VALUE rb_mysql_client_set_server_option(VALUE self, VALUE value) {
1119
+ GET_CLIENT(self);
1120
+
1121
+ if (mysql_set_server_option(wrapper->client, NUM2INT(value)) == 0) {
1122
+ return Qtrue;
1123
+ } else {
1124
+ return Qfalse;
1125
+ }
1126
+ }
1127
+
1113
1128
  /* call-seq:
1114
1129
  * client.more_results?
1115
1130
  *
@@ -1168,7 +1183,8 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1168
1183
  return Qnil;
1169
1184
  }
1170
1185
 
1171
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
1186
+ // Duplicate the options hash and put the copy in the Result object
1187
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
1172
1188
  (void)RB_GC_GUARD(current);
1173
1189
  Check_Type(current, T_HASH);
1174
1190
  resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
@@ -1176,7 +1192,6 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1176
1192
  return resultObj;
1177
1193
  }
1178
1194
 
1179
- #ifdef HAVE_RUBY_ENCODING_H
1180
1195
  /* call-seq:
1181
1196
  * client.encoding
1182
1197
  *
@@ -1186,7 +1201,6 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
1186
1201
  GET_CLIENT(self);
1187
1202
  return wrapper->encoding;
1188
1203
  }
1189
- #endif
1190
1204
 
1191
1205
  /* call-seq:
1192
1206
  * client.automatic_close?
@@ -1256,7 +1270,7 @@ static VALUE set_read_timeout(VALUE self, VALUE value) {
1256
1270
  /* Set the instance variable here even though _mysql_client_options
1257
1271
  might not succeed, because the timeout is used in other ways
1258
1272
  elsewhere */
1259
- rb_iv_set(self, "@read_timeout", value);
1273
+ rb_ivar_set(self, intern_read_timeout, value);
1260
1274
  return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
1261
1275
  }
1262
1276
 
@@ -1272,17 +1286,14 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
1272
1286
 
1273
1287
  static VALUE set_charset_name(VALUE self, VALUE value) {
1274
1288
  char *charset_name;
1275
- #ifdef HAVE_RUBY_ENCODING_H
1276
1289
  const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
1277
1290
  rb_encoding *enc;
1278
1291
  VALUE rb_enc;
1279
- #endif
1280
1292
  GET_CLIENT(self);
1281
1293
 
1282
1294
  Check_Type(value, T_STRING);
1283
1295
  charset_name = RSTRING_PTR(value);
1284
1296
 
1285
- #ifdef HAVE_RUBY_ENCODING_H
1286
1297
  mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
1287
1298
  if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
1288
1299
  VALUE inspect = rb_inspect(value);
@@ -1292,7 +1303,6 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
1292
1303
  rb_enc = rb_enc_from_encoding(enc);
1293
1304
  wrapper->encoding = rb_enc;
1294
1305
  }
1295
- #endif
1296
1306
 
1297
1307
  if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
1298
1308
  /* TODO: warning - unable to set charset */
@@ -1336,6 +1346,10 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1336
1346
  return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1337
1347
  }
1338
1348
 
1349
+ static VALUE set_default_auth(VALUE self, VALUE value) {
1350
+ return _mysql_client_options(self, MYSQL_DEFAULT_AUTH, value);
1351
+ }
1352
+
1339
1353
  static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
1340
1354
  #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
1341
1355
  return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
@@ -1416,6 +1430,7 @@ void init_mysql2_client() {
1416
1430
  rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
1417
1431
  rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
1418
1432
  rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
1433
+ rb_define_method(cMysql2Client, "set_server_option", rb_mysql_client_set_server_option, 1);
1419
1434
  rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
1420
1435
  rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
1421
1436
  rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
@@ -1425,9 +1440,7 @@ void init_mysql2_client() {
1425
1440
  rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
1426
1441
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1427
1442
  rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1428
- #ifdef HAVE_RUBY_ENCODING_H
1429
1443
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1430
- #endif
1431
1444
 
1432
1445
  rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
1433
1446
  rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
@@ -1438,12 +1451,13 @@ void init_mysql2_client() {
1438
1451
  rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
1439
1452
  rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1440
1453
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1454
+ rb_define_private_method(cMysql2Client, "default_auth=", set_default_auth, 1);
1441
1455
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1442
1456
  rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1443
1457
  rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
1444
1458
  rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
1445
- rb_define_private_method(cMysql2Client, "connect", rb_connect, 7);
1446
- rb_define_private_method(cMysql2Client, "_query", rb_query, 2);
1459
+ rb_define_private_method(cMysql2Client, "connect", rb_mysql_connect, 8);
1460
+ rb_define_private_method(cMysql2Client, "_query", rb_mysql_query, 2);
1447
1461
 
1448
1462
  sym_id = ID2SYM(rb_intern("id"));
1449
1463
  sym_version = ID2SYM(rb_intern("version"));
@@ -1454,10 +1468,16 @@ void init_mysql2_client() {
1454
1468
  sym_array = ID2SYM(rb_intern("array"));
1455
1469
  sym_stream = ID2SYM(rb_intern("stream"));
1456
1470
 
1471
+ sym_no_good_index_used = ID2SYM(rb_intern("no_good_index_used"));
1472
+ sym_no_index_used = ID2SYM(rb_intern("no_index_used"));
1473
+ sym_query_was_slow = ID2SYM(rb_intern("query_was_slow"));
1474
+
1457
1475
  intern_brackets = rb_intern("[]");
1458
1476
  intern_merge = rb_intern("merge");
1459
1477
  intern_merge_bang = rb_intern("merge!");
1460
1478
  intern_new_with_args = rb_intern("new_with_args");
1479
+ intern_current_query_options = rb_intern("@current_query_options");
1480
+ intern_read_timeout = rb_intern("@read_timeout");
1461
1481
 
1462
1482
  #ifdef CLIENT_LONG_PASSWORD
1463
1483
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
@@ -1543,6 +1563,16 @@ void init_mysql2_client() {
1543
1563
  rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
1544
1564
  #endif
1545
1565
 
1566
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_ON
1567
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_ON"),
1568
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_ON));
1569
+ #endif
1570
+
1571
+ #ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_OFF
1572
+ rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_OFF"),
1573
+ LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_OFF));
1574
+ #endif
1575
+
1546
1576
  #ifdef CLIENT_MULTI_STATEMENTS
1547
1577
  rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
1548
1578
  LONG2NUM(CLIENT_MULTI_STATEMENTS));
@@ -1573,6 +1603,16 @@ void init_mysql2_client() {
1573
1603
  LONG2NUM(CLIENT_BASIC_FLAGS));
1574
1604
  #endif
1575
1605
 
1606
+ #ifdef CLIENT_CONNECT_ATTRS
1607
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1608
+ LONG2NUM(CLIENT_CONNECT_ATTRS));
1609
+ #else
1610
+ /* HACK because MySQL 5.5 and earlier don't define this constant,
1611
+ * but we're using it in our default connection flags. */
1612
+ rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
1613
+ INT2NUM(0));
1614
+ #endif
1615
+
1576
1616
  #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1577
1617
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1578
1618
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
@@ -1600,3 +1640,29 @@ void init_mysql2_client() {
1600
1640
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
1601
1641
  #endif
1602
1642
  }
1643
+
1644
+ #define flag_to_bool(f) ((client->server_status & f) ? Qtrue : Qfalse)
1645
+
1646
+ void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result) {
1647
+ VALUE server_flags = rb_hash_new();
1648
+
1649
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_GOOD_INDEX_USED
1650
+ rb_hash_aset(server_flags, sym_no_good_index_used, flag_to_bool(SERVER_QUERY_NO_GOOD_INDEX_USED));
1651
+ #else
1652
+ rb_hash_aset(server_flags, sym_no_good_index_used, Qnil);
1653
+ #endif
1654
+
1655
+ #ifdef HAVE_CONST_SERVER_QUERY_NO_INDEX_USED
1656
+ rb_hash_aset(server_flags, sym_no_index_used, flag_to_bool(SERVER_QUERY_NO_INDEX_USED));
1657
+ #else
1658
+ rb_hash_aset(server_flags, sym_no_index_used, Qnil);
1659
+ #endif
1660
+
1661
+ #ifdef HAVE_CONST_SERVER_QUERY_WAS_SLOW
1662
+ rb_hash_aset(server_flags, sym_query_was_slow, flag_to_bool(SERVER_QUERY_WAS_SLOW));
1663
+ #else
1664
+ rb_hash_aset(server_flags, sym_query_was_slow, Qnil);
1665
+ #endif
1666
+
1667
+ rb_iv_set(result, "@server_flags", server_flags);
1668
+ }