mysql2 0.3.18 → 0.4.4

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