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 +1 -0
- data/CHANGELOG.md +17 -0
- data/README.md +2 -0
- data/ext/mysql2/client.c +38 -9
- data/ext/mysql2/result.c +52 -18
- data/lib/mysql2.rb +1 -1
- data/lib/mysql2/version.rb +1 -1
- data/spec/mysql2/client_spec.rb +1 -1
- data/spec/mysql2/result_spec.rb +50 -25
- metadata +4 -4
data/.rspec
CHANGED
data/CHANGELOG.md
CHANGED
@@ -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
data/ext/mysql2/client.c
CHANGED
@@ -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
|
-
|
267
|
-
|
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
|
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
|
475
|
+
return rb_ensure(rb_mysql_client_async_result, self, finish_and_mark_inactive, self);
|
447
476
|
#endif
|
448
477
|
}
|
449
478
|
|
data/ext/mysql2/result.c
CHANGED
@@ -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
|
-
*
|
10
|
-
*
|
11
|
-
|
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
|
-
*
|
17
|
-
*
|
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
|
21
|
+
#define MYSQL2_MAX_TIME 64318634047ULL
|
20
22
|
#endif
|
21
23
|
|
22
|
-
#
|
23
|
-
/*
|
24
|
-
|
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:
|
27
|
-
|
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
|
-
|
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 (
|
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");
|
data/lib/mysql2.rb
CHANGED
@@ -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
|
#
|
data/lib/mysql2/version.rb
CHANGED
data/spec/mysql2/client_spec.rb
CHANGED
@@ -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
|
data/spec/mysql2/result_spec.rb
CHANGED
@@ -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("%
|
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
|
-
|
173
|
-
if
|
174
|
-
|
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
|
-
|
189
|
-
|
190
|
-
|
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(
|
200
|
+
r.first['test'].class.should eql(klass)
|
197
201
|
end
|
198
202
|
elsif 1.size == 8 # 64bit
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
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("%
|
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("%
|
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("%
|
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:
|
4
|
+
hash: 31
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 0.3.
|
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-
|
18
|
+
date: 2011-06-17 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|