mysql2 0.2.7 → 0.2.8

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.8 (June 14th, 2011)
4
+ * disable async support, and access to the underlying file descriptor under Windows. It's never worked reliably and ruby-core has a lot of work to do in order to make it possible.
5
+ * added support for turning eager-casting off. This is especially useful in ORMs that will lazily cast values upon access.
6
+ * added a warning if a 0.2.x release is being used with ActiveRecord 3.1 since both the 0.2.x releases and AR 3.1 have mysql2 adapters, we want you to use the one in AR 3.1
7
+ * added Mysql2::Client.escape (class-level method)
8
+ * disabled eager-casting in the bundled ActiveRecord adapter (for Rails 3.0 or less)
9
+
3
10
  ## 0.2.7 (March 28th, 2011)
4
11
  * various fixes for em_mysql2 and fiber usage
5
12
  * use our own Mysql2IndexDefinition class for better compatibility across ActiveRecord versions
data/README.md ADDED
@@ -0,0 +1,324 @@
1
+ # Mysql2 - A modern, simple and very fast Mysql library for Ruby - binding to libmysql
2
+
3
+ The Mysql2 gem is meant to serve the extremely common use-case of connecting, querying and iterating on results.
4
+ Some database libraries out there serve as direct 1:1 mappings of the already complex C API's available.
5
+ This one is not.
6
+
7
+ 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.
8
+
9
+ The API consists of two clases:
10
+
11
+ Mysql2::Client - your connection to the database
12
+
13
+ Mysql2::Result - returned from issuing a #query on the connection. It includes Enumerable.
14
+
15
+ ## Installing
16
+
17
+ ``` sh
18
+ gem install mysql2
19
+ ```
20
+
21
+ You may have to specify --with-mysql-config=/some/random/path/bin/mysql_config
22
+
23
+ ## Usage
24
+
25
+ Connect to a database:
26
+
27
+ ``` ruby
28
+ # this takes a hash of options, almost all of which map directly
29
+ # to the familiar database.yml in rails
30
+ # See http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html
31
+ client = Mysql2::Client.new(:host => "localhost", :username => "root")
32
+ ```
33
+
34
+ Then query it:
35
+
36
+ ``` ruby
37
+ results = client.query("SELECT * FROM users WHERE group='githubbers'")
38
+ ```
39
+
40
+ Need to escape something first?
41
+
42
+ ``` ruby
43
+ escaped = client.escape("gi'thu\"bbe\0r's")
44
+ results = client.query("SELECT * FROM users WHERE group='#{escaped}'")
45
+ ```
46
+
47
+ Finally, iterate over the results:
48
+
49
+ ``` ruby
50
+ results.each do |row|
51
+ # conveniently, row is a hash
52
+ # the keys are the fields, as you'd expect
53
+ # the values are pre-built ruby primitives mapped from their corresponding field types in MySQL
54
+ # Here's an otter: http://farm1.static.flickr.com/130/398077070_b8795d0ef3_b.jpg
55
+ end
56
+ ```
57
+
58
+ Or, you might just keep it simple:
59
+
60
+ ``` ruby
61
+ client.query("SELECT * FROM users WHERE group='githubbers'").each do |row|
62
+ # do something with row, it's ready to rock
63
+ end
64
+ ```
65
+
66
+ How about with symbolized keys?
67
+
68
+ ``` ruby
69
+ # NOTE: the :symbolize_keys and future options will likely move to the #query method soon
70
+ client.query("SELECT * FROM users WHERE group='githubbers'").each(:symbolize_keys => true) do |row|
71
+ # do something with row, it's ready to rock
72
+ end
73
+ ```
74
+
75
+ You can get the headers and the columns in the order that they were returned
76
+ by the query like this:
77
+
78
+ ``` ruby
79
+ headers = results.fields # <= that's an array of field names, in order
80
+ results.each(:as => :array) do |row|
81
+ # Each row is an array, ordered the same as the query results
82
+ # An otter's den is called a "holt" or "couch"
83
+ end
84
+ ```
85
+
86
+ ## Cascading config
87
+
88
+ The default config hash is at:
89
+
90
+ ``` ruby
91
+ Mysql2::Client.default_query_options
92
+ ```
93
+
94
+ which defaults to:
95
+
96
+ ``` ruby
97
+ {:async => false, :as => :hash, :symbolize_keys => false}
98
+ ```
99
+
100
+ that can be used as so:
101
+
102
+ ``` ruby
103
+ # these are the defaults all Mysql2::Client instances inherit
104
+ Mysql2::Client.default_query_options.merge!(:as => :array)
105
+ ```
106
+
107
+ or
108
+
109
+ ``` ruby
110
+ # this will change the defaults for all future results returned by the #query method _for this connection only_
111
+ c = Mysql2::Client.new
112
+ c.query_options.merge!(:symbolize_keys => true)
113
+ ```
114
+
115
+ or
116
+
117
+ ``` ruby
118
+ # this will set the options for the Mysql2::Result instance returned from the #query method
119
+ c = Mysql2::Client.new
120
+ c.query(sql, :symbolize_keys => true)
121
+ ```
122
+
123
+ ## Result types
124
+
125
+ ### Array of Arrays
126
+
127
+ Pass the `:as => :array` option to any of the above methods of configuration
128
+
129
+ ### Array of Hashes
130
+
131
+ The default result type is set to :hash, but you can override a previous setting to something else with :as => :hash
132
+
133
+ ### Others...
134
+
135
+ I may add support for `:as => :csv` or even `:as => :json` to allow for *much* more efficient generation of those data types from result sets.
136
+ If you'd like to see either of these (or others), open an issue and start bugging me about it ;)
137
+
138
+ ### Timezones
139
+
140
+ Mysql2 now supports two timezone options:
141
+
142
+ ``` ruby
143
+ :database_timezone # this is the timezone Mysql2 will assume fields are already stored as, and will use this when creating the initial Time objects in ruby
144
+ :application_timezone # this is the timezone Mysql2 will convert to before finally handing back to the caller
145
+ ```
146
+
147
+ In other words, if `:database_timezone` is set to `:utc` - Mysql2 will create the Time objects using `Time.utc(...)` from the raw value libmysql hands over initially.
148
+ Then, if `:application_timezone` is set to say - `:local` - Mysql2 will then convert the just-created UTC Time object to local time.
149
+
150
+ Both options only allow two values - `:local` or `:utc` - with the exception that `:application_timezone` can be [and defaults to] nil
151
+
152
+ ### Casting "boolean" columns
153
+
154
+ You can now tell Mysql2 to cast `tinyint(1)` fields to boolean values in Ruby with the `:cast_booleans` option.
155
+
156
+ ``` ruby
157
+ client = Mysql2::Client.new
158
+ result = client.query("SELECT * FROM table_with_boolean_field", :cast_booleans => true)
159
+ ```
160
+
161
+ ### Skipping casting
162
+
163
+ Mysql2 casting is fast, but not as fast as not casting data. In rare cases where typecasting is not needed, it will be faster to disable it by providing :cast => false.
164
+
165
+ ``` ruby
166
+ client = Mysql2::Client.new
167
+ result = client.query("SELECT * FROM table", :cast => false)
168
+ ```
169
+
170
+ Here are the results from the `query_without_mysql_casting.rb` script in the benchmarks folder:
171
+
172
+ ``` sh
173
+ user system total real
174
+ Mysql2 (cast: true) 0.340000 0.000000 0.340000 ( 0.405018)
175
+ Mysql2 (cast: false) 0.160000 0.010000 0.170000 ( 0.209937)
176
+ Mysql 0.080000 0.000000 0.080000 ( 0.129355)
177
+ do_mysql 0.520000 0.010000 0.530000 ( 0.574619)
178
+ ```
179
+
180
+ Although Mysql2 performs reasonably well at retrieving uncasted data, it (currently) is not as fast as the Mysql gem. In spite of this small disadvantage, Mysql2 still sports a friendlier interface and doesn't block the entire ruby process when querying.
181
+
182
+ ### Async
183
+
184
+ NOTE: Not supported on Windows.
185
+
186
+ `Mysql2::Client` takes advantage of the MySQL C API's (undocumented) non-blocking function mysql_send_query for *all* queries.
187
+ But, in order to take full advantage of it in your Ruby code, you can do:
188
+
189
+ ``` ruby
190
+ client.query("SELECT sleep(5)", :async => true)
191
+ ```
192
+
193
+ Which will return nil immediately. At this point you'll probably want to use some socket monitoring mechanism
194
+ like EventMachine or even IO.select. Once the socket becomes readable, you can do:
195
+
196
+ ``` ruby
197
+ # result will be a Mysql2::Result instance
198
+ result = client.async_result
199
+ ```
200
+
201
+ NOTE: Because of the way MySQL's query API works, this method will block until the result is ready.
202
+ So if you really need things to stay async, it's best to just monitor the socket with something like EventMachine.
203
+ If you need multiple query concurrency take a look at using a connection pool.
204
+
205
+ ### Row Caching
206
+
207
+ By default, Mysql2 will cache rows that have been created in Ruby (since this happens lazily).
208
+ This is especially helpful since it saves the cost of creating the row in Ruby if you were to iterate over the collection again.
209
+
210
+ If you only plan on using each row once, then it's much more efficient to disable this behavior by setting the `:cache_rows` option to false.
211
+ This would be helpful if you wanted to iterate over the results in a streaming manner. Meaning the GC would cleanup rows you don't need anymore as you're iterating over the result set.
212
+
213
+ ## ActiveRecord
214
+
215
+ To use the ActiveRecord driver (with our without rails), all you should need to do is have this gem installed and set the adapter in your database.yml to "mysql2".
216
+ That was easy right? :)
217
+
218
+ NOTE: as of 0.3.0, and ActiveRecord 3.1 - the ActiveRecord adapter has been pulled out of this gem and into ActiveRecord itself. If you need to use mysql2 with
219
+ Rails versions < 3.1 make sure and specify `gem "mysql2", "~> 0.2.7"` in your Gemfile
220
+
221
+ ## Asynchronous ActiveRecord
222
+
223
+ You can also use Mysql2 with asynchronous Rails (first introduced at http://www.mikeperham.com/2010/04/03/introducing-phat-an-asynchronous-rails-app/) by
224
+ setting the adapter in your database.yml to "em_mysql2". You must be running Ruby 1.9, thin and the rack-fiber_pool middleware for it to work.
225
+
226
+ ## Sequel
227
+
228
+ The Sequel adapter was pulled out into Sequel core (will be part of the next release) and can be used by specifying the "mysql2://" prefix to your connection specification.
229
+
230
+ ## EventMachine
231
+
232
+ The mysql2 EventMachine deferrable api allows you to make async queries using EventMachine,
233
+ while specifying callbacks for success for failure. Here's a simple example:
234
+
235
+ ``` ruby
236
+ require 'mysql2/em'
237
+
238
+ EM.run do
239
+ client1 = Mysql2::EM::Client.new
240
+ defer1 = client1.query "SELECT sleep(3) as first_query"
241
+ defer1.callback do |result|
242
+ puts "Result: #{result.to_a.inspect}"
243
+ end
244
+
245
+ client2 = Mysql2::EM::Client.new
246
+ defer2 = client2.query "SELECT sleep(1) second_query"
247
+ defer2.callback do |result|
248
+ puts "Result: #{result.to_a.inspect}"
249
+ end
250
+ end
251
+ ```
252
+
253
+ ## Lazy Everything
254
+
255
+ Well... almost ;)
256
+
257
+ Field name strings/symbols are shared across all the rows so only one object is ever created to represent the field name for an entire dataset.
258
+
259
+ Rows themselves are lazily created in ruby-land when an attempt to yield it is made via #each.
260
+ For example, if you were to yield 4 rows from a 100 row dataset, only 4 hashes will be created. The rest will sit and wait in C-land until you want them (or when the GC goes to cleanup your `Mysql2::Result` instance).
261
+ Now say you were to iterate over that same collection again, this time yielding 15 rows - the 4 previous rows that had already been turned into ruby hashes would be pulled from an internal cache, then 11 more would be created and stored in that cache.
262
+ Once the entire dataset has been converted into ruby objects, Mysql2::Result will free the Mysql C result object as it's no longer needed.
263
+
264
+ This caching behavior can be disabled by setting the :cache_rows option to false.
265
+
266
+ As for field values themselves, I'm workin on it - but expect that soon.
267
+
268
+ ## Compatibility
269
+
270
+ The specs pass on my system (SL 10.6.3, x86_64) in these rubies:
271
+
272
+ * 1.8.7-p249
273
+ * ree-1.8.7-2010.01
274
+ * 1.9.1-p378
275
+ * ruby-trunk
276
+ * rbx-head - broken at the moment, working with the rbx team for a solution
277
+
278
+ The ActiveRecord driver should work on 2.3.5 and 3.0
279
+
280
+ ## Yeah... but why?
281
+
282
+ Someone: Dude, the Mysql gem works fiiiiiine.
283
+
284
+ Me: It sure does, but it only hands you nil and strings for field values. Leaving you to convert
285
+ them into proper Ruby types in Ruby-land - which is slow as balls.
286
+
287
+
288
+ Someone: OK fine, but do_mysql can already give me back values with Ruby objects mapped to MySQL types.
289
+
290
+ Me: Yep, but it's API is considerably more complex *and* can be ~2x slower.
291
+
292
+ ## Benchmarks
293
+
294
+ Performing a basic "SELECT * FROM" query on a table with 30k rows and fields of nearly every Ruby-representable data type,
295
+ then iterating over every row using an #each like method yielding a block:
296
+
297
+ These results are from the `query_with_mysql_casting.rb` script in the benchmarks folder
298
+
299
+ ``` sh
300
+ user system total real
301
+ Mysql2
302
+ 0.750000 0.180000 0.930000 ( 1.821655)
303
+ do_mysql
304
+ 1.650000 0.200000 1.850000 ( 2.811357)
305
+ Mysql
306
+ 7.500000 0.210000 7.710000 ( 8.065871)
307
+ ```
308
+
309
+ ## Development
310
+
311
+ To run the tests, you can use RVM and Bundler to create a pristine environment for mysql2 development/hacking.
312
+ Use 'bundle install' to install the necessary development and testing gems:
313
+
314
+ ``` sh
315
+ bundle install
316
+ rake
317
+ ```
318
+
319
+ ## Special Thanks
320
+
321
+ * Eric Wong - for the contribution (and the informative explanations) of some thread-safety, non-blocking I/O and cleanup patches. You rock dude
322
+ * Yury Korolev (http://github.com/yury) - for TONS of help testing the ActiveRecord adapter
323
+ * Aaron Patterson (http://github.com/tenderlove) - tons of contributions, suggestions and general badassness
324
+ * Mike Perham (http://github.com/mperham) - Async ActiveRecord adapter (uses Fibers and EventMachine)
@@ -0,0 +1,42 @@
1
+ # encoding: UTF-8
2
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
3
+
4
+ require 'rubygems'
5
+ require 'benchmark'
6
+ require 'active_record'
7
+
8
+ times = 25
9
+
10
+
11
+ # mysql2
12
+ mysql2_opts = {
13
+ :adapter => 'mysql2',
14
+ :database => 'test',
15
+ :pool => times
16
+ }
17
+ ActiveRecord::Base.establish_connection(mysql2_opts)
18
+ x = Benchmark.realtime do
19
+ threads = []
20
+ times.times do
21
+ threads << Thread.new { ActiveRecord::Base.connection.execute("select sleep(1)") }
22
+ end
23
+ threads.each {|t| t.join }
24
+ end
25
+ puts "mysql2: #{x} seconds"
26
+
27
+
28
+ # mysql
29
+ mysql2_opts = {
30
+ :adapter => 'mysql',
31
+ :database => 'test',
32
+ :pool => times
33
+ }
34
+ ActiveRecord::Base.establish_connection(mysql2_opts)
35
+ x = Benchmark.realtime do
36
+ threads = []
37
+ times.times do
38
+ threads << Thread.new { ActiveRecord::Base.connection.execute("select sleep(1)") }
39
+ end
40
+ threads.each {|t| t.join }
41
+ end
42
+ puts "mysql: #{x} seconds"
@@ -14,9 +14,18 @@ sql = "SELECT * FROM mysql2_test LIMIT 100"
14
14
  Benchmark.bmbm do |x|
15
15
  mysql2 = Mysql2::Client.new(:host => "localhost", :username => "root")
16
16
  mysql2.query "USE #{database}"
17
- x.report "Mysql2" do
17
+ x.report "Mysql2 (cast: true)" do
18
18
  number_of.times do
19
- mysql2_result = mysql2.query sql, :symbolize_keys => true
19
+ mysql2_result = mysql2.query sql, :symbolize_keys => true, :cast => true
20
+ mysql2_result.each do |res|
21
+ # puts res.inspect
22
+ end
23
+ end
24
+ end
25
+
26
+ x.report "Mysql2 (cast: false)" do
27
+ number_of.times do
28
+ mysql2_result = mysql2.query sql, :symbolize_keys => true, :cast => false
20
29
  mysql2_result.each do |res|
21
30
  # puts res.inspect
22
31
  end
@@ -0,0 +1,44 @@
1
+ # encoding: UTF-8
2
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
3
+
4
+ require 'rubygems'
5
+ require 'benchmark'
6
+ require 'active_record'
7
+
8
+ mysql2_opts = {
9
+ :adapter => 'mysql2',
10
+ :database => 'test',
11
+ :pool => 25
12
+ }
13
+ ActiveRecord::Base.establish_connection(mysql2_opts)
14
+ x = Benchmark.realtime do
15
+ threads = []
16
+ 25.times do
17
+ threads << Thread.new { ActiveRecord::Base.connection.execute("select sleep(1)") }
18
+ end
19
+ threads.each {|t| t.join }
20
+ end
21
+ puts x
22
+
23
+ mysql2_opts = {
24
+ :adapter => 'mysql',
25
+ :database => 'test',
26
+ :pool => 25
27
+ }
28
+ ActiveRecord::Base.establish_connection(mysql2_opts)
29
+ x = Benchmark.realtime do
30
+ threads = []
31
+ 25.times do
32
+ threads << Thread.new { ActiveRecord::Base.connection.execute("select sleep(1)") }
33
+ end
34
+ threads.each {|t| t.join }
35
+ end
36
+ puts x
37
+
38
+ # these results are similar on 1.8.7, 1.9.2 and rbx-head
39
+ #
40
+ # $ bundle exec ruby benchmarks/threaded.rb
41
+ # 1.0774750709533691
42
+ #
43
+ # and using the mysql gem
44
+ # 25.099437952041626
data/ext/mysql2/client.c CHANGED
@@ -139,10 +139,6 @@ static VALUE nogvl_close(void *ptr) {
139
139
  flags = fcntl(wrapper->client->net.fd, F_GETFL);
140
140
  if (flags > 0 && !(flags & O_NONBLOCK))
141
141
  fcntl(wrapper->client->net.fd, F_SETFL, flags | O_NONBLOCK);
142
- #else
143
- u_long iMode;
144
- iMode = 1;
145
- ioctlsocket(wrapper->client->net.fd, FIONBIO, &iMode);
146
142
  #endif
147
143
 
148
144
  mysql_close(wrapper->client);
@@ -171,6 +167,31 @@ static VALUE allocate(VALUE klass) {
171
167
  return obj;
172
168
  }
173
169
 
170
+ static VALUE rb_mysql_client_escape(VALUE klass, VALUE str) {
171
+ unsigned char *newStr;
172
+ VALUE rb_str;
173
+ unsigned long newLen, oldLen;
174
+
175
+ Check_Type(str, T_STRING);
176
+
177
+ oldLen = RSTRING_LEN(str);
178
+ newStr = xmalloc(oldLen*2+1);
179
+
180
+ newLen = mysql_escape_string((char *)newStr, StringValuePtr(str), oldLen);
181
+ if (newLen == oldLen) {
182
+ // no need to return a new ruby string if nothing changed
183
+ xfree(newStr);
184
+ return str;
185
+ } else {
186
+ rb_str = rb_str_new((const char*)newStr, newLen);
187
+ #ifdef HAVE_RUBY_ENCODING_H
188
+ rb_enc_copy(rb_str, str);
189
+ #endif
190
+ xfree(newStr);
191
+ return rb_str;
192
+ }
193
+ }
194
+
174
195
  static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags) {
175
196
  struct nogvl_connect_args args;
176
197
  GET_CLIENT(self);
@@ -331,6 +352,7 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
331
352
  return rb_raise_mysql2_error(wrapper);
332
353
  }
333
354
 
355
+ #ifndef _WIN32
334
356
  read_timeout = rb_iv_get(self, "@read_timeout");
335
357
 
336
358
  tvp = NULL;
@@ -355,27 +377,11 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
355
377
  for(;;) {
356
378
  int fd_set_fd = fd;
357
379
 
358
- #ifdef _WIN32
359
- WSAPROTOCOL_INFO wsa_pi;
360
- // dupicate the SOCKET from libmysql
361
- int r = WSADuplicateSocket(fd, GetCurrentProcessId(), &wsa_pi);
362
- SOCKET s = WSASocket(wsa_pi.iAddressFamily, wsa_pi.iSocketType, wsa_pi.iProtocol, &wsa_pi, 0, 0);
363
- // create the CRT fd so ruby can get back to the SOCKET
364
- fd_set_fd = _open_osfhandle(s, O_RDWR|O_BINARY);
365
- #endif
366
-
367
380
  FD_ZERO(&fdset);
368
381
  FD_SET(fd_set_fd, &fdset);
369
382
 
370
383
  retval = rb_thread_select(fd_set_fd + 1, &fdset, NULL, NULL, tvp);
371
384
 
372
- #ifdef _WIN32
373
- // cleanup the CRT fd
374
- _close(fd_set_fd);
375
- // cleanup the duplicated SOCKET
376
- closesocket(s);
377
- #endif
378
-
379
385
  if (retval == 0) {
380
386
  rb_raise(cMysql2Error, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
381
387
  }
@@ -395,9 +401,13 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
395
401
  } else {
396
402
  return Qnil;
397
403
  }
404
+ #else
405
+ // this will just block until the result is ready
406
+ return rb_mysql_client_async_result(self);
407
+ #endif
398
408
  }
399
409
 
400
- static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
410
+ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
401
411
  unsigned char *newStr;
402
412
  VALUE rb_str;
403
413
  unsigned long newLen, oldLen;
@@ -492,8 +502,13 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
492
502
 
493
503
  static VALUE rb_mysql_client_socket(VALUE self) {
494
504
  GET_CLIENT(self);
505
+ #ifndef _WIN32
495
506
  REQUIRE_OPEN_DB(wrapper);
496
- return INT2NUM(wrapper->client->net.fd);
507
+ int fd_set_fd = wrapper->client->net.fd;
508
+ return INT2NUM(fd_set_fd);
509
+ #else
510
+ rb_raise(cMysql2Error, "Raw access to the mysql file descriptor isn't supported on Windows");
511
+ #endif
497
512
  }
498
513
 
499
514
  static VALUE rb_mysql_client_last_id(VALUE self) {
@@ -523,8 +538,7 @@ static VALUE rb_mysql_client_thread_id(VALUE self) {
523
538
  return ULL2NUM(retVal);
524
539
  }
525
540
 
526
- static VALUE nogvl_ping(void *ptr)
527
- {
541
+ static VALUE nogvl_ping(void *ptr) {
528
542
  MYSQL *client = ptr;
529
543
 
530
544
  return mysql_ping(client) == 0 ? Qtrue : Qfalse;
@@ -533,7 +547,11 @@ static VALUE nogvl_ping(void *ptr)
533
547
  static VALUE rb_mysql_client_ping(VALUE self) {
534
548
  GET_CLIENT(self);
535
549
 
536
- return rb_thread_blocking_region(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
550
+ if (wrapper->closed) {
551
+ return Qfalse;
552
+ } else {
553
+ return rb_thread_blocking_region(nogvl_ping, wrapper->client, RUBY_UBF_IO, 0);
554
+ }
537
555
  }
538
556
 
539
557
  #ifdef HAVE_RUBY_ENCODING_H
@@ -654,9 +672,11 @@ void init_mysql2_client() {
654
672
 
655
673
  rb_define_alloc_func(cMysql2Client, allocate);
656
674
 
675
+ rb_define_singleton_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
676
+
657
677
  rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
658
678
  rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
659
- rb_define_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
679
+ rb_define_method(cMysql2Client, "escape", rb_mysql_client_real_escape, 1);
660
680
  rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
661
681
  rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
662
682
  rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);