mysql2 0.3.18 → 0.4.3

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