mysql2 0.1.5 → 0.1.6
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/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
|