mysql2 0.1.0 → 0.1.1
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 +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
|
|