mysql2 0.4.10 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
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
+ }