mysql2 0.3.18-x64-mingw32

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