mysql2 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +7 -0
- data/README.rdoc +4 -2
- data/VERSION +1 -1
- data/ext/extconf.rb +72 -11
- data/ext/mysql2_ext.c +70 -29
- data/ext/mysql2_ext.h +14 -4
- data/lib/mysql2.rb +2 -2
- data/mysql2.gemspec +2 -2
- data/spec/mysql2/client_spec.rb +58 -0
- metadata +3 -3
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,11 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.1.1 (April 6th, 2010)
|
4
|
+
* added affected_rows method (mysql_affected_rows)
|
5
|
+
* added last_id method (last_insert_id)
|
6
|
+
* enable reconnect option by default
|
7
|
+
* added initial async query support
|
8
|
+
* updated extconf (thanks to the mysqlplus project) for easier gem building
|
9
|
+
|
3
10
|
## 0.1.0 (April 6th, 2010)
|
4
11
|
* initial release
|
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= Mysql2 - A modern, simple and very fast Mysql library for Ruby
|
1
|
+
= Mysql2 - A modern, simple and very fast Mysql library for Ruby - binding to libmysql
|
2
2
|
|
3
3
|
The Mysql2 gem is meant to serve the extremely common use-case of connecting, querying and iterating on results.
|
4
4
|
Some database libraries out there serve as direct 1:1 mappings of the already complex C API's available.
|
@@ -14,7 +14,9 @@ Mysql2::Result - returned from issuing a #query on the connection. It includes E
|
|
14
14
|
|
15
15
|
== Installing
|
16
16
|
|
17
|
-
|
17
|
+
gem install mysql2
|
18
|
+
|
19
|
+
You may have to specify --with-mysql-lib=/usr/local/lib/mysql or your path to libmysql (I'll do my best to not require that)
|
18
20
|
|
19
21
|
== Usage
|
20
22
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
data/ext/extconf.rb
CHANGED
@@ -1,19 +1,80 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
|
3
2
|
require 'mkmf'
|
4
|
-
dir_config('mysql')
|
5
3
|
|
6
|
-
|
4
|
+
# borrowed from mysqlplus
|
5
|
+
# http://github.com/oldmoe/mysqlplus/blob/master/ext/extconf.rb
|
6
|
+
dirs = ENV['PATH'].split(':') + %w[
|
7
|
+
/opt
|
8
|
+
/opt/local
|
9
|
+
/opt/local/mysql
|
10
|
+
/opt/local/lib/mysql5
|
11
|
+
/usr
|
12
|
+
/usr/local
|
13
|
+
/usr/local/mysql
|
14
|
+
/usr/local/mysql-*
|
15
|
+
/usr/local/lib/mysql5
|
16
|
+
].map{|dir| "#{dir}/bin" }
|
7
17
|
|
8
|
-
|
9
|
-
# $CFLAGS << ' -O0 -ggdb3'
|
18
|
+
GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5}"
|
10
19
|
|
11
|
-
if
|
12
|
-
|
13
|
-
|
20
|
+
if /mswin32/ =~ RUBY_PLATFORM
|
21
|
+
inc, lib = dir_config('mysql')
|
22
|
+
exit 1 unless have_library("libmysql")
|
23
|
+
elsif mc = (with_config('mysql-config') || Dir[GLOB].first) then
|
24
|
+
mc = Dir[GLOB].first if mc == true
|
25
|
+
cflags = `#{mc} --cflags`.chomp
|
26
|
+
exit 1 if $? != 0
|
27
|
+
libs = `#{mc} --libs`.chomp
|
28
|
+
exit 1 if $? != 0
|
29
|
+
$CPPFLAGS += ' ' + cflags
|
30
|
+
$libs = libs + " " + $libs
|
31
|
+
else
|
32
|
+
inc, lib = dir_config('mysql', '/usr/local')
|
33
|
+
libs = ['m', 'z', 'socket', 'nsl', 'mygcc']
|
34
|
+
while not find_library('mysqlclient', 'mysql_query', lib, "#{lib}/mysql") do
|
35
|
+
exit 1 if libs.empty?
|
36
|
+
have_library(libs.shift)
|
14
37
|
end
|
38
|
+
end
|
39
|
+
|
40
|
+
if have_header('mysql.h') then
|
41
|
+
src = "#include <errmsg.h>\n#include <mysqld_error.h>\n"
|
42
|
+
elsif have_header('mysql/mysql.h') then
|
43
|
+
src = "#include <mysql/errmsg.h>\n#include <mysql/mysqld_error.h>\n"
|
44
|
+
else
|
45
|
+
exit 1
|
46
|
+
end
|
15
47
|
|
16
|
-
|
48
|
+
# make mysql constant
|
49
|
+
File.open("conftest.c", "w") do |f|
|
50
|
+
f.puts src
|
51
|
+
end
|
52
|
+
if defined? cpp_command then
|
53
|
+
cpp = Config.expand(cpp_command(''))
|
17
54
|
else
|
18
|
-
|
19
|
-
end
|
55
|
+
cpp = Config.expand sprintf(CPP, $CPPFLAGS, $CFLAGS, '')
|
56
|
+
end
|
57
|
+
if /mswin32/ =~ RUBY_PLATFORM && !/-E/.match(cpp)
|
58
|
+
cpp << " -E"
|
59
|
+
end
|
60
|
+
unless system "#{cpp} > confout" then
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
File.unlink "conftest.c"
|
64
|
+
|
65
|
+
error_syms = []
|
66
|
+
IO.foreach('confout') do |l|
|
67
|
+
next unless l =~ /errmsg\.h|mysqld_error\.h/
|
68
|
+
fn = l.split(/\"/)[1]
|
69
|
+
IO.foreach(fn) do |m|
|
70
|
+
if m =~ /^#define\s+([CE]R_[0-9A-Z_]+)/ then
|
71
|
+
error_syms << $1
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
File.unlink 'confout'
|
76
|
+
|
77
|
+
$CFLAGS << ' -Wall -Wextra -funroll-loops'
|
78
|
+
# $CFLAGS << ' -O0 -ggdb3'
|
79
|
+
|
80
|
+
create_makefile('mysql2_ext')
|
data/ext/mysql2_ext.c
CHANGED
@@ -14,7 +14,7 @@ static VALUE rb_mysql_client_new(int argc, VALUE * argv, VALUE klass) {
|
|
14
14
|
char *ssl_client_key = NULL, *ssl_client_cert = NULL, *ssl_ca_cert = NULL,
|
15
15
|
*ssl_ca_path = NULL, *ssl_cipher = NULL;
|
16
16
|
unsigned int port = 3306, connect_timeout = 0;
|
17
|
-
my_bool reconnect =
|
17
|
+
my_bool reconnect = 1;
|
18
18
|
|
19
19
|
obj = Data_Make_Struct(klass, MYSQL, NULL, rb_mysql_client_free, client);
|
20
20
|
|
@@ -52,7 +52,7 @@ static VALUE rb_mysql_client_new(int argc, VALUE * argv, VALUE klass) {
|
|
52
52
|
}
|
53
53
|
|
54
54
|
if ((rb_reconnect = rb_hash_aref(opts, sym_reconnect)) != Qnil) {
|
55
|
-
reconnect = rb_reconnect ==
|
55
|
+
reconnect = rb_reconnect == Qfalse ? 0 : 1;
|
56
56
|
}
|
57
57
|
|
58
58
|
if ((rb_connect_timeout = rb_hash_aref(opts, sym_connect_timeout)) != Qnil) {
|
@@ -125,7 +125,7 @@ static VALUE rb_mysql_client_new(int argc, VALUE * argv, VALUE klass) {
|
|
125
125
|
return obj;
|
126
126
|
}
|
127
127
|
|
128
|
-
static VALUE rb_mysql_client_init(
|
128
|
+
static VALUE rb_mysql_client_init(int argc, VALUE * argv, VALUE self) {
|
129
129
|
return self;
|
130
130
|
}
|
131
131
|
|
@@ -136,11 +136,21 @@ void rb_mysql_client_free(void * client) {
|
|
136
136
|
}
|
137
137
|
}
|
138
138
|
|
139
|
-
static VALUE rb_mysql_client_query(VALUE
|
139
|
+
static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
140
140
|
MYSQL * client;
|
141
141
|
MYSQL_RES * result;
|
142
142
|
fd_set fdset;
|
143
143
|
int fd, retval;
|
144
|
+
int async = 0;
|
145
|
+
VALUE sql, opts;
|
146
|
+
VALUE rb_async;
|
147
|
+
|
148
|
+
if (rb_scan_args(argc, argv, "11", &sql, &opts) == 2) {
|
149
|
+
if ((rb_async = rb_hash_aref(opts, sym_async)) != Qnil) {
|
150
|
+
async = rb_async == Qtrue ? 1 : 0;
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
144
154
|
Check_Type(sql, T_STRING);
|
145
155
|
|
146
156
|
GetMysql2Client(self, client);
|
@@ -149,37 +159,29 @@ static VALUE rb_mysql_client_query(VALUE self, VALUE sql) {
|
|
149
159
|
return Qnil;
|
150
160
|
}
|
151
161
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
162
|
+
if (!async) {
|
163
|
+
// the below code is largely from do_mysql
|
164
|
+
// http://github.com/datamapper/do
|
165
|
+
fd = client->net.fd;
|
166
|
+
for(;;) {
|
167
|
+
FD_ZERO(&fdset);
|
168
|
+
FD_SET(fd, &fdset);
|
158
169
|
|
159
|
-
|
170
|
+
retval = rb_thread_select(fd + 1, &fdset, NULL, NULL, NULL);
|
160
171
|
|
161
|
-
|
162
|
-
|
163
|
-
|
172
|
+
if (retval < 0) {
|
173
|
+
rb_sys_fail(0);
|
174
|
+
}
|
164
175
|
|
165
|
-
|
166
|
-
|
176
|
+
if (retval > 0) {
|
177
|
+
break;
|
178
|
+
}
|
167
179
|
}
|
168
|
-
}
|
169
|
-
|
170
|
-
if (mysql_read_query_result(client) != 0) {
|
171
|
-
rb_raise(cMysql2Error, "%s", mysql_error(client));
|
172
|
-
return Qnil;
|
173
|
-
}
|
174
180
|
|
175
|
-
|
176
|
-
|
177
|
-
if (mysql_field_count(client) != 0) {
|
178
|
-
rb_raise(cMysql2Error, "%s", mysql_error(client));
|
179
|
-
}
|
181
|
+
return rb_mysql_client_async_result(self);
|
182
|
+
} else {
|
180
183
|
return Qnil;
|
181
184
|
}
|
182
|
-
return rb_mysql_result_to_obj(result);
|
183
185
|
}
|
184
186
|
|
185
187
|
static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
|
@@ -229,6 +231,41 @@ static VALUE rb_mysql_client_socket(VALUE self) {
|
|
229
231
|
return INT2NUM(client->net.fd);
|
230
232
|
}
|
231
233
|
|
234
|
+
static VALUE rb_mysql_client_async_result(VALUE self) {
|
235
|
+
MYSQL * client;
|
236
|
+
MYSQL_RES * result;
|
237
|
+
GetMysql2Client(self, client);
|
238
|
+
|
239
|
+
if (mysql_read_query_result(client) != 0) {
|
240
|
+
rb_raise(cMysql2Error, "%s", mysql_error(client));
|
241
|
+
return Qnil;
|
242
|
+
}
|
243
|
+
|
244
|
+
result = mysql_store_result(client);
|
245
|
+
if (result == NULL) {
|
246
|
+
if (mysql_field_count(client) != 0) {
|
247
|
+
rb_raise(cMysql2Error, "%s", mysql_error(client));
|
248
|
+
}
|
249
|
+
return Qnil;
|
250
|
+
}
|
251
|
+
|
252
|
+
return rb_mysql_result_to_obj(result);
|
253
|
+
}
|
254
|
+
|
255
|
+
static VALUE rb_mysql_client_last_id(VALUE self) {
|
256
|
+
MYSQL * client;
|
257
|
+
GetMysql2Client(self, client);
|
258
|
+
|
259
|
+
return ULL2NUM(mysql_insert_id(client));
|
260
|
+
}
|
261
|
+
|
262
|
+
static VALUE rb_mysql_client_affected_rows(VALUE self) {
|
263
|
+
MYSQL * client;
|
264
|
+
GetMysql2Client(self, client);
|
265
|
+
|
266
|
+
return ULL2NUM(mysql_affected_rows(client));
|
267
|
+
}
|
268
|
+
|
232
269
|
/* Mysql2::Result */
|
233
270
|
static VALUE rb_mysql_result_to_obj(MYSQL_RES * r) {
|
234
271
|
VALUE obj;
|
@@ -420,11 +457,14 @@ void Init_mysql2_ext() {
|
|
420
457
|
VALUE cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
|
421
458
|
rb_define_singleton_method(cMysql2Client, "new", rb_mysql_client_new, -1);
|
422
459
|
rb_define_method(cMysql2Client, "initialize", rb_mysql_client_init, -1);
|
423
|
-
rb_define_method(cMysql2Client, "query", rb_mysql_client_query, 1);
|
460
|
+
rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
|
424
461
|
rb_define_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
|
425
462
|
rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
426
463
|
rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
|
427
464
|
rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
|
465
|
+
rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
|
466
|
+
rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
|
467
|
+
rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
|
428
468
|
|
429
469
|
cMysql2Error = rb_define_class_under(mMysql2, "Error", rb_eStandardError);
|
430
470
|
|
@@ -453,6 +493,7 @@ void Init_mysql2_ext() {
|
|
453
493
|
sym_sslca = ID2SYM(rb_intern("sslca"));
|
454
494
|
sym_sslcapath = ID2SYM(rb_intern("sslcapath"));
|
455
495
|
sym_sslcipher = ID2SYM(rb_intern("sslcipher"));
|
496
|
+
sym_async = ID2SYM(rb_intern("async"));
|
456
497
|
|
457
498
|
#ifdef HAVE_RUBY_ENCODING_H
|
458
499
|
utf8Encoding = rb_enc_find_index("UTF-8");
|
data/ext/mysql2_ext.h
CHANGED
@@ -1,10 +1,17 @@
|
|
1
1
|
#include <time.h>
|
2
2
|
#include <ruby.h>
|
3
3
|
|
4
|
+
#ifdef HAVE_MYSQL_H
|
5
|
+
#include <mysql.h>
|
6
|
+
#include <mysql_com.h>
|
7
|
+
#include <errmsg.h>
|
8
|
+
#include <mysqld_error.h>
|
9
|
+
#else
|
4
10
|
#include <mysql/mysql.h>
|
5
11
|
#include <mysql/mysql_com.h>
|
6
12
|
#include <mysql/errmsg.h>
|
7
13
|
#include <mysql/mysqld_error.h>
|
14
|
+
#endif
|
8
15
|
|
9
16
|
#ifdef HAVE_RUBY_ENCODING_H
|
10
17
|
#include <ruby/encoding.h>
|
@@ -21,20 +28,23 @@ VALUE cMysql2Error;
|
|
21
28
|
#define GetMysql2Client(obj, sval) (sval = (MYSQL*)DATA_PTR(obj));
|
22
29
|
static ID sym_socket, sym_host, sym_port, sym_username, sym_password,
|
23
30
|
sym_database, sym_reconnect, sym_connect_timeout, sym_id, sym_version,
|
24
|
-
sym_sslkey, sym_sslcert, sym_sslca, sym_sslcapath, sym_sslcipher
|
31
|
+
sym_sslkey, sym_sslcert, sym_sslca, sym_sslcapath, sym_sslcipher,
|
32
|
+
sym_symbolize_keys, sym_async;
|
25
33
|
static VALUE rb_mysql_client_new(int argc, VALUE * argv, VALUE klass);
|
26
|
-
static VALUE rb_mysql_client_init(
|
27
|
-
static VALUE rb_mysql_client_query(VALUE
|
34
|
+
static VALUE rb_mysql_client_init(int argc, VALUE * argv, VALUE self);
|
35
|
+
static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self);
|
28
36
|
static VALUE rb_mysql_client_escape(VALUE self, VALUE str);
|
29
37
|
static VALUE rb_mysql_client_info(VALUE self);
|
30
38
|
static VALUE rb_mysql_client_server_info(VALUE self);
|
31
39
|
static VALUE rb_mysql_client_socket(VALUE self);
|
40
|
+
static VALUE rb_mysql_client_async_result(VALUE self);
|
41
|
+
static VALUE rb_mysql_client_last_id(VALUE self);
|
42
|
+
static VALUE rb_mysql_client_affected_rows(VALUE self);
|
32
43
|
void rb_mysql_client_free(void * client);
|
33
44
|
|
34
45
|
/* Mysql2::Result */
|
35
46
|
#define GetMysql2Result(obj, sval) (sval = (MYSQL_RES*)DATA_PTR(obj));
|
36
47
|
VALUE cMysql2Result;
|
37
|
-
static ID sym_symbolize_keys;
|
38
48
|
static VALUE rb_mysql_result_to_obj(MYSQL_RES * res);
|
39
49
|
static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self);
|
40
50
|
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self);
|
data/lib/mysql2.rb
CHANGED
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.1"
|
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-04-
|
12
|
+
s.date = %q{2010-04-07}
|
13
13
|
s.email = %q{seniorlopez@gmail.com}
|
14
14
|
s.extensions = ["ext/extconf.rb"]
|
15
15
|
s.extra_rdoc_files = [
|
data/spec/mysql2/client_spec.rb
CHANGED
@@ -6,6 +6,11 @@ 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
|
+
|
9
14
|
it "should be able to connect via SSL options" do
|
10
15
|
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.")
|
11
16
|
ssl_client = nil
|
@@ -90,4 +95,57 @@ describe Mysql2::Client do
|
|
90
95
|
good_client = Mysql2::Client.new
|
91
96
|
}.should_not raise_error(Mysql2::Error)
|
92
97
|
end
|
98
|
+
|
99
|
+
it "evented async queries should be supported" do
|
100
|
+
# should immediately return nil
|
101
|
+
@client.query("SELECT sleep(0.1)", :async => true).should eql(nil)
|
102
|
+
|
103
|
+
io_wrapper = IO.for_fd(@client.socket)
|
104
|
+
loops = 0
|
105
|
+
loop do
|
106
|
+
if IO.select([io_wrapper], nil, nil, 0.05)
|
107
|
+
break
|
108
|
+
else
|
109
|
+
loops += 1
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# make sure we waited some period of time
|
114
|
+
(loops >= 1).should be_true
|
115
|
+
|
116
|
+
result = @client.async_result
|
117
|
+
result.class.should eql(Mysql2::Result)
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'write operations api' do
|
121
|
+
before(:each) do
|
122
|
+
@client.query "USE test"
|
123
|
+
@client.query "CREATE TABLE lastIdTest (`id` int(11) NOT NULL AUTO_INCREMENT, blah INT(11), PRIMARY KEY (`id`))"
|
124
|
+
end
|
125
|
+
|
126
|
+
after(:each) do
|
127
|
+
@client.query "DROP TABLE lastIdTest"
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should respond to #last_id" do
|
131
|
+
@client.should respond_to(:last_id)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "#last_id should return a Fixnum, the from the last INSERT/UPDATE" do
|
135
|
+
@client.last_id.should eql(0)
|
136
|
+
@client.query "INSERT INTO lastIdTest (blah) VALUES (1234)"
|
137
|
+
@client.last_id.should eql(1)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should respond to #last_id" do
|
141
|
+
@client.should respond_to(:last_id)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "#last_id should return a Fixnum, the from the last INSERT/UPDATE" do
|
145
|
+
@client.query "INSERT INTO lastIdTest (blah) VALUES (1234)"
|
146
|
+
@client.affected_rows.should eql(1)
|
147
|
+
@client.query "UPDATE lastIdTest SET blah=4321 WHERE id=1"
|
148
|
+
@client.affected_rows.should eql(1)
|
149
|
+
end
|
150
|
+
end
|
93
151
|
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
|
+
- 1
|
9
|
+
version: 0.1.1
|
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-04-
|
17
|
+
date: 2010-04-07 00:00:00 -07:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|