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