pg 0.13.2 → 0.14.0.pre.351

Sign up to get free protection for your applications and to get access to all the features.
@@ -239,6 +239,11 @@ RSpec.configure do |config|
239
239
  config.treat_symbols_as_metadata_keys_with_true_values = true
240
240
 
241
241
  config.mock_with :rspec
242
- config.filter_run_excluding :ruby_19 => true if ruby_version_vec <= [1,9,1].pack( "N*" )
242
+ config.filter_run_excluding :ruby_19 if ruby_version_vec <= [1,9,1].pack( "N*" )
243
+
244
+ config.filter_run_excluding :postgresql_90 unless
245
+ PG::Connection.instance_methods.map( &:to_sym ).include?( :escape_literal )
246
+ config.filter_run_excluding :postgresql_91 unless
247
+ PG.respond_to?( :library_version )
243
248
  end
244
249
 
@@ -86,13 +86,13 @@ describe PG::Connection do
86
86
 
87
87
  it "connects successfully with connection string" do
88
88
  tmpconn = described_class.connect(@conninfo)
89
- tmpconn.status.should== PG::CONNECTION_OK
89
+ tmpconn.status.should == PG::CONNECTION_OK
90
90
  tmpconn.finish
91
91
  end
92
92
 
93
93
  it "connects using 7 arguments converted to strings" do
94
94
  tmpconn = described_class.connect('localhost', @port, nil, nil, :test, nil, nil)
95
- tmpconn.status.should== PG::CONNECTION_OK
95
+ tmpconn.status.should == PG::CONNECTION_OK
96
96
  tmpconn.finish
97
97
  end
98
98
 
@@ -101,7 +101,17 @@ describe PG::Connection do
101
101
  :host => 'localhost',
102
102
  :port => @port,
103
103
  :dbname => :test)
104
- tmpconn.status.should== PG::CONNECTION_OK
104
+ tmpconn.status.should == PG::CONNECTION_OK
105
+ tmpconn.finish
106
+ end
107
+
108
+ it "connects using a hash of optional connection parameters" do
109
+ tmpconn = described_class.connect(
110
+ :host => 'localhost',
111
+ :port => @port,
112
+ :dbname => :test,
113
+ :keepalives => 1)
114
+ tmpconn.status.should == PG::CONNECTION_OK
105
115
  tmpconn.finish
106
116
  end
107
117
 
@@ -111,7 +121,6 @@ describe PG::Connection do
111
121
  }.to raise_error( ArgumentError, /extra positional parameter/i )
112
122
  end
113
123
 
114
-
115
124
  it "can connect asynchronously" do
116
125
  tmpconn = described_class.connect_start( @conninfo )
117
126
  tmpconn.should be_a( described_class )
@@ -379,10 +388,156 @@ describe PG::Connection do
379
388
  @conn.exec( 'UNLISTEN woo' )
380
389
  end
381
390
 
382
- context "under PostgreSQL 9" do
391
+ it "yields the result if block is given to exec" do
392
+ rval = @conn.exec( "select 1234::int as a union select 5678::int as a" ) do |result|
393
+ values = []
394
+ result.should be_kind_of( PG::Result )
395
+ result.ntuples.should == 2
396
+ result.each do |tuple|
397
+ values << tuple['a']
398
+ end
399
+ values
400
+ end
401
+
402
+ rval.should have( 2 ).members
403
+ rval.should include( '5678', '1234' )
404
+ end
405
+
406
+
407
+ it "correctly finishes COPY queries passed to #async_exec" do
408
+ @conn.async_exec( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT" )
409
+
410
+ results = []
411
+ begin
412
+ data = @conn.get_copy_data( true )
413
+ if false == data
414
+ @conn.block( 2.0 )
415
+ data = @conn.get_copy_data( true )
416
+ end
417
+ results << data if data
418
+ end until data.nil?
419
+
420
+ results.should have( 2 ).members
421
+ results.should include( "1\n", "2\n" )
422
+ end
423
+
424
+
425
+ it "described_class#block shouldn't block a second thread" do
426
+ t = Thread.new do
427
+ @conn.send_query( "select pg_sleep(3)" )
428
+ @conn.block
429
+ end
430
+
431
+ # :FIXME: There's a race here, but hopefully it's pretty small.
432
+ t.should be_alive()
433
+
434
+ @conn.cancel
435
+ t.join
436
+ end
437
+
438
+ it "described_class#block should allow a timeout" do
439
+ @conn.send_query( "select pg_sleep(3)" )
440
+
441
+ start = Time.now
442
+ @conn.block( 0.1 )
443
+ finish = Time.now
444
+
445
+ (finish - start).should be_within( 0.05 ).of( 0.1 )
446
+ end
447
+
448
+
449
+ it "can encrypt a string given a password and username" do
450
+ described_class.encrypt_password("postgres", "postgres").
451
+ should =~ /\S+/
452
+ end
453
+
454
+
455
+ it "raises an appropriate error if either of the required arguments for encrypt_password " +
456
+ "is not valid" do
457
+ expect {
458
+ described_class.encrypt_password( nil, nil )
459
+ }.to raise_error( TypeError )
460
+ expect {
461
+ described_class.encrypt_password( "postgres", nil )
462
+ }.to raise_error( TypeError )
463
+ expect {
464
+ described_class.encrypt_password( nil, "postgres" )
465
+ }.to raise_error( TypeError )
466
+ end
467
+
468
+
469
+ it "allows fetching a column of values from a result by column number" do
470
+ res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
471
+ res.column_values( 0 ).should == %w[1 2 3]
472
+ res.column_values( 1 ).should == %w[2 3 4]
473
+ end
474
+
475
+
476
+ it "allows fetching a column of values from a result by field name" do
477
+ res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
478
+ res.field_values( 'column1' ).should == %w[1 2 3]
479
+ res.field_values( 'column2' ).should == %w[2 3 4]
480
+ end
481
+
482
+
483
+ it "raises an error if selecting an invalid column index" do
484
+ res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
485
+ expect {
486
+ res.column_values( 20 )
487
+ }.to raise_error( IndexError )
488
+ end
489
+
490
+
491
+ it "raises an error if selecting an invalid field name" do
492
+ res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
493
+ expect {
494
+ res.field_values( 'hUUuurrg' )
495
+ }.to raise_error( IndexError )
496
+ end
497
+
498
+
499
+ it "raises an error if column index is not a number" do
500
+ res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
501
+ expect {
502
+ res.column_values( 'hUUuurrg' )
503
+ }.to raise_error( TypeError )
504
+ end
505
+
506
+
507
+ it "can connect asynchronously" do
508
+ serv = TCPServer.new( '127.0.0.1', 54320 )
509
+ conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
510
+ conn.connect_poll.should == PG::PGRES_POLLING_WRITING
511
+ select( nil, [IO.for_fd(conn.socket)], nil, 0.2 )
512
+ serv.close
513
+ if conn.connect_poll == PG::PGRES_POLLING_READING
514
+ select( [IO.for_fd(conn.socket)], nil, nil, 0.2 )
515
+ end
516
+ conn.connect_poll.should == PG::PGRES_POLLING_FAILED
517
+ end
518
+
519
+ it "discards previous results (if any) before waiting on an #async_exec"
520
+
521
+ it "calls the block if one is provided to #async_exec" do
522
+ result = nil
523
+ @conn.async_exec( "select 47 as one" ) do |pg_res|
524
+ result = pg_res[0]
525
+ end
526
+ result.should == { 'one' => '47' }
527
+ end
528
+
529
+ it "raises a rescue-able error if #finish is called twice", :without_transaction do
530
+ conn = PG.connect( @conninfo )
531
+
532
+ conn.finish
533
+ expect { conn.finish }.to raise_error( PG::Error, /connection is closed/i )
534
+ end
535
+
536
+
537
+ context "under PostgreSQL 9", :postgresql_90 do
383
538
 
384
539
  before( :each ) do
385
- pending "only works under PostgreSQL 9" if @conn.server_version < 9_00_00
540
+ pending "only works with a PostgreSQL >= 9.0 server" if @conn.server_version < 9_00_00
386
541
  end
387
542
 
388
543
  it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
@@ -534,153 +689,43 @@ describe PG::Connection do
534
689
 
535
690
  end
536
691
 
537
- it "yields the result if block is given to exec" do
538
- rval = @conn.exec( "select 1234::int as a union select 5678::int as a" ) do |result|
539
- values = []
540
- result.should be_kind_of( PG::Result )
541
- result.ntuples.should == 2
542
- result.each do |tuple|
543
- values << tuple['a']
544
- end
545
- values
546
- end
547
-
548
- rval.should have( 2 ).members
549
- rval.should include( '5678', '1234' )
550
- end
551
-
552
-
553
- it "correctly finishes COPY queries passed to #async_exec" do
554
- @conn.async_exec( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT" )
555
-
556
- results = []
557
- begin
558
- data = @conn.get_copy_data( true )
559
- if false == data
560
- @conn.block( 2.0 )
561
- data = @conn.get_copy_data( true )
562
- end
563
- results << data if data
564
- end until data.nil?
565
-
566
- results.should have( 2 ).members
567
- results.should include( "1\n", "2\n" )
568
- end
569
-
692
+ context "under PostgreSQL 9.1 client library", :postgresql_91, :without_transaction do
570
693
 
571
- it "described_class#block shouldn't block a second thread" do
572
- t = Thread.new do
573
- @conn.send_query( "select pg_sleep(3)" )
574
- @conn.block
694
+ it "pings successfully with connection string" do
695
+ ping = described_class.ping(@conninfo)
696
+ ping.should == PG::PQPING_OK
575
697
  end
576
698
 
577
- # :FIXME: There's a race here, but hopefully it's pretty small.
578
- t.should be_alive()
579
-
580
- @conn.cancel
581
- t.join
582
- end
583
-
584
- it "described_class#block should allow a timeout" do
585
- @conn.send_query( "select pg_sleep(3)" )
586
-
587
- start = Time.now
588
- @conn.block( 0.1 )
589
- finish = Time.now
590
-
591
- (finish - start).should be_within( 0.05 ).of( 0.1 )
592
- end
593
-
594
-
595
- it "can encrypt a string given a password and username" do
596
- described_class.encrypt_password("postgres", "postgres").
597
- should =~ /\S+/
598
- end
599
-
600
-
601
- it "raises an appropriate error if either of the required arguments for encrypt_password " +
602
- "is not valid" do
603
- expect {
604
- described_class.encrypt_password( nil, nil )
605
- }.to raise_error( TypeError )
606
- expect {
607
- described_class.encrypt_password( "postgres", nil )
608
- }.to raise_error( TypeError )
609
- expect {
610
- described_class.encrypt_password( nil, "postgres" )
611
- }.to raise_error( TypeError )
612
- end
613
-
614
-
615
- it "allows fetching a column of values from a result by column number" do
616
- res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
617
- res.column_values( 0 ).should == %w[1 2 3]
618
- res.column_values( 1 ).should == %w[2 3 4]
619
- end
620
-
621
-
622
- it "allows fetching a column of values from a result by field name" do
623
- res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
624
- res.field_values( 'column1' ).should == %w[1 2 3]
625
- res.field_values( 'column2' ).should == %w[2 3 4]
626
- end
627
-
628
-
629
- it "raises an error if selecting an invalid column index" do
630
- res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
631
- expect {
632
- res.column_values( 20 )
633
- }.to raise_error( IndexError )
634
- end
635
-
636
-
637
- it "raises an error if selecting an invalid field name" do
638
- res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
639
- expect {
640
- res.field_values( 'hUUuurrg' )
641
- }.to raise_error( IndexError )
642
- end
643
-
644
-
645
- it "raises an error if column index is not a number" do
646
- res = @conn.exec( 'VALUES (1,2),(2,3),(3,4)' )
647
- expect {
648
- res.column_values( 'hUUuurrg' )
649
- }.to raise_error( TypeError )
650
- end
651
-
699
+ it "pings using 7 arguments converted to strings" do
700
+ ping = described_class.ping('localhost', @port, nil, nil, :test, nil, nil)
701
+ ping.should == PG::PQPING_OK
702
+ end
652
703
 
653
- it "can connect asynchronously" do
654
- serv = TCPServer.new( '127.0.0.1', 54320 )
655
- conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
656
- conn.connect_poll.should == PG::PGRES_POLLING_WRITING
657
- select( nil, [IO.for_fd(conn.socket)], nil, 0.2 )
658
- serv.close
659
- if conn.connect_poll == PG::PGRES_POLLING_READING
660
- select( [IO.for_fd(conn.socket)], nil, nil, 0.2 )
704
+ it "pings using a hash of connection parameters" do
705
+ ping = described_class.ping(
706
+ :host => 'localhost',
707
+ :port => @port,
708
+ :dbname => :test)
709
+ ping.should == PG::PQPING_OK
661
710
  end
662
- conn.connect_poll.should == PG::PGRES_POLLING_FAILED
663
- end
664
711
 
665
- it "discards previous results (if any) before waiting on an #async_exec"
712
+ it "returns correct response when ping connection cannot be established" do
713
+ ping = described_class.ping(
714
+ :host => 'localhost',
715
+ :port => 9999,
716
+ :dbname => :test)
717
+ ping.should == PG::PQPING_NO_RESPONSE
718
+ end
666
719
 
667
- it "calls the block if one is provided to #async_exec" do
668
- result = nil
669
- @conn.async_exec( "select 47 as one" ) do |pg_res|
670
- result = pg_res[0]
720
+ it "returns correct response when ping connection arguments are wrong" do
721
+ ping = described_class.ping('localhost', 'localhost', nil, nil, :test, nil, nil)
722
+ ping.should == PG::PQPING_NO_ATTEMPT
671
723
  end
672
- result.should == { 'one' => '47' }
673
- end
674
724
 
675
- it "raises a rescue-able error if #finish is called twice", :without_transaction do
676
- conn = PG.connect( @conninfo )
677
725
 
678
- conn.finish
679
- expect { conn.finish }.to raise_error( PG::Error, /connection is closed/i )
680
726
  end
681
727
 
682
-
683
- describe "multinationalization support", :ruby_19 => true do
728
+ context "multinationalization support", :ruby_19 do
684
729
 
685
730
  it "should return the same bytes in text format that are sent as inline text" do
686
731
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
@@ -771,6 +816,12 @@ describe PG::Connection do
771
816
  escaped = @conn.escape( original )
772
817
  escaped.encoding.should == Encoding::EUC_JP
773
818
  end
819
+
820
+ it "escapes string as literal" do
821
+ original = "string to\0 escape"
822
+ escaped = @conn.escape_literal( original )
823
+ escaped.should == "'string to'"
824
+ end
774
825
  end
775
826
 
776
827
 
@@ -797,6 +848,19 @@ describe PG::Connection do
797
848
  end
798
849
  end
799
850
 
851
+ it "allows users of the async interface to set the client_encoding to the default_internal" do
852
+ begin
853
+ prev_encoding = Encoding.default_internal
854
+ Encoding.default_internal = Encoding::KOI8_U
855
+
856
+ @conn.set_default_encoding
857
+
858
+ @conn.internal_encoding.should == Encoding::KOI8_U
859
+ ensure
860
+ Encoding.default_internal = prev_encoding
861
+ end
862
+ end
863
+
800
864
  end
801
865
 
802
866
 
@@ -92,16 +92,20 @@ describe PG::Result do
92
92
  res = @conn.exec('VALUES ($1::bytea)',
93
93
  [ { :value => bytes, :format => 1 } ], 1)
94
94
  res[0]['column1'].should== bytes
95
+ res.getvalue(0,0).should == bytes
96
+ res.values[0][0].should == bytes
97
+ res.column_values(0)[0].should == bytes
95
98
  end
96
99
 
97
100
  it "should return the same bytes in binary format that are sent as inline text" do
98
101
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
99
- in_bytes = File.open(binary_file, 'rb').read
100
- out_bytes = nil
102
+ bytes = File.open(binary_file, 'rb').read
101
103
  @conn.exec("SET standard_conforming_strings=on")
102
- res = @conn.exec("VALUES ('#{PG::Connection.escape_bytea(in_bytes)}'::bytea)", [], 1)
103
- out_bytes = res[0]['column1']
104
- out_bytes.should == in_bytes
104
+ res = @conn.exec("VALUES ('#{PG::Connection.escape_bytea(bytes)}'::bytea)", [], 1)
105
+ res[0]['column1'].should == bytes
106
+ res.getvalue(0,0).should == bytes
107
+ res.values[0][0].should == bytes
108
+ res.column_values(0)[0].should == bytes
105
109
  end
106
110
 
107
111
  it "should return the same bytes in text format that are sent in binary format" do
@@ -123,7 +127,7 @@ describe PG::Result do
123
127
  out_bytes.should == in_bytes
124
128
  end
125
129
 
126
- it "should return the parameter type of the specified prepared statment parameter" do
130
+ it "should return the parameter type of the specified prepared statement parameter" do
127
131
  query = 'SELECT * FROM pg_stat_activity WHERE user = $1::name AND current_query = $2::text'
128
132
  @conn.prepare( 'queryfinder', query )
129
133
  res = @conn.describe_prepared( 'queryfinder' )
@@ -248,4 +252,12 @@ describe PG::Result do
248
252
  res.ftablecol(1).should == 0 # and it shouldn't raise an exception, either
249
253
  end
250
254
 
255
+ it "can be manually checked for failed result status (async API)" do
256
+ @conn.send_query( "SELECT * FROM nonexistant_table" )
257
+ res = @conn.get_result
258
+ expect {
259
+ res.check
260
+ }.to raise_error( PG::Error, /relation "nonexistant_table" does not exist/ )
261
+ end
262
+
251
263
  end