mysql2 0.3.20 → 0.5.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.
@@ -1,921 +0,0 @@
1
- # encoding: UTF-8
2
- require 'spec_helper'
3
-
4
- describe Mysql2::Client do
5
- context "using defaults file" do
6
- let(:cnf_file) { File.expand_path('../../my.cnf', __FILE__) }
7
-
8
- it "should not raise an exception for valid defaults group" do
9
- lambda {
10
- opts = DatabaseCredentials['root'].merge(:default_file => cnf_file, :default_group => "test")
11
- @client = Mysql2::Client.new(opts)
12
- }.should_not raise_error(Mysql2::Error)
13
- end
14
-
15
- it "should not raise an exception without default group" do
16
- lambda {
17
- @client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:default_file => cnf_file))
18
- }.should_not raise_error(Mysql2::Error)
19
- end
20
- end
21
-
22
- it "should raise an exception upon connection failure" do
23
- lambda {
24
- # The odd local host IP address forces the mysql client library to
25
- # use a TCP socket rather than a domain socket.
26
- Mysql2::Client.new DatabaseCredentials['root'].merge('host' => '127.0.0.2', 'port' => 999999)
27
- }.should raise_error(Mysql2::Error)
28
- end
29
-
30
- if defined? Encoding
31
- it "should raise an exception on create for invalid encodings" do
32
- lambda {
33
- Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "fake"))
34
- }.should raise_error(Mysql2::Error)
35
- end
36
-
37
- it "should not raise an exception on create for a valid encoding" do
38
- lambda {
39
- Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "utf8"))
40
- }.should_not raise_error(Mysql2::Error)
41
-
42
- lambda {
43
- Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "big5"))
44
- }.should_not raise_error(Mysql2::Error)
45
- end
46
- end
47
-
48
- it "should accept connect flags and pass them to #connect" do
49
- klient = Class.new(Mysql2::Client) do
50
- attr_reader :connect_args
51
- def connect *args
52
- @connect_args ||= []
53
- @connect_args << args
54
- end
55
- end
56
- client = klient.new :flags => Mysql2::Client::FOUND_ROWS
57
- (client.connect_args.last[6] & Mysql2::Client::FOUND_ROWS).should be_true
58
- end
59
-
60
- it "should default flags to (REMEMBER_OPTIONS, LONG_PASSWORD, LONG_FLAG, TRANSACTIONS, PROTOCOL_41, SECURE_CONNECTION)" do
61
- klient = Class.new(Mysql2::Client) do
62
- attr_reader :connect_args
63
- def connect *args
64
- @connect_args ||= []
65
- @connect_args << args
66
- end
67
- end
68
- client = klient.new
69
- client_flags = Mysql2::Client::REMEMBER_OPTIONS |
70
- Mysql2::Client::LONG_PASSWORD |
71
- Mysql2::Client::LONG_FLAG |
72
- Mysql2::Client::TRANSACTIONS |
73
- Mysql2::Client::PROTOCOL_41 |
74
- Mysql2::Client::SECURE_CONNECTION
75
- client.connect_args.last[6].should eql(client_flags)
76
- end
77
-
78
- it "should execute init command" do
79
- options = DatabaseCredentials['root'].dup
80
- options[:init_command] = "SET @something = 'setting_value';"
81
- client = Mysql2::Client.new(options)
82
- result = client.query("SELECT @something;")
83
- result.first['@something'].should eq('setting_value')
84
- end
85
-
86
- it "should send init_command after reconnect" do
87
- options = DatabaseCredentials['root'].dup
88
- options[:init_command] = "SET @something = 'setting_value';"
89
- options[:reconnect] = true
90
- client = Mysql2::Client.new(options)
91
-
92
- result = client.query("SELECT @something;")
93
- result.first['@something'].should eq('setting_value')
94
-
95
- # get the current connection id
96
- result = client.query("SELECT CONNECTION_ID()")
97
- first_conn_id = result.first['CONNECTION_ID()']
98
-
99
- # break the current connection
100
- begin
101
- client.query("KILL #{first_conn_id}")
102
- rescue Mysql2::Error
103
- end
104
-
105
- client.ping # reconnect now
106
-
107
- # get the new connection id
108
- result = client.query("SELECT CONNECTION_ID()")
109
- second_conn_id = result.first['CONNECTION_ID()']
110
-
111
- # confirm reconnect by checking the new connection id
112
- first_conn_id.should_not == second_conn_id
113
-
114
- # At last, check that the init command executed
115
- result = client.query("SELECT @something;")
116
- result.first['@something'].should eq('setting_value')
117
- end
118
-
119
- it "should have a global default_query_options hash" do
120
- Mysql2::Client.should respond_to(:default_query_options)
121
- end
122
-
123
- it "should be able to connect via SSL options" do
124
- ssl = @client.query "SHOW VARIABLES LIKE 'have_ssl'"
125
- ssl_uncompiled = ssl.any? {|x| x['Value'] == 'OFF'}
126
- pending("DON'T WORRY, THIS TEST PASSES - but SSL is not compiled into your MySQL daemon.") if ssl_uncompiled
127
- ssl_disabled = ssl.any? {|x| x['Value'] == 'DISABLED'}
128
- pending("DON'T WORRY, THIS TEST PASSES - but SSL is not enabled in your MySQL daemon.") if ssl_disabled
129
-
130
- # You may need to adjust the lines below to match your SSL certificate paths
131
- ssl_client = nil
132
- lambda {
133
- ssl_client = Mysql2::Client.new(
134
- :sslkey => '/etc/mysql/client-key.pem',
135
- :sslcert => '/etc/mysql/client-cert.pem',
136
- :sslca => '/etc/mysql/ca-cert.pem',
137
- :sslcapath => '/etc/mysql/',
138
- :sslcipher => 'DHE-RSA-AES256-SHA'
139
- )
140
- }.should_not raise_error(Mysql2::Error)
141
-
142
- results = ssl_client.query("SHOW STATUS WHERE Variable_name = \"Ssl_version\" OR Variable_name = \"Ssl_cipher\"").to_a
143
- results[0]['Variable_name'].should eql('Ssl_cipher')
144
- results[0]['Value'].should_not be_nil
145
- results[0]['Value'].should be_kind_of(String)
146
- results[0]['Value'].should_not be_empty
147
-
148
- results[1]['Variable_name'].should eql('Ssl_version')
149
- results[1]['Value'].should_not be_nil
150
- results[1]['Value'].should be_kind_of(String)
151
- results[1]['Value'].should_not be_empty
152
-
153
- ssl_client.close
154
- end
155
-
156
- it "should not leave dangling connections after garbage collection" do
157
- GC.start
158
- sleep 0.300 # Let GC do its work
159
- client = Mysql2::Client.new(DatabaseCredentials['root'])
160
- before_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
161
-
162
- 10.times do
163
- Mysql2::Client.new(DatabaseCredentials['root']).query('SELECT 1')
164
- end
165
- after_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
166
- after_count.should == before_count + 10
167
-
168
- GC.start
169
- sleep 0.300 # Let GC do its work
170
- final_count = client.query("SHOW STATUS LIKE 'Threads_connected'").first['Value'].to_i
171
- final_count.should == before_count
172
- end
173
-
174
- if Process.respond_to?(:fork)
175
- it "should not close connections when running in a child process" do
176
- GC.start
177
- sleep 1 if defined? Rubinius # Let the rbx GC thread do its work
178
- client = Mysql2::Client.new(DatabaseCredentials['root'])
179
-
180
- fork do
181
- client.query('SELECT 1')
182
- client = nil
183
- GC.start
184
- sleep 1 if defined? Rubinius # Let the rbx GC thread do its work
185
- end
186
-
187
- Process.wait
188
-
189
- # this will throw an error if the underlying socket was shutdown by the
190
- # child's GC
191
- expect { client.query('SELECT 1') }.to_not raise_exception
192
- end
193
- end
194
-
195
- it "should be able to connect to database with numeric-only name" do
196
- lambda {
197
- creds = DatabaseCredentials['numericuser']
198
- @client.query "CREATE DATABASE IF NOT EXISTS `#{creds['database']}`"
199
- @client.query "GRANT ALL ON `#{creds['database']}`.* TO #{creds['username']}@`#{creds['host']}`"
200
- client = Mysql2::Client.new creds
201
- @client.query "DROP DATABASE IF EXISTS `#{creds['database']}`"
202
- }.should_not raise_error
203
- end
204
-
205
- it "should respond to #close" do
206
- @client.should respond_to(:close)
207
- end
208
-
209
- it "should be able to close properly" do
210
- @client.close.should be_nil
211
- lambda {
212
- @client.query "SELECT 1"
213
- }.should raise_error(Mysql2::Error)
214
- end
215
-
216
- it "should respond to #query" do
217
- @client.should respond_to(:query)
218
- end
219
-
220
- it "should respond to #warning_count" do
221
- @client.should respond_to(:warning_count)
222
- end
223
-
224
- context "#warning_count" do
225
- context "when no warnings" do
226
- it "should 0" do
227
- @client.query('select 1')
228
- @client.warning_count.should == 0
229
- end
230
- end
231
- context "when has a warnings" do
232
- it "should > 0" do
233
- # "the statement produces extra information that can be viewed by issuing a SHOW WARNINGS"
234
- # http://dev.mysql.com/doc/refman/5.0/en/explain-extended.html
235
- @client.query("explain extended select 1")
236
- @client.warning_count.should > 0
237
- end
238
- end
239
- end
240
-
241
- it "should respond to #query_info" do
242
- @client.should respond_to(:query_info)
243
- end
244
-
245
- context "#query_info" do
246
- context "when no info present" do
247
- it "should 0" do
248
- @client.query('select 1')
249
- @client.query_info.should be_empty
250
- @client.query_info_string.should be_nil
251
- end
252
- end
253
- context "when has some info" do
254
- it "should retrieve it" do
255
- @client.query "USE test"
256
- @client.query "CREATE TABLE IF NOT EXISTS infoTest (`id` int(11) NOT NULL AUTO_INCREMENT, blah INT(11), PRIMARY KEY (`id`))"
257
-
258
- # http://dev.mysql.com/doc/refman/5.0/en/mysql-info.html says
259
- # # Note that mysql_info() returns a non-NULL value for INSERT ... VALUES only for the multiple-row form of the statement (that is, only if multiple value lists are specified).
260
- @client.query("INSERT INTO infoTest (blah) VALUES (1234),(4535)")
261
-
262
- @client.query_info.should eql({:records => 2, :duplicates => 0, :warnings => 0})
263
- @client.query_info_string.should eq('Records: 2 Duplicates: 0 Warnings: 0')
264
-
265
- @client.query "DROP TABLE infoTest"
266
- end
267
- end
268
- end
269
-
270
- context ":local_infile" do
271
- before(:all) do
272
- @client_i = Mysql2::Client.new DatabaseCredentials['root'].merge(:local_infile => true)
273
- local = @client_i.query "SHOW VARIABLES LIKE 'local_infile'"
274
- local_enabled = local.any? {|x| x['Value'] == 'ON'}
275
- pending("DON'T WORRY, THIS TEST PASSES - but LOCAL INFILE is not enabled in your MySQL daemon.") unless local_enabled
276
-
277
- @client_i.query %[
278
- CREATE TABLE IF NOT EXISTS infileTest (
279
- id MEDIUMINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
280
- foo VARCHAR(10),
281
- bar MEDIUMTEXT
282
- )
283
- ]
284
- end
285
-
286
- after(:all) do
287
- @client_i.query "DROP TABLE infileTest"
288
- end
289
-
290
- it "should raise an error when local_infile is disabled" do
291
- client = Mysql2::Client.new DatabaseCredentials['root'].merge(:local_infile => false)
292
- lambda {
293
- client.query "LOAD DATA LOCAL INFILE 'spec/test_data' INTO TABLE infileTest"
294
- }.should raise_error(Mysql2::Error, %r{command is not allowed})
295
- end
296
-
297
- it "should raise an error when a non-existent file is loaded" do
298
- lambda {
299
- @client_i.query "LOAD DATA LOCAL INFILE 'this/file/is/not/here' INTO TABLE infileTest"
300
- }.should_not raise_error(Mysql2::Error, %r{file not found: this/file/is/not/here})
301
- end
302
-
303
- it "should LOAD DATA LOCAL INFILE" do
304
- @client_i.query "LOAD DATA LOCAL INFILE 'spec/test_data' INTO TABLE infileTest"
305
- info = @client_i.query_info
306
- info.should eql({:records => 1, :deleted => 0, :skipped => 0, :warnings => 0})
307
-
308
- result = @client_i.query "SELECT * FROM infileTest"
309
- result.first.should eql({'id' => 1, 'foo' => 'Hello', 'bar' => 'World'})
310
- end
311
- end
312
-
313
- it "should expect connect_timeout to be a positive integer" do
314
- lambda {
315
- Mysql2::Client.new(:connect_timeout => -1)
316
- }.should raise_error(Mysql2::Error)
317
- end
318
-
319
- it "should expect read_timeout to be a positive integer" do
320
- lambda {
321
- Mysql2::Client.new(:read_timeout => -1)
322
- }.should raise_error(Mysql2::Error)
323
- end
324
-
325
- it "should expect write_timeout to be a positive integer" do
326
- lambda {
327
- Mysql2::Client.new(:write_timeout => -1)
328
- }.should raise_error(Mysql2::Error)
329
- end
330
-
331
- context "#query" do
332
- it "should let you query again if iterating is finished when streaming" do
333
- @client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false).each.to_a
334
-
335
- expect {
336
- @client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false)
337
- }.to_not raise_exception(Mysql2::Error)
338
- end
339
-
340
- it "should not let you query again if iterating is not finished when streaming" do
341
- @client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false).first
342
-
343
- expect {
344
- @client.query("SELECT 1 UNION SELECT 2", :stream => true, :cache_rows => false)
345
- }.to raise_exception(Mysql2::Error)
346
- end
347
-
348
- it "should only accept strings as the query parameter" do
349
- lambda {
350
- @client.query ["SELECT 'not right'"]
351
- }.should raise_error(TypeError)
352
- end
353
-
354
- it "should not retain query options set on a query for subsequent queries, but should retain it in the result" do
355
- result = @client.query "SELECT 1", :something => :else
356
- @client.query_options[:something].should be_nil
357
- result.instance_variable_get('@query_options').should eql(@client.query_options.merge(:something => :else))
358
- @client.instance_variable_get('@current_query_options').should eql(@client.query_options.merge(:something => :else))
359
-
360
- result = @client.query "SELECT 1"
361
- result.instance_variable_get('@query_options').should eql(@client.query_options)
362
- @client.instance_variable_get('@current_query_options').should eql(@client.query_options)
363
- end
364
-
365
- it "should allow changing query options for subsequent queries" do
366
- @client.query_options.merge!(:something => :else)
367
- result = @client.query "SELECT 1"
368
- @client.query_options[:something].should eql(:else)
369
- result.instance_variable_get('@query_options')[:something].should eql(:else)
370
-
371
- # Clean up after this test
372
- @client.query_options.delete(:something)
373
- @client.query_options[:something].should be_nil
374
- end
375
-
376
- it "should return results as a hash by default" do
377
- @client.query("SELECT 1").first.class.should eql(Hash)
378
- end
379
-
380
- it "should be able to return results as an array" do
381
- @client.query("SELECT 1", :as => :array).first.class.should eql(Array)
382
- @client.query("SELECT 1").each(:as => :array)
383
- end
384
-
385
- it "should be able to return results with symbolized keys" do
386
- @client.query("SELECT 1", :symbolize_keys => true).first.keys[0].class.should eql(Symbol)
387
- end
388
-
389
- it "should require an open connection" do
390
- @client.close
391
- lambda {
392
- @client.query "SELECT 1"
393
- }.should raise_error(Mysql2::Error)
394
- end
395
-
396
- if RUBY_PLATFORM !~ /mingw|mswin/
397
- it "should not allow another query to be sent without fetching a result first" do
398
- @client.query("SELECT 1", :async => true)
399
- lambda {
400
- @client.query("SELECT 1")
401
- }.should raise_error(Mysql2::Error)
402
- end
403
-
404
- it "should describe the thread holding the active query" do
405
- thr = Thread.new { @client.query("SELECT 1", :async => true) }
406
-
407
- thr.join
408
- begin
409
- @client.query("SELECT 1")
410
- rescue Mysql2::Error => e
411
- message = e.message
412
- end
413
- re = Regexp.escape(thr.inspect)
414
- message.should match(Regexp.new(re))
415
- end
416
-
417
- it "should timeout if we wait longer than :read_timeout" do
418
- client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:read_timeout => 1))
419
- lambda {
420
- client.query("SELECT sleep(2)")
421
- }.should raise_error(Mysql2::Error)
422
- end
423
-
424
- if !defined? Rubinius
425
- # XXX this test is not deterministic (because Unix signal handling is not)
426
- # and may fail on a loaded system
427
- it "should run signal handlers while waiting for a response" do
428
- mark = {}
429
- trap(:USR1) { mark[:USR1] = Time.now }
430
- begin
431
- mark[:START] = Time.now
432
- pid = fork do
433
- sleep 1 # wait for client "SELECT sleep(2)" query to start
434
- Process.kill(:USR1, Process.ppid)
435
- sleep # wait for explicit kill to prevent GC disconnect
436
- end
437
- @client.query("SELECT sleep(2)")
438
- mark[:END] = Time.now
439
- mark.include?(:USR1).should be_true
440
- (mark[:USR1] - mark[:START]).should >= 1
441
- (mark[:USR1] - mark[:START]).should < 1.3
442
- (mark[:END] - mark[:USR1]).should > 0.9
443
- (mark[:END] - mark[:START]).should >= 2
444
- (mark[:END] - mark[:START]).should < 2.3
445
- Process.kill(:TERM, pid)
446
- Process.waitpid2(pid)
447
- ensure
448
- trap(:USR1, 'DEFAULT')
449
- end
450
- end
451
- end
452
-
453
- it "#socket should return a Fixnum (file descriptor from C)" do
454
- @client.socket.class.should eql(Fixnum)
455
- @client.socket.should_not eql(0)
456
- end
457
-
458
- it "#socket should require an open connection" do
459
- @client.close
460
- lambda {
461
- @client.socket
462
- }.should raise_error(Mysql2::Error)
463
- end
464
-
465
- it "should close the connection when an exception is raised" do
466
- begin
467
- Timeout.timeout(1, Timeout::Error) do
468
- @client.query("SELECT sleep(2)")
469
- end
470
- rescue Timeout::Error
471
- end
472
-
473
- lambda {
474
- @client.query("SELECT 1")
475
- }.should raise_error(Mysql2::Error, 'closed MySQL connection')
476
- end
477
-
478
- it "should handle Timeouts without leaving the connection hanging if reconnect is true" do
479
- client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:reconnect => true))
480
- begin
481
- Timeout.timeout(1, Timeout::Error) do
482
- client.query("SELECT sleep(2)")
483
- end
484
- rescue Timeout::Error
485
- end
486
-
487
- lambda {
488
- client.query("SELECT 1")
489
- }.should_not raise_error(Mysql2::Error)
490
- end
491
-
492
- it "should handle Timeouts without leaving the connection hanging if reconnect is set to true after construction true" do
493
- client = Mysql2::Client.new(DatabaseCredentials['root'])
494
- begin
495
- Timeout.timeout(1, Timeout::Error) do
496
- client.query("SELECT sleep(2)")
497
- end
498
- rescue Timeout::Error
499
- end
500
-
501
- lambda {
502
- client.query("SELECT 1")
503
- }.should raise_error(Mysql2::Error)
504
-
505
- client.reconnect = true
506
-
507
- begin
508
- Timeout.timeout(1, Timeout::Error) do
509
- client.query("SELECT sleep(2)")
510
- end
511
- rescue Timeout::Error
512
- end
513
-
514
- lambda {
515
- client.query("SELECT 1")
516
- }.should_not raise_error(Mysql2::Error)
517
-
518
- end
519
-
520
- it "threaded queries should be supported" do
521
- threads, results = [], {}
522
- lock = Mutex.new
523
- connect = lambda{
524
- Mysql2::Client.new(DatabaseCredentials['root'])
525
- }
526
- Timeout.timeout(0.7) do
527
- 5.times {
528
- threads << Thread.new do
529
- result = connect.call.query("SELECT sleep(0.5) as result")
530
- lock.synchronize do
531
- results[Thread.current.object_id] = result
532
- end
533
- end
534
- }
535
- end
536
- threads.each{|t| t.join }
537
- results.keys.sort.should eql(threads.map{|t| t.object_id }.sort)
538
- end
539
-
540
- it "evented async queries should be supported" do
541
- # should immediately return nil
542
- @client.query("SELECT sleep(0.1)", :async => true).should eql(nil)
543
-
544
- io_wrapper = IO.for_fd(@client.socket)
545
- loops = 0
546
- loop do
547
- if IO.select([io_wrapper], nil, nil, 0.05)
548
- break
549
- else
550
- loops += 1
551
- end
552
- end
553
-
554
- # make sure we waited some period of time
555
- (loops >= 1).should be_true
556
-
557
- result = @client.async_result
558
- result.class.should eql(Mysql2::Result)
559
- end
560
- end
561
-
562
- context "Multiple results sets" do
563
- before(:each) do
564
- @multi_client = Mysql2::Client.new(DatabaseCredentials['root'].merge(:flags => Mysql2::Client::MULTI_STATEMENTS))
565
- end
566
-
567
- it "should raise an exception when one of multiple statements fails" do
568
- result = @multi_client.query("SELECT 1 AS 'set_1'; SELECT * FROM invalid_table_name; SELECT 2 AS 'set_2';")
569
- result.first['set_1'].should be(1)
570
- lambda {
571
- @multi_client.next_result
572
- }.should raise_error(Mysql2::Error)
573
- @multi_client.next_result.should be_false
574
- end
575
-
576
- it "returns multiple result sets" do
577
- @multi_client.query("SELECT 1 AS 'set_1'; SELECT 2 AS 'set_2'").first.should eql({ 'set_1' => 1 })
578
-
579
- @multi_client.next_result.should be_true
580
- @multi_client.store_result.first.should eql({ 'set_2' => 2 })
581
-
582
- @multi_client.next_result.should be_false
583
- end
584
-
585
- it "does not interfere with other statements" do
586
- @multi_client.query("SELECT 1 AS 'set_1'; SELECT 2 AS 'set_2'")
587
- while( @multi_client.next_result )
588
- @multi_client.store_result
589
- end
590
-
591
- @multi_client.query("SELECT 3 AS 'next'").first.should == { 'next' => 3 }
592
- end
593
-
594
- it "will raise on query if there are outstanding results to read" do
595
- @multi_client.query("SELECT 1; SELECT 2; SELECT 3")
596
- lambda {
597
- @multi_client.query("SELECT 4")
598
- }.should raise_error(Mysql2::Error)
599
- end
600
-
601
- it "#abandon_results! should work" do
602
- @multi_client.query("SELECT 1; SELECT 2; SELECT 3")
603
- @multi_client.abandon_results!
604
- lambda {
605
- @multi_client.query("SELECT 4")
606
- }.should_not raise_error(Mysql2::Error)
607
- end
608
-
609
- it "#more_results? should work" do
610
- @multi_client.query("SELECT 1 AS 'set_1'; SELECT 2 AS 'set_2'")
611
- @multi_client.more_results?.should be_true
612
-
613
- @multi_client.next_result
614
- @multi_client.store_result
615
-
616
- @multi_client.more_results?.should be_false
617
- end
618
-
619
- it "#more_results? should work with stored procedures" do
620
- @multi_client.query("DROP PROCEDURE IF EXISTS test_proc")
621
- @multi_client.query("CREATE PROCEDURE test_proc() BEGIN SELECT 1 AS 'set_1'; SELECT 2 AS 'set_2'; END")
622
- @multi_client.query("CALL test_proc()").first.should eql({ 'set_1' => 1 })
623
- @multi_client.more_results?.should be_true
624
-
625
- @multi_client.next_result
626
- @multi_client.store_result.first.should eql({ 'set_2' => 2 })
627
-
628
- @multi_client.next_result
629
- @multi_client.store_result.should be_nil # this is the result from CALL itself
630
-
631
- @multi_client.more_results?.should be_false
632
- end
633
- end
634
- end
635
-
636
- it "should respond to #socket" do
637
- @client.should respond_to(:socket)
638
- end
639
-
640
- if RUBY_PLATFORM =~ /mingw|mswin/
641
- it "#socket should raise as it's not supported" do
642
- lambda {
643
- @client.socket
644
- }.should raise_error(Mysql2::Error)
645
- end
646
- end
647
-
648
- it "should respond to escape" do
649
- Mysql2::Client.should respond_to(:escape)
650
- end
651
-
652
- context "escape" do
653
- it "should return a new SQL-escape version of the passed string" do
654
- Mysql2::Client.escape("abc'def\"ghi\0jkl%mno").should eql("abc\\'def\\\"ghi\\0jkl%mno")
655
- end
656
-
657
- it "should return the passed string if nothing was escaped" do
658
- str = "plain"
659
- Mysql2::Client.escape(str).object_id.should eql(str.object_id)
660
- end
661
-
662
- it "should not overflow the thread stack" do
663
- lambda {
664
- Thread.new { Mysql2::Client.escape("'" * 256 * 1024) }.join
665
- }.should_not raise_error(SystemStackError)
666
- end
667
-
668
- it "should not overflow the process stack" do
669
- lambda {
670
- Thread.new { Mysql2::Client.escape("'" * 1024 * 1024 * 4) }.join
671
- }.should_not raise_error(SystemStackError)
672
- end
673
-
674
- unless RUBY_VERSION =~ /1.8/
675
- it "should carry over the original string's encoding" do
676
- str = "abc'def\"ghi\0jkl%mno"
677
- escaped = Mysql2::Client.escape(str)
678
- escaped.encoding.should eql(str.encoding)
679
-
680
- str.encode!('us-ascii')
681
- escaped = Mysql2::Client.escape(str)
682
- escaped.encoding.should eql(str.encoding)
683
- end
684
- end
685
- end
686
-
687
- it "should respond to #escape" do
688
- @client.should respond_to(:escape)
689
- end
690
-
691
- context "#escape" do
692
- it "should return a new SQL-escape version of the passed string" do
693
- @client.escape("abc'def\"ghi\0jkl%mno").should eql("abc\\'def\\\"ghi\\0jkl%mno")
694
- end
695
-
696
- it "should return the passed string if nothing was escaped" do
697
- str = "plain"
698
- @client.escape(str).object_id.should eql(str.object_id)
699
- end
700
-
701
- it "should not overflow the thread stack" do
702
- lambda {
703
- Thread.new { @client.escape("'" * 256 * 1024) }.join
704
- }.should_not raise_error(SystemStackError)
705
- end
706
-
707
- it "should not overflow the process stack" do
708
- lambda {
709
- Thread.new { @client.escape("'" * 1024 * 1024 * 4) }.join
710
- }.should_not raise_error(SystemStackError)
711
- end
712
-
713
- it "should require an open connection" do
714
- @client.close
715
- lambda {
716
- @client.escape ""
717
- }.should raise_error(Mysql2::Error)
718
- end
719
-
720
- context 'when mysql encoding is not utf8' do
721
- before { pending('Encoding is undefined') unless defined?(Encoding) }
722
-
723
- let(:client) { Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => "ujis")) }
724
-
725
- it 'should return a internal encoding string if Encoding.default_internal is set' do
726
- with_internal_encoding Encoding::UTF_8 do
727
- client.escape("\u{30C6}\u{30B9}\u{30C8}").should eql("\u{30C6}\u{30B9}\u{30C8}")
728
- client.escape("\u{30C6}'\u{30B9}\"\u{30C8}").should eql("\u{30C6}\\'\u{30B9}\\\"\u{30C8}")
729
- end
730
- end
731
- end
732
- end
733
-
734
- it "should respond to #info" do
735
- @client.should respond_to(:info)
736
- end
737
-
738
- it "#info should return a hash containing the client version ID and String" do
739
- info = @client.info
740
- info.class.should eql(Hash)
741
- info.should have_key(:id)
742
- info[:id].class.should eql(Fixnum)
743
- info.should have_key(:version)
744
- info[:version].class.should eql(String)
745
- end
746
-
747
- context "strings returned by #info" do
748
- before { pending('Encoding is undefined') unless defined?(Encoding) }
749
-
750
- it "should be tagged as ascii" do
751
- @client.info[:version].encoding.should eql(Encoding::US_ASCII)
752
- @client.info[:header_version].encoding.should eql(Encoding::US_ASCII)
753
- end
754
- end
755
-
756
- context "strings returned by .info" do
757
- before { pending('Encoding is undefined') unless defined?(Encoding) }
758
-
759
- it "should be tagged as ascii" do
760
- Mysql2::Client.info[:version].encoding.should eql(Encoding::US_ASCII)
761
- Mysql2::Client.info[:header_version].encoding.should eql(Encoding::US_ASCII)
762
- end
763
- end
764
-
765
- it "should respond to #server_info" do
766
- @client.should respond_to(:server_info)
767
- end
768
-
769
- it "#server_info should return a hash containing the client version ID and String" do
770
- server_info = @client.server_info
771
- server_info.class.should eql(Hash)
772
- server_info.should have_key(:id)
773
- server_info[:id].class.should eql(Fixnum)
774
- server_info.should have_key(:version)
775
- server_info[:version].class.should eql(String)
776
- end
777
-
778
- it "#server_info should require an open connection" do
779
- @client.close
780
- lambda {
781
- @client.server_info
782
- }.should raise_error(Mysql2::Error)
783
- end
784
-
785
- if defined? Encoding
786
- context "strings returned by #server_info" do
787
- it "should default to the connection's encoding if Encoding.default_internal is nil" do
788
- with_internal_encoding nil do
789
- @client.server_info[:version].encoding.should eql(Encoding.find('utf-8'))
790
-
791
- client2 = Mysql2::Client.new(DatabaseCredentials['root'].merge(:encoding => 'ascii'))
792
- client2.server_info[:version].encoding.should eql(Encoding.find('us-ascii'))
793
- end
794
- end
795
-
796
- it "should use Encoding.default_internal" do
797
- with_internal_encoding 'utf-8' do
798
- @client.server_info[:version].encoding.should eql(Encoding.default_internal)
799
- end
800
-
801
- with_internal_encoding 'us-ascii' do
802
- @client.server_info[:version].encoding.should eql(Encoding.default_internal)
803
- end
804
- end
805
- end
806
- end
807
-
808
- it "should raise a Mysql2::Error exception upon connection failure" do
809
- lambda {
810
- Mysql2::Client.new :host => "localhost", :username => 'asdfasdf8d2h', :password => 'asdfasdfw42'
811
- }.should raise_error(Mysql2::Error)
812
-
813
- lambda {
814
- Mysql2::Client.new DatabaseCredentials['root']
815
- }.should_not raise_error(Mysql2::Error)
816
- end
817
-
818
- context 'write operations api' do
819
- before(:each) do
820
- @client.query "USE test"
821
- @client.query "CREATE TABLE IF NOT EXISTS lastIdTest (`id` BIGINT NOT NULL AUTO_INCREMENT, blah INT(11), PRIMARY KEY (`id`))"
822
- end
823
-
824
- after(:each) do
825
- @client.query "DROP TABLE lastIdTest"
826
- end
827
-
828
- it "should respond to #last_id" do
829
- @client.should respond_to(:last_id)
830
- end
831
-
832
- it "#last_id should return a Fixnum, the from the last INSERT/UPDATE" do
833
- @client.last_id.should eql(0)
834
- @client.query "INSERT INTO lastIdTest (blah) VALUES (1234)"
835
- @client.last_id.should eql(1)
836
- end
837
-
838
- it "should respond to #last_id" do
839
- @client.should respond_to(:last_id)
840
- end
841
-
842
- it "#last_id should return a Fixnum, the from the last INSERT/UPDATE" do
843
- @client.query "INSERT INTO lastIdTest (blah) VALUES (1234)"
844
- @client.affected_rows.should eql(1)
845
- @client.query "UPDATE lastIdTest SET blah=4321 WHERE id=1"
846
- @client.affected_rows.should eql(1)
847
- end
848
-
849
- it "#last_id should handle BIGINT auto-increment ids above 32 bits" do
850
- # The id column type must be BIGINT. Surprise: INT(x) is limited to 32-bits for all values of x.
851
- # Insert a row with a given ID, this should raise the auto-increment state
852
- @client.query "INSERT INTO lastIdTest (id, blah) VALUES (5000000000, 5000)"
853
- @client.last_id.should eql(5000000000)
854
- @client.query "INSERT INTO lastIdTest (blah) VALUES (5001)"
855
- @client.last_id.should eql(5000000001)
856
- end
857
- end
858
-
859
- it "should respond to #thread_id" do
860
- @client.should respond_to(:thread_id)
861
- end
862
-
863
- it "#thread_id should be a Fixnum" do
864
- @client.thread_id.class.should eql(Fixnum)
865
- end
866
-
867
- it "should respond to #ping" do
868
- @client.should respond_to(:ping)
869
- end
870
-
871
- context "select_db" do
872
- before(:each) do
873
- 2.times do |i|
874
- @client.query("CREATE DATABASE test_selectdb_#{i}")
875
- @client.query("USE test_selectdb_#{i}")
876
- @client.query("CREATE TABLE test#{i} (`id` int NOT NULL PRIMARY KEY)")
877
- end
878
- end
879
-
880
- after(:each) do
881
- 2.times do |i|
882
- @client.query("DROP DATABASE test_selectdb_#{i}")
883
- end
884
- end
885
-
886
- it "should respond to #select_db" do
887
- @client.should respond_to(:select_db)
888
- end
889
-
890
- it "should switch databases" do
891
- @client.select_db("test_selectdb_0")
892
- @client.query("SHOW TABLES").first.values.first.should eql("test0")
893
- @client.select_db("test_selectdb_1")
894
- @client.query("SHOW TABLES").first.values.first.should eql("test1")
895
- @client.select_db("test_selectdb_0")
896
- @client.query("SHOW TABLES").first.values.first.should eql("test0")
897
- end
898
-
899
- it "should raise a Mysql2::Error when the database doesn't exist" do
900
- lambda {
901
- @client.select_db("nopenothere")
902
- }.should raise_error(Mysql2::Error)
903
- end
904
-
905
- it "should return the database switched to" do
906
- @client.select_db("test_selectdb_1").should eq("test_selectdb_1")
907
- end
908
- end
909
-
910
- it "#thread_id should return a boolean" do
911
- @client.ping.should eql(true)
912
- @client.close
913
- @client.ping.should eql(false)
914
- end
915
-
916
- unless RUBY_VERSION =~ /1.8/
917
- it "should respond to #encoding" do
918
- @client.should respond_to(:encoding)
919
- end
920
- end
921
- end