mysql2 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ Makefile
2
+ *.dSYM
3
+ *.o
4
+ *.bundle
5
+ *.so
6
+ *.a
@@ -0,0 +1,4 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (April 6th, 2010)
4
+ * initial release
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Brian Lopez - http://github.com/brianmario
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,93 @@
1
+ = Mysql2 - A modern, simple and very fast Mysql library for Ruby, binding to libmysql
2
+
3
+ The Mysql2 gem is meant to serve the extremely common use-case of connecting, querying and iterating on results.
4
+ Some database libraries out there serve as direct 1:1 mappings of the already complex C API's available.
5
+ This one is not.
6
+
7
+ It also forces the use of UTF-8 [or binary] for the connection [and all strings in 1.9] and uses encoding-aware MySQL API calls where it can.
8
+
9
+ The API consists of two clases:
10
+
11
+ Mysql2::Client - your connection to the database
12
+
13
+ Mysql2::Result - returned from issuing a #query on the connection. It includes Enumerable.
14
+
15
+ == Installing
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.
18
+
19
+ == Usage
20
+
21
+ Connect to a database:
22
+
23
+ # this takes a hash of options, almost all of which map directly
24
+ # to the familiar database.yml in rails
25
+ # See http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html
26
+ client = Mysql2::Client.new(:host => "localhost", :username => "root")
27
+
28
+ Then query it:
29
+
30
+ results = client.query("SELECT * FROM users WHERE group='githubbers'")
31
+
32
+ Need to escape something first?
33
+
34
+ escaped = client.escape("gi'thu\"bbe\0r's")
35
+ results = client.query("SELECT * FROM users WHERE group='#{escaped}'")
36
+
37
+ Finally, iterate over the results:
38
+
39
+ results.each do |row|
40
+ # conveniently, row is a hash
41
+ # the keys are the fields, as you'd expect
42
+ # the values are pre-built ruby primitives mapped from their corresponding field types in MySQL
43
+ # Here's an otter: http://farm1.static.flickr.com/130/398077070_b8795d0ef3_b.jpg
44
+ end
45
+
46
+ Or, you might just keep it simple:
47
+
48
+ client.query("SELECT * FROM users WHERE group='githubbers'").each do |row|
49
+ # do something with row, it's ready to rock
50
+ end
51
+
52
+ How about with symbolized keys?
53
+
54
+ # NOTE: the :symbolize_keys and future options will likely move to the #query method soon
55
+ client.query("SELECT * FROM users WHERE group='githubbers'").each(:symbolize_keys => true) do |row|
56
+ # do something with row, it's ready to rock
57
+ end
58
+
59
+ == Compatibility
60
+
61
+ The specs pass on my system (SL 10.6.3, x86_64) in these rubies:
62
+
63
+ * 1.8.7-p249
64
+ * ree-1.8.7-2010.01
65
+ * 1.9.1-p378
66
+ * ruby-trunk
67
+ * rbx-head
68
+
69
+ == Yeah... but why?
70
+
71
+ Someone: Dude, the Mysql gem works fiiiiiine.
72
+
73
+ Me: It sure does, but it only hands you nil and strings for field values. Leaving you to convert
74
+ them into proper Ruby types in Ruby-land - which is slow as balls.
75
+
76
+
77
+ Someone: OK fine, but do_mysql can already give me back values with Ruby objects mapped to MySQL types.
78
+
79
+ Me: Yep, but it's API is considerably more complex *and* is 2-3x slower.
80
+
81
+ == Benchmarks
82
+
83
+ Performing a basic "SELECT * FROM" query on a table with 30k rows and fields of nearly every Ruby-representable data type,
84
+ then iterating over every row using an #each like method yielding a block:
85
+
86
+ # And remember, the Mysql gem only gives back nil and strings.
87
+ user system total real
88
+ Mysql2
89
+ 2.080000 0.790000 2.870000 ( 3.418861)
90
+ Mysql
91
+ 1.210000 0.790000 2.000000 ( 4.527824)
92
+ do_mysql
93
+ 5.450000 0.980000 6.430000 ( 7.001563)
@@ -0,0 +1,35 @@
1
+ # encoding: UTF-8
2
+ begin
3
+ require 'jeweler'
4
+ Jeweler::Tasks.new do |gem|
5
+ gem.name = "mysql2"
6
+ gem.summary = "A simple, fast Mysql library for Ruby, binding to libmysql"
7
+ gem.email = "seniorlopez@gmail.com"
8
+ gem.homepage = "http://github.com/brianmario/mysql2"
9
+ gem.authors = ["Brian Lopez"]
10
+ gem.require_paths = ["lib", "ext"]
11
+ gem.extra_rdoc_files = `git ls-files *.rdoc`.split("\n")
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.extensions = ["ext/extconf.rb"]
14
+ gem.files.include %w(lib/jeweler/templates/.document lib/jeweler/templates/.gitignore)
15
+ # gem.rubyforge_project = "mysql2"
16
+ end
17
+ rescue LoadError
18
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler -s http://gems.github.com"
19
+ end
20
+
21
+ require 'rake'
22
+ require 'spec/rake/spectask'
23
+
24
+ desc "Run all examples with RCov"
25
+ Spec::Rake::SpecTask.new('spec:rcov') do |t|
26
+ t.spec_files = FileList['spec/']
27
+ t.rcov = true
28
+ t.rcov_opts = lambda do
29
+ IO.readlines("spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
30
+ end
31
+ end
32
+ Spec::Rake::SpecTask.new('spec') do |t|
33
+ t.spec_files = FileList['spec/']
34
+ t.spec_opts << '--options' << 'spec/spec.opts'
35
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,39 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rubygems'
4
+ require 'benchmark'
5
+ require 'mysql'
6
+ require 'mysql2_ext'
7
+ require 'do_mysql'
8
+
9
+ number_of = 1000
10
+ database = 'nbb_1_production'
11
+ str = "abc'def\"ghi\0jkl%mno"
12
+
13
+ Benchmark.bmbm do |x|
14
+ mysql = Mysql.new("localhost", "root")
15
+ mysql.query "USE #{database}"
16
+ x.report do
17
+ puts "Mysql"
18
+ number_of.times do
19
+ mysql.quote str
20
+ end
21
+ end
22
+
23
+ mysql2 = Mysql2::Client.new(:host => "localhost", :username => "root")
24
+ mysql2.query "USE #{database}"
25
+ x.report do
26
+ puts "Mysql2"
27
+ number_of.times do
28
+ mysql2.escape str
29
+ end
30
+ end
31
+
32
+ do_mysql = DataObjects::Connection.new("mysql://localhost/#{database}")
33
+ x.report do
34
+ puts "do_mysql"
35
+ number_of.times do
36
+ do_mysql.quote_string str
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rubygems'
4
+ require 'benchmark'
5
+ require 'mysql'
6
+ require 'mysql2_ext'
7
+ require 'do_mysql'
8
+
9
+ number_of = 1
10
+ database = 'test'
11
+ sql = "SELECT * FROM mysql2_test"
12
+
13
+ Benchmark.bmbm do |x|
14
+ mysql2 = Mysql2::Client.new(:host => "localhost", :username => "root")
15
+ mysql2.query "USE #{database}"
16
+ x.report do
17
+ puts "Mysql2"
18
+ number_of.times do
19
+ mysql2_result = mysql2.query sql
20
+ mysql2_result.each(:symbolize_keys => true) do |res|
21
+ # puts res.inspect
22
+ end
23
+ end
24
+ end
25
+
26
+ mysql = Mysql.new("localhost", "root")
27
+ mysql.query "USE #{database}"
28
+ x.report do
29
+ puts "Mysql"
30
+ number_of.times do
31
+ mysql_result = mysql.query sql
32
+ mysql_result.each_hash do |res|
33
+ # puts res.inspect
34
+ end
35
+ end
36
+ end
37
+
38
+ do_mysql = DataObjects::Connection.new("mysql://localhost/#{database}")
39
+ command = DataObjects::Mysql::Command.new do_mysql, sql
40
+ x.report do
41
+ puts "do_mysql"
42
+ number_of.times do
43
+ do_result = command.execute_reader
44
+ do_result.each do |res|
45
+ # puts res.inspect
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,110 @@
1
+ # encoding: UTF-8
2
+
3
+ # This script is for generating psudo-random data into a single table consisting of nearly every
4
+ # data type MySQL 5.1 supports.
5
+ #
6
+ # It's meant to be used with the query.rb benchmark script (or others in the future)
7
+
8
+ require 'mysql2_ext'
9
+ require 'rubygems'
10
+ require 'faker'
11
+
12
+ num = ENV['NUM'] && ENV['NUM'].to_i || 10_000
13
+
14
+ create_table_sql = %[
15
+ CREATE TABLE IF NOT EXISTS mysql2_test (
16
+ null_test VARCHAR(10),
17
+ bit_test BIT,
18
+ tiny_int_test TINYINT,
19
+ small_int_test SMALLINT,
20
+ medium_int_test MEDIUMINT,
21
+ int_test INT,
22
+ big_int_test BIGINT,
23
+ float_test FLOAT(10,3),
24
+ double_test DOUBLE(10,3),
25
+ decimal_test DECIMAL(10,3),
26
+ date_test DATE,
27
+ date_time_test DATETIME,
28
+ timestamp_test TIMESTAMP,
29
+ time_test TIME,
30
+ year_test YEAR(4),
31
+ char_test CHAR(10),
32
+ varchar_test VARCHAR(10),
33
+ binary_test BINARY(10),
34
+ varbinary_test VARBINARY(10),
35
+ tiny_blob_test TINYBLOB,
36
+ tiny_text_test TINYTEXT,
37
+ blob_test BLOB,
38
+ text_test TEXT,
39
+ medium_blob_test MEDIUMBLOB,
40
+ medium_text_test MEDIUMTEXT,
41
+ long_blob_test LONGBLOB,
42
+ long_text_test LONGTEXT,
43
+ enum_test ENUM('val1', 'val2'),
44
+ set_test SET('val1', 'val2')
45
+ ) DEFAULT CHARSET=utf8
46
+ ]
47
+
48
+ # connect to localhost by default, pass options as needed
49
+ @client = Mysql2::Client.new :host => "localhost", :username => "root", :database => "test"
50
+
51
+ @client.query create_table_sql
52
+
53
+ def insert_record(args)
54
+ insert_sql = "
55
+ INSERT INTO mysql2_test (
56
+ null_test, bit_test, tiny_int_test, small_int_test, medium_int_test, int_test, big_int_test,
57
+ float_test, double_test, decimal_test, date_test, date_time_test, timestamp_test, time_test,
58
+ year_test, char_test, varchar_test, binary_test, varbinary_test, tiny_blob_test,
59
+ tiny_text_test, blob_test, text_test, medium_blob_test, medium_text_test,
60
+ long_blob_test, long_text_test, enum_test, set_test
61
+ )
62
+
63
+ VALUES (
64
+ NULL, #{args[:bit_test]}, #{args[:tiny_int_test]}, #{args[:small_int_test]}, #{args[:medium_int_test]}, #{args[:int_test]}, #{args[:big_int_test]},
65
+ #{args[:float_test]}, #{args[:double_test]}, #{args[:decimal_test]}, '#{args[:date_test]}', '#{args[:date_time_test]}', '#{args[:timestamp_test]}', '#{args[:time_test]}',
66
+ #{args[:year_test]}, '#{args[:char_test]}', '#{args[:varchar_test]}', '#{args[:binary_test]}', '#{args[:varbinary_test]}', '#{args[:tiny_blob_test]}',
67
+ '#{args[:tiny_text_test]}', '#{args[:blob_test]}', '#{args[:text_test]}', '#{args[:medium_blob_test]}', '#{args[:medium_text_test]}',
68
+ '#{args[:long_blob_test]}', '#{args[:long_text_test]}', '#{args[:enum_test]}', '#{args[:set_test]}'
69
+ )
70
+ "
71
+ @client.query insert_sql
72
+ end
73
+
74
+ puts "Creating #{num} records"
75
+ num.times do |n|
76
+ insert_record(
77
+ :bit_test => 1,
78
+ :tiny_int_test => rand(128),
79
+ :small_int_test => rand(32767),
80
+ :medium_int_test => rand(8388607),
81
+ :int_test => rand(2147483647),
82
+ :big_int_test => rand(9223372036854775807),
83
+ :float_test => rand(32767)/1.87,
84
+ :double_test => rand(8388607)/1.87,
85
+ :decimal_test => rand(8388607)/1.87,
86
+ :date_test => '2010-4-4',
87
+ :date_time_test => '2010-4-4 11:44:00',
88
+ :timestamp_test => '2010-4-4 11:44:00',
89
+ :time_test => '11:44:00',
90
+ :year_test => Time.now.year,
91
+ :char_test => Faker::Lorem.words(rand(5)),
92
+ :varchar_test => Faker::Lorem.words(rand(5)),
93
+ :binary_test => Faker::Lorem.words(rand(5)),
94
+ :varbinary_test => Faker::Lorem.words(rand(5)),
95
+ :tiny_blob_test => Faker::Lorem.words(rand(5)),
96
+ :tiny_text_test => Faker::Lorem.paragraph(rand(5)),
97
+ :blob_test => Faker::Lorem.paragraphs(rand(25)),
98
+ :text_test => Faker::Lorem.paragraphs(rand(25)),
99
+ :medium_blob_test => Faker::Lorem.paragraphs(rand(25)),
100
+ :medium_text_test => Faker::Lorem.paragraphs(rand(25)),
101
+ :long_blob_test => Faker::Lorem.paragraphs(rand(25)),
102
+ :long_text_test => Faker::Lorem.paragraphs(rand(25)),
103
+ :enum_test => ['val1', 'val2'].rand,
104
+ :set_test => ['val1', 'val2', 'val1,val2'].rand
105
+ )
106
+ $stdout.putc '.'
107
+ $stdout.flush
108
+ end
109
+ puts
110
+ puts "Done"
@@ -0,0 +1,19 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'mkmf'
4
+ dir_config('mysql')
5
+
6
+ have_header('mysql/mysql.h')
7
+
8
+ $CFLAGS << ' -Wall -Wextra -funroll-loops'
9
+ # $CFLAGS << ' -O0 -ggdb3'
10
+
11
+ if have_library('mysqlclient')
12
+ if RUBY_VERSION =~ /1.9/
13
+ $CFLAGS << ' -DRUBY_19_COMPATIBILITY'
14
+ end
15
+
16
+ create_makefile('mysql2_ext')
17
+ else
18
+ puts 'libmysql not found, maybe try manually specifying --with-mysql-lib to find it?'
19
+ end
@@ -0,0 +1,461 @@
1
+ #include "mysql2_ext.h"
2
+
3
+ /* Mysql2::Client */
4
+ static VALUE rb_mysql_client_new(int argc, VALUE * argv, VALUE klass) {
5
+ MYSQL * client;
6
+ VALUE obj, opts;
7
+ VALUE rb_host, rb_socket, rb_port, rb_database,
8
+ rb_username, rb_password, rb_reconnect,
9
+ rb_connect_timeout;
10
+ VALUE rb_ssl_client_key, rb_ssl_client_cert, rb_ssl_ca_cert,
11
+ rb_ssl_ca_path, rb_ssl_cipher;
12
+ char *host = "localhost", *socket = NULL, *username = NULL,
13
+ *password = NULL, *database = NULL;
14
+ char *ssl_client_key = NULL, *ssl_client_cert = NULL, *ssl_ca_cert = NULL,
15
+ *ssl_ca_path = NULL, *ssl_cipher = NULL;
16
+ unsigned int port = 3306, connect_timeout = 0;
17
+ my_bool reconnect = 0;
18
+
19
+ obj = Data_Make_Struct(klass, MYSQL, NULL, rb_mysql_client_free, client);
20
+
21
+ if (rb_scan_args(argc, argv, "01", &opts) == 1) {
22
+ Check_Type(opts, T_HASH);
23
+
24
+ if ((rb_host = rb_hash_aref(opts, sym_host)) != Qnil) {
25
+ Check_Type(rb_host, T_STRING);
26
+ host = RSTRING_PTR(rb_host);
27
+ }
28
+
29
+ if ((rb_socket = rb_hash_aref(opts, sym_socket)) != Qnil) {
30
+ Check_Type(rb_socket, T_STRING);
31
+ socket = RSTRING_PTR(rb_socket);
32
+ }
33
+
34
+ if ((rb_port = rb_hash_aref(opts, sym_port)) != Qnil) {
35
+ Check_Type(rb_port, T_FIXNUM);
36
+ port = FIX2INT(rb_port);
37
+ }
38
+
39
+ if ((rb_username = rb_hash_aref(opts, sym_username)) != Qnil) {
40
+ Check_Type(rb_username, T_STRING);
41
+ username = RSTRING_PTR(rb_username);
42
+ }
43
+
44
+ if ((rb_password = rb_hash_aref(opts, sym_password)) != Qnil) {
45
+ Check_Type(rb_password, T_STRING);
46
+ password = RSTRING_PTR(rb_password);
47
+ }
48
+
49
+ if ((rb_database = rb_hash_aref(opts, sym_database)) != Qnil) {
50
+ Check_Type(rb_database, T_STRING);
51
+ database = RSTRING_PTR(rb_database);
52
+ }
53
+
54
+ if ((rb_reconnect = rb_hash_aref(opts, sym_reconnect)) != Qnil) {
55
+ reconnect = rb_reconnect == Qtrue ? 1 : 0;
56
+ }
57
+
58
+ if ((rb_connect_timeout = rb_hash_aref(opts, sym_connect_timeout)) != Qnil) {
59
+ Check_Type(rb_connect_timeout, T_FIXNUM);
60
+ connect_timeout = FIX2INT(rb_connect_timeout);
61
+ }
62
+
63
+ // SSL options
64
+ if ((rb_ssl_client_key = rb_hash_aref(opts, sym_sslkey)) != Qnil) {
65
+ Check_Type(rb_ssl_client_key, T_STRING);
66
+ ssl_client_key = RSTRING_PTR(rb_ssl_client_key);
67
+ }
68
+
69
+ if ((rb_ssl_client_cert = rb_hash_aref(opts, sym_sslcert)) != Qnil) {
70
+ Check_Type(rb_ssl_client_cert, T_STRING);
71
+ ssl_client_cert = RSTRING_PTR(rb_ssl_client_cert);
72
+ }
73
+
74
+ if ((rb_ssl_ca_cert = rb_hash_aref(opts, sym_sslca)) != Qnil) {
75
+ Check_Type(rb_ssl_ca_cert, T_STRING);
76
+ ssl_ca_cert = RSTRING_PTR(rb_ssl_ca_cert);
77
+ }
78
+
79
+ if ((rb_ssl_ca_path = rb_hash_aref(opts, sym_sslcapath)) != Qnil) {
80
+ Check_Type(rb_ssl_ca_path, T_STRING);
81
+ ssl_ca_path = RSTRING_PTR(rb_ssl_ca_path);
82
+ }
83
+
84
+ if ((rb_ssl_cipher = rb_hash_aref(opts, sym_sslcipher)) != Qnil) {
85
+ Check_Type(rb_ssl_cipher, T_STRING);
86
+ ssl_cipher = RSTRING_PTR(rb_ssl_cipher);
87
+ }
88
+ }
89
+
90
+ if (!mysql_init(client)) {
91
+ // TODO: warning - not enough memory?
92
+ rb_raise(cMysql2Error, "%s", mysql_error(client));
93
+ return Qnil;
94
+ }
95
+
96
+ // set default reconnect behavior
97
+ if (mysql_options(client, MYSQL_OPT_RECONNECT, &reconnect) != 0) {
98
+ // TODO: warning - unable to set reconnect behavior
99
+ rb_warn("%s\n", mysql_error(client));
100
+ }
101
+
102
+ // set default connection timeout behavior
103
+ if (connect_timeout != 0 && mysql_options(client, MYSQL_OPT_CONNECT_TIMEOUT, &connect_timeout) != 0) {
104
+ // TODO: warning - unable to set connection timeout
105
+ rb_warn("%s\n", mysql_error(client));
106
+ }
107
+
108
+ // force the encoding to utf8
109
+ if (mysql_options(client, MYSQL_SET_CHARSET_NAME, "utf8") != 0) {
110
+ // TODO: warning - unable to set charset
111
+ rb_warn("%s\n", mysql_error(client));
112
+ }
113
+
114
+ if (ssl_ca_cert != NULL || ssl_client_key != NULL) {
115
+ mysql_ssl_set(client, ssl_client_key, ssl_client_cert, ssl_ca_cert, ssl_ca_path, ssl_cipher);
116
+ }
117
+
118
+ if (mysql_real_connect(client, host, username, password, database, port, socket, 0) == NULL) {
119
+ // unable to connect
120
+ rb_raise(cMysql2Error, "%s", mysql_error(client));
121
+ return Qnil;
122
+ }
123
+
124
+ rb_obj_call_init(obj, argc, argv);
125
+ return obj;
126
+ }
127
+
128
+ static VALUE rb_mysql_client_init(VALUE self, int argc, VALUE * argv) {
129
+ return self;
130
+ }
131
+
132
+ void rb_mysql_client_free(void * client) {
133
+ MYSQL * c = client;
134
+ if (c) {
135
+ mysql_close(client);
136
+ }
137
+ }
138
+
139
+ static VALUE rb_mysql_client_query(VALUE self, VALUE sql) {
140
+ MYSQL * client;
141
+ MYSQL_RES * result;
142
+ fd_set fdset;
143
+ int fd, retval;
144
+ Check_Type(sql, T_STRING);
145
+
146
+ GetMysql2Client(self, client);
147
+ if (mysql_send_query(client, RSTRING_PTR(sql), RSTRING_LEN(sql)) != 0) {
148
+ rb_raise(cMysql2Error, "%s", mysql_error(client));
149
+ return Qnil;
150
+ }
151
+
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);
158
+
159
+ retval = rb_thread_select(fd + 1, &fdset, NULL, NULL, NULL);
160
+
161
+ if (retval < 0) {
162
+ rb_sys_fail(0);
163
+ }
164
+
165
+ if (retval > 0) {
166
+ break;
167
+ }
168
+ }
169
+
170
+ if (mysql_read_query_result(client) != 0) {
171
+ rb_raise(cMysql2Error, "%s", mysql_error(client));
172
+ return Qnil;
173
+ }
174
+
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
+ }
180
+ return Qnil;
181
+ }
182
+ return rb_mysql_result_to_obj(result);
183
+ }
184
+
185
+ static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
186
+ MYSQL * client;
187
+ VALUE newStr;
188
+ unsigned long newLen, oldLen;
189
+
190
+ Check_Type(str, T_STRING);
191
+ oldLen = RSTRING_LEN(str);
192
+ char escaped[(oldLen*2)+1];
193
+
194
+ GetMysql2Client(self, client);
195
+
196
+ newLen = mysql_real_escape_string(client, escaped, RSTRING_PTR(str), RSTRING_LEN(str));
197
+ if (newLen == oldLen) {
198
+ // no need to return a new ruby string if nothing changed
199
+ return str;
200
+ } else {
201
+ newStr = rb_str_new(escaped, newLen);
202
+ #ifdef HAVE_RUBY_ENCODING_H
203
+ rb_enc_associate_index(newStr, utf8Encoding);
204
+ #endif
205
+ return newStr;
206
+ }
207
+ }
208
+
209
+ static VALUE rb_mysql_client_info(VALUE self) {
210
+ VALUE version = rb_hash_new();
211
+ rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_client_version()));
212
+ rb_hash_aset(version, sym_version, rb_str_new2(mysql_get_client_info()));
213
+ return version;
214
+ }
215
+
216
+ static VALUE rb_mysql_client_server_info(VALUE self) {
217
+ MYSQL * client;
218
+ VALUE version;
219
+
220
+ GetMysql2Client(self, client);
221
+ version = rb_hash_new();
222
+ rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(client)));
223
+ rb_hash_aset(version, sym_version, rb_str_new2(mysql_get_server_info(client)));
224
+ return version;
225
+ }
226
+
227
+ static VALUE rb_mysql_client_socket(VALUE self) {
228
+ MYSQL * client = GetMysql2Client(self, client);;
229
+ return INT2NUM(client->net.fd);
230
+ }
231
+
232
+ /* Mysql2::Result */
233
+ static VALUE rb_mysql_result_to_obj(MYSQL_RES * r) {
234
+ VALUE obj;
235
+ obj = Data_Wrap_Struct(cMysql2Result, 0, rb_mysql_result_free, r);
236
+ rb_obj_call_init(obj, 0, NULL);
237
+ return obj;
238
+ }
239
+
240
+ void rb_mysql_result_free(void * result) {
241
+ MYSQL_RES * r = result;
242
+ if (r) {
243
+ mysql_free_result(r);
244
+ }
245
+ }
246
+
247
+ static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self) {
248
+ VALUE rowHash, opts, block;
249
+ MYSQL_RES * result;
250
+ MYSQL_ROW row;
251
+ MYSQL_FIELD * fields;
252
+ struct tm parsedTime;
253
+ unsigned int i = 0, numFields = 0, symbolizeKeys = 0;
254
+ unsigned long * fieldLengths;
255
+
256
+ GetMysql2Result(self, result);
257
+
258
+ if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
259
+ Check_Type(opts, T_HASH);
260
+ if (rb_hash_aref(opts, sym_symbolize_keys) == Qtrue) {
261
+ symbolizeKeys = 1;
262
+ }
263
+ }
264
+
265
+ row = mysql_fetch_row(result);
266
+ if (row == NULL) {
267
+ return Qnil;
268
+ }
269
+
270
+ numFields = mysql_num_fields(result);
271
+ fieldLengths = mysql_fetch_lengths(result);
272
+ fields = mysql_fetch_fields(result);
273
+
274
+ rowHash = rb_hash_new();
275
+ for (i = 0; i < numFields; i++) {
276
+ VALUE key;
277
+ if (symbolizeKeys) {
278
+ char buf[fields[i].name_length+1];
279
+ memcpy(buf, fields[i].name, fields[i].name_length);
280
+ buf[fields[i].name_length] = 0;
281
+ key = ID2SYM(rb_intern(buf));
282
+ } else {
283
+ key = rb_str_new(fields[i].name, fields[i].name_length);
284
+ #ifdef HAVE_RUBY_ENCODING_H
285
+ rb_enc_associate_index(key, utf8Encoding);
286
+ #endif
287
+ }
288
+ if (row[i]) {
289
+ VALUE val;
290
+ switch(fields[i].type) {
291
+ case MYSQL_TYPE_NULL: // NULL-type field
292
+ val = Qnil;
293
+ break;
294
+ case MYSQL_TYPE_TINY: // TINYINT field
295
+ case MYSQL_TYPE_BIT: // BIT field (MySQL 5.0.3 and up)
296
+ case MYSQL_TYPE_SHORT: // SMALLINT field
297
+ case MYSQL_TYPE_LONG: // INTEGER field
298
+ case MYSQL_TYPE_INT24: // MEDIUMINT field
299
+ case MYSQL_TYPE_LONGLONG: // BIGINT field
300
+ case MYSQL_TYPE_YEAR: // YEAR field
301
+ val = rb_cstr2inum(row[i], 10);
302
+ break;
303
+ case MYSQL_TYPE_DECIMAL: // DECIMAL or NUMERIC field
304
+ case MYSQL_TYPE_NEWDECIMAL: // Precision math DECIMAL or NUMERIC field (MySQL 5.0.3 and up)
305
+ val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i]));
306
+ break;
307
+ case MYSQL_TYPE_FLOAT: // FLOAT field
308
+ case MYSQL_TYPE_DOUBLE: // DOUBLE or REAL field
309
+ val = rb_float_new(strtod(row[i], NULL));
310
+ break;
311
+ case MYSQL_TYPE_TIME: // TIME field
312
+ if (memcmp("00:00:00", row[i], 10) == 0) {
313
+ val = rb_str_new(row[i], fieldLengths[i]);
314
+ } else {
315
+ strptime(row[i], "%T", &parsedTime);
316
+ val = rb_funcall(rb_cTime, intern_local, 6, INT2NUM(1900+parsedTime.tm_year), INT2NUM(parsedTime.tm_mon+1), INT2NUM(parsedTime.tm_mday), INT2NUM(parsedTime.tm_hour), INT2NUM(parsedTime.tm_min), INT2NUM(parsedTime.tm_sec));
317
+ }
318
+ break;
319
+ case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field
320
+ case MYSQL_TYPE_DATETIME: // DATETIME field
321
+ if (memcmp("0000-00-00 00:00:00", row[i], 19) == 0) {
322
+ val = Qnil;
323
+ } else {
324
+ strptime(row[i], "%F %T", &parsedTime);
325
+ val = rb_funcall(rb_cTime, intern_local, 6, INT2NUM(1900+parsedTime.tm_year), INT2NUM(parsedTime.tm_mon+1), INT2NUM(parsedTime.tm_mday), INT2NUM(parsedTime.tm_hour), INT2NUM(parsedTime.tm_min), INT2NUM(parsedTime.tm_sec));
326
+ }
327
+ break;
328
+ case MYSQL_TYPE_DATE: // DATE field
329
+ case MYSQL_TYPE_NEWDATE: // Newer const used > 5.0
330
+ if (memcmp("0000-00-00", row[i], 10) == 0) {
331
+ val = Qnil;
332
+ } else {
333
+ strptime(row[i], "%F", &parsedTime);
334
+ val = rb_funcall(rb_cTime, intern_local, 3, INT2NUM(1900+parsedTime.tm_year), INT2NUM(parsedTime.tm_mon+1), INT2NUM(parsedTime.tm_mday));
335
+ }
336
+ break;
337
+ case MYSQL_TYPE_TINY_BLOB:
338
+ case MYSQL_TYPE_MEDIUM_BLOB:
339
+ case MYSQL_TYPE_LONG_BLOB:
340
+ case MYSQL_TYPE_BLOB:
341
+ case MYSQL_TYPE_VAR_STRING:
342
+ case MYSQL_TYPE_VARCHAR:
343
+ case MYSQL_TYPE_STRING: // CHAR or BINARY field
344
+ case MYSQL_TYPE_SET: // SET field
345
+ case MYSQL_TYPE_ENUM: // ENUM field
346
+ case MYSQL_TYPE_GEOMETRY: // Spatial fielda
347
+ default:
348
+ val = rb_str_new(row[i], fieldLengths[i]);
349
+ #ifdef HAVE_RUBY_ENCODING_H
350
+ // rudimentary check for binary content
351
+ if ((fields[i].flags & BINARY_FLAG) || fields[i].charsetnr == 63) {
352
+ rb_enc_associate_index(val, binaryEncoding);
353
+ } else {
354
+ rb_enc_associate_index(val, utf8Encoding);
355
+ }
356
+ #endif
357
+ break;
358
+ }
359
+ rb_hash_aset(rowHash, key, val);
360
+ } else {
361
+ rb_hash_aset(rowHash, key, Qnil);
362
+ }
363
+ }
364
+ return rowHash;
365
+ }
366
+
367
+ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
368
+ VALUE dataset, opts, block;
369
+ MYSQL_RES * result;
370
+ unsigned long numRows, i;
371
+
372
+ GetMysql2Result(self, result);
373
+
374
+ rb_scan_args(argc, argv, "01&", &opts, &block);
375
+
376
+ // force-start at the beginning of the result set for proper
377
+ // behavior of #each
378
+ mysql_data_seek(result, 0);
379
+
380
+ numRows = mysql_num_rows(result);
381
+ if (numRows == 0) {
382
+ return Qnil;
383
+ }
384
+
385
+ // TODO: allow yielding datasets of configurable size
386
+ // like find_in_batches from AR...
387
+ if (block != Qnil) {
388
+ for (i = 0; i < numRows; i++) {
389
+ VALUE row = rb_mysql_result_fetch_row(argc, argv, self);
390
+ if (row == Qnil) {
391
+ return Qnil;
392
+ }
393
+ rb_yield(row);
394
+ }
395
+ } else {
396
+ dataset = rb_ary_new2(numRows);
397
+ for (i = 0; i < numRows; i++) {
398
+ VALUE row = rb_mysql_result_fetch_row(argc, argv, self);
399
+ if (row == Qnil) {
400
+ return Qnil;
401
+ }
402
+ rb_ary_store(dataset, i, row);
403
+ }
404
+ return dataset;
405
+ }
406
+ return Qnil;
407
+ }
408
+
409
+ /* Ruby Extension initializer */
410
+ void Init_mysql2_ext() {
411
+ rb_require("date");
412
+ rb_require("bigdecimal");
413
+
414
+ cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
415
+ cDate = rb_const_get(rb_cObject, rb_intern("Date"));
416
+ cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
417
+
418
+ VALUE mMysql2 = rb_define_module("Mysql2");
419
+
420
+ VALUE cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
421
+ rb_define_singleton_method(cMysql2Client, "new", rb_mysql_client_new, -1);
422
+ rb_define_method(cMysql2Client, "initialize", rb_mysql_client_init, -1);
423
+ rb_define_method(cMysql2Client, "query", rb_mysql_client_query, 1);
424
+ rb_define_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
425
+ rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
426
+ rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
427
+ rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
428
+
429
+ cMysql2Error = rb_define_class_under(mMysql2, "Error", rb_eStandardError);
430
+
431
+ cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
432
+ rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
433
+
434
+ VALUE mEnumerable = rb_const_get(rb_cObject, rb_intern("Enumerable"));
435
+ rb_include_module(cMysql2Result, mEnumerable);
436
+
437
+ intern_new = rb_intern("new");
438
+ intern_local = rb_intern("local");
439
+
440
+ sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
441
+ sym_reconnect = ID2SYM(rb_intern("reconnect"));
442
+ sym_database = ID2SYM(rb_intern("database"));
443
+ sym_username = ID2SYM(rb_intern("username"));
444
+ sym_password = ID2SYM(rb_intern("password"));
445
+ sym_host = ID2SYM(rb_intern("host"));
446
+ sym_port = ID2SYM(rb_intern("port"));
447
+ sym_socket = ID2SYM(rb_intern("socket"));
448
+ sym_connect_timeout = ID2SYM(rb_intern("connect_timeout"));
449
+ sym_id = ID2SYM(rb_intern("id"));
450
+ sym_version = ID2SYM(rb_intern("version"));
451
+ sym_sslkey = ID2SYM(rb_intern("sslkey"));
452
+ sym_sslcert = ID2SYM(rb_intern("sslcert"));
453
+ sym_sslca = ID2SYM(rb_intern("sslca"));
454
+ sym_sslcapath = ID2SYM(rb_intern("sslcapath"));
455
+ sym_sslcipher = ID2SYM(rb_intern("sslcipher"));
456
+
457
+ #ifdef HAVE_RUBY_ENCODING_H
458
+ utf8Encoding = rb_enc_find_index("UTF-8");
459
+ binaryEncoding = rb_enc_find_index("binary");
460
+ #endif
461
+ }
@@ -0,0 +1,41 @@
1
+ #include <time.h>
2
+ #include <ruby.h>
3
+
4
+ #include <mysql/mysql.h>
5
+ #include <mysql/mysql_com.h>
6
+ #include <mysql/errmsg.h>
7
+ #include <mysql/mysqld_error.h>
8
+
9
+ #ifdef HAVE_RUBY_ENCODING_H
10
+ #include <ruby/encoding.h>
11
+ int utf8Encoding, binaryEncoding;
12
+ #endif
13
+
14
+ static VALUE cBigDecimal, cDate, cDateTime;
15
+ ID intern_new, intern_local;
16
+
17
+ /* Mysql2::Error */
18
+ VALUE cMysql2Error;
19
+
20
+ /* Mysql2::Client */
21
+ #define GetMysql2Client(obj, sval) (sval = (MYSQL*)DATA_PTR(obj));
22
+ static ID sym_socket, sym_host, sym_port, sym_username, sym_password,
23
+ sym_database, sym_reconnect, sym_connect_timeout, sym_id, sym_version,
24
+ sym_sslkey, sym_sslcert, sym_sslca, sym_sslcapath, sym_sslcipher;
25
+ 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);
28
+ static VALUE rb_mysql_client_escape(VALUE self, VALUE str);
29
+ static VALUE rb_mysql_client_info(VALUE self);
30
+ static VALUE rb_mysql_client_server_info(VALUE self);
31
+ static VALUE rb_mysql_client_socket(VALUE self);
32
+ void rb_mysql_client_free(void * client);
33
+
34
+ /* Mysql2::Result */
35
+ #define GetMysql2Result(obj, sval) (sval = (MYSQL_RES*)DATA_PTR(obj));
36
+ VALUE cMysql2Result;
37
+ static ID sym_symbolize_keys;
38
+ static VALUE rb_mysql_result_to_obj(MYSQL_RES * res);
39
+ static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self);
40
+ static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self);
41
+ void rb_mysql_result_free(void * result);
@@ -0,0 +1,9 @@
1
+ # encoding: UTF-8
2
+ require 'mysql2_ext'
3
+
4
+ # = Mysql2
5
+ #
6
+ # A modern MySQL client library for Ruby, binding to libmysql
7
+ module Mysql2
8
+ VERSION = "0.1.0"
9
+ end
@@ -0,0 +1,60 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{mysql2}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Brian Lopez"]
12
+ s.date = %q{2010-04-06}
13
+ s.email = %q{seniorlopez@gmail.com}
14
+ s.extensions = ["ext/extconf.rb"]
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "CHANGELOG.md",
21
+ "MIT-LICENSE",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "benchmark/escape.rb",
26
+ "benchmark/query.rb",
27
+ "benchmark/setup_db.rb",
28
+ "ext/extconf.rb",
29
+ "ext/mysql2_ext.c",
30
+ "ext/mysql2_ext.h",
31
+ "lib/mysql2.rb",
32
+ "mysql2.gemspec",
33
+ "spec/mysql2/client_spec.rb",
34
+ "spec/mysql2/result_spec.rb",
35
+ "spec/rcov.opts",
36
+ "spec/spec.opts",
37
+ "spec/spec_helper.rb"
38
+ ]
39
+ s.homepage = %q{http://github.com/brianmario/mysql2}
40
+ s.rdoc_options = ["--charset=UTF-8"]
41
+ s.require_paths = ["lib", "ext"]
42
+ s.rubygems_version = %q{1.3.6}
43
+ s.summary = %q{A simple, fast Mysql library for Ruby, binding to libmysql}
44
+ s.test_files = [
45
+ "spec/mysql2/client_spec.rb",
46
+ "spec/mysql2/result_spec.rb",
47
+ "spec/spec_helper.rb"
48
+ ]
49
+
50
+ if s.respond_to? :specification_version then
51
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
52
+ s.specification_version = 3
53
+
54
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
55
+ else
56
+ end
57
+ else
58
+ end
59
+ end
60
+
@@ -0,0 +1,93 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
3
+
4
+ describe Mysql2::Client do
5
+ before(:each) do
6
+ @client = Mysql2::Client.new
7
+ end
8
+
9
+ it "should be able to connect via SSL options" do
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.")
11
+ ssl_client = nil
12
+ lambda {
13
+ ssl_client = Mysql2::Client.new(
14
+ :sslkey => '/path/to/client-key.pem',
15
+ :sslcert => '/path/to/client-cert.pem',
16
+ :sslca => '/path/to/ca-cert.pem',
17
+ :sslcapath => '/path/to/newcerts/',
18
+ :sslcipher => 'DHE-RSA-AES256-SHA'
19
+ )
20
+ }.should_not raise_error(Mysql2::Error)
21
+
22
+ results = ssl_client.query("SHOW STATUS WHERE Variable_name = \"Ssl_version\" OR Variable_name = \"Ssl_cipher\"").to_a
23
+ results[0]['Variable_name'].should eql('Ssl_cipher')
24
+ results[0]['Value'].should_not be_nil
25
+ results[0]['Value'].class.should eql(String)
26
+
27
+ results[1]['Variable_name'].should eql('Ssl_version')
28
+ results[1]['Value'].should_not be_nil
29
+ results[1]['Value'].class.should eql(String)
30
+ end
31
+
32
+ it "should respond to #query" do
33
+ @client.should respond_to :query
34
+ end
35
+
36
+ it "should respond to #escape" do
37
+ @client.should respond_to :escape
38
+ end
39
+
40
+ it "#escape should return a new SQL-escape version of the passed string" do
41
+ @client.escape("abc'def\"ghi\0jkl%mno").should eql("abc\\'def\\\"ghi\\0jkl%mno")
42
+ end
43
+
44
+ it "#escape should return the passed string if nothing was escaped" do
45
+ str = "plain"
46
+ @client.escape(str).object_id.should eql(str.object_id)
47
+ end
48
+
49
+ it "should respond to #info" do
50
+ @client.should respond_to :info
51
+ end
52
+
53
+ it "#info should return a hash containing the client version ID and String" do
54
+ info = @client.info
55
+ info.class.should eql(Hash)
56
+ info.should have_key(:id)
57
+ info[:id].class.should eql(Fixnum)
58
+ info.should have_key(:version)
59
+ info[:version].class.should eql(String)
60
+ end
61
+
62
+ it "should respond to #server_info" do
63
+ @client.should respond_to :server_info
64
+ end
65
+
66
+ it "#server_info should return a hash containing the client version ID and String" do
67
+ server_info = @client.server_info
68
+ server_info.class.should eql(Hash)
69
+ server_info.should have_key(:id)
70
+ server_info[:id].class.should eql(Fixnum)
71
+ server_info.should have_key(:version)
72
+ server_info[:version].class.should eql(String)
73
+ end
74
+
75
+ it "should respond to #socket" do
76
+ @client.should respond_to :socket
77
+ end
78
+
79
+ it "#socket should return a Fixnum (file descriptor from C)" do
80
+ @client.socket.class.should eql(Fixnum)
81
+ @client.socket.should_not eql(0)
82
+ end
83
+
84
+ it "should raise a Mysql2::Error exception upon connection failure" do
85
+ lambda {
86
+ bad_client = Mysql2::Client.new :host => "dfjhdi9wrhw", :username => 'asdfasdf8d2h'
87
+ }.should raise_error(Mysql2::Error)
88
+
89
+ lambda {
90
+ good_client = Mysql2::Client.new
91
+ }.should_not raise_error(Mysql2::Error)
92
+ end
93
+ end
@@ -0,0 +1,170 @@
1
+ # encoding: UTF-8
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
3
+
4
+ describe Mysql2::Result do
5
+ before(:all) do
6
+ @client = Mysql2::Client.new :host => "localhost", :username => "root"
7
+ end
8
+
9
+ before(:each) do
10
+ @result = @client.query "SELECT 1"
11
+ end
12
+
13
+ it "should have included Enumerable" do
14
+ Mysql2::Result.ancestors.include?(Enumerable).should be_true
15
+ end
16
+
17
+ it "should respond to #each" do
18
+ @result.should respond_to :each
19
+ end
20
+
21
+ it "should raise a Mysql2::Error exception upon a bad query" do
22
+ lambda {
23
+ @client.query "bad sql"
24
+ }.should raise_error(Mysql2::Error)
25
+
26
+ lambda {
27
+ @client.query "SELECT 1"
28
+ }.should_not raise_error(Mysql2::Error)
29
+ end
30
+
31
+ context "#each" do
32
+ it "should yield rows as hash's" do
33
+ @result.each do |row|
34
+ row.class.should eql(Hash)
35
+ end
36
+ end
37
+
38
+ it "should yield rows as hash's with symbol keys if :symbolize_keys was set to true" do
39
+ @result.each(:symbolize_keys => true) do |row|
40
+ row.class.should eql(Hash)
41
+ row.keys.first.class.should eql(Symbol)
42
+ end
43
+ end
44
+ end
45
+
46
+ context "row data type mapping" do
47
+ before(:all) do
48
+ @client.query "USE test"
49
+ @client.query %[
50
+ CREATE TABLE IF NOT EXISTS mysql2_test (
51
+ null_test VARCHAR(10),
52
+ bit_test BIT,
53
+ tiny_int_test TINYINT,
54
+ small_int_test SMALLINT,
55
+ medium_int_test MEDIUMINT,
56
+ int_test INT,
57
+ big_int_test BIGINT,
58
+ float_test FLOAT(10,3),
59
+ double_test DOUBLE(10,3),
60
+ decimal_test DECIMAL(10,3),
61
+ date_test DATE,
62
+ date_time_test DATETIME,
63
+ timestamp_test TIMESTAMP,
64
+ time_test TIME,
65
+ year_test YEAR(4),
66
+ char_test CHAR(10),
67
+ varchar_test VARCHAR(10),
68
+ binary_test BINARY(10),
69
+ varbinary_test VARBINARY(10),
70
+ tiny_blob_test TINYBLOB,
71
+ tiny_text_test TINYTEXT,
72
+ blob_test BLOB,
73
+ text_test TEXT,
74
+ medium_blob_test MEDIUMBLOB,
75
+ medium_text_test MEDIUMTEXT,
76
+ long_blob_test LONGBLOB,
77
+ long_text_test LONGTEXT,
78
+ enum_test ENUM('val1', 'val2'),
79
+ set_test SET('val1', 'val2')
80
+ )
81
+ ]
82
+ @client.query %[
83
+ INSERT INTO mysql2_test (
84
+ null_test, bit_test, tiny_int_test, small_int_test, medium_int_test, int_test, big_int_test,
85
+ float_test, double_test, decimal_test, date_test, date_time_test, timestamp_test, time_test,
86
+ year_test, char_test, varchar_test, binary_test, varbinary_test, tiny_blob_test,
87
+ tiny_text_test, blob_test, text_test, medium_blob_test, medium_text_test,
88
+ long_blob_test, long_text_test, enum_test, set_test
89
+ )
90
+
91
+ VALUES (
92
+ NULL, 1, 1, 10, 10, 10, 10,
93
+ 10.3, 10.3, 10.3, '2010-4-4', '2010-4-4 11:44:00', '2010-4-4 11:44:00', '11:44:00',
94
+ 2009, "test", "test", "test", "test", "test",
95
+ "test", "test", "test", "test", "test",
96
+ "test", "test", 'val1', 'val1,val2'
97
+ )
98
+ ]
99
+ @test_result = @client.query("SELECT * FROM mysql2_test LIMIT 1").first
100
+ end
101
+
102
+ it "should return nil for a NULL value" do
103
+ @test_result['null_test'].class.should eql(NilClass)
104
+ @test_result['null_test'].should eql(nil)
105
+ end
106
+
107
+ {
108
+ 'bit_test' => 'BIT',
109
+ 'tiny_int_test' => 'TINYINT',
110
+ 'small_int_test' => 'SMALLINT',
111
+ 'medium_int_test' => 'MEDIUMINT',
112
+ 'int_test' => 'INT',
113
+ 'big_int_test' => 'BIGINT',
114
+ 'year_test' => 'YEAR'
115
+ }.each do |field, type|
116
+ it "should return a Fixnum for #{type}" do
117
+ [Fixnum, Bignum].should include(@test_result[field].class)
118
+ end
119
+ end
120
+
121
+ {
122
+ 'decimal_test' => 'DECIMAL'
123
+ }.each do |field, type|
124
+ it "should return a Fixnum for #{type}" do
125
+ @test_result[field].class.should eql(BigDecimal)
126
+ end
127
+ end
128
+
129
+ {
130
+ 'float_test' => 'FLOAT',
131
+ 'double_test' => 'DOUBLE'
132
+ }.each do |field, type|
133
+ it "should return a Float for #{type}" do
134
+ @test_result[field].class.should eql(Float)
135
+ end
136
+ end
137
+
138
+ {
139
+ 'date_test' => 'DATE',
140
+ 'date_time_test' => 'DATETIME',
141
+ 'timestamp_test' => 'TIMESTAMP',
142
+ 'time_test' => 'TIME'
143
+ }.each do |field, type|
144
+ it "should return a Time for #{type}" do
145
+ @test_result[field].class.should eql(Time)
146
+ end
147
+ end
148
+
149
+ {
150
+ 'char_test' => 'CHAR',
151
+ 'varchar_test' => 'VARCHAR',
152
+ 'binary_test' => 'BINARY',
153
+ 'varbinary_test' => 'VARBINARY',
154
+ 'tiny_blob_test' => 'TINYBLOB',
155
+ 'tiny_text_test' => 'TINYTEXT',
156
+ 'blob_test' => 'BLOB',
157
+ 'text_test' => 'TEXT',
158
+ 'medium_blob_test' => 'MEDIUMBLOB',
159
+ 'medium_text_test' => 'MEDIUMTEXT',
160
+ 'long_blob_test' => 'LONGBLOB',
161
+ 'long_text_test' => 'LONGTEXT',
162
+ 'enum_test' => 'ENUM',
163
+ 'set_test' => 'SET'
164
+ }.each do |field, type|
165
+ it "should return a String for #{type}" do
166
+ @test_result[field].class.should eql(String)
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,3 @@
1
+ --exclude spec,gem
2
+ --text-summary
3
+ --sort coverage --sort-reverse
@@ -0,0 +1,2 @@
1
+ --format specdoc
2
+ --colour
@@ -0,0 +1,4 @@
1
+ # encoding: UTF-8
2
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
3
+
4
+ require 'mysql2'
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mysql2
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Brian Lopez
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-06 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description:
22
+ email: seniorlopez@gmail.com
23
+ executables: []
24
+
25
+ extensions:
26
+ - ext/extconf.rb
27
+ extra_rdoc_files:
28
+ - README.rdoc
29
+ files:
30
+ - .gitignore
31
+ - CHANGELOG.md
32
+ - MIT-LICENSE
33
+ - README.rdoc
34
+ - Rakefile
35
+ - VERSION
36
+ - benchmark/escape.rb
37
+ - benchmark/query.rb
38
+ - benchmark/setup_db.rb
39
+ - ext/extconf.rb
40
+ - ext/mysql2_ext.c
41
+ - ext/mysql2_ext.h
42
+ - lib/mysql2.rb
43
+ - mysql2.gemspec
44
+ - spec/mysql2/client_spec.rb
45
+ - spec/mysql2/result_spec.rb
46
+ - spec/rcov.opts
47
+ - spec/spec.opts
48
+ - spec/spec_helper.rb
49
+ has_rdoc: true
50
+ homepage: http://github.com/brianmario/mysql2
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options:
55
+ - --charset=UTF-8
56
+ require_paths:
57
+ - lib
58
+ - ext
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.6
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: A simple, fast Mysql library for Ruby, binding to libmysql
80
+ test_files:
81
+ - spec/mysql2/client_spec.rb
82
+ - spec/mysql2/result_spec.rb
83
+ - spec/spec_helper.rb