mysql2 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -1,4 +1,4 @@
1
- = Mysql2 - A modern, simple and very fast Mysql library for Ruby, binding to libmysql
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
- I plan to release the initial gem version within the next day or so. Just wanted to tweak extconf to help find the libmysql binary easier, and add the ssl options.
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.0
1
+ 0.1.1
@@ -1,19 +1,80 @@
1
1
  # encoding: UTF-8
2
-
3
2
  require 'mkmf'
4
- dir_config('mysql')
5
3
 
6
- have_header('mysql/mysql.h')
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
- $CFLAGS << ' -Wall -Wextra -funroll-loops'
9
- # $CFLAGS << ' -O0 -ggdb3'
18
+ GLOB = "{#{dirs.join(',')}}/{mysql_config,mysql_config5}"
10
19
 
11
- if have_library('mysqlclient')
12
- if RUBY_VERSION =~ /1.9/
13
- $CFLAGS << ' -DRUBY_19_COMPATIBILITY'
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
- create_makefile('mysql2_ext')
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
- puts 'libmysql not found, maybe try manually specifying --with-mysql-lib to find it?'
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')
@@ -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 = 0;
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 == Qtrue ? 1 : 0;
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(VALUE self, int argc, VALUE * argv) {
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 self, VALUE sql) {
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
- // the below code is largely from do_mysql
153
- // http://github.com/datamapper/do
154
- fd = client->net.fd;
155
- for(;;) {
156
- FD_ZERO(&fdset);
157
- FD_SET(fd, &fdset);
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
- retval = rb_thread_select(fd + 1, &fdset, NULL, NULL, NULL);
170
+ retval = rb_thread_select(fd + 1, &fdset, NULL, NULL, NULL);
160
171
 
161
- if (retval < 0) {
162
- rb_sys_fail(0);
163
- }
172
+ if (retval < 0) {
173
+ rb_sys_fail(0);
174
+ }
164
175
 
165
- if (retval > 0) {
166
- break;
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
- result = mysql_store_result(client);
176
- if (result == NULL) {
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");
@@ -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(VALUE self, int argc, VALUE * argv);
27
- static VALUE rb_mysql_client_query(VALUE self, VALUE query);
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);
@@ -3,7 +3,7 @@ require 'mysql2_ext'
3
3
 
4
4
  # = Mysql2
5
5
  #
6
- # A modern MySQL client library for Ruby, binding to libmysql
6
+ # A modern, simple and very fast Mysql library for Ruby - binding to libmysql
7
7
  module Mysql2
8
- VERSION = "0.1.0"
8
+ VERSION = "0.1.1"
9
9
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{mysql2}
8
- s.version = "0.1.0"
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-06}
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 = [
@@ -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
- - 0
9
- version: 0.1.0
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-06 00:00:00 -07:00
17
+ date: 2010-04-07 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies: []
20
20