mysql2 0.2.10 → 0.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --format documentation
2
2
  --colour
3
+ --fail-fast
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.11 (June 17th, 2011)
4
+ * fix bug in Time/DateTime range detection
5
+ * (win32) fix bug where the Mysql2::Client object wasn't cleaned up properly if interrupted during a query
6
+ * add Mysql2::Result#count (aliased as size) to get the row count for the dataset
7
+ this can be especially helpful if you want to get the number of rows without having to inflate
8
+ the entire dataset into ruby (since this happens lazily)
9
+
3
10
  ## 0.2.10 (June 15th, 2011)
4
11
  * bug fix for Time/DateTime usage depending on 32/64bit Ruby
5
12
 
data/README.md CHANGED
@@ -44,6 +44,8 @@ escaped = client.escape("gi'thu\"bbe\0r's")
44
44
  results = client.query("SELECT * FROM users WHERE group='#{escaped}'")
45
45
  ```
46
46
 
47
+ You can get a count of your results with `results.count`.
48
+
47
49
  Finally, iterate over the results:
48
50
 
49
51
  ``` ruby
@@ -321,4 +323,4 @@ rake
321
323
  * Eric Wong - for the contribution (and the informative explanations) of some thread-safety, non-blocking I/O and cleanup patches. You rock dude
322
324
  * Yury Korolev (http://github.com/yury) - for TONS of help testing the ActiveRecord adapter
323
325
  * 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)
326
+ * Mike Perham (http://github.com/mperham) - Async ActiveRecord adapter (uses Fibers and EventMachine)
@@ -171,7 +171,7 @@ static VALUE allocate(VALUE klass) {
171
171
  return obj;
172
172
  }
173
173
 
174
- static VALUE rb_mysql_client_escape(VALUE klass, VALUE str) {
174
+ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
175
175
  unsigned char *newStr;
176
176
  VALUE rb_str;
177
177
  unsigned long newLen, oldLen;
@@ -263,8 +263,17 @@ static VALUE nogvl_read_query_result(void *ptr) {
263
263
 
264
264
  /* mysql_store_result may (unlikely) read rows off the socket */
265
265
  static VALUE nogvl_store_result(void *ptr) {
266
- MYSQL * client = ptr;
267
- return (VALUE)mysql_store_result(client);
266
+ mysql_client_wrapper *wrapper;
267
+ MYSQL_RES *result;
268
+
269
+ wrapper = (mysql_client_wrapper *)ptr;
270
+ result = mysql_store_result(wrapper->client);
271
+
272
+ // once our result is stored off, this connection is
273
+ // ready for another command to be issued
274
+ wrapper->active = 0;
275
+
276
+ return (VALUE)result;
268
277
  }
269
278
 
270
279
  static VALUE rb_mysql_client_async_result(VALUE self) {
@@ -286,10 +295,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
286
295
  return rb_raise_mysql2_error(wrapper);
287
296
  }
288
297
 
289
- result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper->client, RUBY_UBF_IO, 0);
290
-
291
- // we have our result, mark this connection inactive
292
- MARK_CONN_INACTIVE(self);
298
+ result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
293
299
 
294
300
  if (result == NULL) {
295
301
  if (mysql_field_count(wrapper->client) != 0) {
@@ -329,7 +335,6 @@ static VALUE disconnect_and_raise(VALUE self, VALUE error) {
329
335
 
330
336
  return Qnil;
331
337
  }
332
- #endif
333
338
 
334
339
  static VALUE do_query(void *args) {
335
340
  struct async_query_args *async_args;
@@ -383,9 +388,33 @@ static VALUE do_query(void *args) {
383
388
 
384
389
  return Qnil;
385
390
  }
391
+ #else
392
+ static VALUE finish_and_mark_inactive(void *args) {
393
+ VALUE self;
394
+ MYSQL_RES *result;
395
+
396
+ self = (VALUE)args;
397
+
398
+ GET_CLIENT(self);
399
+
400
+ if (wrapper->active) {
401
+ // if we got here, the result hasn't been read off the wire yet
402
+ // so lets do that and then throw it away because we have no way
403
+ // of getting it back up to the caller from here
404
+ result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, wrapper, RUBY_UBF_IO, 0);
405
+ mysql_free_result(result);
406
+
407
+ wrapper->active = 0;
408
+ }
409
+
410
+ return Qnil;
411
+ }
412
+ #endif
386
413
 
387
414
  static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
415
+ #ifndef _WIN32
388
416
  struct async_query_args async_args;
417
+ #endif
389
418
  struct nogvl_send_query_args args;
390
419
  int async = 0;
391
420
  VALUE opts, defaults;
@@ -443,7 +472,7 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
443
472
  }
444
473
  #else
445
474
  // this will just block until the result is ready
446
- return rb_mysql_client_async_result(self);
475
+ return rb_ensure(rb_mysql_client_async_result, self, finish_and_mark_inactive, self);
447
476
  #endif
448
477
  }
449
478
 
@@ -1,30 +1,50 @@
1
1
  #include <mysql2_ext.h>
2
+ #include <stdint.h>
2
3
 
3
4
  #ifdef HAVE_RUBY_ENCODING_H
4
5
  static rb_encoding *binaryEncoding;
5
6
  #endif
6
7
 
7
- #if SIZEOF_INT < SIZEOF_LONG
8
- /**
9
- * on 64bit platforms we can handle dates way outside 2038-01-19T03:14:07
10
- * because of how I'm performing the math below, this will allow a maximum
11
- * timestamp of 9846-12-12T11:5999:59
12
- */
13
- #define MYSQL2_MAX_YEAR 9999
8
+ #if (SIZEOF_INT < SIZEOF_LONG) || defined(HAVE_RUBY_ENCODING_H)
9
+ /* on 64bit platforms we can handle dates way outside 2038-01-19T03:14:07
10
+ *
11
+ * (9999*31557600) + (12*2592000) + (31*86400) + (11*3600) + (59*60) + 59
12
+ */
13
+ #define MYSQL2_MAX_TIME 315578267999ULL
14
14
  #else
15
15
  /**
16
- * on 32bit platforms the maximum date the Time class can handle is 2038-01-19T03:14:07
17
- * 2082 = 2038+1+19+3+14+7
16
+ * On 32bit platforms the maximum date the Time class can handle is 2038-01-19T03:14:07
17
+ * 2038 years + 1 month + 19 days + 3 hours + 14 minutes + 7 seconds = 64318634047 seconds
18
+ *
19
+ * (2038*31557600) + (1*2592000) + (19*86400) + (3*3600) + (14*60) + 7
18
20
  */
19
- #define MYSQL2_MAX_YEAR 2082
21
+ #define MYSQL2_MAX_TIME 64318634047ULL
20
22
  #endif
21
23
 
22
- #ifdef NEGATIVE_TIME_T
23
- /* 1901-12-13 20:45:52 UTC : The oldest time in 32-bit signed time_t. */
24
- #define MYSQL2_MIN_YEAR 1902
24
+ #if defined(HAVE_RUBY_ENCODING_H)
25
+ /* 0000-1-1 00:00:00 UTC
26
+ *
27
+ * (0*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0
28
+ */
29
+ #define MYSQL2_MIN_TIME 2678400ULL
30
+ #elif SIZEOF_INT < SIZEOF_LONG // 64bit Ruby 1.8
31
+ /* 0139-1-1 00:00:00 UTC
32
+ *
33
+ * (139*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0
34
+ */
35
+ #define MYSQL2_MIN_TIME 4389184800ULL
36
+ #elif defined(NEGATIVE_TIME_T)
37
+ /* 1901-12-13 20:45:52 UTC : The oldest time in 32-bit signed time_t.
38
+ *
39
+ * (1901*31557600) + (12*2592000) + (13*86400) + (20*3600) + (45*60) + 52
40
+ */
41
+ #define MYSQL2_MIN_TIME 60023299552ULL
25
42
  #else
26
- /* 1970-01-01 00:00:00 UTC : The Unix epoch - the oldest time in portable time_t. */
27
- #define MYSQL2_MIN_YEAR 1970
43
+ /* 1970-01-01 00:00:01 UTC : The Unix epoch - the oldest time in portable time_t.
44
+ *
45
+ * (1970*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 1
46
+ */
47
+ #define MYSQL2_MIN_TIME 62171150401ULL
28
48
  #endif
29
49
 
30
50
  static VALUE cMysql2Result;
@@ -244,16 +264,20 @@ static VALUE rb_mysql_result_fetch_row(VALUE self, ID db_timezone, ID app_timezo
244
264
  }
245
265
  case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field
246
266
  case MYSQL_TYPE_DATETIME: { // DATETIME field
247
- int year, month, day, hour, min, sec, tokens;
267
+ unsigned int year, month, day, hour, min, sec, tokens;
268
+ uint64_t seconds;
269
+
248
270
  tokens = sscanf(row[i], "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
249
- if (year+month+day+hour+min+sec == 0) {
271
+ seconds = (year*31557600ULL) + (month*2592000ULL) + (day*86400ULL) + (hour*3600ULL) + (min*60ULL) + sec;
272
+
273
+ if (seconds == 0) {
250
274
  val = Qnil;
251
275
  } else {
252
276
  if (month < 1 || day < 1) {
253
277
  rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
254
278
  val = Qnil;
255
279
  } else {
256
- if (year < MYSQL2_MIN_YEAR || year+month+day+hour+min+sec > MYSQL2_MAX_YEAR) { // use DateTime instead
280
+ if (seconds < MYSQL2_MIN_TIME || seconds > MYSQL2_MAX_TIME) { // use DateTime instead
257
281
  VALUE offset = INT2NUM(0);
258
282
  if (db_timezone == intern_local) {
259
283
  offset = rb_funcall(cMysql2Client, intern_local_offset, 0);
@@ -464,6 +488,14 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
464
488
  return wrapper->rows;
465
489
  }
466
490
 
491
+ static VALUE rb_mysql_result_count(VALUE self) {
492
+ mysql2_result_wrapper *wrapper;
493
+
494
+ GetMysql2Result(self, wrapper);
495
+
496
+ return INT2FIX(mysql_num_rows(wrapper->result));
497
+ }
498
+
467
499
  /* Mysql2::Result */
468
500
  VALUE rb_mysql_result_to_obj(MYSQL_RES * r) {
469
501
  VALUE obj;
@@ -489,6 +521,8 @@ void init_mysql2_result() {
489
521
  cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
490
522
  rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
491
523
  rb_define_method(cMysql2Result, "fields", rb_mysql_result_fetch_fields, 0);
524
+ rb_define_method(cMysql2Result, "count", rb_mysql_result_count, 0);
525
+ rb_define_alias(cMysql2Result, "size", "count");
492
526
 
493
527
  intern_encoding_from_charset = rb_intern("encoding_from_charset");
494
528
  intern_encoding_from_charset_code = rb_intern("encoding_from_charset_code");
@@ -5,9 +5,9 @@ require 'rational' unless RUBY_VERSION >= '1.9.2'
5
5
 
6
6
  require 'mysql2/version' unless defined? Mysql2::VERSION
7
7
  require 'mysql2/error'
8
+ require 'mysql2/result'
8
9
  require 'mysql2/mysql2'
9
10
  require 'mysql2/client'
10
- require 'mysql2/result'
11
11
 
12
12
  # = Mysql2
13
13
  #
@@ -1,3 +1,3 @@
1
1
  module Mysql2
2
- VERSION = "0.2.10"
2
+ VERSION = "0.2.11"
3
3
  end
@@ -410,7 +410,7 @@ describe Mysql2::Client do
410
410
  context 'write operations api' do
411
411
  before(:each) do
412
412
  @client.query "USE test"
413
- @client.query "CREATE TABLE lastIdTest (`id` int(11) NOT NULL AUTO_INCREMENT, blah INT(11), PRIMARY KEY (`id`))"
413
+ @client.query "CREATE TABLE IF NOT EXISTS lastIdTest (`id` int(11) NOT NULL AUTO_INCREMENT, blah INT(11), PRIMARY KEY (`id`))"
414
414
  end
415
415
 
416
416
  after(:each) do
@@ -28,6 +28,18 @@ describe Mysql2::Result do
28
28
  }.should_not raise_error(Mysql2::Error)
29
29
  end
30
30
 
31
+ it "should respond to #count, which is aliased as #size" do
32
+ r = @client.query "SELECT 1"
33
+ r.should respond_to :count
34
+ r.should respond_to :size
35
+ end
36
+
37
+ it "should be able to return the number of rows in the result set" do
38
+ r = @client.query "SELECT 1"
39
+ r.count.should eql(1)
40
+ r.size.should eql(1)
41
+ end
42
+
31
43
  context "metadata queries" do
32
44
  it "should show tables" do
33
45
  @result = @client.query "SHOW TABLES"
@@ -166,56 +178,69 @@ describe Mysql2::Result do
166
178
 
167
179
  it "should return Time for a DATETIME value when within the supported range" do
168
180
  @test_result['date_time_test'].class.should eql(Time)
169
- @test_result['date_time_test'].strftime("%F %T").should eql('2010-04-04 11:44:00')
181
+ @test_result['date_time_test'].strftime("%Y-%m-%d %H:%M:%S").should eql('2010-04-04 11:44:00')
170
182
  end
171
183
 
172
- it "should return DateTime for a DATETIME value when outside the supported range, Time if otherwise" do
173
- if RUBY_PLATFORM =~ /mswin/
174
- inside_year = 1970
175
- outside_year = inside_year-1
184
+ if 1.size == 4 # 32bit
185
+ if RUBY_VERSION =~ /1.9/
186
+ klass = Time
176
187
  else
177
- inside_year = 1902
178
- outside_year = inside_year-1
179
- end
180
- r = @client.query("SELECT CAST('#{inside_year}-1-1 01:01:01' AS DATETIME) as test")
181
- if defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /rbx/
182
188
  klass = DateTime
183
- else
184
- klass = Time
185
189
  end
186
- r.first['test'].class.should eql(klass)
187
190
 
188
- r = @client.query("SELECT CAST('#{outside_year}-1-1 01:01:01' AS DATETIME) as test")
189
- r.first['test'].class.should eql(DateTime)
190
- end
191
+ it "should return DateTime when timestamp is < 1901-12-13 20:45:52" do
192
+ # 1901-12-13T20:45:52 is the min for 32bit Ruby 1.8
193
+ r = @client.query("SELECT CAST('1901-12-13 20:45:51' AS DATETIME) as test")
194
+ r.first['test'].class.should eql(klass)
195
+ end
191
196
 
192
- if 1.size == 4 # 32bit
193
197
  it "should return DateTime when timestamp is > 2038-01-19T03:14:07" do
194
198
  # 2038-01-19T03:14:07 is the max for 32bit Ruby 1.8
195
199
  r = @client.query("SELECT CAST('2038-01-19 03:14:08' AS DATETIME) as test")
196
- r.first['test'].class.should eql(DateTime)
200
+ r.first['test'].class.should eql(klass)
197
201
  end
198
202
  elsif 1.size == 8 # 64bit
199
- it "should return Time when timestamp is > 2038-01-19T03:14:07" do
200
- # 2038-01-19 03:14:07 is the max for 32bit Ruby 1.8
201
- r = @client.query("SELECT CAST('2038-01-19 03:14:08' AS DATETIME) as test")
202
- r.first['test'].class.should eql(Time)
203
+ if RUBY_VERSION =~ /1.9/
204
+ it "should return Time when timestamp is < 1901-12-13 20:45:52" do
205
+ r = @client.query("SELECT CAST('1901-12-13 20:45:51' AS DATETIME) as test")
206
+ r.first['test'].class.should eql(Time)
207
+ end
208
+
209
+ it "should return Time when timestamp is > 2038-01-19T03:14:07" do
210
+ r = @client.query("SELECT CAST('2038-01-19 03:14:08' AS DATETIME) as test")
211
+ r.first['test'].class.should eql(Time)
212
+ end
213
+ else
214
+ it "should return Time when timestamp is > 0138-12-31 11:59:59" do
215
+ r = @client.query("SELECT CAST('0139-1-1 00:00:00' AS DATETIME) as test")
216
+ r.first['test'].class.should eql(Time)
217
+ end
218
+
219
+ it "should return DateTime when timestamp is < 0139-1-1T00:00:00" do
220
+ r = @client.query("SELECT CAST('0138-12-31 11:59:59' AS DATETIME) as test")
221
+ r.first['test'].class.should eql(DateTime)
222
+ end
223
+
224
+ it "should return Time when timestamp is > 2038-01-19T03:14:07" do
225
+ r = @client.query("SELECT CAST('2038-01-19 03:14:08' AS DATETIME) as test")
226
+ r.first['test'].class.should eql(Time)
227
+ end
203
228
  end
204
229
  end
205
230
 
206
231
  it "should return Time for a TIMESTAMP value when within the supported range" do
207
232
  @test_result['timestamp_test'].class.should eql(Time)
208
- @test_result['timestamp_test'].strftime("%F %T").should eql('2010-04-04 11:44:00')
233
+ @test_result['timestamp_test'].strftime("%Y-%m-%d %H:%M:%S").should eql('2010-04-04 11:44:00')
209
234
  end
210
235
 
211
236
  it "should return Time for a TIME value" do
212
237
  @test_result['time_test'].class.should eql(Time)
213
- @test_result['time_test'].strftime("%F %T").should eql('2000-01-01 11:44:00')
238
+ @test_result['time_test'].strftime("%Y-%m-%d %H:%M:%S").should eql('2000-01-01 11:44:00')
214
239
  end
215
240
 
216
241
  it "should return Date for a DATE value" do
217
242
  @test_result['date_test'].class.should eql(Date)
218
- @test_result['date_test'].strftime("%F").should eql('2010-04-04')
243
+ @test_result['date_test'].strftime("%Y-%m-%d").should eql('2010-04-04')
219
244
  end
220
245
 
221
246
  it "should return String for an ENUM value" do
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mysql2
3
3
  version: !ruby/object:Gem::Version
4
- hash: 3
4
+ hash: 1
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 10
10
- version: 0.2.10
9
+ - 11
10
+ version: 0.2.11
11
11
  platform: ruby
12
12
  authors:
13
13
  - Brian Lopez
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-06-16 00:00:00 -07:00
18
+ date: 2011-06-17 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency