mysql2 0.1.8 → 0.1.9
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 +16 -0
- data/README.rdoc +12 -4
- data/Rakefile +10 -3
- data/VERSION +1 -1
- data/benchmark/active_record.rb +0 -1
- data/benchmark/escape.rb +2 -2
- data/benchmark/query_with_mysql_casting.rb +2 -2
- data/benchmark/query_without_mysql_casting.rb +2 -2
- data/benchmark/sequel.rb +0 -1
- data/benchmark/setup_db.rb +2 -2
- data/ext/{extconf.rb → mysql2/extconf.rb} +11 -31
- data/ext/mysql2/mysql2_ext.c +496 -0
- data/ext/{mysql2_ext.h → mysql2/mysql2_ext.h} +11 -39
- data/ext/mysql2/result.c +340 -0
- data/ext/mysql2/result.h +7 -0
- data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +59 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +7 -7
- data/lib/active_record/fiber_patches.rb +104 -0
- data/lib/mysql2.rb +9 -3
- data/lib/mysql2/client.rb +211 -0
- data/lib/mysql2/error.rb +11 -0
- data/lib/mysql2/result.rb +5 -0
- data/lib/sequel/adapters/mysql2.rb +2 -3
- data/mysql2.gemspec +15 -8
- data/spec/active_record/active_record_spec.rb +6 -4
- data/spec/em/em_spec.rb +2 -2
- data/spec/mysql2/client_spec.rb +48 -2
- data/spec/mysql2/error_spec.rb +2 -2
- data/spec/mysql2/result_spec.rb +127 -3
- metadata +20 -8
- data/ext/mysql2_ext.c +0 -748
@@ -1,5 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'rubygems'
|
3
4
|
require 'active_record'
|
4
5
|
require 'active_record/connection_adapters/mysql2_adapter'
|
5
6
|
|
@@ -28,6 +29,7 @@ describe ActiveRecord::ConnectionAdapters::Mysql2Adapter do
|
|
28
29
|
context "columns" do
|
29
30
|
before(:all) do
|
30
31
|
ActiveRecord::Base.default_timezone = 'Pacific Time (US & Canada)'
|
32
|
+
ActiveRecord::Base.time_zone_aware_attributes = true
|
31
33
|
ActiveRecord::Base.establish_connection(:adapter => 'mysql2', :database => 'test')
|
32
34
|
Mysql2Test2.connection.execute %[
|
33
35
|
CREATE TABLE IF NOT EXISTS mysql2_test2 (
|
@@ -123,9 +125,9 @@ describe ActiveRecord::ConnectionAdapters::Mysql2Adapter do
|
|
123
125
|
test.double_test.should eql('1.0000'.to_f)
|
124
126
|
test.decimal_test.should eql(BigDecimal.new('1.0000'))
|
125
127
|
test.date_test.should eql(Date.parse('2010-01-01'))
|
126
|
-
test.date_time_test.should eql(
|
128
|
+
test.date_time_test.should eql(Time.utc(2010,1,1,0,0,0))
|
127
129
|
test.timestamp_test.class.should eql(ActiveSupport::TimeWithZone)
|
128
|
-
test.time_test.class.should eql(
|
130
|
+
test.time_test.class.should eql(Time)
|
129
131
|
test.year_test.should eql(2010)
|
130
132
|
test.char_test.should eql('abcdefghij')
|
131
133
|
test.varchar_test.should eql('abcdefghij')
|
@@ -144,4 +146,4 @@ describe ActiveRecord::ConnectionAdapters::Mysql2Adapter do
|
|
144
146
|
test.set_test.should eql('val1,val2')
|
145
147
|
end
|
146
148
|
end
|
147
|
-
end
|
149
|
+
end
|
data/spec/em/em_spec.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require
|
2
|
+
require 'spec_helper'
|
3
3
|
require 'mysql2/em'
|
4
4
|
|
5
5
|
describe Mysql2::EM::Client do
|
@@ -23,4 +23,4 @@ describe Mysql2::EM::Client do
|
|
23
23
|
results[0].keys.should include("second_query")
|
24
24
|
results[1].keys.should include("first_query")
|
25
25
|
end
|
26
|
-
end
|
26
|
+
end
|
data/spec/mysql2/client_spec.rb
CHANGED
@@ -1,11 +1,19 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require
|
2
|
+
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Mysql2::Client do
|
5
5
|
before(:each) do
|
6
6
|
@client = Mysql2::Client.new
|
7
7
|
end
|
8
8
|
|
9
|
+
if defined? Encoding
|
10
|
+
it "should raise an exception on create for invalid encodings" do
|
11
|
+
lambda {
|
12
|
+
c = Mysql2::Client.new(:encoding => "fake")
|
13
|
+
}.should raise_error(Mysql2::Error)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
9
17
|
it "should be able to connect via SSL options" do
|
10
18
|
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
19
|
ssl_client = nil
|
@@ -74,6 +82,25 @@ describe Mysql2::Client do
|
|
74
82
|
info[:version].class.should eql(String)
|
75
83
|
end
|
76
84
|
|
85
|
+
if defined? Encoding
|
86
|
+
context "strings returned by #info" do
|
87
|
+
it "should default to the connection's encoding if Encoding.default_internal is nil" do
|
88
|
+
Encoding.default_internal = nil
|
89
|
+
@client.info[:version].encoding.should eql(Encoding.find('utf-8'))
|
90
|
+
|
91
|
+
client2 = Mysql2::Client.new :encoding => 'ascii'
|
92
|
+
client2.info[:version].encoding.should eql(Encoding.find('us-ascii'))
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should use Encoding.default_internal" do
|
96
|
+
Encoding.default_internal = Encoding.find('utf-8')
|
97
|
+
@client.info[:version].encoding.should eql(Encoding.default_internal)
|
98
|
+
Encoding.default_internal = Encoding.find('us-ascii')
|
99
|
+
@client.info[:version].encoding.should eql(Encoding.default_internal)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
77
104
|
it "should respond to #server_info" do
|
78
105
|
@client.should respond_to :server_info
|
79
106
|
end
|
@@ -87,6 +114,25 @@ describe Mysql2::Client do
|
|
87
114
|
server_info[:version].class.should eql(String)
|
88
115
|
end
|
89
116
|
|
117
|
+
if defined? Encoding
|
118
|
+
context "strings returned by #server_info" do
|
119
|
+
it "should default to the connection's encoding if Encoding.default_internal is nil" do
|
120
|
+
Encoding.default_internal = nil
|
121
|
+
@client.server_info[:version].encoding.should eql(Encoding.find('utf-8'))
|
122
|
+
|
123
|
+
client2 = Mysql2::Client.new :encoding => 'ascii'
|
124
|
+
client2.server_info[:version].encoding.should eql(Encoding.find('us-ascii'))
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should use Encoding.default_internal" do
|
128
|
+
Encoding.default_internal = Encoding.find('utf-8')
|
129
|
+
@client.server_info[:version].encoding.should eql(Encoding.default_internal)
|
130
|
+
Encoding.default_internal = Encoding.find('us-ascii')
|
131
|
+
@client.server_info[:version].encoding.should eql(Encoding.default_internal)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
90
136
|
it "should respond to #socket" do
|
91
137
|
@client.should respond_to :socket
|
92
138
|
end
|
@@ -158,4 +204,4 @@ describe Mysql2::Client do
|
|
158
204
|
@client.affected_rows.should eql(1)
|
159
205
|
end
|
160
206
|
end
|
161
|
-
end
|
207
|
+
end
|
data/spec/mysql2/error_spec.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require
|
2
|
+
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Mysql2::Error do
|
5
5
|
before(:each) do
|
@@ -13,4 +13,4 @@ describe Mysql2::Error do
|
|
13
13
|
it "should respond to #sql_state" do
|
14
14
|
@error.should respond_to(:sql_state)
|
15
15
|
end
|
16
|
-
end
|
16
|
+
end
|
data/spec/mysql2/result_spec.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
require
|
2
|
+
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Mysql2::Result do
|
5
5
|
before(:all) do
|
@@ -47,6 +47,22 @@ describe Mysql2::Result do
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
+
context "#fields" do
|
51
|
+
before(:all) do
|
52
|
+
@client.query "USE test"
|
53
|
+
@test_result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1")
|
54
|
+
end
|
55
|
+
|
56
|
+
it "method should exist" do
|
57
|
+
@test_result.should respond_to(:fields)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should return an array of field names in proper order" do
|
61
|
+
result = @client.query "SELECT 'a', 'b', 'c'"
|
62
|
+
result.fields.should eql(['a', 'b', 'c'])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
50
66
|
context "row data type mapping" do
|
51
67
|
before(:all) do
|
52
68
|
@client.query "USE test"
|
@@ -176,7 +192,7 @@ describe Mysql2::Result do
|
|
176
192
|
|
177
193
|
it "should return Time for a TIME value" do
|
178
194
|
@test_result['time_test'].class.should eql(Time)
|
179
|
-
if RUBY_VERSION
|
195
|
+
if RUBY_VERSION >= "1.9.2"
|
180
196
|
@test_result['time_test'].strftime("%F %T").should eql('0000-01-01 11:44:00')
|
181
197
|
else
|
182
198
|
@test_result['time_test'].strftime("%F %T").should eql('2000-01-01 11:44:00')
|
@@ -193,16 +209,83 @@ describe Mysql2::Result do
|
|
193
209
|
@test_result['enum_test'].should eql('val1')
|
194
210
|
end
|
195
211
|
|
212
|
+
if defined? Encoding
|
213
|
+
context "string encoding for ENUM values" do
|
214
|
+
it "should default to the connection's encoding if Encoding.default_internal is nil" do
|
215
|
+
Encoding.default_internal = nil
|
216
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
217
|
+
result['enum_test'].encoding.should eql(Encoding.find('utf-8'))
|
218
|
+
|
219
|
+
client2 = Mysql2::Client.new :encoding => 'ascii'
|
220
|
+
client2.query "USE test"
|
221
|
+
result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
222
|
+
result['enum_test'].encoding.should eql(Encoding.find('us-ascii'))
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should use Encoding.default_internal" do
|
226
|
+
Encoding.default_internal = Encoding.find('utf-8')
|
227
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
228
|
+
result['enum_test'].encoding.should eql(Encoding.default_internal)
|
229
|
+
Encoding.default_internal = Encoding.find('us-ascii')
|
230
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
231
|
+
result['enum_test'].encoding.should eql(Encoding.default_internal)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
196
236
|
it "should return String for a SET value" do
|
197
237
|
@test_result['set_test'].class.should eql(String)
|
198
238
|
@test_result['set_test'].should eql('val1,val2')
|
199
239
|
end
|
200
240
|
|
241
|
+
if defined? Encoding
|
242
|
+
context "string encoding for SET values" do
|
243
|
+
it "should default to the connection's encoding if Encoding.default_internal is nil" do
|
244
|
+
Encoding.default_internal = nil
|
245
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
246
|
+
result['set_test'].encoding.should eql(Encoding.find('utf-8'))
|
247
|
+
|
248
|
+
client2 = Mysql2::Client.new :encoding => 'ascii'
|
249
|
+
client2.query "USE test"
|
250
|
+
result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
251
|
+
result['set_test'].encoding.should eql(Encoding.find('us-ascii'))
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should use Encoding.default_internal" do
|
255
|
+
Encoding.default_internal = Encoding.find('utf-8')
|
256
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
257
|
+
result['set_test'].encoding.should eql(Encoding.default_internal)
|
258
|
+
Encoding.default_internal = Encoding.find('us-ascii')
|
259
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
260
|
+
result['set_test'].encoding.should eql(Encoding.default_internal)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
201
265
|
it "should return String for a BINARY value" do
|
202
266
|
@test_result['binary_test'].class.should eql(String)
|
203
267
|
@test_result['binary_test'].should eql("test#{"\000"*6}")
|
204
268
|
end
|
205
269
|
|
270
|
+
if defined? Encoding
|
271
|
+
context "string encoding for BINARY values" do
|
272
|
+
it "should default to binary if Encoding.default_internal is nil" do
|
273
|
+
Encoding.default_internal = nil
|
274
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
275
|
+
result['binary_test'].encoding.should eql(Encoding.find('binary'))
|
276
|
+
end
|
277
|
+
|
278
|
+
it "should not use Encoding.default_internal" do
|
279
|
+
Encoding.default_internal = Encoding.find('utf-8')
|
280
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
281
|
+
result['binary_test'].encoding.should eql(Encoding.find('binary'))
|
282
|
+
Encoding.default_internal = Encoding.find('us-ascii')
|
283
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
284
|
+
result['binary_test'].encoding.should eql(Encoding.find('binary'))
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
206
289
|
{
|
207
290
|
'char_test' => 'CHAR',
|
208
291
|
'varchar_test' => 'VARCHAR',
|
@@ -220,6 +303,47 @@ describe Mysql2::Result do
|
|
220
303
|
@test_result[field].class.should eql(String)
|
221
304
|
@test_result[field].should eql("test")
|
222
305
|
end
|
306
|
+
|
307
|
+
if defined? Encoding
|
308
|
+
context "string encoding for #{type} values" do
|
309
|
+
if ['VARBINARY', 'TINYBLOB', 'BLOB', 'MEDIUMBLOB', 'LONGBLOB'].include?(type)
|
310
|
+
it "should default to binary if Encoding.default_internal is nil" do
|
311
|
+
Encoding.default_internal = nil
|
312
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
313
|
+
result['binary_test'].encoding.should eql(Encoding.find('binary'))
|
314
|
+
end
|
315
|
+
|
316
|
+
it "should not use Encoding.default_internal" do
|
317
|
+
Encoding.default_internal = Encoding.find('utf-8')
|
318
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
319
|
+
result['binary_test'].encoding.should eql(Encoding.find('binary'))
|
320
|
+
Encoding.default_internal = Encoding.find('us-ascii')
|
321
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
322
|
+
result['binary_test'].encoding.should eql(Encoding.find('binary'))
|
323
|
+
end
|
324
|
+
else
|
325
|
+
it "should default to utf-8 if Encoding.default_internal is nil" do
|
326
|
+
Encoding.default_internal = nil
|
327
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
328
|
+
result[field].encoding.should eql(Encoding.find('utf-8'))
|
329
|
+
|
330
|
+
client2 = Mysql2::Client.new :encoding => 'ascii'
|
331
|
+
client2.query "USE test"
|
332
|
+
result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
333
|
+
result[field].encoding.should eql(Encoding.find('us-ascii'))
|
334
|
+
end
|
335
|
+
|
336
|
+
it "should use Encoding.default_internal" do
|
337
|
+
Encoding.default_internal = Encoding.find('utf-8')
|
338
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
339
|
+
result[field].encoding.should eql(Encoding.default_internal)
|
340
|
+
Encoding.default_internal = Encoding.find('us-ascii')
|
341
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
342
|
+
result[field].encoding.should eql(Encoding.default_internal)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
223
347
|
end
|
224
348
|
end
|
225
|
-
end
|
349
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mysql2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
9
|
+
- 9
|
10
|
+
version: 0.1.9
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Brian Lopez
|
@@ -14,7 +15,7 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-07-17 00:00:00 -07:00
|
18
19
|
default_executable:
|
19
20
|
dependencies: []
|
20
21
|
|
@@ -23,7 +24,7 @@ email: seniorlopez@gmail.com
|
|
23
24
|
executables: []
|
24
25
|
|
25
26
|
extensions:
|
26
|
-
- ext/extconf.rb
|
27
|
+
- ext/mysql2/extconf.rb
|
27
28
|
extra_rdoc_files:
|
28
29
|
- README.rdoc
|
29
30
|
files:
|
@@ -40,13 +41,20 @@ files:
|
|
40
41
|
- benchmark/sequel.rb
|
41
42
|
- benchmark/setup_db.rb
|
42
43
|
- examples/eventmachine.rb
|
43
|
-
- ext/extconf.rb
|
44
|
-
- ext/mysql2_ext.c
|
45
|
-
- ext/mysql2_ext.h
|
44
|
+
- ext/mysql2/extconf.rb
|
45
|
+
- ext/mysql2/mysql2_ext.c
|
46
|
+
- ext/mysql2/mysql2_ext.h
|
47
|
+
- ext/mysql2/result.c
|
48
|
+
- ext/mysql2/result.h
|
49
|
+
- lib/active_record/connection_adapters/em_mysql2_adapter.rb
|
46
50
|
- lib/active_record/connection_adapters/mysql2_adapter.rb
|
51
|
+
- lib/active_record/fiber_patches.rb
|
47
52
|
- lib/arel/engines/sql/compilers/mysql2_compiler.rb
|
48
53
|
- lib/mysql2.rb
|
54
|
+
- lib/mysql2/client.rb
|
49
55
|
- lib/mysql2/em.rb
|
56
|
+
- lib/mysql2/error.rb
|
57
|
+
- lib/mysql2/result.rb
|
50
58
|
- lib/sequel/adapters/mysql2.rb
|
51
59
|
- mysql2.gemspec
|
52
60
|
- spec/active_record/active_record_spec.rb
|
@@ -68,23 +76,27 @@ require_paths:
|
|
68
76
|
- lib
|
69
77
|
- ext
|
70
78
|
required_ruby_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
71
80
|
requirements:
|
72
81
|
- - ">="
|
73
82
|
- !ruby/object:Gem::Version
|
83
|
+
hash: 3
|
74
84
|
segments:
|
75
85
|
- 0
|
76
86
|
version: "0"
|
77
87
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
78
89
|
requirements:
|
79
90
|
- - ">="
|
80
91
|
- !ruby/object:Gem::Version
|
92
|
+
hash: 3
|
81
93
|
segments:
|
82
94
|
- 0
|
83
95
|
version: "0"
|
84
96
|
requirements: []
|
85
97
|
|
86
98
|
rubyforge_project:
|
87
|
-
rubygems_version: 1.3.
|
99
|
+
rubygems_version: 1.3.7
|
88
100
|
signing_key:
|
89
101
|
specification_version: 3
|
90
102
|
summary: A simple, fast Mysql library for Ruby, binding to libmysql
|
data/ext/mysql2_ext.c
DELETED
@@ -1,748 +0,0 @@
|
|
1
|
-
#include "mysql2_ext.h"
|
2
|
-
|
3
|
-
/*
|
4
|
-
* non-blocking mysql_*() functions that we won't be wrapping since
|
5
|
-
* they do not appear to hit the network nor issue any interruptible
|
6
|
-
* or blocking system calls.
|
7
|
-
*
|
8
|
-
* - mysql_affected_rows()
|
9
|
-
* - mysql_error()
|
10
|
-
* - mysql_fetch_fields()
|
11
|
-
* - mysql_fetch_lengths() - calls cli_fetch_lengths or emb_fetch_lengths
|
12
|
-
* - mysql_field_count()
|
13
|
-
* - mysql_get_client_info()
|
14
|
-
* - mysql_get_client_version()
|
15
|
-
* - mysql_get_server_info()
|
16
|
-
* - mysql_get_server_version()
|
17
|
-
* - mysql_insert_id()
|
18
|
-
* - mysql_num_fields()
|
19
|
-
* - mysql_num_rows()
|
20
|
-
* - mysql_options()
|
21
|
-
* - mysql_real_escape_string()
|
22
|
-
* - mysql_ssl_set()
|
23
|
-
*/
|
24
|
-
|
25
|
-
static VALUE nogvl_init(void *ptr) {
|
26
|
-
struct nogvl_connect_args *args = ptr;
|
27
|
-
|
28
|
-
/* may initialize embedded server and read /etc/services off disk */
|
29
|
-
args->mysql = mysql_init(NULL);
|
30
|
-
|
31
|
-
return args->mysql == NULL ? Qfalse : Qtrue;
|
32
|
-
}
|
33
|
-
|
34
|
-
static VALUE nogvl_connect(void *ptr)
|
35
|
-
{
|
36
|
-
struct nogvl_connect_args *args = ptr;
|
37
|
-
MYSQL *client;
|
38
|
-
|
39
|
-
client = mysql_real_connect(args->mysql, args->host,
|
40
|
-
args->user, args->passwd,
|
41
|
-
args->db, args->port, args->unix_socket,
|
42
|
-
args->client_flag);
|
43
|
-
|
44
|
-
return client ? Qtrue : Qfalse;
|
45
|
-
}
|
46
|
-
|
47
|
-
/* Mysql2::Client */
|
48
|
-
static VALUE rb_mysql_client_new(int argc, VALUE * argv, VALUE klass) {
|
49
|
-
mysql2_client_wrapper * client;
|
50
|
-
struct nogvl_connect_args args = {
|
51
|
-
.host = "localhost",
|
52
|
-
.user = NULL,
|
53
|
-
.passwd = NULL,
|
54
|
-
.db = NULL,
|
55
|
-
.port = 3306,
|
56
|
-
.unix_socket = NULL,
|
57
|
-
.client_flag = 0
|
58
|
-
};
|
59
|
-
VALUE obj, opts;
|
60
|
-
VALUE rb_host, rb_socket, rb_port, rb_database,
|
61
|
-
rb_username, rb_password, rb_reconnect,
|
62
|
-
rb_connect_timeout;
|
63
|
-
VALUE rb_ssl_client_key, rb_ssl_client_cert, rb_ssl_ca_cert,
|
64
|
-
rb_ssl_ca_path, rb_ssl_cipher;
|
65
|
-
char *ssl_client_key = NULL, *ssl_client_cert = NULL, *ssl_ca_cert = NULL,
|
66
|
-
*ssl_ca_path = NULL, *ssl_cipher = NULL;
|
67
|
-
unsigned int connect_timeout = 0;
|
68
|
-
my_bool reconnect = 1;
|
69
|
-
|
70
|
-
obj = Data_Make_Struct(klass, mysql2_client_wrapper, NULL, rb_mysql_client_free, client);
|
71
|
-
|
72
|
-
if (rb_scan_args(argc, argv, "01", &opts) == 1) {
|
73
|
-
Check_Type(opts, T_HASH);
|
74
|
-
|
75
|
-
if ((rb_host = rb_hash_aref(opts, sym_host)) != Qnil) {
|
76
|
-
Check_Type(rb_host, T_STRING);
|
77
|
-
args.host = RSTRING_PTR(rb_host);
|
78
|
-
}
|
79
|
-
|
80
|
-
if ((rb_socket = rb_hash_aref(opts, sym_socket)) != Qnil) {
|
81
|
-
Check_Type(rb_socket, T_STRING);
|
82
|
-
args.unix_socket = RSTRING_PTR(rb_socket);
|
83
|
-
}
|
84
|
-
|
85
|
-
if ((rb_port = rb_hash_aref(opts, sym_port)) != Qnil) {
|
86
|
-
Check_Type(rb_port, T_FIXNUM);
|
87
|
-
args.port = FIX2INT(rb_port);
|
88
|
-
}
|
89
|
-
|
90
|
-
if ((rb_username = rb_hash_aref(opts, sym_username)) != Qnil) {
|
91
|
-
Check_Type(rb_username, T_STRING);
|
92
|
-
args.user = RSTRING_PTR(rb_username);
|
93
|
-
}
|
94
|
-
|
95
|
-
if ((rb_password = rb_hash_aref(opts, sym_password)) != Qnil) {
|
96
|
-
Check_Type(rb_password, T_STRING);
|
97
|
-
args.passwd = RSTRING_PTR(rb_password);
|
98
|
-
}
|
99
|
-
|
100
|
-
if ((rb_database = rb_hash_aref(opts, sym_database)) != Qnil) {
|
101
|
-
Check_Type(rb_database, T_STRING);
|
102
|
-
args.db = RSTRING_PTR(rb_database);
|
103
|
-
}
|
104
|
-
|
105
|
-
if ((rb_reconnect = rb_hash_aref(opts, sym_reconnect)) != Qnil) {
|
106
|
-
reconnect = rb_reconnect == Qfalse ? 0 : 1;
|
107
|
-
}
|
108
|
-
|
109
|
-
if ((rb_connect_timeout = rb_hash_aref(opts, sym_connect_timeout)) != Qnil) {
|
110
|
-
Check_Type(rb_connect_timeout, T_FIXNUM);
|
111
|
-
connect_timeout = FIX2INT(rb_connect_timeout);
|
112
|
-
}
|
113
|
-
|
114
|
-
// SSL options
|
115
|
-
if ((rb_ssl_client_key = rb_hash_aref(opts, sym_sslkey)) != Qnil) {
|
116
|
-
Check_Type(rb_ssl_client_key, T_STRING);
|
117
|
-
ssl_client_key = RSTRING_PTR(rb_ssl_client_key);
|
118
|
-
}
|
119
|
-
|
120
|
-
if ((rb_ssl_client_cert = rb_hash_aref(opts, sym_sslcert)) != Qnil) {
|
121
|
-
Check_Type(rb_ssl_client_cert, T_STRING);
|
122
|
-
ssl_client_cert = RSTRING_PTR(rb_ssl_client_cert);
|
123
|
-
}
|
124
|
-
|
125
|
-
if ((rb_ssl_ca_cert = rb_hash_aref(opts, sym_sslca)) != Qnil) {
|
126
|
-
Check_Type(rb_ssl_ca_cert, T_STRING);
|
127
|
-
ssl_ca_cert = RSTRING_PTR(rb_ssl_ca_cert);
|
128
|
-
}
|
129
|
-
|
130
|
-
if ((rb_ssl_ca_path = rb_hash_aref(opts, sym_sslcapath)) != Qnil) {
|
131
|
-
Check_Type(rb_ssl_ca_path, T_STRING);
|
132
|
-
ssl_ca_path = RSTRING_PTR(rb_ssl_ca_path);
|
133
|
-
}
|
134
|
-
|
135
|
-
if ((rb_ssl_cipher = rb_hash_aref(opts, sym_sslcipher)) != Qnil) {
|
136
|
-
Check_Type(rb_ssl_cipher, T_STRING);
|
137
|
-
ssl_cipher = RSTRING_PTR(rb_ssl_cipher);
|
138
|
-
}
|
139
|
-
}
|
140
|
-
|
141
|
-
if (rb_thread_blocking_region(nogvl_init, &args, RUBY_UBF_IO, 0) == Qfalse) {
|
142
|
-
// TODO: warning - not enough memory?
|
143
|
-
return rb_raise_mysql2_error(args.mysql);
|
144
|
-
}
|
145
|
-
|
146
|
-
// set default reconnect behavior
|
147
|
-
if (mysql_options(args.mysql, MYSQL_OPT_RECONNECT, &reconnect) != 0) {
|
148
|
-
// TODO: warning - unable to set reconnect behavior
|
149
|
-
rb_warn("%s\n", mysql_error(args.mysql));
|
150
|
-
}
|
151
|
-
|
152
|
-
// set default connection timeout behavior
|
153
|
-
if (connect_timeout != 0 && mysql_options(args.mysql, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&connect_timeout) != 0) {
|
154
|
-
// TODO: warning - unable to set connection timeout
|
155
|
-
rb_warn("%s\n", mysql_error(args.mysql));
|
156
|
-
}
|
157
|
-
|
158
|
-
// force the encoding to utf8
|
159
|
-
if (mysql_options(args.mysql, MYSQL_SET_CHARSET_NAME, "utf8") != 0) {
|
160
|
-
// TODO: warning - unable to set charset
|
161
|
-
rb_warn("%s\n", mysql_error(args.mysql));
|
162
|
-
}
|
163
|
-
|
164
|
-
if (ssl_ca_cert != NULL || ssl_client_key != NULL) {
|
165
|
-
mysql_ssl_set(args.mysql, ssl_client_key, ssl_client_cert, ssl_ca_cert, ssl_ca_path, ssl_cipher);
|
166
|
-
}
|
167
|
-
|
168
|
-
if (rb_thread_blocking_region(nogvl_connect, &args, RUBY_UBF_IO, 0) == Qfalse) {
|
169
|
-
// unable to connect
|
170
|
-
return rb_raise_mysql2_error(args.mysql);;
|
171
|
-
}
|
172
|
-
|
173
|
-
client->client = args.mysql;
|
174
|
-
|
175
|
-
rb_obj_call_init(obj, argc, argv);
|
176
|
-
return obj;
|
177
|
-
}
|
178
|
-
|
179
|
-
static VALUE rb_mysql_client_init(RB_MYSQL_UNUSED int argc, RB_MYSQL_UNUSED VALUE * argv, VALUE self) {
|
180
|
-
return self;
|
181
|
-
}
|
182
|
-
|
183
|
-
static void rb_mysql_client_free(void * ptr) {
|
184
|
-
mysql2_client_wrapper * client = ptr;
|
185
|
-
|
186
|
-
if (client->client) {
|
187
|
-
/*
|
188
|
-
* we'll send a QUIT message to the server, but that message is more of a
|
189
|
-
* formality than a hard requirement since the socket is getting shutdown
|
190
|
-
* anyways, so ensure the socket write does not block our interpreter
|
191
|
-
*/
|
192
|
-
int fd = client->client->net.fd;
|
193
|
-
int flags;
|
194
|
-
|
195
|
-
if (fd >= 0) {
|
196
|
-
/*
|
197
|
-
* if the socket is dead we have no chance of blocking,
|
198
|
-
* so ignore any potential fcntl errors since they don't matter
|
199
|
-
*/
|
200
|
-
flags = fcntl(fd, F_GETFL);
|
201
|
-
if (flags > 0 && !(flags & O_NONBLOCK))
|
202
|
-
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
203
|
-
}
|
204
|
-
|
205
|
-
mysql_close(client->client);
|
206
|
-
}
|
207
|
-
xfree(ptr);
|
208
|
-
}
|
209
|
-
|
210
|
-
static VALUE nogvl_close(void * ptr) {
|
211
|
-
mysql_close((MYSQL *)ptr);
|
212
|
-
return Qnil;
|
213
|
-
}
|
214
|
-
|
215
|
-
/*
|
216
|
-
* Immediately disconnect from the server, normally the garbage collector
|
217
|
-
* will disconnect automatically when a connection is no longer needed.
|
218
|
-
* Explicitly closing this will free up server resources sooner than waiting
|
219
|
-
* for the garbage collector.
|
220
|
-
*/
|
221
|
-
static VALUE rb_mysql_client_close(VALUE self) {
|
222
|
-
mysql2_client_wrapper *client;
|
223
|
-
|
224
|
-
Data_Get_Struct(self, mysql2_client_wrapper, client);
|
225
|
-
|
226
|
-
if (client->client) {
|
227
|
-
rb_thread_blocking_region(nogvl_close, client->client, RUBY_UBF_IO, 0);
|
228
|
-
client->client = NULL;
|
229
|
-
} else {
|
230
|
-
rb_raise(cMysql2Error, "already closed MySQL connection");
|
231
|
-
}
|
232
|
-
return Qnil;
|
233
|
-
}
|
234
|
-
|
235
|
-
/*
|
236
|
-
* mysql_send_query is unlikely to block since most queries are small
|
237
|
-
* enough to fit in a socket buffer, but sometimes large UPDATE and
|
238
|
-
* INSERTs will cause the process to block
|
239
|
-
*/
|
240
|
-
static VALUE nogvl_send_query(void *ptr)
|
241
|
-
{
|
242
|
-
struct nogvl_send_query_args *args = ptr;
|
243
|
-
int rv;
|
244
|
-
const char *sql = RSTRING_PTR(args->sql);
|
245
|
-
long sql_len = RSTRING_LEN(args->sql);
|
246
|
-
|
247
|
-
rv = mysql_send_query(args->mysql, sql, sql_len);
|
248
|
-
|
249
|
-
return rv == 0 ? Qtrue : Qfalse;
|
250
|
-
}
|
251
|
-
|
252
|
-
static VALUE rb_mysql_client_query(int argc, VALUE * argv, VALUE self) {
|
253
|
-
struct nogvl_send_query_args args;
|
254
|
-
fd_set fdset;
|
255
|
-
int fd, retval;
|
256
|
-
int async = 0;
|
257
|
-
VALUE opts;
|
258
|
-
VALUE rb_async;
|
259
|
-
|
260
|
-
if (rb_scan_args(argc, argv, "11", &args.sql, &opts) == 2) {
|
261
|
-
if ((rb_async = rb_hash_aref(opts, sym_async)) != Qnil) {
|
262
|
-
async = rb_async == Qtrue ? 1 : 0;
|
263
|
-
}
|
264
|
-
}
|
265
|
-
|
266
|
-
Check_Type(args.sql, T_STRING);
|
267
|
-
|
268
|
-
GetMysql2Client(self, args.mysql);
|
269
|
-
if (!args.mysql) {
|
270
|
-
rb_raise(cMysql2Error, "closed MySQL connection");
|
271
|
-
return Qnil;
|
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
|
-
}
|
276
|
-
|
277
|
-
if (!async) {
|
278
|
-
// the below code is largely from do_mysql
|
279
|
-
// http://github.com/datamapper/do
|
280
|
-
fd = args.mysql->net.fd;
|
281
|
-
for(;;) {
|
282
|
-
FD_ZERO(&fdset);
|
283
|
-
FD_SET(fd, &fdset);
|
284
|
-
|
285
|
-
retval = rb_thread_select(fd + 1, &fdset, NULL, NULL, NULL);
|
286
|
-
|
287
|
-
if (retval < 0) {
|
288
|
-
rb_sys_fail(0);
|
289
|
-
}
|
290
|
-
|
291
|
-
if (retval > 0) {
|
292
|
-
break;
|
293
|
-
}
|
294
|
-
}
|
295
|
-
|
296
|
-
return rb_mysql_client_async_result(self);
|
297
|
-
} else {
|
298
|
-
return Qnil;
|
299
|
-
}
|
300
|
-
}
|
301
|
-
|
302
|
-
static VALUE rb_mysql_client_escape(VALUE self, VALUE str) {
|
303
|
-
MYSQL * client;
|
304
|
-
VALUE newStr;
|
305
|
-
unsigned long newLen, oldLen;
|
306
|
-
|
307
|
-
Check_Type(str, T_STRING);
|
308
|
-
oldLen = RSTRING_LEN(str);
|
309
|
-
char escaped[(oldLen*2)+1];
|
310
|
-
|
311
|
-
GetMysql2Client(self, client);
|
312
|
-
if (!client) {
|
313
|
-
rb_raise(cMysql2Error, "closed MySQL connection");
|
314
|
-
return Qnil;
|
315
|
-
}
|
316
|
-
newLen = mysql_real_escape_string(client, escaped, RSTRING_PTR(str), RSTRING_LEN(str));
|
317
|
-
if (newLen == oldLen) {
|
318
|
-
// no need to return a new ruby string if nothing changed
|
319
|
-
return str;
|
320
|
-
} else {
|
321
|
-
newStr = rb_str_new(escaped, newLen);
|
322
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
323
|
-
rb_enc_associate_index(newStr, utf8Encoding);
|
324
|
-
#endif
|
325
|
-
return newStr;
|
326
|
-
}
|
327
|
-
}
|
328
|
-
|
329
|
-
static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE self) {
|
330
|
-
VALUE version = rb_hash_new();
|
331
|
-
rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_client_version()));
|
332
|
-
rb_hash_aset(version, sym_version, rb_str_new2(mysql_get_client_info()));
|
333
|
-
return version;
|
334
|
-
}
|
335
|
-
|
336
|
-
static VALUE rb_mysql_client_server_info(VALUE self) {
|
337
|
-
MYSQL * client;
|
338
|
-
VALUE version;
|
339
|
-
|
340
|
-
GetMysql2Client(self, client);
|
341
|
-
if (!client) {
|
342
|
-
rb_raise(cMysql2Error, "closed MySQL connection");
|
343
|
-
return Qnil;
|
344
|
-
}
|
345
|
-
version = rb_hash_new();
|
346
|
-
rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(client)));
|
347
|
-
rb_hash_aset(version, sym_version, rb_str_new2(mysql_get_server_info(client)));
|
348
|
-
return version;
|
349
|
-
}
|
350
|
-
|
351
|
-
static VALUE rb_mysql_client_socket(VALUE self) {
|
352
|
-
MYSQL * client = GetMysql2Client(self, client);
|
353
|
-
if (!client) {
|
354
|
-
rb_raise(cMysql2Error, "closed MySQL connection");
|
355
|
-
return Qnil;
|
356
|
-
}
|
357
|
-
return INT2NUM(client->net.fd);
|
358
|
-
}
|
359
|
-
|
360
|
-
/*
|
361
|
-
* even though we did rb_thread_select before calling this, a large
|
362
|
-
* response can overflow the socket buffers and cause us to eventually
|
363
|
-
* block while calling mysql_read_query_result
|
364
|
-
*/
|
365
|
-
static VALUE nogvl_read_query_result(void *ptr)
|
366
|
-
{
|
367
|
-
MYSQL * client = ptr;
|
368
|
-
my_bool res = mysql_read_query_result(client);
|
369
|
-
|
370
|
-
return res == 0 ? Qtrue : Qfalse;
|
371
|
-
}
|
372
|
-
|
373
|
-
/* mysql_store_result may (unlikely) read rows off the socket */
|
374
|
-
static VALUE nogvl_store_result(void *ptr)
|
375
|
-
{
|
376
|
-
MYSQL * client = ptr;
|
377
|
-
return (VALUE)mysql_store_result(client);
|
378
|
-
}
|
379
|
-
|
380
|
-
static VALUE rb_mysql_client_async_result(VALUE self) {
|
381
|
-
MYSQL * client;
|
382
|
-
MYSQL_RES * result;
|
383
|
-
GetMysql2Client(self, client);
|
384
|
-
if (!client) {
|
385
|
-
rb_raise(cMysql2Error, "closed MySQL connection");
|
386
|
-
return Qnil;
|
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
|
-
}
|
391
|
-
|
392
|
-
result = (MYSQL_RES *)rb_thread_blocking_region(nogvl_store_result, client, RUBY_UBF_IO, 0);
|
393
|
-
if (result == NULL) {
|
394
|
-
if (mysql_field_count(client) != 0) {
|
395
|
-
rb_raise_mysql2_error(client);
|
396
|
-
}
|
397
|
-
return Qnil;
|
398
|
-
}
|
399
|
-
|
400
|
-
return rb_mysql_result_to_obj(result);
|
401
|
-
}
|
402
|
-
|
403
|
-
static VALUE rb_mysql_client_last_id(VALUE self) {
|
404
|
-
MYSQL * client;
|
405
|
-
GetMysql2Client(self, client);
|
406
|
-
if (!client) {
|
407
|
-
rb_raise(cMysql2Error, "closed MySQL connection");
|
408
|
-
return Qnil;
|
409
|
-
}
|
410
|
-
return ULL2NUM(mysql_insert_id(client));
|
411
|
-
}
|
412
|
-
|
413
|
-
static VALUE rb_mysql_client_affected_rows(VALUE self) {
|
414
|
-
MYSQL * client;
|
415
|
-
GetMysql2Client(self, client);
|
416
|
-
if (!client) {
|
417
|
-
rb_raise(cMysql2Error, "closed MySQL connection");
|
418
|
-
return Qnil;
|
419
|
-
}
|
420
|
-
return ULL2NUM(mysql_affected_rows(client));
|
421
|
-
}
|
422
|
-
|
423
|
-
/* Mysql2::Result */
|
424
|
-
static VALUE rb_mysql_result_to_obj(MYSQL_RES * r) {
|
425
|
-
VALUE obj;
|
426
|
-
mysql2_result_wrapper * wrapper;
|
427
|
-
obj = Data_Make_Struct(cMysql2Result, mysql2_result_wrapper, rb_mysql_result_mark, rb_mysql_result_free, wrapper);
|
428
|
-
wrapper->numberOfFields = 0;
|
429
|
-
wrapper->numberOfRows = 0;
|
430
|
-
wrapper->lastRowProcessed = 0;
|
431
|
-
wrapper->resultFreed = 0;
|
432
|
-
wrapper->result = r;
|
433
|
-
rb_obj_call_init(obj, 0, NULL);
|
434
|
-
return obj;
|
435
|
-
}
|
436
|
-
|
437
|
-
/* this may be called manually or during GC */
|
438
|
-
static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper) {
|
439
|
-
if (wrapper && wrapper->resultFreed != 1) {
|
440
|
-
mysql_free_result(wrapper->result);
|
441
|
-
wrapper->resultFreed = 1;
|
442
|
-
}
|
443
|
-
}
|
444
|
-
|
445
|
-
/* this is called during GC */
|
446
|
-
static void rb_mysql_result_free(void * wrapper) {
|
447
|
-
mysql2_result_wrapper * w = wrapper;
|
448
|
-
/* FIXME: this may call flush_use_result, which can hit the socket */
|
449
|
-
rb_mysql_result_free_result(w);
|
450
|
-
xfree(wrapper);
|
451
|
-
}
|
452
|
-
|
453
|
-
static void rb_mysql_result_mark(void * wrapper) {
|
454
|
-
mysql2_result_wrapper * w = wrapper;
|
455
|
-
if (w) {
|
456
|
-
rb_gc_mark(w->fields);
|
457
|
-
rb_gc_mark(w->rows);
|
458
|
-
}
|
459
|
-
}
|
460
|
-
|
461
|
-
/*
|
462
|
-
* for small results, this won't hit the network, but there's no
|
463
|
-
* reliable way for us to tell this so we'll always release the GVL
|
464
|
-
* to be safe
|
465
|
-
*/
|
466
|
-
static VALUE nogvl_fetch_row(void *ptr)
|
467
|
-
{
|
468
|
-
MYSQL_RES *result = ptr;
|
469
|
-
|
470
|
-
return (VALUE)mysql_fetch_row(result);
|
471
|
-
}
|
472
|
-
|
473
|
-
static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self) {
|
474
|
-
VALUE rowHash, opts, block;
|
475
|
-
mysql2_result_wrapper * wrapper;
|
476
|
-
MYSQL_ROW row;
|
477
|
-
MYSQL_FIELD * fields = NULL;
|
478
|
-
unsigned int i = 0, symbolizeKeys = 0;
|
479
|
-
unsigned long * fieldLengths;
|
480
|
-
void * ptr;
|
481
|
-
|
482
|
-
GetMysql2Result(self, wrapper);
|
483
|
-
|
484
|
-
if (rb_scan_args(argc, argv, "01&", &opts, &block) == 1) {
|
485
|
-
Check_Type(opts, T_HASH);
|
486
|
-
if (rb_hash_aref(opts, sym_symbolize_keys) == Qtrue) {
|
487
|
-
symbolizeKeys = 1;
|
488
|
-
}
|
489
|
-
}
|
490
|
-
|
491
|
-
ptr = wrapper->result;
|
492
|
-
row = (MYSQL_ROW)rb_thread_blocking_region(nogvl_fetch_row, ptr, RUBY_UBF_IO, 0);
|
493
|
-
if (row == NULL) {
|
494
|
-
return Qnil;
|
495
|
-
}
|
496
|
-
|
497
|
-
if (wrapper->numberOfFields == 0) {
|
498
|
-
wrapper->numberOfFields = mysql_num_fields(wrapper->result);
|
499
|
-
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
500
|
-
}
|
501
|
-
|
502
|
-
rowHash = rb_hash_new();
|
503
|
-
fields = mysql_fetch_fields(wrapper->result);
|
504
|
-
fieldLengths = mysql_fetch_lengths(wrapper->result);
|
505
|
-
for (i = 0; i < wrapper->numberOfFields; i++) {
|
506
|
-
|
507
|
-
// lazily create fields, but only once
|
508
|
-
// we'll use cached versions from here on out
|
509
|
-
VALUE field = rb_ary_entry(wrapper->fields, i);
|
510
|
-
if (field == Qnil) {
|
511
|
-
if (symbolizeKeys) {
|
512
|
-
char buf[fields[i].name_length+1];
|
513
|
-
memcpy(buf, fields[i].name, fields[i].name_length);
|
514
|
-
buf[fields[i].name_length] = 0;
|
515
|
-
field = ID2SYM(rb_intern(buf));
|
516
|
-
} else {
|
517
|
-
field = rb_str_new(fields[i].name, fields[i].name_length);
|
518
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
519
|
-
rb_enc_associate_index(field, utf8Encoding);
|
520
|
-
#endif
|
521
|
-
}
|
522
|
-
rb_ary_store(wrapper->fields, i, field);
|
523
|
-
}
|
524
|
-
|
525
|
-
if (row[i]) {
|
526
|
-
VALUE val;
|
527
|
-
switch(fields[i].type) {
|
528
|
-
case MYSQL_TYPE_NULL: // NULL-type field
|
529
|
-
val = Qnil;
|
530
|
-
break;
|
531
|
-
case MYSQL_TYPE_BIT: // BIT field (MySQL 5.0.3 and up)
|
532
|
-
val = rb_str_new(row[i], fieldLengths[i]);
|
533
|
-
break;
|
534
|
-
case MYSQL_TYPE_TINY: // TINYINT field
|
535
|
-
case MYSQL_TYPE_SHORT: // SMALLINT field
|
536
|
-
case MYSQL_TYPE_LONG: // INTEGER field
|
537
|
-
case MYSQL_TYPE_INT24: // MEDIUMINT field
|
538
|
-
case MYSQL_TYPE_LONGLONG: // BIGINT field
|
539
|
-
case MYSQL_TYPE_YEAR: // YEAR field
|
540
|
-
val = rb_cstr2inum(row[i], 10);
|
541
|
-
break;
|
542
|
-
case MYSQL_TYPE_DECIMAL: // DECIMAL or NUMERIC field
|
543
|
-
case MYSQL_TYPE_NEWDECIMAL: // Precision math DECIMAL or NUMERIC field (MySQL 5.0.3 and up)
|
544
|
-
val = rb_funcall(cBigDecimal, intern_new, 1, rb_str_new(row[i], fieldLengths[i]));
|
545
|
-
break;
|
546
|
-
case MYSQL_TYPE_FLOAT: // FLOAT field
|
547
|
-
case MYSQL_TYPE_DOUBLE: // DOUBLE or REAL field
|
548
|
-
val = rb_float_new(strtod(row[i], NULL));
|
549
|
-
break;
|
550
|
-
case MYSQL_TYPE_TIME: { // TIME field
|
551
|
-
int hour, min, sec, tokens;
|
552
|
-
tokens = sscanf(row[i], "%2d:%2d:%2d", &hour, &min, &sec);
|
553
|
-
val = rb_funcall(rb_cTime, intern_utc, 6, INT2NUM(0), INT2NUM(1), INT2NUM(1), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
|
554
|
-
break;
|
555
|
-
}
|
556
|
-
case MYSQL_TYPE_TIMESTAMP: // TIMESTAMP field
|
557
|
-
case MYSQL_TYPE_DATETIME: { // DATETIME field
|
558
|
-
int year, month, day, hour, min, sec, tokens;
|
559
|
-
tokens = sscanf(row[i], "%4d-%2d-%2d %2d:%2d:%2d", &year, &month, &day, &hour, &min, &sec);
|
560
|
-
if (year+month+day+hour+min+sec == 0) {
|
561
|
-
val = Qnil;
|
562
|
-
} else {
|
563
|
-
if (month < 1 || day < 1) {
|
564
|
-
rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
|
565
|
-
val = Qnil;
|
566
|
-
} else {
|
567
|
-
val = rb_funcall(rb_cTime, intern_utc, 6, INT2NUM(year), INT2NUM(month), INT2NUM(day), INT2NUM(hour), INT2NUM(min), INT2NUM(sec));
|
568
|
-
}
|
569
|
-
}
|
570
|
-
break;
|
571
|
-
}
|
572
|
-
case MYSQL_TYPE_DATE: // DATE field
|
573
|
-
case MYSQL_TYPE_NEWDATE: { // Newer const used > 5.0
|
574
|
-
int year, month, day, tokens;
|
575
|
-
tokens = sscanf(row[i], "%4d-%2d-%2d", &year, &month, &day);
|
576
|
-
if (year+month+day == 0) {
|
577
|
-
val = Qnil;
|
578
|
-
} else {
|
579
|
-
if (month < 1 || day < 1) {
|
580
|
-
rb_raise(cMysql2Error, "Invalid date: %s", row[i]);
|
581
|
-
val = Qnil;
|
582
|
-
} else {
|
583
|
-
val = rb_funcall(cDate, intern_new, 3, INT2NUM(year), INT2NUM(month), INT2NUM(day));
|
584
|
-
}
|
585
|
-
}
|
586
|
-
break;
|
587
|
-
}
|
588
|
-
case MYSQL_TYPE_TINY_BLOB:
|
589
|
-
case MYSQL_TYPE_MEDIUM_BLOB:
|
590
|
-
case MYSQL_TYPE_LONG_BLOB:
|
591
|
-
case MYSQL_TYPE_BLOB:
|
592
|
-
case MYSQL_TYPE_VAR_STRING:
|
593
|
-
case MYSQL_TYPE_VARCHAR:
|
594
|
-
case MYSQL_TYPE_STRING: // CHAR or BINARY field
|
595
|
-
case MYSQL_TYPE_SET: // SET field
|
596
|
-
case MYSQL_TYPE_ENUM: // ENUM field
|
597
|
-
case MYSQL_TYPE_GEOMETRY: // Spatial fielda
|
598
|
-
default:
|
599
|
-
val = rb_str_new(row[i], fieldLengths[i]);
|
600
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
601
|
-
// rudimentary check for binary content
|
602
|
-
if ((fields[i].flags & BINARY_FLAG) || fields[i].charsetnr == 63) {
|
603
|
-
rb_enc_associate_index(val, binaryEncoding);
|
604
|
-
} else {
|
605
|
-
rb_enc_associate_index(val, utf8Encoding);
|
606
|
-
}
|
607
|
-
#endif
|
608
|
-
break;
|
609
|
-
}
|
610
|
-
rb_hash_aset(rowHash, field, val);
|
611
|
-
} else {
|
612
|
-
rb_hash_aset(rowHash, field, Qnil);
|
613
|
-
}
|
614
|
-
}
|
615
|
-
return rowHash;
|
616
|
-
}
|
617
|
-
|
618
|
-
static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self) {
|
619
|
-
VALUE opts, block;
|
620
|
-
mysql2_result_wrapper * wrapper;
|
621
|
-
unsigned long i;
|
622
|
-
|
623
|
-
GetMysql2Result(self, wrapper);
|
624
|
-
|
625
|
-
rb_scan_args(argc, argv, "01&", &opts, &block);
|
626
|
-
|
627
|
-
if (wrapper->lastRowProcessed == 0) {
|
628
|
-
wrapper->numberOfRows = mysql_num_rows(wrapper->result);
|
629
|
-
if (wrapper->numberOfRows == 0) {
|
630
|
-
return Qnil;
|
631
|
-
}
|
632
|
-
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
633
|
-
}
|
634
|
-
|
635
|
-
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
636
|
-
// we've already read the entire dataset from the C result into our
|
637
|
-
// internal array. Lets hand that over to the user since it's ready to go
|
638
|
-
for (i = 0; i < wrapper->numberOfRows; i++) {
|
639
|
-
rb_yield(rb_ary_entry(wrapper->rows, i));
|
640
|
-
}
|
641
|
-
} else {
|
642
|
-
unsigned long rowsProcessed = 0;
|
643
|
-
rowsProcessed = RARRAY_LEN(wrapper->rows);
|
644
|
-
for (i = 0; i < wrapper->numberOfRows; i++) {
|
645
|
-
VALUE row;
|
646
|
-
if (i < rowsProcessed) {
|
647
|
-
row = rb_ary_entry(wrapper->rows, i);
|
648
|
-
} else {
|
649
|
-
row = rb_mysql_result_fetch_row(argc, argv, self);
|
650
|
-
rb_ary_store(wrapper->rows, i, row);
|
651
|
-
wrapper->lastRowProcessed++;
|
652
|
-
}
|
653
|
-
|
654
|
-
if (row == Qnil) {
|
655
|
-
// we don't need the mysql C dataset around anymore, peace it
|
656
|
-
rb_mysql_result_free_result(wrapper);
|
657
|
-
return Qnil;
|
658
|
-
}
|
659
|
-
|
660
|
-
if (block != Qnil) {
|
661
|
-
rb_yield(row);
|
662
|
-
}
|
663
|
-
}
|
664
|
-
if (wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
665
|
-
// we don't need the mysql C dataset around anymore, peace it
|
666
|
-
rb_mysql_result_free_result(wrapper);
|
667
|
-
}
|
668
|
-
}
|
669
|
-
|
670
|
-
return wrapper->rows;
|
671
|
-
}
|
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
|
-
|
689
|
-
/* Ruby Extension initializer */
|
690
|
-
void Init_mysql2_ext() {
|
691
|
-
rb_require("date");
|
692
|
-
rb_require("bigdecimal");
|
693
|
-
|
694
|
-
cBigDecimal = rb_const_get(rb_cObject, rb_intern("BigDecimal"));
|
695
|
-
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
696
|
-
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
697
|
-
|
698
|
-
VALUE mMysql2 = rb_define_module("Mysql2");
|
699
|
-
|
700
|
-
VALUE cMysql2Client = rb_define_class_under(mMysql2, "Client", rb_cObject);
|
701
|
-
rb_define_singleton_method(cMysql2Client, "new", rb_mysql_client_new, -1);
|
702
|
-
rb_define_method(cMysql2Client, "initialize", rb_mysql_client_init, -1);
|
703
|
-
rb_define_method(cMysql2Client, "close", rb_mysql_client_close, 0);
|
704
|
-
rb_define_method(cMysql2Client, "query", rb_mysql_client_query, -1);
|
705
|
-
rb_define_method(cMysql2Client, "escape", rb_mysql_client_escape, 1);
|
706
|
-
rb_define_method(cMysql2Client, "info", rb_mysql_client_info, 0);
|
707
|
-
rb_define_method(cMysql2Client, "server_info", rb_mysql_client_server_info, 0);
|
708
|
-
rb_define_method(cMysql2Client, "socket", rb_mysql_client_socket, 0);
|
709
|
-
rb_define_method(cMysql2Client, "async_result", rb_mysql_client_async_result, 0);
|
710
|
-
rb_define_method(cMysql2Client, "last_id", rb_mysql_client_last_id, 0);
|
711
|
-
rb_define_method(cMysql2Client, "affected_rows", rb_mysql_client_affected_rows, 0);
|
712
|
-
|
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);
|
716
|
-
|
717
|
-
cMysql2Result = rb_define_class_under(mMysql2, "Result", rb_cObject);
|
718
|
-
rb_define_method(cMysql2Result, "each", rb_mysql_result_each, -1);
|
719
|
-
|
720
|
-
VALUE mEnumerable = rb_const_get(rb_cObject, rb_intern("Enumerable"));
|
721
|
-
rb_include_module(cMysql2Result, mEnumerable);
|
722
|
-
|
723
|
-
intern_new = rb_intern("new");
|
724
|
-
intern_utc = rb_intern("utc");
|
725
|
-
|
726
|
-
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
727
|
-
sym_reconnect = ID2SYM(rb_intern("reconnect"));
|
728
|
-
sym_database = ID2SYM(rb_intern("database"));
|
729
|
-
sym_username = ID2SYM(rb_intern("username"));
|
730
|
-
sym_password = ID2SYM(rb_intern("password"));
|
731
|
-
sym_host = ID2SYM(rb_intern("host"));
|
732
|
-
sym_port = ID2SYM(rb_intern("port"));
|
733
|
-
sym_socket = ID2SYM(rb_intern("socket"));
|
734
|
-
sym_connect_timeout = ID2SYM(rb_intern("connect_timeout"));
|
735
|
-
sym_id = ID2SYM(rb_intern("id"));
|
736
|
-
sym_version = ID2SYM(rb_intern("version"));
|
737
|
-
sym_sslkey = ID2SYM(rb_intern("sslkey"));
|
738
|
-
sym_sslcert = ID2SYM(rb_intern("sslcert"));
|
739
|
-
sym_sslca = ID2SYM(rb_intern("sslca"));
|
740
|
-
sym_sslcapath = ID2SYM(rb_intern("sslcapath"));
|
741
|
-
sym_sslcipher = ID2SYM(rb_intern("sslcipher"));
|
742
|
-
sym_async = ID2SYM(rb_intern("async"));
|
743
|
-
|
744
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
745
|
-
utf8Encoding = rb_enc_find_index("UTF-8");
|
746
|
-
binaryEncoding = rb_enc_find_index("binary");
|
747
|
-
#endif
|
748
|
-
}
|