mysql2 0.3.5 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
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.3.6 (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.3.5 (June 15th, 2011)
4
11
  * bug fix for Time/DateTime usage depending on 32/64bit Ruby
5
12
 
@@ -26,6 +33,16 @@
26
33
  * BREAKING CHANGE: the ActiveRecord adapter has been pulled into Rails 3.1 and is no longer part of the gem
27
34
  * added Mysql2::Client.escape (class-level) for raw one-off non-encoding-aware escaping
28
35
 
36
+ ## 0.2.11 (June 17th, 2011)
37
+ * fix bug in Time/DateTime range detection
38
+ * (win32) fix bug where the Mysql2::Client object wasn't cleaned up properly if interrupted during a query
39
+ * add Mysql2::Result#count (aliased as size) to get the row count for the dataset
40
+ this can be especially helpful if you want to get the number of rows without having to inflate
41
+ the entire dataset into ruby (since this happens lazily)
42
+
43
+ ## 0.2.10 (June 15th, 2011)
44
+ * bug fix for Time/DateTime usage depending on 32/64bit Ruby
45
+
29
46
  ## 0.2.9 (June 15th, 2011)
30
47
  * fix a long standing bug where a signal would interrupt rb_thread_select and put the connection in a permanently broken state
31
48
  * turn on casting in the ActiveRecord again, users can disable it if they need to for performance reasons
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
@@ -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.3.5"
2
+ VERSION = "0.3.6"
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: 25
4
+ hash: 31
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 5
10
- version: 0.3.5
9
+ - 6
10
+ version: 0.3.6
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