mysql2 0.5.0 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +81 -34
  3. data/ext/mysql2/client.c +123 -25
  4. data/ext/mysql2/extconf.rb +24 -6
  5. data/ext/mysql2/mysql2_ext.c +6 -1
  6. data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -56
  7. data/ext/mysql2/mysql_enc_to_ruby.h +64 -3
  8. data/ext/mysql2/result.c +231 -15
  9. data/ext/mysql2/result.h +1 -0
  10. data/ext/mysql2/statement.c +46 -14
  11. data/lib/mysql2/client.rb +20 -3
  12. data/lib/mysql2/error.rb +4 -3
  13. data/lib/mysql2/statement.rb +1 -3
  14. data/lib/mysql2/version.rb +1 -1
  15. data/lib/mysql2.rb +7 -3
  16. data/support/3A79BD29.asc +49 -0
  17. data/support/5072E1F5.asc +5 -5
  18. data/support/mysql_enc_to_ruby.rb +6 -1
  19. data/support/ruby_enc_to_mysql.rb +2 -0
  20. metadata +10 -55
  21. data/examples/eventmachine.rb +0 -19
  22. data/examples/threaded.rb +0 -16
  23. data/spec/configuration.yml.example +0 -11
  24. data/spec/em/em_spec.rb +0 -135
  25. data/spec/my.cnf.example +0 -9
  26. data/spec/mysql2/client_spec.rb +0 -1072
  27. data/spec/mysql2/error_spec.rb +0 -78
  28. data/spec/mysql2/result_spec.rb +0 -485
  29. data/spec/mysql2/statement_spec.rb +0 -708
  30. data/spec/rcov.opts +0 -3
  31. data/spec/spec_helper.rb +0 -112
  32. data/spec/ssl/ca-cert.pem +0 -17
  33. data/spec/ssl/ca-key.pem +0 -27
  34. data/spec/ssl/ca.cnf +0 -22
  35. data/spec/ssl/cert.cnf +0 -22
  36. data/spec/ssl/client-cert.pem +0 -17
  37. data/spec/ssl/client-key.pem +0 -27
  38. data/spec/ssl/client-req.pem +0 -15
  39. data/spec/ssl/gen_certs.sh +0 -48
  40. data/spec/ssl/pkcs8-client-key.pem +0 -28
  41. data/spec/ssl/pkcs8-server-key.pem +0 -28
  42. data/spec/ssl/server-cert.pem +0 -17
  43. data/spec/ssl/server-key.pem +0 -27
  44. data/spec/ssl/server-req.pem +0 -15
  45. data/spec/test_data +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 658cf051d498ace207785b75cead2b0f938bba92
4
- data.tar.gz: 7f50c5cf9a45656a5347809d4494ee0c17eb8208
2
+ SHA256:
3
+ metadata.gz: f387f841adc885eb4000a960ee34f475c7edab79e276d17ce8889f370bdcf387
4
+ data.tar.gz: 0c8732c2a45ed8ac68aea92765daba5969f860f1fe5a95d44e89b76deae7108b
5
5
  SHA512:
6
- metadata.gz: '05994292d6c1de48d3fcc1af99c5c2ec85c09a6bb1111e19129d9434fcdf191d838a91fee9d61d6a67576a6ff316a39b418b9cf13e159d99cbbad2e49eb28e78'
7
- data.tar.gz: 5d668a60aa6ed4053787b84c1cb3114604b2c22c225492b53124d7d023fd02f6c9b54f848453fedce72ac64092723b989f3cafb471605ffa5606444cf0a4d26b
6
+ metadata.gz: 8d6afb1b661525183075d14046630195bc79a5f945b92489d3daa0aeea8582e8d17a0fa9d1781fadeaaefaaeac567ead5ad7d0266e1fb9aa96076d26f1c03ad1
7
+ data.tar.gz: 9b2202a06e36ca910a9648f71d8646e67a6e665153b3ac04dcd535cd768bb78942dd799893e205df841b0461aeb5810985704e18e4a045b540f2d272b11776cd
data/README.md CHANGED
@@ -1,7 +1,12 @@
1
1
  # Mysql2 - A modern, simple and very fast MySQL library for Ruby - binding to libmysql
2
2
 
3
- Travis CI [![Travis CI Status](https://travis-ci.org/brianmario/mysql2.png)](https://travis-ci.org/brianmario/mysql2)
4
- Appveyor CI [![Appveyor CI Status](https://ci.appveyor.com/api/projects/status/github/sodabrew/mysql2)](https://ci.appveyor.com/project/sodabrew/mysql2)
3
+ GitHub Actions
4
+ [![GitHub Actions Status: Build](https://github.com/brianmario/mysql2/actions/workflows/build.yml/badge.svg)](https://github.com/brianmario/mysql2/actions/workflows/build.yml)
5
+ [![GitHub Actions Status: Container](https://github.com/brianmario/mysql2/actions/workflows/container.yml/badge.svg)](https://github.com/brianmario/mysql2/actions/workflows/container.yml)
6
+ Travis CI
7
+ [![Travis CI Status](https://travis-ci.org/brianmario/mysql2.png)](https://travis-ci.org/brianmario/mysql2)
8
+ Appveyor CI
9
+ [![Appveyor CI Status](https://ci.appveyor.com/api/projects/status/github/sodabrew/mysql2)](https://ci.appveyor.com/project/sodabrew/mysql2)
5
10
 
6
11
  The Mysql2 gem is meant to serve the extremely common use-case of connecting, querying and iterating on results.
7
12
  Some database libraries out there serve as direct 1:1 mappings of the already complex C APIs available.
@@ -18,16 +23,18 @@ The API consists of three classes:
18
23
  `Mysql2::Statement` - returned from issuing a #prepare on the connection. Execute the statement to get a Result.
19
24
 
20
25
  ## Installing
26
+
21
27
  ### General Instructions
28
+
22
29
  ``` sh
23
30
  gem install mysql2
24
31
  ```
25
32
 
26
33
  This gem links against MySQL's `libmysqlclient` library or `Connector/C`
27
34
  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.
35
+ You may need to install a package such as `libmariadb-dev`, `libmysqlclient-dev`,
36
+ `mysql-devel`, or other appropriate package for your system. See below for
37
+ system-specific instructions.
31
38
 
32
39
  By default, the mysql2 gem will try to find a copy of MySQL in this order:
33
40
 
@@ -74,10 +81,11 @@ To see line numbers in backtraces, declare these environment variables
74
81
 
75
82
  ### Linux and other Unixes
76
83
 
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.
84
+ You may need to install a package such as `libmariadb-dev`, `libmysqlclient-dev`,
85
+ `mysql-devel`, or `default-libmysqlclient-dev`; refer to your distribution's package guide to
86
+ find the particular package. The most common issue we see is a user who has
87
+ the library file `libmysqlclient.so` but is missing the header file `mysql.h`
88
+ -- double check that you have the _-dev_ packages installed.
81
89
 
82
90
  ### Mac OS X
83
91
 
@@ -89,6 +97,7 @@ If you have not done so already, you will need to install the XCode select tools
89
97
  `xcode-select --install`.
90
98
 
91
99
  ### Windows
100
+
92
101
  Make sure that you have Ruby and the DevKit compilers installed. We recommend
93
102
  the [Ruby Installer](http://rubyinstaller.org) distribution.
94
103
 
@@ -139,7 +148,7 @@ results.each do |row|
139
148
  # the keys are the fields, as you'd expect
140
149
  # the values are pre-built ruby primitives mapped from their corresponding field types in MySQL
141
150
  puts row["id"] # row["id"].is_a? Integer
142
- if row["dne"] # non-existant hash entry is nil
151
+ if row["dne"] # non-existent hash entry is nil
143
152
  puts row["dne"]
144
153
  end
145
154
  end
@@ -156,16 +165,17 @@ end
156
165
  How about with symbolized keys?
157
166
 
158
167
  ``` ruby
159
- client.query("SELECT * FROM users WHERE group='githubbers'", :symbolize_keys => true) do |row|
168
+ client.query("SELECT * FROM users WHERE group='githubbers'", :symbolize_keys => true).each do |row|
160
169
  # do something with row, it's ready to rock
161
170
  end
162
171
  ```
163
172
 
164
- You can get the headers and the columns in the order that they were returned
173
+ You can get the headers, columns, and the field types in the order that they were returned
165
174
  by the query like this:
166
175
 
167
176
  ``` ruby
168
177
  headers = results.fields # <= that's an array of field names, in order
178
+ types = results.field_types # <= that's an array of field types, in order
169
179
  results.each(:as => :array) do |row|
170
180
  # Each row is an array, ordered the same as the query results
171
181
  # An otter's den is called a "holt" or "couch"
@@ -178,6 +188,9 @@ Pass your arguments to the execute method in the same number and order as the
178
188
  question marks in the statement. Query options can be passed as keyword arguments
179
189
  to the execute method.
180
190
 
191
+ Be sure to read about the known limitations of prepared statements at
192
+ [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)
193
+
181
194
  ``` ruby
182
195
  statement = @client.prepare("SELECT * FROM users WHERE login_count = ?")
183
196
  result1 = statement.execute(1)
@@ -190,6 +203,23 @@ statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND locat
190
203
  result = statement.execute(1, "CA", :as => :array)
191
204
  ```
192
205
 
206
+ Session Tracking information can be accessed with
207
+
208
+ ```ruby
209
+ c = Mysql2::Client.new(
210
+ host: "127.0.0.1",
211
+ username: "root",
212
+ flags: "SESSION_TRACK",
213
+ init_command: "SET @@SESSION.session_track_schema=ON"
214
+ )
215
+ c.query("INSERT INTO test VALUES (1)")
216
+ session_track_type = Mysql2::Client::SESSION_TRACK_SCHEMA
217
+ session_track_data = c.session_track(session_track_type)
218
+ ```
219
+
220
+ The types of session track types can be found at
221
+ [https://dev.mysql.com/doc/refman/5.7/en/session-state-tracking.html](https://dev.mysql.com/doc/refman/5.7/en/session-state-tracking.html)
222
+
193
223
  ## Connection options
194
224
 
195
225
  You may set the following connection options in Mysql2::Client.new(...):
@@ -214,6 +244,7 @@ Mysql2::Client.new(
214
244
  :ssl_mode = :disabled / :preferred / :required / :verify_ca / :verify_identity,
215
245
  :default_file = '/path/to/my.cfg',
216
246
  :default_group = 'my.cfg section',
247
+ :default_auth = 'authentication_windows_client'
217
248
  :init_command => sql
218
249
  )
219
250
  ```
@@ -254,7 +285,7 @@ Mysql2::Client.new(
254
285
 
255
286
  ### Secure auth
256
287
 
257
- Starting wih MySQL 5.6.5, secure_auth is enabled by default on servers (it was disabled by default prior to this).
288
+ Starting with MySQL 5.6.5, secure_auth is enabled by default on servers (it was disabled by default prior to this).
258
289
  When secure_auth is enabled, the server will refuse a connection if the account password is stored in old pre-MySQL 4.1 format.
259
290
  The MySQL 5.6.5 client library may also refuse to attempt a connection if provided an older format password.
260
291
  To bypass this restriction in the client, pass the option `:secure_auth => false` to Mysql2::Client.new().
@@ -270,8 +301,10 @@ The string form will be split on whitespace and parsed as with the array form:
270
301
  Plain flags are added to the default flags, while flags prefixed with `-`
271
302
  (minus) are removed from the default flags.
272
303
 
273
- This allows easier use with ActiveRecord's database.yml, avoiding the need for magic flag numbers.
274
- For example, to disable protocol compression, and enable multiple statements and result sets:
304
+ ### Using Active Record's database.yml
305
+
306
+ Active Record typically reads its configuration from a file named `database.yml` or an environment variable `DATABASE_URL`.
307
+ Use the value `mysql2` as the adapter name. For example:
275
308
 
276
309
  ``` yaml
277
310
  development:
@@ -289,6 +322,17 @@ development:
289
322
  secure_auth: false
290
323
  ```
291
324
 
325
+ In this example, the compression flag is negated with `-COMPRESS`.
326
+
327
+ ### Using Active Record's DATABASE_URL
328
+
329
+ Active Record typically reads its configuration from a file named `database.yml` or an environment variable `DATABASE_URL`.
330
+ Use the value `mysql2` as the protocol name. For example:
331
+
332
+ ``` shell
333
+ DATABASE_URL=mysql2://sql_user:sql_pass@sql_host_name:port/sql_db_name?option1=value1&option2=value2
334
+ ```
335
+
292
336
  ### Reading a MySQL config file
293
337
 
294
338
  You may read configuration options from a MySQL configuration file by passing
@@ -343,7 +387,8 @@ end
343
387
  ```
344
388
 
345
389
  Yields:
346
- ```
390
+
391
+ ```ruby
347
392
  {"1"=>1}
348
393
  {"2"=>2}
349
394
  next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
@@ -403,7 +448,7 @@ Pass the `:as => :array` option to any of the above methods of configuration
403
448
 
404
449
  ### Array of Hashes
405
450
 
406
- The default result type is set to :hash, but you can override a previous setting to something else with :as => :hash
451
+ The default result type is set to `:hash`, but you can override a previous setting to something else with `:as => :hash`
407
452
 
408
453
  ### Timezones
409
454
 
@@ -502,7 +547,7 @@ There are a few things that need to be kept in mind while using streaming:
502
547
  * `:cache_rows` is ignored currently. (if you want to use `:cache_rows` you probably don't want to be using `:stream`)
503
548
  * You must fetch all rows in the result set of your query before you can make new queries. (i.e. with `Mysql2::Result#each`)
504
549
 
505
- 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.
550
+ 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).
506
551
 
507
552
  ### Lazy Everything
508
553
 
@@ -523,20 +568,21 @@ As for field values themselves, I'm workin on it - but expect that soon.
523
568
 
524
569
  This gem is tested with the following Ruby versions on Linux and Mac OS X:
525
570
 
526
- * Ruby MRI 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x, 2.5.x, 2.6.x
527
- * Rubinius 2.x and 3.x do work but may fail under some workloads
571
+ * Ruby MRI 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x, 2.5.x, 2.6.x
572
+ * Rubinius 2.x and 3.x do work but may fail under some workloads
528
573
 
529
574
  This gem is tested with the following MySQL and MariaDB versions:
530
575
 
531
- * MySQL 5.5, 5.6, 5.7, 8.0
532
- * MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
533
- * MariaDB 5.5, 10.0, 10.1, 10.2, 10.3
576
+ * MySQL 5.5, 5.6, 5.7, 8.0
577
+ * MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
578
+ * MariaDB 5.5, 10.0, 10.1, 10.2, 10.3
534
579
 
535
580
  ### Ruby on Rails / Active Record
536
581
 
537
- * mysql2 0.4.x works with Rails / Active Record 4.2.5 - 5.0 and higher.
538
- * mysql2 0.3.x works with Rails / Active Record 3.1, 3.2, 4.x, 5.0.
539
- * mysql2 0.2.x works with Rails / Active Record 2.3 - 3.0.
582
+ * mysql2 0.5.x works with Rails / Active Record 4.2.11, 5.0.7, 5.1.6, and higher.
583
+ * mysql2 0.4.x works with Rails / Active Record 4.2.5 - 5.0 and higher.
584
+ * mysql2 0.3.x works with Rails / Active Record 3.1, 3.2, 4.x, 5.0.
585
+ * mysql2 0.2.x works with Rails / Active Record 2.3 - 3.0.
540
586
 
541
587
  ### Asynchronous Active Record
542
588
 
@@ -619,11 +665,12 @@ though.
619
665
  ## Special Thanks
620
666
 
621
667
  * Eric Wong - for the contribution (and the informative explanations) of some thread-safety, non-blocking I/O and cleanup patches. You rock dude
622
- * Yury Korolev (http://github.com/yury) - for TONS of help testing the Active Record adapter
623
- * Aaron Patterson (http://github.com/tenderlove) - tons of contributions, suggestions and general badassness
624
- * Mike Perham (http://github.com/mperham) - Async Active Record adapter (uses Fibers and EventMachine)
625
- * Aaron Stone (http://github.com/sodabrew) - additional client settings, local files, microsecond time, maintenance support
626
- * Kouhei Ueno (https://github.com/nyaxt) - for the original work on Prepared Statements way back in 2012
627
- * John Cant (http://github.com/johncant) - polishing and updating Prepared Statements support
628
- * Justin Case (http://github.com/justincase) - polishing and updating Prepared Statements support and getting it merged
629
- * Tamir Duberstein (http://github.com/tamird) - for help with timeouts and all around updates and cleanups
668
+ * [Yury Korolev](http://github.com/yury) - for TONS of help testing the Active Record adapter
669
+ * [Aaron Patterson](http://github.com/tenderlove) - tons of contributions, suggestions and general badassness
670
+ * [Mike Perham](http://github.com/mperham) - Async Active Record adapter (uses Fibers and EventMachine)
671
+ * [Aaron Stone](http://github.com/sodabrew) - additional client settings, local files, microsecond time, maintenance support
672
+ * [Kouhei Ueno](https://github.com/nyaxt) - for the original work on Prepared Statements way back in 2012
673
+ * [John Cant](http://github.com/johncant) - polishing and updating Prepared Statements support
674
+ * [Justin Case](http://github.com/justincase) - polishing and updating Prepared Statements support and getting it merged
675
+ * [Tamir Duberstein](http://github.com/tamird) - for help with timeouts and all around updates and cleanups
676
+ * [Jun Aruga](http://github.com/junaruga) - for migrating CI tests to GitHub Actions and other improvements
data/ext/mysql2/client.c CHANGED
@@ -18,7 +18,8 @@ VALUE cMysql2Client;
18
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
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;
21
+ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args,
22
+ intern_current_query_options, intern_read_timeout;
22
23
 
23
24
  #define REQUIRE_INITIALIZED(wrapper) \
24
25
  if (!wrapper->initialized) { \
@@ -44,7 +45,7 @@ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args
44
45
  }
45
46
 
46
47
  /*
47
- * compatability with mysql-connector-c, where LIBMYSQL_VERSION is the correct
48
+ * compatibility with mysql-connector-c, where LIBMYSQL_VERSION is the correct
48
49
  * variable to use, but MYSQL_SERVER_VERSION gives the correct numbers when
49
50
  * linking against the server itself
50
51
  */
@@ -57,8 +58,23 @@ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args
57
58
  #endif
58
59
 
59
60
  /*
60
- * compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
61
+ * mariadb-connector-c defines CLIENT_SESSION_TRACKING and SESSION_TRACK_TRANSACTION_TYPE
62
+ * while mysql-connector-c defines CLIENT_SESSION_TRACK and SESSION_TRACK_TRANSACTION_STATE
63
+ * This is a hack to take care of both clients.
61
64
  */
65
+ #if defined(CLIENT_SESSION_TRACK)
66
+ #elif defined(CLIENT_SESSION_TRACKING)
67
+ #define CLIENT_SESSION_TRACK CLIENT_SESSION_TRACKING
68
+ #define SESSION_TRACK_TRANSACTION_STATE SESSION_TRACK_TRANSACTION_TYPE
69
+ #endif
70
+
71
+ /*
72
+ * compatibility with mysql-connector-c 6.1.x, MySQL 5.7.3 - 5.7.10 & with MariaDB 10.x and later.
73
+ */
74
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT
75
+ #define SSL_MODE_VERIFY_IDENTITY 5
76
+ #define HAVE_CONST_SSL_MODE_VERIFY_IDENTITY
77
+ #endif
62
78
  #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
63
79
  #define SSL_MODE_DISABLED 1
64
80
  #define SSL_MODE_REQUIRED 3
@@ -109,18 +125,30 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
109
125
  rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
110
126
  return Qnil;
111
127
  }
112
- #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
128
+ #if defined(HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT) || defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE)
113
129
  GET_CLIENT(self);
114
130
  int val = NUM2INT( setting );
115
- if (version >= 50703 && version < 50711) {
131
+ // Either MySQL 5.7.3 - 5.7.10, or Connector/C 6.1.3 - 6.1.x, or MariaDB 10.x and later
132
+ if ((version >= 50703 && version < 50711) || (version >= 60103 && version < 60200) || version >= 100000) {
133
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT
134
+ if (val == SSL_MODE_VERIFY_IDENTITY) {
135
+ my_bool b = 1;
136
+ int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &b );
137
+ return INT2NUM(result);
138
+ }
139
+ #endif
140
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
116
141
  if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
117
142
  my_bool b = ( val == SSL_MODE_REQUIRED );
118
143
  int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
119
144
  return INT2NUM(result);
120
- } else {
121
- rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
122
- return Qnil;
123
145
  }
146
+ #endif
147
+ rb_warn( "Your mysql client library does not support ssl_mode %d.", val );
148
+ return Qnil;
149
+ } else {
150
+ rb_warn( "Your mysql client library does not support ssl_mode as expected." );
151
+ return Qnil;
124
152
  }
125
153
  #endif
126
154
  #ifdef FULL_SSL_MODE_SUPPORT
@@ -135,6 +163,7 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
135
163
  return INT2NUM(result);
136
164
  #endif
137
165
  #ifdef NO_SSL_MODE_SUPPORT
166
+ rb_warn( "Your mysql client library does not support setting ssl_mode; full support comes with 5.7.11." );
138
167
  return Qnil;
139
168
  #endif
140
169
  }
@@ -170,7 +199,7 @@ static void rb_mysql_client_mark(void * wrapper) {
170
199
 
171
200
  static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
172
201
  VALUE rb_error_msg = rb_str_new2(mysql_error(wrapper->client));
173
- VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
202
+ VALUE rb_sql_state = rb_str_new2(mysql_sqlstate(wrapper->client));
174
203
  VALUE e;
175
204
 
176
205
  rb_enc_associate(rb_error_msg, rb_utf8_encoding());
@@ -264,7 +293,7 @@ static VALUE invalidate_fd(int clientfd)
264
293
  static void *nogvl_close(void *ptr) {
265
294
  mysql_client_wrapper *wrapper = ptr;
266
295
 
267
- if (!wrapper->closed) {
296
+ if (wrapper->initialized && !wrapper->closed) {
268
297
  mysql_close(wrapper->client);
269
298
  wrapper->closed = 1;
270
299
  wrapper->reconnect_enabled = 0;
@@ -318,9 +347,9 @@ static VALUE allocate(VALUE klass) {
318
347
  wrapper->server_version = 0;
319
348
  wrapper->reconnect_enabled = 0;
320
349
  wrapper->connect_timeout = 0;
321
- wrapper->initialized = 0; /* means that that the wrapper is initialized */
350
+ wrapper->initialized = 0; /* will be set true after calling mysql_init */
351
+ wrapper->closed = 1; /* will be set false after calling mysql_real_connect */
322
352
  wrapper->refcount = 1;
323
- wrapper->closed = 0;
324
353
  wrapper->client = (MYSQL*)xmalloc(sizeof(MYSQL));
325
354
 
326
355
  return obj;
@@ -462,6 +491,7 @@ static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VA
462
491
  rb_raise_mysql2_error(wrapper);
463
492
  }
464
493
 
494
+ wrapper->closed = 0;
465
495
  wrapper->server_version = mysql_get_server_version(wrapper->client);
466
496
  return self;
467
497
  }
@@ -508,10 +538,10 @@ static void *nogvl_send_query(void *ptr) {
508
538
  return (void*)(rv == 0 ? Qtrue : Qfalse);
509
539
  }
510
540
 
511
- static VALUE do_send_query(void *args) {
512
- struct nogvl_send_query_args *query_args = args;
541
+ static VALUE do_send_query(VALUE args) {
542
+ struct nogvl_send_query_args *query_args = (void *)args;
513
543
  mysql_client_wrapper *wrapper = query_args->wrapper;
514
- if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, args, RUBY_UBF_IO, 0) == Qfalse) {
544
+ if ((VALUE)rb_thread_call_without_gvl(nogvl_send_query, query_args, RUBY_UBF_IO, 0) == Qfalse) {
515
545
  /* an error occurred, we're not active anymore */
516
546
  wrapper->active_thread = Qnil;
517
547
  rb_raise_mysql2_error(wrapper);
@@ -579,7 +609,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
579
609
  rb_raise_mysql2_error(wrapper);
580
610
  }
581
611
 
582
- is_streaming = rb_hash_aref(rb_iv_get(self, "@current_query_options"), sym_stream);
612
+ is_streaming = rb_hash_aref(rb_ivar_get(self, intern_current_query_options), sym_stream);
583
613
  if (is_streaming == Qtrue) {
584
614
  result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
585
615
  } else {
@@ -596,7 +626,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
596
626
  }
597
627
 
598
628
  // Duplicate the options hash and put the copy in the Result object
599
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
629
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
600
630
  (void)RB_GC_GUARD(current);
601
631
  Check_Type(current, T_HASH);
602
632
  resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
@@ -631,15 +661,15 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
631
661
  rb_exc_raise(error);
632
662
  }
633
663
 
634
- static VALUE do_query(void *args) {
635
- struct async_query_args *async_args = args;
664
+ static VALUE do_query(VALUE args) {
665
+ struct async_query_args *async_args = (void *)args;
636
666
  struct timeval tv;
637
667
  struct timeval *tvp;
638
668
  long int sec;
639
669
  int retval;
640
670
  VALUE read_timeout;
641
671
 
642
- read_timeout = rb_iv_get(async_args->self, "@read_timeout");
672
+ read_timeout = rb_ivar_get(async_args->self, intern_read_timeout);
643
673
 
644
674
  tvp = NULL;
645
675
  if (!NIL_P(read_timeout)) {
@@ -767,7 +797,7 @@ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
767
797
 
768
798
  (void)RB_GC_GUARD(current);
769
799
  Check_Type(current, T_HASH);
770
- rb_iv_set(self, "@current_query_options", current);
800
+ rb_ivar_set(self, intern_current_query_options, current);
771
801
 
772
802
  Check_Type(sql, T_STRING);
773
803
  /* ensure the string is in the encoding the connection is expecting */
@@ -780,6 +810,7 @@ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
780
810
 
781
811
  #ifndef _WIN32
782
812
  rb_rescue2(do_send_query, (VALUE)&args, disconnect_and_raise, self, rb_eException, (VALUE)0);
813
+ (void)RB_GC_GUARD(sql);
783
814
 
784
815
  if (rb_hash_aref(current, sym_async) == Qtrue) {
785
816
  return Qnil;
@@ -792,7 +823,8 @@ static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
792
823
  return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
793
824
  }
794
825
  #else
795
- do_send_query(&args);
826
+ do_send_query((VALUE)&args);
827
+ (void)RB_GC_GUARD(sql);
796
828
 
797
829
  /* this will just block until the result is ready */
798
830
  return rb_ensure(rb_mysql_client_async_result, self, disconnect_and_mark_inactive, self);
@@ -903,6 +935,13 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
903
935
  retval = charval;
904
936
  break;
905
937
 
938
+ #ifdef HAVE_MYSQL_DEFAULT_AUTH
939
+ case MYSQL_DEFAULT_AUTH:
940
+ charval = (const char *)StringValueCStr(value);
941
+ retval = charval;
942
+ break;
943
+ #endif
944
+
906
945
  #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
907
946
  case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
908
947
  boolval = (value == Qfalse ? 0 : 1);
@@ -1011,6 +1050,36 @@ static VALUE rb_mysql_client_last_id(VALUE self) {
1011
1050
  return ULL2NUM(mysql_insert_id(wrapper->client));
1012
1051
  }
1013
1052
 
1053
+ /* call-seq:
1054
+ * client.session_track
1055
+ *
1056
+ * Returns information about changes to the session state on the server.
1057
+ */
1058
+ static VALUE rb_mysql_client_session_track(VALUE self, VALUE type) {
1059
+ #ifdef CLIENT_SESSION_TRACK
1060
+ const char *data;
1061
+ size_t length;
1062
+ my_ulonglong retVal;
1063
+ GET_CLIENT(self);
1064
+
1065
+ REQUIRE_CONNECTED(wrapper);
1066
+ retVal = mysql_session_track_get_first(wrapper->client, NUM2INT(type), &data, &length);
1067
+ if (retVal != 0) {
1068
+ return Qnil;
1069
+ }
1070
+ VALUE rbAry = rb_ary_new();
1071
+ VALUE rbFirst = rb_str_new(data, length);
1072
+ rb_ary_push(rbAry, rbFirst);
1073
+ while(mysql_session_track_get_next(wrapper->client, NUM2INT(type), &data, &length) == 0) {
1074
+ VALUE rbNext = rb_str_new(data, length);
1075
+ rb_ary_push(rbAry, rbNext);
1076
+ }
1077
+ return rbAry;
1078
+ #else
1079
+ return Qnil;
1080
+ #endif
1081
+ }
1082
+
1014
1083
  /* call-seq:
1015
1084
  * client.affected_rows
1016
1085
  *
@@ -1174,7 +1243,7 @@ static VALUE rb_mysql_client_store_result(VALUE self)
1174
1243
  }
1175
1244
 
1176
1245
  // Duplicate the options hash and put the copy in the Result object
1177
- current = rb_hash_dup(rb_iv_get(self, "@current_query_options"));
1246
+ current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
1178
1247
  (void)RB_GC_GUARD(current);
1179
1248
  Check_Type(current, T_HASH);
1180
1249
  resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
@@ -1260,7 +1329,7 @@ static VALUE set_read_timeout(VALUE self, VALUE value) {
1260
1329
  /* Set the instance variable here even though _mysql_client_options
1261
1330
  might not succeed, because the timeout is used in other ways
1262
1331
  elsewhere */
1263
- rb_iv_set(self, "@read_timeout", value);
1332
+ rb_ivar_set(self, intern_read_timeout, value);
1264
1333
  return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
1265
1334
  }
1266
1335
 
@@ -1336,6 +1405,14 @@ static VALUE set_init_command(VALUE self, VALUE value) {
1336
1405
  return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
1337
1406
  }
1338
1407
 
1408
+ static VALUE set_default_auth(VALUE self, VALUE value) {
1409
+ #ifdef HAVE_MYSQL_DEFAULT_AUTH
1410
+ return _mysql_client_options(self, MYSQL_DEFAULT_AUTH, value);
1411
+ #else
1412
+ rb_raise(cMysql2Error, "pluggable authentication is not available, you may need a newer MySQL client library");
1413
+ #endif
1414
+ }
1415
+
1339
1416
  static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
1340
1417
  #ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
1341
1418
  return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
@@ -1397,6 +1474,7 @@ void init_mysql2_client() {
1397
1474
  mMysql2 = rb_define_module("Mysql2"); Teach RDoc about Mysql2 constant.
1398
1475
  #endif
1399
1476
  cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
1477
+ rb_global_variable(&cMysql2Client);
1400
1478
 
1401
1479
  rb_define_alloc_func(cMysql2Client, allocate);
1402
1480
 
@@ -1427,6 +1505,7 @@ void init_mysql2_client() {
1427
1505
  rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
1428
1506
  rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
1429
1507
  rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1508
+ rb_define_method(cMysql2Client, "session_track", rb_mysql_client_session_track, 1);
1430
1509
 
1431
1510
  rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
1432
1511
  rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
@@ -1437,6 +1516,7 @@ void init_mysql2_client() {
1437
1516
  rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
1438
1517
  rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
1439
1518
  rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
1519
+ rb_define_private_method(cMysql2Client, "default_auth=", set_default_auth, 1);
1440
1520
  rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
1441
1521
  rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
1442
1522
  rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
@@ -1461,6 +1541,8 @@ void init_mysql2_client() {
1461
1541
  intern_merge = rb_intern("merge");
1462
1542
  intern_merge_bang = rb_intern("merge!");
1463
1543
  intern_new_with_args = rb_intern("new_with_args");
1544
+ intern_current_query_options = rb_intern("@current_query_options");
1545
+ intern_read_timeout = rb_intern("@read_timeout");
1464
1546
 
1465
1547
  #ifdef CLIENT_LONG_PASSWORD
1466
1548
  rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
@@ -1596,16 +1678,32 @@ void init_mysql2_client() {
1596
1678
  INT2NUM(0));
1597
1679
  #endif
1598
1680
 
1681
+ #ifdef CLIENT_SESSION_TRACK
1682
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK"), INT2NUM(CLIENT_SESSION_TRACK));
1683
+ /* From mysql_com.h -- but stable from at least 5.7.4 through 8.0.20 */
1684
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SYSTEM_VARIABLES"), INT2NUM(SESSION_TRACK_SYSTEM_VARIABLES));
1685
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SCHEMA"), INT2NUM(SESSION_TRACK_SCHEMA));
1686
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_STATE_CHANGE"), INT2NUM(SESSION_TRACK_STATE_CHANGE));
1687
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_GTIDS"), INT2NUM(SESSION_TRACK_GTIDS));
1688
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_CHARACTERISTICS"), INT2NUM(SESSION_TRACK_TRANSACTION_CHARACTERISTICS));
1689
+ rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_STATE"), INT2NUM(SESSION_TRACK_TRANSACTION_STATE));
1690
+ #endif
1691
+
1599
1692
  #if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
1600
1693
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1601
1694
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
1602
1695
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1603
1696
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_CA"), INT2NUM(SSL_MODE_VERIFY_CA));
1604
1697
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1605
- #elif defined(HAVE_CONST_MYSQL_OPT_SSL_ENFORCE) // MySQL 5.7.3 - 5.7.10
1698
+ #else
1699
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_VERIFY_SERVER_CERT // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
1700
+ rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(SSL_MODE_VERIFY_IDENTITY));
1701
+ #endif
1702
+ #ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE // MySQL 5.7.3 - 5.7.10 & MariaDB 10.x and later
1606
1703
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
1607
1704
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_REQUIRED"), INT2NUM(SSL_MODE_REQUIRED));
1608
1705
  #endif
1706
+ #endif
1609
1707
 
1610
1708
  #ifndef HAVE_CONST_SSL_MODE_DISABLED
1611
1709
  rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(0));