mysql2 0.3.18 → 0.4.7

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