mysql2 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +5 -0
- data/README.rdoc +6 -2
- data/VERSION +1 -1
- data/ext/mysql2_ext.c +54 -14
- data/ext/mysql2_ext.h +5 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +36 -15
- data/lib/mysql2.rb +1 -1
- data/lib/sequel/adapters/mysql2.rb +6 -6
- data/mysql2.gemspec +4 -2
- data/spec/mysql2/client_spec.rb +0 -5
- data/spec/mysql2/error_spec.rb +16 -0
- metadata +5 -3
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.1.6 (May 14th, 2010)
|
4
|
+
* more fixes to the AR adapter related to casting
|
5
|
+
* add missing index creation override method to AR adapter
|
6
|
+
* added sql_state and error_number methods to the Mysql2::Error exception class
|
7
|
+
|
3
8
|
## 0.1.5 (May 12th, 2010)
|
4
9
|
* quite a few patches from Eric Wong related to thread-safety, non-blocking I/O and general cleanup
|
5
10
|
** wrap mysql_real_connect with rb_thread_blocking_region
|
data/README.rdoc
CHANGED
@@ -109,7 +109,7 @@ The specs pass on my system (SL 10.6.3, x86_64) in these rubies:
|
|
109
109
|
* ree-1.8.7-2010.01
|
110
110
|
* 1.9.1-p378
|
111
111
|
* ruby-trunk
|
112
|
-
* rbx-head
|
112
|
+
* rbx-head - broken at the moment, working with the rbx team for a solution
|
113
113
|
|
114
114
|
The ActiveRecord driver should work on 2.3.5 and 3.0
|
115
115
|
|
@@ -137,4 +137,8 @@ then iterating over every row using an #each like method yielding a block:
|
|
137
137
|
Mysql
|
138
138
|
7.330000 0.350000 7.680000 ( 8.013160)
|
139
139
|
do_mysql
|
140
|
-
1.740000 0.220000 1.960000 ( 2.909290)
|
140
|
+
1.740000 0.220000 1.960000 ( 2.909290)
|
141
|
+
|
142
|
+
== Special Thanks
|
143
|
+
|
144
|
+
* Eric Wong - for the contribution (and informative explanations of) of some thread-safety, non-blocking I/O and cleanup patches. You rock dude
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.6
|
data/ext/mysql2_ext.c
CHANGED
@@ -140,8 +140,7 @@ static VALUE rb_mysql_client_new(int argc, VALUE * argv, VALUE klass) {
|
|
140
140
|
|
141
141
|
if (rb_thread_blocking_region(nogvl_init, &args, RUBY_UBF_IO, 0) == Qfalse) {
|
142
142
|
// TODO: warning - not enough memory?
|
143
|
-
|
144
|
-
return Qnil;
|
143
|
+
return rb_raise_mysql2_error(args.mysql);
|
145
144
|
}
|
146
145
|
|
147
146
|
// set default reconnect behavior
|
@@ -168,8 +167,7 @@ static VALUE rb_mysql_client_new(int argc, VALUE * argv, VALUE klass) {
|
|
168
167
|
|
169
168
|
if (rb_thread_blocking_region(nogvl_connect, &args, RUBY_UBF_IO, 0) == Qfalse) {
|
170
169
|
// unable to connect
|
171
|
-
|
172
|
-
return Qnil;
|
170
|
+
return rb_raise_mysql2_error(args.mysql);;
|
173
171
|
}
|
174
172
|
|
175
173
|
client->client = args.mysql;
|
@@ -268,10 +266,13 @@ static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
|
268
266
|
Check_Type(args.sql, T_STRING);
|
269
267
|
|
270
268
|
GetMysql2Client(self, args.mysql);
|
271
|
-
if (
|
272
|
-
rb_raise(cMysql2Error, "
|
269
|
+
if (!args.mysql) {
|
270
|
+
rb_raise(cMysql2Error, "closed MySQL connection");
|
273
271
|
return Qnil;
|
274
272
|
}
|
273
|
+
if (rb_thread_blocking_region(nogvl_send_query, &args, RUBY_UBF_IO, 0) == Qfalse) {
|
274
|
+
return rb_raise_mysql2_error(args.mysql);;
|
275
|
+
}
|
275
276
|
|
276
277
|
if (!async) {
|
277
278
|
// the below code is largely from do_mysql
|
@@ -308,7 +309,10 @@ static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
|
|
308
309
|
char escaped[(oldLen*2)+1];
|
309
310
|
|
310
311
|
GetMysql2Client(self, client);
|
311
|
-
|
312
|
+
if (!client) {
|
313
|
+
rb_raise(cMysql2Error, "closed MySQL connection");
|
314
|
+
return Qnil;
|
315
|
+
}
|
312
316
|
newLen = mysql_real_escape_string(client, escaped, RSTRING_PTR(str), RSTRING_LEN(str));
|
313
317
|
if (newLen == oldLen) {
|
314
318
|
// no need to return a new ruby string if nothing changed
|
@@ -334,6 +338,10 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
|
|
334
338
|
VALUE version;
|
335
339
|
|
336
340
|
GetMysql2Client(self, client);
|
341
|
+
if (!client) {
|
342
|
+
rb_raise(cMysql2Error, "closed MySQL connection");
|
343
|
+
return Qnil;
|
344
|
+
}
|
337
345
|
version = rb_hash_new();
|
338
346
|
rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(client)));
|
339
347
|
rb_hash_aset(version, sym_version, rb_str_new2(mysql_get_server_info(client)));
|
@@ -341,7 +349,11 @@ static VALUE rb_mysql_client_server_info(VALUE self) {
|
|
341
349
|
}
|
342
350
|
|
343
351
|
static VALUE rb_mysql_client_socket(VALUE self) {
|
344
|
-
MYSQL * client = GetMysql2Client(self, client)
|
352
|
+
MYSQL * client = GetMysql2Client(self, client);
|
353
|
+
if (!client) {
|
354
|
+
rb_raise(cMysql2Error, "closed MySQL connection");
|
355
|
+
return Qnil;
|
356
|
+
}
|
345
357
|
return INT2NUM(client->net.fd);
|
346
358
|
}
|
347
359
|
|
@@ -369,16 +381,18 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
369
381
|
MYSQL * client;
|
370
382
|
MYSQL_RES * result;
|
371
383
|
GetMysql2Client(self, client);
|
372
|
-
|
373
|
-
|
374
|
-
rb_raise(cMysql2Error, "%s", mysql_error(client));
|
384
|
+
if (!client) {
|
385
|
+
rb_raise(cMysql2Error, "closed MySQL connection");
|
375
386
|
return Qnil;
|
376
387
|
}
|
388
|
+
if (rb_thread_blocking_region(nogvl_read_query_result, client, RUBY_UBF_IO, 0) == Qfalse) {
|
389
|
+
return rb_raise_mysql2_error(client);
|
390
|
+
}
|
377
391
|
|
378
392
|
result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, client, RUBY_UBF_IO, 0);
|
379
393
|
if (result == NULL) {
|
380
394
|
if (mysql_field_count(client) != 0) {
|
381
|
-
|
395
|
+
rb_raise_mysql2_error(client);
|
382
396
|
}
|
383
397
|
return Qnil;
|
384
398
|
}
|
@@ -389,14 +403,20 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
389
403
|
static VALUE rb_mysql_client_last_id(VALUE self) {
|
390
404
|
MYSQL * client;
|
391
405
|
GetMysql2Client(self, client);
|
392
|
-
|
406
|
+
if (!client) {
|
407
|
+
rb_raise(cMysql2Error, "closed MySQL connection");
|
408
|
+
return Qnil;
|
409
|
+
}
|
393
410
|
return ULL2NUM(mysql_insert_id(client));
|
394
411
|
}
|
395
412
|
|
396
413
|
static VALUE rb_mysql_client_affected_rows(VALUE self) {
|
397
414
|
MYSQL * client;
|
398
415
|
GetMysql2Client(self, client);
|
399
|
-
|
416
|
+
if (!client) {
|
417
|
+
rb_raise(cMysql2Error, "closed MySQL connection");
|
418
|
+
return Qnil;
|
419
|
+
}
|
400
420
|
return ULL2NUM(mysql_affected_rows(client));
|
401
421
|
}
|
402
422
|
|
@@ -542,6 +562,7 @@ static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self) {
|
|
542
562
|
} else {
|
543
563
|
if (month < 1 || day < 1) {
|
544
564
|
rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
|
565
|
+
val = Qnil;
|
545
566
|
} else {
|
546
567
|
val = rb_funcall(rb_cTime, intern_local, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
|
547
568
|
}
|
@@ -557,6 +578,7 @@ static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self) {
|
|
557
578
|
} else {
|
558
579
|
if (month < 1 || day < 1) {
|
559
580
|
rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
|
581
|
+
val = Qnil;
|
560
582
|
} else {
|
561
583
|
val = rb_funcall(cDate, intern_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
|
562
584
|
}
|
@@ -648,6 +670,22 @@ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
|
648
670
|
return wrapper->rows;
|
649
671
|
}
|
650
672
|
|
673
|
+
static VALUE rb_mysql_error_error_number(VALUE obj) {
|
674
|
+
return rb_iv_get(obj, "error_number");
|
675
|
+
}
|
676
|
+
|
677
|
+
static VALUE rb_mysql_error_sql_state(VALUE obj) {
|
678
|
+
return rb_iv_get(obj, "sql_state");
|
679
|
+
}
|
680
|
+
|
681
|
+
static VALUE rb_raise_mysql2_error(MYSQL *client) {
|
682
|
+
VALUE e = rb_exc_new2(cMysql2Error, mysql_error(client));
|
683
|
+
rb_iv_set(e, "error_number", INT2FIX(mysql_errno(client)));
|
684
|
+
rb_iv_set(e, "sql_state", rb_tainted_str_new2(mysql_sqlstate(client)));
|
685
|
+
rb_exc_raise(e);
|
686
|
+
return Qnil;
|
687
|
+
}
|
688
|
+
|
651
689
|
/* Ruby Extension initializer */
|
652
690
|
void Init_mysql2_ext() {
|
653
691
|
rb_require("date");
|
@@ -673,6 +711,8 @@ void Init_mysql2_ext() {
|
|
673
711
|
rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
|
674
712
|
|
675
713
|
cMysql2Error = rb_define_class_under(mMysql2, "Error", rb_eStandardError);
|
714
|
+
rb_define_method(cMysql2Error, "error_number", rb_mysql_error_error_number, 0);
|
715
|
+
rb_define_method(cMysql2Error, "sql_state", rb_mysql_error_sql_state, 0);
|
676
716
|
|
677
717
|
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
|
678
718
|
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
|
data/ext/mysql2_ext.h
CHANGED
@@ -70,6 +70,11 @@ static void rb_mysql_result_free(void * wrapper);
|
|
70
70
|
static void rb_mysql_result_mark(void * wrapper);
|
71
71
|
static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper);
|
72
72
|
|
73
|
+
/* Mysql2::Error */
|
74
|
+
static VALUE rb_raise_mysql2_error(MYSQL *client);
|
75
|
+
static VALUE rb_mysql_error_error_number(VALUE obj);
|
76
|
+
static VALUE rb_mysql_error_sql_state(VALUE obj);
|
77
|
+
|
73
78
|
/*
|
74
79
|
* used to pass all arguments to mysql_real_connect while inside
|
75
80
|
* rb_thread_blocking_region
|
@@ -52,26 +52,33 @@ module ActiveRecord
|
|
52
52
|
def type_cast(value)
|
53
53
|
return nil if value.nil?
|
54
54
|
case type
|
55
|
-
when :string
|
56
|
-
when :text
|
57
|
-
when :integer
|
58
|
-
when :float
|
59
|
-
when :decimal
|
60
|
-
when :datetime then value.class == Time ? value : self.class.string_to_time(value)
|
61
|
-
when :
|
62
|
-
when :
|
63
|
-
when :
|
64
|
-
when :
|
65
|
-
when :boolean then self.class.value_to_boolean(value)
|
55
|
+
when :string then value
|
56
|
+
when :text then value
|
57
|
+
when :integer then value.to_i rescue value ? 1 : 0
|
58
|
+
when :float then value.to_f # returns self if it's already a Float
|
59
|
+
when :decimal then self.class.value_to_decimal(value)
|
60
|
+
when :datetime, :timestamp then value.class == Time ? value : self.class.string_to_time(value)
|
61
|
+
when :time then value.class == Time ? value : self.class.string_to_dummy_time(value)
|
62
|
+
when :date then value.class == Date ? value : self.class.string_to_date(value)
|
63
|
+
when :binary then value
|
64
|
+
when :boolean then self.class.value_to_boolean(value)
|
66
65
|
else value
|
67
66
|
end
|
68
67
|
end
|
69
68
|
|
70
69
|
def type_cast_code(var_name)
|
71
70
|
case type
|
72
|
-
|
73
|
-
|
74
|
-
|
71
|
+
when :string then nil
|
72
|
+
when :text then nil
|
73
|
+
when :integer then "#{var_name}.to_i rescue #{var_name} ? 1 : 0"
|
74
|
+
when :float then "#{var_name}.to_f"
|
75
|
+
when :decimal then "#{self.class.name}.value_to_decimal(#{var_name})"
|
76
|
+
when :datetime, :timestamp then "#{var_name}.class == Time ? #{var_name} : #{self.class.name}.string_to_time(#{var_name})"
|
77
|
+
when :time then "#{var_name}.class == Time ? #{var_name} : #{self.class.name}.string_to_dummy_time(#{var_name})"
|
78
|
+
when :date then "#{var_name}.class == Date ? #{var_name} : #{self.class.name}.string_to_date(#{var_name})"
|
79
|
+
when :binary then nil
|
80
|
+
when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})"
|
81
|
+
else nil
|
75
82
|
end
|
76
83
|
end
|
77
84
|
|
@@ -234,10 +241,11 @@ module ActiveRecord
|
|
234
241
|
end
|
235
242
|
|
236
243
|
def disconnect!
|
237
|
-
@connection
|
244
|
+
@connection.close
|
238
245
|
end
|
239
246
|
|
240
247
|
def reset!
|
248
|
+
disconnect!
|
241
249
|
@connection = Mysql2::Client.new(@config)
|
242
250
|
end
|
243
251
|
|
@@ -523,6 +531,19 @@ module ActiveRecord
|
|
523
531
|
end
|
524
532
|
|
525
533
|
protected
|
534
|
+
def quoted_columns_for_index(column_names, options = {})
|
535
|
+
length = options[:length] if options.is_a?(Hash)
|
536
|
+
|
537
|
+
quoted_column_names = case length
|
538
|
+
when Hash
|
539
|
+
column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
|
540
|
+
when Fixnum
|
541
|
+
column_names.map {|name| "#{quote_column_name(name)}(#{length})"}
|
542
|
+
else
|
543
|
+
column_names.map {|name| quote_column_name(name) }
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
526
547
|
# TODO: implement error_number method on Mysql2::Exception
|
527
548
|
def translate_exception(exception, message)
|
528
549
|
return super unless exception.respond_to?(:error_number)
|
data/lib/mysql2.rb
CHANGED
@@ -150,12 +150,12 @@ module Sequel
|
|
150
150
|
|
151
151
|
# Closes given database connection.
|
152
152
|
def disconnect_connection(c)
|
153
|
-
c
|
153
|
+
c.close
|
154
154
|
end
|
155
155
|
|
156
156
|
# Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
|
157
157
|
def schema_column_type(db_type)
|
158
|
-
Sequel::
|
158
|
+
Sequel::Mysql2.convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
@@ -204,12 +204,12 @@ module Sequel
|
|
204
204
|
|
205
205
|
# Insert a new value into this dataset
|
206
206
|
def insert(*values)
|
207
|
-
execute_dui(insert_sql(*values)){|c| return c.
|
207
|
+
execute_dui(insert_sql(*values)){|c| return c.last_id}
|
208
208
|
end
|
209
209
|
|
210
210
|
# Replace (update or insert) the matching row.
|
211
211
|
def replace(*args)
|
212
|
-
execute_dui(replace_sql(*args)){|c| return c.
|
212
|
+
execute_dui(replace_sql(*args)){|c| return c.last_id}
|
213
213
|
end
|
214
214
|
|
215
215
|
# Update the matching rows.
|
@@ -229,9 +229,9 @@ module Sequel
|
|
229
229
|
super(sql, {:type=>:dui}.merge(opts), &block)
|
230
230
|
end
|
231
231
|
|
232
|
-
# Handle correct quoting of strings using ::
|
232
|
+
# Handle correct quoting of strings using ::Mysql2#escape.
|
233
233
|
def literal_string(v)
|
234
|
-
db.synchronize{|c| "'#{c.
|
234
|
+
db.synchronize{|c| "'#{c.escape(v)}'"}
|
235
235
|
end
|
236
236
|
end
|
237
237
|
end
|
data/mysql2.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{mysql2}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.6"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Brian Lopez"]
|
12
|
-
s.date = %q{2010-05-
|
12
|
+
s.date = %q{2010-05-14}
|
13
13
|
s.email = %q{seniorlopez@gmail.com}
|
14
14
|
s.extensions = ["ext/extconf.rb"]
|
15
15
|
s.extra_rdoc_files = [
|
@@ -41,6 +41,7 @@ Gem::Specification.new do |s|
|
|
41
41
|
"spec/active_record/active_record_spec.rb",
|
42
42
|
"spec/em/em_spec.rb",
|
43
43
|
"spec/mysql2/client_spec.rb",
|
44
|
+
"spec/mysql2/error_spec.rb",
|
44
45
|
"spec/mysql2/result_spec.rb",
|
45
46
|
"spec/rcov.opts",
|
46
47
|
"spec/spec.opts",
|
@@ -55,6 +56,7 @@ Gem::Specification.new do |s|
|
|
55
56
|
"spec/active_record/active_record_spec.rb",
|
56
57
|
"spec/em/em_spec.rb",
|
57
58
|
"spec/mysql2/client_spec.rb",
|
59
|
+
"spec/mysql2/error_spec.rb",
|
58
60
|
"spec/mysql2/result_spec.rb",
|
59
61
|
"spec/spec_helper.rb",
|
60
62
|
"examples/eventmachine.rb"
|
data/spec/mysql2/client_spec.rb
CHANGED
@@ -6,11 +6,6 @@ describe Mysql2::Client do
|
|
6
6
|
@client = Mysql2::Client.new
|
7
7
|
end
|
8
8
|
|
9
|
-
after(:each) do
|
10
|
-
# forcefully clean up old connections
|
11
|
-
GC.start
|
12
|
-
end
|
13
|
-
|
14
9
|
it "should be able to connect via SSL options" do
|
15
10
|
pending("DON'T WORRY, THIS TEST PASSES :) - but is machine-specific. You need to have MySQL running with SSL configured and enabled. Then update the paths in this test to your needs and remove the pending state.")
|
16
11
|
ssl_client = nil
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
|
3
|
+
|
4
|
+
describe Mysql2::Error do
|
5
|
+
before(:each) do
|
6
|
+
@error = Mysql2::Error.new "testing"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should respond to #error_number" do
|
10
|
+
@error.should respond_to(:error_number)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should respond to #sql_state" do
|
14
|
+
@error.should respond_to(:sql_state)
|
15
|
+
end
|
16
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
8
|
+
- 6
|
9
|
+
version: 0.1.6
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Brian Lopez
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-05-
|
17
|
+
date: 2010-05-14 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -52,6 +52,7 @@ files:
|
|
52
52
|
- spec/active_record/active_record_spec.rb
|
53
53
|
- spec/em/em_spec.rb
|
54
54
|
- spec/mysql2/client_spec.rb
|
55
|
+
- spec/mysql2/error_spec.rb
|
55
56
|
- spec/mysql2/result_spec.rb
|
56
57
|
- spec/rcov.opts
|
57
58
|
- spec/spec.opts
|
@@ -91,6 +92,7 @@ test_files:
|
|
91
92
|
- spec/active_record/active_record_spec.rb
|
92
93
|
- spec/em/em_spec.rb
|
93
94
|
- spec/mysql2/client_spec.rb
|
95
|
+
- spec/mysql2/error_spec.rb
|
94
96
|
- spec/mysql2/result_spec.rb
|
95
97
|
- spec/spec_helper.rb
|
96
98
|
- examples/eventmachine.rb
|