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 +1 -0
- data/CHANGELOG.md +7 -0
- data/README.md +3 -1
- 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.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)
|
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: 1
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
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-
|
18
|
+
date: 2011-06-17 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|