mysql2 0.3.1 → 0.5.2
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1 -151
- data/LICENSE +21 -0
- data/README.md +634 -0
- data/examples/eventmachine.rb +1 -3
- data/examples/threaded.rb +5 -9
- data/ext/mysql2/client.c +1154 -342
- data/ext/mysql2/client.h +20 -33
- data/ext/mysql2/extconf.rb +229 -37
- data/ext/mysql2/infile.c +122 -0
- data/ext/mysql2/infile.h +1 -0
- data/ext/mysql2/mysql2_ext.c +3 -1
- data/ext/mysql2/mysql2_ext.h +18 -16
- data/ext/mysql2/mysql_enc_name_to_ruby.h +168 -0
- data/ext/mysql2/mysql_enc_to_ruby.h +259 -0
- data/ext/mysql2/result.c +708 -191
- data/ext/mysql2/result.h +15 -6
- data/ext/mysql2/statement.c +602 -0
- data/ext/mysql2/statement.h +17 -0
- data/ext/mysql2/wait_for_single_fd.h +37 -0
- data/lib/mysql2.rb +69 -7
- data/lib/mysql2/client.rb +126 -211
- data/lib/mysql2/console.rb +5 -0
- data/lib/mysql2/em.rb +24 -8
- data/lib/mysql2/error.rb +93 -8
- data/lib/mysql2/field.rb +3 -0
- data/lib/mysql2/result.rb +2 -0
- data/lib/mysql2/statement.rb +11 -0
- data/lib/mysql2/version.rb +2 -2
- data/spec/configuration.yml.example +11 -0
- data/spec/em/em_spec.rb +101 -15
- data/spec/my.cnf.example +9 -0
- data/spec/mysql2/client_spec.rb +874 -232
- data/spec/mysql2/error_spec.rb +55 -46
- data/spec/mysql2/result_spec.rb +306 -154
- data/spec/mysql2/statement_spec.rb +712 -0
- data/spec/spec_helper.rb +103 -57
- data/spec/ssl/ca-cert.pem +17 -0
- data/spec/ssl/ca-key.pem +27 -0
- data/spec/ssl/ca.cnf +22 -0
- data/spec/ssl/cert.cnf +22 -0
- data/spec/ssl/client-cert.pem +17 -0
- data/spec/ssl/client-key.pem +27 -0
- data/spec/ssl/client-req.pem +15 -0
- data/spec/ssl/gen_certs.sh +48 -0
- data/spec/ssl/pkcs8-client-key.pem +28 -0
- data/spec/ssl/pkcs8-server-key.pem +28 -0
- data/spec/ssl/server-cert.pem +17 -0
- data/spec/ssl/server-key.pem +27 -0
- data/spec/ssl/server-req.pem +15 -0
- data/spec/test_data +1 -0
- data/support/5072E1F5.asc +432 -0
- data/support/libmysql.def +219 -0
- data/support/mysql_enc_to_ruby.rb +81 -0
- data/support/ruby_enc_to_mysql.rb +61 -0
- metadata +82 -188
- data/.gitignore +0 -12
- data/.rspec +0 -2
- data/.rvmrc +0 -1
- data/Gemfile +0 -3
- data/MIT-LICENSE +0 -20
- data/README.rdoc +0 -257
- data/Rakefile +0 -5
- data/benchmark/active_record.rb +0 -51
- data/benchmark/active_record_threaded.rb +0 -42
- data/benchmark/allocations.rb +0 -33
- data/benchmark/escape.rb +0 -36
- data/benchmark/query_with_mysql_casting.rb +0 -80
- data/benchmark/query_without_mysql_casting.rb +0 -47
- data/benchmark/sequel.rb +0 -37
- data/benchmark/setup_db.rb +0 -119
- data/benchmark/threaded.rb +0 -44
- data/lib/active_record/connection_adapters/em_mysql2_adapter.rb +0 -64
- data/lib/active_record/fiber_patches.rb +0 -104
- data/lib/mysql2/em_fiber.rb +0 -31
- data/mysql2.gemspec +0 -32
- data/spec/em/em_fiber_spec.rb +0 -22
- data/tasks/benchmarks.rake +0 -20
- data/tasks/compile.rake +0 -71
- data/tasks/rspec.rake +0 -16
- data/tasks/vendor_mysql.rake +0 -40
@@ -0,0 +1,712 @@
|
|
1
|
+
require './spec/spec_helper.rb'
|
2
|
+
|
3
|
+
RSpec.describe Mysql2::Statement do
|
4
|
+
before :each do
|
5
|
+
@client = new_client(encoding: "utf8")
|
6
|
+
end
|
7
|
+
|
8
|
+
def stmt_count
|
9
|
+
# Use the performance schema in MySQL 5.7 and above
|
10
|
+
@client.query("SELECT COUNT(1) AS count FROM performance_schema.prepared_statements_instances").first['count'].to_i
|
11
|
+
rescue Mysql2::Error
|
12
|
+
# Fall back to the global prepapred statement counter
|
13
|
+
@client.query("SHOW STATUS LIKE 'Prepared_stmt_count'").first['Value'].to_i
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should create a statement" do
|
17
|
+
statement = nil
|
18
|
+
expect { statement = @client.prepare 'SELECT 1' }.to change(&method(:stmt_count)).by(1)
|
19
|
+
expect(statement).to be_an_instance_of(Mysql2::Statement)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should raise an exception when server disconnects" do
|
23
|
+
@client.close
|
24
|
+
expect { @client.prepare 'SELECT 1' }.to raise_error(Mysql2::Error)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should tell us the param count" do
|
28
|
+
statement = @client.prepare 'SELECT ?, ?'
|
29
|
+
expect(statement.param_count).to eq(2)
|
30
|
+
|
31
|
+
statement2 = @client.prepare 'SELECT 1'
|
32
|
+
expect(statement2.param_count).to eq(0)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should tell us the field count" do
|
36
|
+
statement = @client.prepare 'SELECT ?, ?'
|
37
|
+
expect(statement.field_count).to eq(2)
|
38
|
+
|
39
|
+
statement2 = @client.prepare 'SELECT 1'
|
40
|
+
expect(statement2.field_count).to eq(1)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should let us execute our statement" do
|
44
|
+
statement = @client.prepare 'SELECT 1'
|
45
|
+
expect(statement.execute).not_to eq(nil)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should raise an exception without a block" do
|
49
|
+
statement = @client.prepare 'SELECT 1'
|
50
|
+
expect { statement.execute.each }.to raise_error(LocalJumpError)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should tell us the result count" do
|
54
|
+
statement = @client.prepare 'SELECT 1'
|
55
|
+
result = statement.execute
|
56
|
+
expect(result.count).to eq(1)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should let us iterate over results" do
|
60
|
+
statement = @client.prepare 'SELECT 1'
|
61
|
+
result = statement.execute
|
62
|
+
rows = []
|
63
|
+
result.each { |r| rows << r }
|
64
|
+
expect(rows).to eq([{ "1" => 1 }])
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should handle booleans" do
|
68
|
+
stmt = @client.prepare('SELECT ? AS `true`, ? AS `false`')
|
69
|
+
result = stmt.execute(true, false)
|
70
|
+
expect(result.to_a).to eq(['true' => 1, 'false' => 0])
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should handle bignum but in int64_t" do
|
74
|
+
stmt = @client.prepare('SELECT ? AS max, ? AS min')
|
75
|
+
int64_max = (1 << 63) - 1
|
76
|
+
int64_min = -(1 << 63)
|
77
|
+
result = stmt.execute(int64_max, int64_min)
|
78
|
+
expect(result.to_a).to eq(['max' => int64_max, 'min' => int64_min])
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should handle bignum but beyond int64_t" do
|
82
|
+
stmt = @client.prepare('SELECT ? AS max1, ? AS max2, ? AS max3, ? AS min1, ? AS min2, ? AS min3')
|
83
|
+
int64_max1 = (1 << 63)
|
84
|
+
int64_max2 = (1 << 64) - 1
|
85
|
+
int64_max3 = 1 << 64
|
86
|
+
int64_min1 = -(1 << 63) - 1
|
87
|
+
int64_min2 = -(1 << 64) + 1
|
88
|
+
int64_min3 = -0xC000000000000000
|
89
|
+
result = stmt.execute(int64_max1, int64_max2, int64_max3, int64_min1, int64_min2, int64_min3)
|
90
|
+
expect(result.to_a).to eq(['max1' => int64_max1, 'max2' => int64_max2, 'max3' => int64_max3, 'min1' => int64_min1, 'min2' => int64_min2, 'min3' => int64_min3])
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should accept keyword arguments on statement execute" do
|
94
|
+
stmt = @client.prepare 'SELECT 1 AS a'
|
95
|
+
|
96
|
+
expect(stmt.execute(as: :hash).first).to eq("a" => 1)
|
97
|
+
expect(stmt.execute(as: :array).first).to eq([1])
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should accept bind arguments and keyword arguments on statement execute" do
|
101
|
+
stmt = @client.prepare 'SELECT ? AS a'
|
102
|
+
|
103
|
+
expect(stmt.execute(1, as: :hash).first).to eq("a" => 1)
|
104
|
+
expect(stmt.execute(1, as: :array).first).to eq([1])
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should keep its result after other query" do
|
108
|
+
@client.query 'USE test'
|
109
|
+
@client.query 'CREATE TABLE IF NOT EXISTS mysql2_stmt_q(a int)'
|
110
|
+
@client.query 'INSERT INTO mysql2_stmt_q (a) VALUES (1), (2)'
|
111
|
+
stmt = @client.prepare('SELECT a FROM mysql2_stmt_q WHERE a = ?')
|
112
|
+
result1 = stmt.execute(1)
|
113
|
+
result2 = stmt.execute(2)
|
114
|
+
expect(result2.first).to eq("a" => 2)
|
115
|
+
expect(result1.first).to eq("a" => 1)
|
116
|
+
@client.query 'DROP TABLE IF EXISTS mysql2_stmt_q'
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should be reusable 1000 times" do
|
120
|
+
statement = @client.prepare 'SELECT 1'
|
121
|
+
1000.times do
|
122
|
+
result = statement.execute
|
123
|
+
expect(result.to_a.length).to eq(1)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should be reusable 10000 times" do
|
128
|
+
statement = @client.prepare 'SELECT 1'
|
129
|
+
10000.times do
|
130
|
+
result = statement.execute
|
131
|
+
expect(result.to_a.length).to eq(1)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should handle comparisons and likes" do
|
136
|
+
@client.query 'USE test'
|
137
|
+
@client.query 'CREATE TABLE IF NOT EXISTS mysql2_stmt_q(a int, b varchar(10))'
|
138
|
+
@client.query 'INSERT INTO mysql2_stmt_q (a, b) VALUES (1, "Hello"), (2, "World")'
|
139
|
+
statement = @client.prepare 'SELECT * FROM mysql2_stmt_q WHERE a < ?'
|
140
|
+
results = statement.execute(2)
|
141
|
+
expect(results.first).to eq("a" => 1, "b" => "Hello")
|
142
|
+
|
143
|
+
statement = @client.prepare 'SELECT * FROM mysql2_stmt_q WHERE b LIKE ?'
|
144
|
+
results = statement.execute('%orld')
|
145
|
+
expect(results.first).to eq("a" => 2, "b" => "World")
|
146
|
+
|
147
|
+
@client.query 'DROP TABLE IF EXISTS mysql2_stmt_q'
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should select dates" do
|
151
|
+
statement = @client.prepare 'SELECT NOW()'
|
152
|
+
result = statement.execute
|
153
|
+
expect(result.first.first[1]).to be_an_instance_of(Time)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should prepare Date values" do
|
157
|
+
now = Date.today
|
158
|
+
statement = @client.prepare('SELECT ? AS a')
|
159
|
+
result = statement.execute(now)
|
160
|
+
expect(result.first['a'].to_s).to eql(now.strftime('%F'))
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should prepare Time values with microseconds" do
|
164
|
+
now = Time.now
|
165
|
+
statement = @client.prepare('SELECT ? AS a')
|
166
|
+
result = statement.execute(now)
|
167
|
+
# microseconds is six digits after the decimal, but only test on 5 significant figures
|
168
|
+
expect(result.first['a'].strftime('%F %T.%5N %z')).to eql(now.strftime('%F %T.%5N %z'))
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should prepare DateTime values with microseconds" do
|
172
|
+
now = DateTime.now
|
173
|
+
statement = @client.prepare('SELECT ? AS a')
|
174
|
+
result = statement.execute(now)
|
175
|
+
# microseconds is six digits after the decimal, but only test on 5 significant figures
|
176
|
+
expect(result.first['a'].strftime('%F %T.%5N %z')).to eql(now.strftime('%F %T.%5N %z'))
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should tell us about the fields" do
|
180
|
+
statement = @client.prepare 'SELECT 1 as foo, 2'
|
181
|
+
statement.execute
|
182
|
+
list = statement.fields
|
183
|
+
expect(list.length).to eq(2)
|
184
|
+
expect(list.first).to eq('foo')
|
185
|
+
expect(list[1]).to eq('2')
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should handle as a decimal binding a BigDecimal" do
|
189
|
+
stmt = @client.prepare('SELECT ? AS decimal_test')
|
190
|
+
test_result = stmt.execute(BigDecimal("123.45")).first
|
191
|
+
expect(test_result['decimal_test']).to be_an_instance_of(BigDecimal)
|
192
|
+
expect(test_result['decimal_test']).to eql(123.45)
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should update a DECIMAL value passing a BigDecimal" do
|
196
|
+
@client.query 'USE test'
|
197
|
+
@client.query 'DROP TABLE IF EXISTS mysql2_stmt_decimal_test'
|
198
|
+
@client.query 'CREATE TABLE mysql2_stmt_decimal_test (decimal_test DECIMAL(10,3))'
|
199
|
+
|
200
|
+
@client.prepare("INSERT INTO mysql2_stmt_decimal_test VALUES (?)").execute(BigDecimal("123.45"))
|
201
|
+
|
202
|
+
test_result = @client.query("SELECT * FROM mysql2_stmt_decimal_test").first
|
203
|
+
expect(test_result['decimal_test']).to eql(123.45)
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should warn but still work if cache_rows is set to false" do
|
207
|
+
statement = @client.prepare 'SELECT 1'
|
208
|
+
result = nil
|
209
|
+
expect { result = statement.execute(cache_rows: false).to_a }.to output(/:cache_rows is forced for prepared statements/).to_stderr
|
210
|
+
expect(result.length).to eq(1)
|
211
|
+
end
|
212
|
+
|
213
|
+
context "utf8_db" do
|
214
|
+
before(:each) do
|
215
|
+
@client.query("DROP DATABASE IF EXISTS test_mysql2_stmt_utf8")
|
216
|
+
@client.query("CREATE DATABASE test_mysql2_stmt_utf8")
|
217
|
+
@client.query("USE test_mysql2_stmt_utf8")
|
218
|
+
@client.query("CREATE TABLE テーブル (整数 int, 文字列 varchar(32)) charset=utf8")
|
219
|
+
@client.query("INSERT INTO テーブル (整数, 文字列) VALUES (1, 'イチ'), (2, '弐'), (3, 'さん')")
|
220
|
+
end
|
221
|
+
|
222
|
+
after(:each) do
|
223
|
+
@client.query("DROP DATABASE test_mysql2_stmt_utf8")
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should be able to retrieve utf8 field names correctly" do
|
227
|
+
stmt = @client.prepare 'SELECT * FROM `テーブル`'
|
228
|
+
expect(stmt.fields).to eq(%w[整数 文字列])
|
229
|
+
result = stmt.execute
|
230
|
+
|
231
|
+
expect(result.to_a).to eq([{ "整数" => 1, "文字列" => "イチ" }, { "整数" => 2, "文字列" => "弐" }, { "整数" => 3, "文字列" => "さん" }])
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should be able to retrieve utf8 param query correctly" do
|
235
|
+
stmt = @client.prepare 'SELECT 整数 FROM テーブル WHERE 文字列 = ?'
|
236
|
+
expect(stmt.param_count).to eq(1)
|
237
|
+
|
238
|
+
result = stmt.execute 'イチ'
|
239
|
+
|
240
|
+
expect(result.to_a).to eq([{ "整数" => 1 }])
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should be able to retrieve query with param in different encoding correctly" do
|
244
|
+
stmt = @client.prepare 'SELECT 整数 FROM テーブル WHERE 文字列 = ?'
|
245
|
+
expect(stmt.param_count).to eq(1)
|
246
|
+
|
247
|
+
param = 'イチ'.encode("EUC-JP")
|
248
|
+
result = stmt.execute param
|
249
|
+
|
250
|
+
expect(result.to_a).to eq([{ "整数" => 1 }])
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
context "streaming result" do
|
255
|
+
it "should be able to stream query result" do
|
256
|
+
n = 1
|
257
|
+
stmt = @client.prepare("SELECT 1 UNION SELECT 2")
|
258
|
+
stmt.execute(stream: true, cache_rows: false, as: :array).each do |r|
|
259
|
+
case n
|
260
|
+
when 1
|
261
|
+
expect(r).to eq([1])
|
262
|
+
when 2
|
263
|
+
expect(r).to eq([2])
|
264
|
+
else
|
265
|
+
violated "returned more than two rows"
|
266
|
+
end
|
267
|
+
n += 1
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
context "#each" do
|
273
|
+
# note: The current impl. of prepared statement requires results to be cached on #execute except for streaming queries
|
274
|
+
# The drawback of this is that args of Result#each is ignored...
|
275
|
+
|
276
|
+
it "should yield rows as hash's" do
|
277
|
+
@result = @client.prepare("SELECT 1").execute
|
278
|
+
@result.each do |row|
|
279
|
+
expect(row).to be_an_instance_of(Hash)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should yield rows as hash's with symbol keys if :symbolize_keys was set to true" do
|
284
|
+
@result = @client.prepare("SELECT 1").execute(symbolize_keys: true)
|
285
|
+
@result.each do |row|
|
286
|
+
expect(row.keys.first).to be_an_instance_of(Symbol)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should be able to return results as an array" do
|
291
|
+
@result = @client.prepare("SELECT 1").execute(as: :array)
|
292
|
+
@result.each do |row|
|
293
|
+
expect(row).to be_an_instance_of(Array)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
it "should cache previously yielded results by default" do
|
298
|
+
@result = @client.prepare("SELECT 1").execute
|
299
|
+
expect(@result.first.object_id).to eql(@result.first.object_id)
|
300
|
+
end
|
301
|
+
|
302
|
+
it "should yield different value for #first if streaming" do
|
303
|
+
result = @client.prepare("SELECT 1 UNION SELECT 2").execute(stream: true, cache_rows: true)
|
304
|
+
expect(result.first).not_to eql(result.first)
|
305
|
+
end
|
306
|
+
|
307
|
+
it "should yield the same value for #first if streaming is disabled" do
|
308
|
+
result = @client.prepare("SELECT 1 UNION SELECT 2").execute(stream: false)
|
309
|
+
expect(result.first).to eql(result.first)
|
310
|
+
end
|
311
|
+
|
312
|
+
it "should throw an exception if we try to iterate twice when streaming is enabled" do
|
313
|
+
result = @client.prepare("SELECT 1 UNION SELECT 2").execute(stream: true, cache_rows: false)
|
314
|
+
expect do
|
315
|
+
result.each {}
|
316
|
+
result.each {}
|
317
|
+
end.to raise_exception(Mysql2::Error)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
context "#fields" do
|
322
|
+
it "method should exist" do
|
323
|
+
stmt = @client.prepare("SELECT 1")
|
324
|
+
expect(stmt).to respond_to(:fields)
|
325
|
+
end
|
326
|
+
|
327
|
+
it "should return an array of field names in proper order" do
|
328
|
+
stmt = @client.prepare("SELECT 'a', 'b', 'c'")
|
329
|
+
expect(stmt.fields).to eql(%w[a b c])
|
330
|
+
end
|
331
|
+
|
332
|
+
it "should return nil for statement with no result fields" do
|
333
|
+
stmt = @client.prepare("INSERT INTO mysql2_test () VALUES ()")
|
334
|
+
expect(stmt.fields).to eql(nil)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
context "row data type mapping" do
|
339
|
+
let(:test_result) { @client.prepare("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").execute.first }
|
340
|
+
|
341
|
+
it "should return nil for a NULL value" do
|
342
|
+
expect(test_result['null_test']).to be_an_instance_of(NilClass)
|
343
|
+
expect(test_result['null_test']).to eql(nil)
|
344
|
+
end
|
345
|
+
|
346
|
+
it "should return String for a BIT(64) value" do
|
347
|
+
expect(test_result['bit_test']).to be_an_instance_of(String)
|
348
|
+
expect(test_result['bit_test']).to eql("\000\000\000\000\000\000\000\005")
|
349
|
+
end
|
350
|
+
|
351
|
+
it "should return String for a BIT(1) value" do
|
352
|
+
expect(test_result['single_bit_test']).to be_an_instance_of(String)
|
353
|
+
expect(test_result['single_bit_test']).to eql("\001")
|
354
|
+
end
|
355
|
+
|
356
|
+
it "should return Fixnum for a TINYINT value" do
|
357
|
+
expect(num_classes).to include(test_result['tiny_int_test'].class)
|
358
|
+
expect(test_result['tiny_int_test']).to eql(1)
|
359
|
+
end
|
360
|
+
|
361
|
+
context "cast booleans for TINYINT if :cast_booleans is enabled" do
|
362
|
+
# rubocop:disable Style/Semicolon
|
363
|
+
let(:id1) { @client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES ( 1)'; @client.last_id }
|
364
|
+
let(:id2) { @client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES ( 0)'; @client.last_id }
|
365
|
+
let(:id3) { @client.query 'INSERT INTO mysql2_test (bool_cast_test) VALUES (-1)'; @client.last_id }
|
366
|
+
# rubocop:enable Style/Semicolon
|
367
|
+
|
368
|
+
after do
|
369
|
+
@client.query "DELETE from mysql2_test WHERE id IN(#{id1},#{id2},#{id3})"
|
370
|
+
end
|
371
|
+
|
372
|
+
it "should return TrueClass or FalseClass for a TINYINT value if :cast_booleans is enabled" do
|
373
|
+
query = @client.prepare 'SELECT bool_cast_test FROM mysql2_test WHERE id = ?'
|
374
|
+
result1 = query.execute id1, cast_booleans: true
|
375
|
+
result2 = query.execute id2, cast_booleans: true
|
376
|
+
result3 = query.execute id3, cast_booleans: true
|
377
|
+
expect(result1.first['bool_cast_test']).to be true
|
378
|
+
expect(result2.first['bool_cast_test']).to be false
|
379
|
+
expect(result3.first['bool_cast_test']).to be true
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
context "cast booleans for BIT(1) if :cast_booleans is enabled" do
|
384
|
+
# rubocop:disable Style/Semicolon
|
385
|
+
let(:id1) { @client.query 'INSERT INTO mysql2_test (single_bit_test) VALUES (1)'; @client.last_id }
|
386
|
+
let(:id2) { @client.query 'INSERT INTO mysql2_test (single_bit_test) VALUES (0)'; @client.last_id }
|
387
|
+
# rubocop:enable Style/Semicolon
|
388
|
+
|
389
|
+
after do
|
390
|
+
@client.query "DELETE from mysql2_test WHERE id IN(#{id1},#{id2})"
|
391
|
+
end
|
392
|
+
|
393
|
+
it "should return TrueClass or FalseClass for a BIT(1) value if :cast_booleans is enabled" do
|
394
|
+
query = @client.prepare 'SELECT single_bit_test FROM mysql2_test WHERE id = ?'
|
395
|
+
result1 = query.execute id1, cast_booleans: true
|
396
|
+
result2 = query.execute id2, cast_booleans: true
|
397
|
+
expect(result1.first['single_bit_test']).to be true
|
398
|
+
expect(result2.first['single_bit_test']).to be false
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
it "should return Fixnum for a SMALLINT value" do
|
403
|
+
expect(num_classes).to include(test_result['small_int_test'].class)
|
404
|
+
expect(test_result['small_int_test']).to eql(10)
|
405
|
+
end
|
406
|
+
|
407
|
+
it "should return Fixnum for a MEDIUMINT value" do
|
408
|
+
expect(num_classes).to include(test_result['medium_int_test'].class)
|
409
|
+
expect(test_result['medium_int_test']).to eql(10)
|
410
|
+
end
|
411
|
+
|
412
|
+
it "should return Fixnum for an INT value" do
|
413
|
+
expect(num_classes).to include(test_result['int_test'].class)
|
414
|
+
expect(test_result['int_test']).to eql(10)
|
415
|
+
end
|
416
|
+
|
417
|
+
it "should return Fixnum for a BIGINT value" do
|
418
|
+
expect(num_classes).to include(test_result['big_int_test'].class)
|
419
|
+
expect(test_result['big_int_test']).to eql(10)
|
420
|
+
end
|
421
|
+
|
422
|
+
it "should return Fixnum for a YEAR value" do
|
423
|
+
expect(num_classes).to include(test_result['year_test'].class)
|
424
|
+
expect(test_result['year_test']).to eql(2009)
|
425
|
+
end
|
426
|
+
|
427
|
+
it "should return BigDecimal for a DECIMAL value" do
|
428
|
+
expect(test_result['decimal_test']).to be_an_instance_of(BigDecimal)
|
429
|
+
expect(test_result['decimal_test']).to eql(10.3)
|
430
|
+
end
|
431
|
+
|
432
|
+
it "should return Float for a FLOAT value" do
|
433
|
+
expect(test_result['float_test']).to be_an_instance_of(Float)
|
434
|
+
expect(test_result['float_test']).to be_within(1e-5).of(10.3)
|
435
|
+
end
|
436
|
+
|
437
|
+
it "should return Float for a DOUBLE value" do
|
438
|
+
expect(test_result['double_test']).to be_an_instance_of(Float)
|
439
|
+
expect(test_result['double_test']).to eql(10.3)
|
440
|
+
end
|
441
|
+
|
442
|
+
it "should return Time for a DATETIME value when within the supported range" do
|
443
|
+
expect(test_result['date_time_test']).to be_an_instance_of(Time)
|
444
|
+
expect(test_result['date_time_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 11:44:00')
|
445
|
+
end
|
446
|
+
|
447
|
+
it "should return Time when timestamp is < 1901-12-13 20:45:52" do
|
448
|
+
r = @client.prepare("SELECT CAST('1901-12-13 20:45:51' AS DATETIME) as test").execute
|
449
|
+
expect(r.first['test']).to be_an_instance_of(Time)
|
450
|
+
end
|
451
|
+
|
452
|
+
it "should return Time when timestamp is > 2038-01-19T03:14:07" do
|
453
|
+
r = @client.prepare("SELECT CAST('2038-01-19 03:14:08' AS DATETIME) as test").execute
|
454
|
+
expect(r.first['test']).to be_an_instance_of(Time)
|
455
|
+
end
|
456
|
+
|
457
|
+
it "should return Time for a TIMESTAMP value when within the supported range" do
|
458
|
+
expect(test_result['timestamp_test']).to be_an_instance_of(Time)
|
459
|
+
expect(test_result['timestamp_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2010-04-04 11:44:00')
|
460
|
+
end
|
461
|
+
|
462
|
+
it "should return Time for a TIME value" do
|
463
|
+
expect(test_result['time_test']).to be_an_instance_of(Time)
|
464
|
+
expect(test_result['time_test'].strftime("%Y-%m-%d %H:%M:%S")).to eql('2000-01-01 11:44:00')
|
465
|
+
end
|
466
|
+
|
467
|
+
it "should return Date for a DATE value" do
|
468
|
+
expect(test_result['date_test']).to be_an_instance_of(Date)
|
469
|
+
expect(test_result['date_test'].strftime("%Y-%m-%d")).to eql('2010-04-04')
|
470
|
+
end
|
471
|
+
|
472
|
+
it "should return String for an ENUM value" do
|
473
|
+
expect(test_result['enum_test']).to be_an_instance_of(String)
|
474
|
+
expect(test_result['enum_test']).to eql('val1')
|
475
|
+
end
|
476
|
+
|
477
|
+
it "should raise an error given an invalid DATETIME" do
|
478
|
+
expect { @client.query("SELECT CAST('1972-00-27 00:00:00' AS DATETIME) as bad_datetime").each }.to \
|
479
|
+
raise_error(Mysql2::Error, "Invalid date in field 'bad_datetime': 1972-00-27 00:00:00")
|
480
|
+
end
|
481
|
+
|
482
|
+
context "string encoding for ENUM values" do
|
483
|
+
it "should default to the connection's encoding if Encoding.default_internal is nil" do
|
484
|
+
with_internal_encoding nil do
|
485
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
486
|
+
expect(result['enum_test'].encoding).to eql(Encoding::UTF_8)
|
487
|
+
|
488
|
+
client2 = new_client(encoding: 'ascii')
|
489
|
+
result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
490
|
+
expect(result['enum_test'].encoding).to eql(Encoding::US_ASCII)
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
it "should use Encoding.default_internal" do
|
495
|
+
with_internal_encoding Encoding::UTF_8 do
|
496
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
497
|
+
expect(result['enum_test'].encoding).to eql(Encoding.default_internal)
|
498
|
+
end
|
499
|
+
|
500
|
+
with_internal_encoding Encoding::ASCII do
|
501
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
502
|
+
expect(result['enum_test'].encoding).to eql(Encoding.default_internal)
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
it "should return String for a SET value" do
|
508
|
+
expect(test_result['set_test']).to be_an_instance_of(String)
|
509
|
+
expect(test_result['set_test']).to eql('val1,val2')
|
510
|
+
end
|
511
|
+
|
512
|
+
context "string encoding for SET values" do
|
513
|
+
it "should default to the connection's encoding if Encoding.default_internal is nil" do
|
514
|
+
with_internal_encoding nil do
|
515
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
516
|
+
expect(result['set_test'].encoding).to eql(Encoding::UTF_8)
|
517
|
+
|
518
|
+
client2 = new_client(encoding: 'ascii')
|
519
|
+
result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
520
|
+
expect(result['set_test'].encoding).to eql(Encoding::US_ASCII)
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
it "should use Encoding.default_internal" do
|
525
|
+
with_internal_encoding Encoding::UTF_8 do
|
526
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
527
|
+
expect(result['set_test'].encoding).to eql(Encoding.default_internal)
|
528
|
+
end
|
529
|
+
|
530
|
+
with_internal_encoding Encoding::ASCII do
|
531
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
532
|
+
expect(result['set_test'].encoding).to eql(Encoding.default_internal)
|
533
|
+
end
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
it "should return String for a BINARY value" do
|
538
|
+
expect(test_result['binary_test']).to be_an_instance_of(String)
|
539
|
+
expect(test_result['binary_test']).to eql("test#{"\000" * 6}")
|
540
|
+
end
|
541
|
+
|
542
|
+
context "string encoding for BINARY values" do
|
543
|
+
it "should default to binary if Encoding.default_internal is nil" do
|
544
|
+
with_internal_encoding nil do
|
545
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
546
|
+
expect(result['binary_test'].encoding).to eql(Encoding::BINARY)
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
it "should not use Encoding.default_internal" do
|
551
|
+
with_internal_encoding Encoding::UTF_8 do
|
552
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
553
|
+
expect(result['binary_test'].encoding).to eql(Encoding::BINARY)
|
554
|
+
end
|
555
|
+
|
556
|
+
with_internal_encoding Encoding::ASCII do
|
557
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
558
|
+
expect(result['binary_test'].encoding).to eql(Encoding::BINARY)
|
559
|
+
end
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
{
|
564
|
+
'char_test' => 'CHAR',
|
565
|
+
'varchar_test' => 'VARCHAR',
|
566
|
+
'varbinary_test' => 'VARBINARY',
|
567
|
+
'tiny_blob_test' => 'TINYBLOB',
|
568
|
+
'tiny_text_test' => 'TINYTEXT',
|
569
|
+
'blob_test' => 'BLOB',
|
570
|
+
'text_test' => 'TEXT',
|
571
|
+
'medium_blob_test' => 'MEDIUMBLOB',
|
572
|
+
'medium_text_test' => 'MEDIUMTEXT',
|
573
|
+
'long_blob_test' => 'LONGBLOB',
|
574
|
+
'long_text_test' => 'LONGTEXT',
|
575
|
+
}.each do |field, type|
|
576
|
+
it "should return a String for #{type}" do
|
577
|
+
expect(test_result[field]).to be_an_instance_of(String)
|
578
|
+
expect(test_result[field]).to eql("test")
|
579
|
+
end
|
580
|
+
|
581
|
+
context "string encoding for #{type} values" do
|
582
|
+
if %w[VARBINARY TINYBLOB BLOB MEDIUMBLOB LONGBLOB].include?(type)
|
583
|
+
it "should default to binary if Encoding.default_internal is nil" do
|
584
|
+
with_internal_encoding nil do
|
585
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
586
|
+
expect(result['binary_test'].encoding).to eql(Encoding::BINARY)
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
it "should not use Encoding.default_internal" do
|
591
|
+
with_internal_encoding Encoding::UTF_8 do
|
592
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
593
|
+
expect(result['binary_test'].encoding).to eql(Encoding::BINARY)
|
594
|
+
end
|
595
|
+
|
596
|
+
with_internal_encoding Encoding::ASCII do
|
597
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
598
|
+
expect(result['binary_test'].encoding).to eql(Encoding::BINARY)
|
599
|
+
end
|
600
|
+
end
|
601
|
+
else
|
602
|
+
it "should default to utf-8 if Encoding.default_internal is nil" do
|
603
|
+
with_internal_encoding nil do
|
604
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
605
|
+
expect(result[field].encoding).to eql(Encoding::UTF_8)
|
606
|
+
|
607
|
+
client2 = new_client(encoding: 'ascii')
|
608
|
+
result = client2.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
609
|
+
expect(result[field].encoding).to eql(Encoding::US_ASCII)
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
it "should use Encoding.default_internal" do
|
614
|
+
with_internal_encoding Encoding::UTF_8 do
|
615
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
616
|
+
expect(result[field].encoding).to eql(Encoding.default_internal)
|
617
|
+
end
|
618
|
+
|
619
|
+
with_internal_encoding Encoding::ASCII do
|
620
|
+
result = @client.query("SELECT * FROM mysql2_test ORDER BY id DESC LIMIT 1").first
|
621
|
+
expect(result[field].encoding).to eql(Encoding.default_internal)
|
622
|
+
end
|
623
|
+
end
|
624
|
+
end
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
context 'last_id' do
|
630
|
+
before(:each) do
|
631
|
+
@client.query 'USE test'
|
632
|
+
@client.query 'CREATE TABLE IF NOT EXISTS lastIdTest (`id` BIGINT NOT NULL AUTO_INCREMENT, blah INT(11), PRIMARY KEY (`id`))'
|
633
|
+
end
|
634
|
+
|
635
|
+
after(:each) do
|
636
|
+
@client.query 'DROP TABLE lastIdTest'
|
637
|
+
end
|
638
|
+
|
639
|
+
it 'should return last insert id' do
|
640
|
+
stmt = @client.prepare 'INSERT INTO lastIdTest (blah) VALUES (?)'
|
641
|
+
expect(stmt.last_id).to eq 0
|
642
|
+
stmt.execute 1
|
643
|
+
expect(stmt.last_id).to eq 1
|
644
|
+
end
|
645
|
+
|
646
|
+
it 'should handle bigint ids' do
|
647
|
+
stmt = @client.prepare 'INSERT INTO lastIdTest (id, blah) VALUES (?, ?)'
|
648
|
+
stmt.execute 5000000000, 5000
|
649
|
+
expect(stmt.last_id).to eql(5000000000)
|
650
|
+
|
651
|
+
stmt = @client.prepare 'INSERT INTO lastIdTest (blah) VALUES (?)'
|
652
|
+
stmt.execute 5001
|
653
|
+
expect(stmt.last_id).to eql(5000000001)
|
654
|
+
end
|
655
|
+
end
|
656
|
+
|
657
|
+
context 'affected_rows' do
|
658
|
+
before :each do
|
659
|
+
@client.query 'USE test'
|
660
|
+
@client.query 'CREATE TABLE IF NOT EXISTS lastIdTest (`id` BIGINT NOT NULL AUTO_INCREMENT, blah INT(11), PRIMARY KEY (`id`))'
|
661
|
+
end
|
662
|
+
|
663
|
+
after :each do
|
664
|
+
@client.query 'DROP TABLE lastIdTest'
|
665
|
+
end
|
666
|
+
|
667
|
+
it 'should return number of rows affected by an insert' do
|
668
|
+
stmt = @client.prepare 'INSERT INTO lastIdTest (blah) VALUES (?)'
|
669
|
+
stmt.execute 1
|
670
|
+
expect(stmt.affected_rows).to eq 1
|
671
|
+
end
|
672
|
+
|
673
|
+
it 'should return number of rows affected by an update' do
|
674
|
+
stmt = @client.prepare 'INSERT INTO lastIdTest (blah) VALUES (?)'
|
675
|
+
stmt.execute 1
|
676
|
+
expect(stmt.affected_rows).to eq 1
|
677
|
+
stmt.execute 2
|
678
|
+
expect(stmt.affected_rows).to eq 1
|
679
|
+
|
680
|
+
stmt = @client.prepare 'UPDATE lastIdTest SET blah=? WHERE blah=?'
|
681
|
+
stmt.execute 0, 1
|
682
|
+
expect(stmt.affected_rows).to eq 1
|
683
|
+
end
|
684
|
+
|
685
|
+
it 'should return number of rows affected by a delete' do
|
686
|
+
stmt = @client.prepare 'INSERT INTO lastIdTest (blah) VALUES (?)'
|
687
|
+
stmt.execute 1
|
688
|
+
expect(stmt.affected_rows).to eq 1
|
689
|
+
stmt.execute 2
|
690
|
+
expect(stmt.affected_rows).to eq 1
|
691
|
+
|
692
|
+
stmt = @client.prepare 'DELETE FROM lastIdTest WHERE blah=?'
|
693
|
+
stmt.execute 1
|
694
|
+
expect(stmt.affected_rows).to eq 1
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
context 'close' do
|
699
|
+
it 'should free server resources' do
|
700
|
+
stmt = @client.prepare 'SELECT 1'
|
701
|
+
GC.disable
|
702
|
+
expect { stmt.close }.to change(&method(:stmt_count)).by(-1)
|
703
|
+
GC.enable
|
704
|
+
end
|
705
|
+
|
706
|
+
it 'should raise an error on subsequent execution' do
|
707
|
+
stmt = @client.prepare 'SELECT 1'
|
708
|
+
stmt.close
|
709
|
+
expect { stmt.execute }.to raise_error(Mysql2::Error, /Invalid statement handle/)
|
710
|
+
end
|
711
|
+
end
|
712
|
+
end
|