pg 0.18.0 → 1.0.0
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.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data/BSDL +2 -2
- data/ChangeLog +1221 -4
- data/History.rdoc +130 -0
- data/Manifest.txt +0 -18
- data/README-Windows.rdoc +15 -26
- data/README.rdoc +16 -10
- data/Rakefile +32 -23
- data/Rakefile.cross +56 -38
- data/ext/errorcodes.def +33 -0
- data/ext/errorcodes.txt +15 -1
- data/ext/extconf.rb +27 -35
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +27 -39
- data/ext/pg.c +19 -51
- data/ext/pg.h +22 -79
- data/ext/pg_binary_decoder.c +3 -1
- data/ext/pg_binary_encoder.c +14 -12
- data/ext/pg_coder.c +31 -10
- data/ext/pg_connection.c +350 -263
- data/ext/pg_copy_coder.c +34 -4
- data/ext/pg_result.c +27 -25
- data/ext/pg_text_decoder.c +9 -10
- data/ext/pg_text_encoder.c +93 -73
- data/ext/pg_type_map.c +20 -13
- data/ext/pg_type_map_by_column.c +7 -7
- data/ext/pg_type_map_by_mri_type.c +2 -2
- data/ext/pg_type_map_in_ruby.c +4 -7
- data/ext/util.c +3 -3
- data/ext/util.h +1 -1
- data/lib/pg/basic_type_mapping.rb +69 -42
- data/lib/pg/connection.rb +89 -38
- data/lib/pg/result.rb +10 -5
- data/lib/pg/text_decoder.rb +12 -3
- data/lib/pg/text_encoder.rb +8 -0
- data/lib/pg.rb +18 -10
- data/spec/helpers.rb +9 -16
- data/spec/pg/basic_type_mapping_spec.rb +58 -4
- data/spec/pg/connection_spec.rb +477 -217
- data/spec/pg/result_spec.rb +14 -7
- data/spec/pg/type_map_by_class_spec.rb +2 -2
- data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
- data/spec/pg/type_spec.rb +145 -33
- data/spec/pg_spec.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +67 -66
- metadata.gz.sig +0 -0
- data/sample/array_insert.rb +0 -20
- data/sample/async_api.rb +0 -106
- data/sample/async_copyto.rb +0 -39
- data/sample/async_mixed.rb +0 -56
- data/sample/check_conn.rb +0 -21
- data/sample/copyfrom.rb +0 -81
- data/sample/copyto.rb +0 -19
- data/sample/cursor.rb +0 -21
- data/sample/disk_usage_report.rb +0 -186
- data/sample/issue-119.rb +0 -94
- data/sample/losample.rb +0 -69
- data/sample/minimal-testcase.rb +0 -17
- data/sample/notify_wait.rb +0 -72
- data/sample/pg_statistics.rb +0 -294
- data/sample/replication_monitor.rb +0 -231
- data/sample/test_binary_values.rb +0 -33
- data/sample/wal_shipper.rb +0 -434
- data/sample/warehouse_partitions.rb +0 -320
data/spec/pg/connection_spec.rb
CHANGED
@@ -45,6 +45,14 @@ describe PG::Connection do
|
|
45
45
|
expect( optstring ).to match( /(^|\s)user='jrandom'/ )
|
46
46
|
end
|
47
47
|
|
48
|
+
it "can create a connection option string from an option string and a hash" do
|
49
|
+
optstring = described_class.parse_connect_args( 'dbname=original', :user => 'jrandom' )
|
50
|
+
|
51
|
+
expect( optstring ).to be_a( String )
|
52
|
+
expect( optstring ).to match( /(^|\s)dbname=original/ )
|
53
|
+
expect( optstring ).to match( /(^|\s)user='jrandom'/ )
|
54
|
+
end
|
55
|
+
|
48
56
|
it "escapes single quotes and backslashes in connection parameters" do
|
49
57
|
expect(
|
50
58
|
described_class.parse_connect_args( "DB 'browser' \\" )
|
@@ -52,18 +60,85 @@ describe PG::Connection do
|
|
52
60
|
|
53
61
|
end
|
54
62
|
|
63
|
+
let(:uri) { 'postgresql://user:pass@pgsql.example.com:222/db01?sslmode=require' }
|
64
|
+
|
65
|
+
it "can connect using a URI" do
|
66
|
+
string = described_class.parse_connect_args( uri )
|
67
|
+
|
68
|
+
expect( string ).to be_a( String )
|
69
|
+
expect( string ).to match( %r{^postgresql://user:pass@pgsql.example.com:222/db01\?} )
|
70
|
+
expect( string ).to match( %r{\?.*sslmode=require} )
|
71
|
+
|
72
|
+
string = described_class.parse_connect_args( URI.parse(uri) )
|
73
|
+
|
74
|
+
expect( string ).to be_a( String )
|
75
|
+
expect( string ).to match( %r{^postgresql://user:pass@pgsql.example.com:222/db01\?} )
|
76
|
+
expect( string ).to match( %r{\?.*sslmode=require} )
|
77
|
+
end
|
78
|
+
|
79
|
+
it "can create a connection URI from a URI and a hash" do
|
80
|
+
string = described_class.parse_connect_args( uri, :connect_timeout => 2 )
|
81
|
+
|
82
|
+
expect( string ).to be_a( String )
|
83
|
+
expect( string ).to match( %r{^postgresql://user:pass@pgsql.example.com:222/db01\?} )
|
84
|
+
expect( string ).to match( %r{\?.*sslmode=require} )
|
85
|
+
expect( string ).to match( %r{\?.*connect_timeout=2} )
|
86
|
+
|
87
|
+
string = described_class.parse_connect_args( uri,
|
88
|
+
:user => 'a',
|
89
|
+
:password => 'b',
|
90
|
+
:host => 'localhost',
|
91
|
+
:port => 555,
|
92
|
+
:dbname => 'x' )
|
93
|
+
|
94
|
+
expect( string ).to be_a( String )
|
95
|
+
expect( string ).to match( %r{^postgresql://\?} )
|
96
|
+
expect( string ).to match( %r{\?.*user=a} )
|
97
|
+
expect( string ).to match( %r{\?.*password=b} )
|
98
|
+
expect( string ).to match( %r{\?.*host=localhost} )
|
99
|
+
expect( string ).to match( %r{\?.*port=555} )
|
100
|
+
expect( string ).to match( %r{\?.*dbname=x} )
|
101
|
+
end
|
102
|
+
|
103
|
+
it "can create a connection URI with a non-standard domain socket directory" do
|
104
|
+
string = described_class.parse_connect_args( 'postgresql://%2Fvar%2Flib%2Fpostgresql/dbname' )
|
105
|
+
|
106
|
+
expect( string ).to be_a( String )
|
107
|
+
expect( string ).to match( %r{^postgresql://%2Fvar%2Flib%2Fpostgresql/dbname} )
|
108
|
+
|
109
|
+
string = described_class.
|
110
|
+
parse_connect_args( 'postgresql:///dbname', :host => '/var/lib/postgresql' )
|
111
|
+
|
112
|
+
expect( string ).to be_a( String )
|
113
|
+
expect( string ).to match( %r{^postgresql:///dbname\?} )
|
114
|
+
expect( string ).to match( %r{\?.*host=%2Fvar%2Flib%2Fpostgresql} )
|
115
|
+
end
|
116
|
+
|
55
117
|
it "connects with defaults if no connection parameters are given" do
|
56
118
|
expect( described_class.parse_connect_args ).to eq( '' )
|
57
119
|
end
|
58
120
|
|
59
121
|
it "connects successfully with connection string" do
|
60
|
-
|
122
|
+
conninfo_with_colon_in_password = "host=localhost user=a port=555 dbname=test password=a:a"
|
123
|
+
|
124
|
+
string = described_class.parse_connect_args( conninfo_with_colon_in_password )
|
125
|
+
|
126
|
+
expect( string ).to be_a( String )
|
127
|
+
expect( string ).to match( %r{(^|\s)user=a} )
|
128
|
+
expect( string ).to match( %r{(^|\s)password=a:a} )
|
129
|
+
expect( string ).to match( %r{(^|\s)host=localhost} )
|
130
|
+
expect( string ).to match( %r{(^|\s)port=555} )
|
131
|
+
expect( string ).to match( %r{(^|\s)dbname=test} )
|
132
|
+
end
|
133
|
+
|
134
|
+
it "connects successfully with connection string" do
|
135
|
+
tmpconn = described_class.connect( @conninfo )
|
61
136
|
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
62
137
|
tmpconn.finish
|
63
138
|
end
|
64
139
|
|
65
140
|
it "connects using 7 arguments converted to strings" do
|
66
|
-
tmpconn = described_class.connect('localhost', @port, nil, nil, :test, nil, nil)
|
141
|
+
tmpconn = described_class.connect( 'localhost', @port, nil, nil, :test, nil, nil )
|
67
142
|
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
68
143
|
tmpconn.finish
|
69
144
|
end
|
@@ -77,7 +152,7 @@ describe PG::Connection do
|
|
77
152
|
tmpconn.finish
|
78
153
|
end
|
79
154
|
|
80
|
-
it "connects using a hash of optional connection parameters"
|
155
|
+
it "connects using a hash of optional connection parameters" do
|
81
156
|
tmpconn = described_class.connect(
|
82
157
|
:host => 'localhost',
|
83
158
|
:port => @port,
|
@@ -89,8 +164,13 @@ describe PG::Connection do
|
|
89
164
|
|
90
165
|
it "raises an exception when connecting with an invalid number of arguments" do
|
91
166
|
expect {
|
92
|
-
described_class.connect( 1, 2, 3, 4, 5, 6, 7, 'extra' )
|
93
|
-
}.to raise_error
|
167
|
+
described_class.connect( 1, 2, 3, 4, 5, 6, 7, 'the-extra-arg' )
|
168
|
+
}.to raise_error do |error|
|
169
|
+
expect( error ).to be_an( ArgumentError )
|
170
|
+
expect( error.message ).to match( /extra positional parameter/i )
|
171
|
+
expect( error.message ).to match( /8/ )
|
172
|
+
expect( error.message ).to match( /the-extra-arg/ )
|
173
|
+
end
|
94
174
|
end
|
95
175
|
|
96
176
|
it "can connect asynchronously", :socket_io do
|
@@ -152,7 +232,7 @@ describe PG::Connection do
|
|
152
232
|
described_class.connect(@conninfo).finish
|
153
233
|
sleep 0.5
|
154
234
|
res = @conn.exec(%[SELECT COUNT(*) AS n FROM pg_stat_activity
|
155
|
-
WHERE usename IS NOT NULL])
|
235
|
+
WHERE usename IS NOT NULL AND application_name != ''])
|
156
236
|
# there's still the global @conn, but should be no more
|
157
237
|
expect( res[0]['n'] ).to eq( '1' )
|
158
238
|
end
|
@@ -161,13 +241,14 @@ describe PG::Connection do
|
|
161
241
|
expect( @conn.db ).to eq( "test" )
|
162
242
|
expect( @conn.user ).to be_a_kind_of( String )
|
163
243
|
expect( @conn.pass ).to eq( "" )
|
164
|
-
expect( @conn.host ).to eq( "localhost" )
|
165
|
-
# TODO: Not sure why libpq returns a NULL ptr instead of "127.0.0.1"
|
166
|
-
expect( @conn.hostaddr ).to eq( nil ) if @conn.server_version >= 9_04_00
|
167
244
|
expect( @conn.port ).to eq( 54321 )
|
168
245
|
expect( @conn.tty ).to eq( "" )
|
169
246
|
expect( @conn.options ).to eq( "" )
|
170
247
|
end
|
248
|
+
it "can retrieve it's connection parameters for the established connection",
|
249
|
+
skip: RUBY_PLATFORM=~/x64-mingw/ ? "host segfaults on Windows-x64" : false do
|
250
|
+
expect( @conn.host ).to eq( "localhost" )
|
251
|
+
end
|
171
252
|
|
172
253
|
EXPECTED_TRACE_OUTPUT = %{
|
173
254
|
To backend> Msg Q
|
@@ -456,7 +537,7 @@ describe PG::Connection do
|
|
456
537
|
@conn.exec( 'UNLISTEN woo' )
|
457
538
|
end
|
458
539
|
|
459
|
-
it "can receive notices while waiting for NOTIFY without exceeding the timeout"
|
540
|
+
it "can receive notices while waiting for NOTIFY without exceeding the timeout" do
|
460
541
|
notices = []
|
461
542
|
@conn.set_notice_processor do |msg|
|
462
543
|
notices << [msg, Time.now]
|
@@ -518,7 +599,7 @@ describe PG::Connection do
|
|
518
599
|
expect( @conn ).to still_be_usable
|
519
600
|
end
|
520
601
|
|
521
|
-
it "can handle server errors in #copy_data for output"
|
602
|
+
it "can handle server errors in #copy_data for output" do
|
522
603
|
@conn.exec "ROLLBACK"
|
523
604
|
@conn.transaction do
|
524
605
|
@conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
|
@@ -575,6 +656,31 @@ describe PG::Connection do
|
|
575
656
|
expect( @conn ).to still_be_usable
|
576
657
|
end
|
577
658
|
|
659
|
+
it "gracefully handle SQL statements while in #copy_data for input" do
|
660
|
+
@conn.exec "ROLLBACK"
|
661
|
+
@conn.transaction do
|
662
|
+
@conn.exec( "CREATE TEMP TABLE copytable (col1 INT)" )
|
663
|
+
expect {
|
664
|
+
@conn.copy_data( "COPY copytable FROM STDOUT" ) do |res|
|
665
|
+
@conn.exec "SELECT 1"
|
666
|
+
end
|
667
|
+
}.to raise_error(PG::Error, /no COPY in progress/)
|
668
|
+
end
|
669
|
+
expect( @conn ).to still_be_usable
|
670
|
+
end
|
671
|
+
|
672
|
+
it "gracefully handle SQL statements while in #copy_data for output" do
|
673
|
+
@conn.exec "ROLLBACK"
|
674
|
+
@conn.transaction do
|
675
|
+
expect {
|
676
|
+
@conn.copy_data( "COPY (VALUES(1), (2)) TO STDOUT" ) do |res|
|
677
|
+
@conn.exec "SELECT 3"
|
678
|
+
end
|
679
|
+
}.to raise_error(PG::Error, /no COPY in progress/)
|
680
|
+
end
|
681
|
+
expect( @conn ).to still_be_usable
|
682
|
+
end
|
683
|
+
|
578
684
|
it "should raise an error for non copy statements in #copy_data" do
|
579
685
|
expect {
|
580
686
|
@conn.copy_data( "SELECT 1" ){}
|
@@ -616,18 +722,13 @@ describe PG::Connection do
|
|
616
722
|
end
|
617
723
|
|
618
724
|
it "described_class#block should allow a timeout" do
|
619
|
-
@conn.send_query( "select pg_sleep(
|
725
|
+
@conn.send_query( "select pg_sleep(1)" )
|
620
726
|
|
621
727
|
start = Time.now
|
622
|
-
@conn.block( 0.
|
728
|
+
@conn.block( 0.3 )
|
623
729
|
finish = Time.now
|
624
730
|
|
625
|
-
expect( (finish - start) ).to be_within( 0.
|
626
|
-
end
|
627
|
-
|
628
|
-
|
629
|
-
it "can encrypt a string given a password and username" do
|
630
|
-
expect( described_class.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
|
731
|
+
expect( (finish - start) ).to be_within( 0.2 ).of( 0.3 )
|
631
732
|
end
|
632
733
|
|
633
734
|
it "can return the default connection options" do
|
@@ -640,7 +741,7 @@ describe PG::Connection do
|
|
640
741
|
it "can return the default connection options as a Hash" do
|
641
742
|
expect( described_class.conndefaults_hash ).to be_a( Hash )
|
642
743
|
expect( described_class.conndefaults_hash ).to include( :user, :password, :dbname, :host, :port )
|
643
|
-
expect(
|
744
|
+
expect( ['5432', '54321'] ).to include( described_class.conndefaults_hash[:port] )
|
644
745
|
expect( @conn.conndefaults_hash ).to eq( described_class.conndefaults_hash )
|
645
746
|
end
|
646
747
|
|
@@ -657,6 +758,25 @@ describe PG::Connection do
|
|
657
758
|
expect( @conn.conninfo_hash[:dbname] ).to eq( 'test' )
|
658
759
|
end
|
659
760
|
|
761
|
+
describe "connection information related to SSL" do
|
762
|
+
|
763
|
+
it "can retrieve connection's ssl state", :postgresql_95 do
|
764
|
+
expect( @conn.ssl_in_use? ).to be false
|
765
|
+
end
|
766
|
+
|
767
|
+
it "can retrieve connection's ssl attribute_names", :postgresql_95 do
|
768
|
+
expect( @conn.ssl_attribute_names ).to be_a(Array)
|
769
|
+
end
|
770
|
+
|
771
|
+
it "can retrieve a single ssl connection attribute", :postgresql_95 do
|
772
|
+
expect( @conn.ssl_attribute('dbname') ).to eq( nil )
|
773
|
+
end
|
774
|
+
|
775
|
+
it "can retrieve all connection's ssl attributes", :postgresql_95 do
|
776
|
+
expect( @conn.ssl_attributes ).to be_a_kind_of( Hash )
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
660
780
|
|
661
781
|
it "honors the connect_timeout connection parameter", :postgresql_93 do
|
662
782
|
conn = PG.connect( port: @port, dbname: 'test', connect_timeout: 11 )
|
@@ -667,18 +787,52 @@ describe PG::Connection do
|
|
667
787
|
end
|
668
788
|
end
|
669
789
|
|
790
|
+
describe "deprecated password encryption method" do
|
791
|
+
it "can encrypt password for a given user" do
|
792
|
+
expect( described_class.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
|
793
|
+
end
|
670
794
|
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
795
|
+
it "raises an appropriate error if either of the required arguments is not valid" do
|
796
|
+
expect {
|
797
|
+
described_class.encrypt_password( nil, nil )
|
798
|
+
}.to raise_error( TypeError )
|
799
|
+
expect {
|
800
|
+
described_class.encrypt_password( "postgres", nil )
|
801
|
+
}.to raise_error( TypeError )
|
802
|
+
expect {
|
803
|
+
described_class.encrypt_password( nil, "postgres" )
|
804
|
+
}.to raise_error( TypeError )
|
805
|
+
end
|
806
|
+
end
|
807
|
+
|
808
|
+
describe "password encryption method", :postgresql_10 do
|
809
|
+
it "can encrypt without algorithm" do
|
810
|
+
expect( @conn.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
|
811
|
+
expect( @conn.encrypt_password("postgres", "postgres", nil) ).to match( /\S+/ )
|
812
|
+
end
|
813
|
+
|
814
|
+
it "can encrypt with algorithm" do
|
815
|
+
expect( @conn.encrypt_password("postgres", "postgres", "md5") ).to match( /md5\S+/i )
|
816
|
+
expect( @conn.encrypt_password("postgres", "postgres", "scram-sha-256") ).to match( /SCRAM-SHA-256\S+/i )
|
817
|
+
end
|
818
|
+
|
819
|
+
it "raises an appropriate error if either of the required arguments is not valid" do
|
820
|
+
expect {
|
821
|
+
@conn.encrypt_password( nil, nil )
|
822
|
+
}.to raise_error( TypeError )
|
823
|
+
expect {
|
824
|
+
@conn.encrypt_password( "postgres", nil )
|
825
|
+
}.to raise_error( TypeError )
|
826
|
+
expect {
|
827
|
+
@conn.encrypt_password( nil, "postgres" )
|
828
|
+
}.to raise_error( TypeError )
|
829
|
+
expect {
|
830
|
+
@conn.encrypt_password( "postgres", "postgres", :invalid )
|
831
|
+
}.to raise_error( TypeError )
|
832
|
+
expect {
|
833
|
+
@conn.encrypt_password( "postgres", "postgres", "invalid" )
|
834
|
+
}.to raise_error( PG::Error, /unrecognized/ )
|
835
|
+
end
|
682
836
|
end
|
683
837
|
|
684
838
|
|
@@ -777,155 +931,147 @@ describe PG::Connection do
|
|
777
931
|
expect{ conn.block }.to raise_error(PG::ConnectionBad, /can't get socket descriptor/)
|
778
932
|
end
|
779
933
|
|
780
|
-
|
934
|
+
it "sets the fallback_application_name on new connections" do
|
935
|
+
conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
|
781
936
|
|
782
|
-
|
783
|
-
|
784
|
-
|
937
|
+
conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
|
938
|
+
expect( conn_name ).to include( $0[0..10] )
|
939
|
+
expect( conn_name ).to include( $0[-10..-1] )
|
940
|
+
expect( conn_name.length ).to be <= 64
|
941
|
+
end
|
785
942
|
|
786
|
-
|
943
|
+
it "sets a shortened fallback_application_name on new connections" do
|
944
|
+
old_0 = $0
|
945
|
+
begin
|
946
|
+
$0 = "/this/is/a/very/long/path/with/many/directories/to/our/beloved/ruby"
|
787
947
|
conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
|
788
|
-
|
789
948
|
conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
|
790
949
|
expect( conn_name ).to include( $0[0..10] )
|
791
950
|
expect( conn_name ).to include( $0[-10..-1] )
|
792
951
|
expect( conn_name.length ).to be <= 64
|
952
|
+
ensure
|
953
|
+
$0 = old_0
|
793
954
|
end
|
955
|
+
end
|
794
956
|
|
795
|
-
|
796
|
-
|
797
|
-
begin
|
798
|
-
$0 = "/this/is/a/very/long/path/with/many/directories/to/our/beloved/ruby"
|
799
|
-
conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
|
800
|
-
conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
|
801
|
-
expect( conn_name ).to include( $0[0..10] )
|
802
|
-
expect( conn_name ).to include( $0[-10..-1] )
|
803
|
-
expect( conn_name.length ).to be <= 64
|
804
|
-
ensure
|
805
|
-
$0 = old_0
|
806
|
-
end
|
807
|
-
end
|
808
|
-
|
809
|
-
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
810
|
-
"any number of arguments" do
|
811
|
-
|
812
|
-
@conn.exec( 'ROLLBACK' )
|
813
|
-
@conn.exec( 'LISTEN knees' )
|
957
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
958
|
+
"any number of arguments" do
|
814
959
|
|
815
|
-
|
816
|
-
|
817
|
-
conn.finish
|
960
|
+
@conn.exec( 'ROLLBACK' )
|
961
|
+
@conn.exec( 'LISTEN knees' )
|
818
962
|
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
end
|
823
|
-
@conn.exec( 'UNLISTEN knees' )
|
963
|
+
conn = described_class.connect( @conninfo )
|
964
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
965
|
+
conn.finish
|
824
966
|
|
825
|
-
|
826
|
-
|
827
|
-
|
967
|
+
event, pid, msg = nil
|
968
|
+
@conn.wait_for_notify( 10 ) do |*args|
|
969
|
+
event, pid, msg = *args
|
828
970
|
end
|
971
|
+
@conn.exec( 'UNLISTEN knees' )
|
829
972
|
|
830
|
-
|
831
|
-
|
832
|
-
|
973
|
+
expect( event ).to eq( 'knees' )
|
974
|
+
expect( pid ).to be_a_kind_of( Integer )
|
975
|
+
expect( msg ).to eq( 'skirt and boots' )
|
976
|
+
end
|
833
977
|
|
834
|
-
|
835
|
-
|
836
|
-
|
978
|
+
it "accepts nil as the timeout in #wait_for_notify " do
|
979
|
+
@conn.exec( 'ROLLBACK' )
|
980
|
+
@conn.exec( 'LISTEN knees' )
|
837
981
|
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
end
|
842
|
-
@conn.exec( 'UNLISTEN knees' )
|
982
|
+
conn = described_class.connect( @conninfo )
|
983
|
+
conn.exec( %Q{NOTIFY knees} )
|
984
|
+
conn.finish
|
843
985
|
|
844
|
-
|
845
|
-
|
986
|
+
event, pid = nil
|
987
|
+
@conn.wait_for_notify( nil ) do |*args|
|
988
|
+
event, pid = *args
|
846
989
|
end
|
990
|
+
@conn.exec( 'UNLISTEN knees' )
|
847
991
|
|
848
|
-
|
849
|
-
|
850
|
-
|
992
|
+
expect( event ).to eq( 'knees' )
|
993
|
+
expect( pid ).to be_a_kind_of( Integer )
|
994
|
+
end
|
851
995
|
|
852
|
-
|
853
|
-
|
854
|
-
|
996
|
+
it "sends nil as the payload if the notification wasn't given one" do
|
997
|
+
@conn.exec( 'ROLLBACK' )
|
998
|
+
@conn.exec( 'LISTEN knees' )
|
855
999
|
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
end
|
860
|
-
@conn.exec( 'UNLISTEN knees' )
|
1000
|
+
conn = described_class.connect( @conninfo )
|
1001
|
+
conn.exec( %Q{NOTIFY knees} )
|
1002
|
+
conn.finish
|
861
1003
|
|
862
|
-
|
1004
|
+
payload = :notnil
|
1005
|
+
@conn.wait_for_notify( nil ) do |*args|
|
1006
|
+
payload = args[ 2 ]
|
863
1007
|
end
|
1008
|
+
@conn.exec( 'UNLISTEN knees' )
|
864
1009
|
|
865
|
-
|
866
|
-
|
1010
|
+
expect( payload ).to be_nil()
|
1011
|
+
end
|
867
1012
|
|
868
|
-
|
869
|
-
|
1013
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
1014
|
+
"two arguments" do
|
870
1015
|
|
871
|
-
|
872
|
-
|
873
|
-
conn.finish
|
1016
|
+
@conn.exec( 'ROLLBACK' )
|
1017
|
+
@conn.exec( 'LISTEN knees' )
|
874
1018
|
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
end
|
879
|
-
@conn.exec( 'UNLISTEN knees' )
|
1019
|
+
conn = described_class.connect( @conninfo )
|
1020
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1021
|
+
conn.finish
|
880
1022
|
|
881
|
-
|
882
|
-
|
883
|
-
|
1023
|
+
event, pid, msg = nil
|
1024
|
+
@conn.wait_for_notify( 10 ) do |arg1, arg2|
|
1025
|
+
event, pid, msg = arg1, arg2
|
884
1026
|
end
|
1027
|
+
@conn.exec( 'UNLISTEN knees' )
|
885
1028
|
|
886
|
-
|
887
|
-
|
1029
|
+
expect( event ).to eq( 'knees' )
|
1030
|
+
expect( pid ).to be_a_kind_of( Integer )
|
1031
|
+
expect( msg ).to be_nil()
|
1032
|
+
end
|
888
1033
|
|
889
|
-
|
890
|
-
|
1034
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it " +
|
1035
|
+
"doesn't accept arguments" do
|
891
1036
|
|
892
|
-
|
893
|
-
|
894
|
-
conn.finish
|
1037
|
+
@conn.exec( 'ROLLBACK' )
|
1038
|
+
@conn.exec( 'LISTEN knees' )
|
895
1039
|
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
end
|
900
|
-
@conn.exec( 'UNLISTEN knees' )
|
1040
|
+
conn = described_class.connect( @conninfo )
|
1041
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1042
|
+
conn.finish
|
901
1043
|
|
902
|
-
|
1044
|
+
notification_received = false
|
1045
|
+
@conn.wait_for_notify( 10 ) do
|
1046
|
+
notification_received = true
|
903
1047
|
end
|
1048
|
+
@conn.exec( 'UNLISTEN knees' )
|
904
1049
|
|
905
|
-
|
906
|
-
|
1050
|
+
expect( notification_received ).to be_truthy()
|
1051
|
+
end
|
907
1052
|
|
908
|
-
|
909
|
-
|
1053
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
1054
|
+
"three arguments" do
|
910
1055
|
|
911
|
-
|
912
|
-
|
913
|
-
conn.finish
|
1056
|
+
@conn.exec( 'ROLLBACK' )
|
1057
|
+
@conn.exec( 'LISTEN knees' )
|
914
1058
|
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
end
|
919
|
-
@conn.exec( 'UNLISTEN knees' )
|
1059
|
+
conn = described_class.connect( @conninfo )
|
1060
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1061
|
+
conn.finish
|
920
1062
|
|
921
|
-
|
922
|
-
|
923
|
-
|
1063
|
+
event, pid, msg = nil
|
1064
|
+
@conn.wait_for_notify( 10 ) do |arg1, arg2, arg3|
|
1065
|
+
event, pid, msg = arg1, arg2, arg3
|
924
1066
|
end
|
1067
|
+
@conn.exec( 'UNLISTEN knees' )
|
925
1068
|
|
1069
|
+
expect( event ).to eq( 'knees' )
|
1070
|
+
expect( pid ).to be_a_kind_of( Integer )
|
1071
|
+
expect( msg ).to eq( 'skirt and boots' )
|
926
1072
|
end
|
927
1073
|
|
928
|
-
context "
|
1074
|
+
context "server ping", :without_transaction do
|
929
1075
|
|
930
1076
|
it "pings successfully with connection string" do
|
931
1077
|
ping = described_class.ping(@conninfo)
|
@@ -958,71 +1104,69 @@ describe PG::Connection do
|
|
958
1104
|
expect( ping ).to eq( PG::PQPING_NO_ATTEMPT )
|
959
1105
|
end
|
960
1106
|
|
961
|
-
|
962
1107
|
end
|
963
1108
|
|
964
|
-
|
965
|
-
describe "set_single_row_mode" do
|
1109
|
+
describe "set_single_row_mode" do
|
966
1110
|
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
1111
|
+
it "raises an error when called at the wrong time" do
|
1112
|
+
expect {
|
1113
|
+
@conn.set_single_row_mode
|
1114
|
+
}.to raise_error(PG::Error)
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
it "should work in single row mode" do
|
1118
|
+
@conn.send_query( "SELECT generate_series(1,10)" )
|
1119
|
+
@conn.set_single_row_mode
|
1120
|
+
|
1121
|
+
results = []
|
1122
|
+
loop do
|
1123
|
+
@conn.block
|
1124
|
+
res = @conn.get_result or break
|
1125
|
+
results << res
|
1126
|
+
end
|
1127
|
+
expect( results.length ).to eq( 11 )
|
1128
|
+
results[0..-2].each do |res|
|
1129
|
+
expect( res.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
1130
|
+
values = res.field_values('generate_series')
|
1131
|
+
expect( values.length ).to eq( 1 )
|
1132
|
+
expect( values.first.to_i ).to be > 0
|
971
1133
|
end
|
1134
|
+
expect( results.last.result_status ).to eq( PG::PGRES_TUPLES_OK )
|
1135
|
+
expect( results.last.ntuples ).to eq( 0 )
|
1136
|
+
end
|
972
1137
|
|
973
|
-
|
974
|
-
|
975
|
-
|
1138
|
+
it "should receive rows before entire query is finished" do
|
1139
|
+
@conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, pg_sleep(1);" )
|
1140
|
+
@conn.set_single_row_mode
|
976
1141
|
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
expect( results.length ).to eq( 11 )
|
984
|
-
results[0..-2].each do |res|
|
985
|
-
expect( res.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
986
|
-
values = res.field_values('generate_series')
|
987
|
-
expect( values.length ).to eq( 1 )
|
988
|
-
expect( values.first.to_i ).to be > 0
|
989
|
-
end
|
990
|
-
expect( results.last.result_status ).to eq( PG::PGRES_TUPLES_OK )
|
991
|
-
expect( results.last.ntuples ).to eq( 0 )
|
1142
|
+
start_time = Time.now
|
1143
|
+
first_row_time = nil
|
1144
|
+
loop do
|
1145
|
+
res = @conn.get_result or break
|
1146
|
+
res.check
|
1147
|
+
first_row_time = Time.now unless first_row_time
|
992
1148
|
end
|
1149
|
+
expect( (Time.now - start_time) ).to be >= 0.9
|
1150
|
+
expect( (first_row_time - start_time) ).to be < 0.9
|
1151
|
+
end
|
993
1152
|
|
994
|
-
|
995
|
-
|
996
|
-
|
1153
|
+
it "should receive rows before entire query fails" do
|
1154
|
+
@conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
|
1155
|
+
@conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, errfunc();" )
|
1156
|
+
@conn.set_single_row_mode
|
997
1157
|
|
998
|
-
|
999
|
-
|
1158
|
+
first_result = nil
|
1159
|
+
expect do
|
1000
1160
|
loop do
|
1001
1161
|
res = @conn.get_result or break
|
1002
1162
|
res.check
|
1003
|
-
|
1163
|
+
first_result ||= res
|
1004
1164
|
end
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
it "should receive rows before entire query fails" do
|
1010
|
-
@conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
|
1011
|
-
@conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, errfunc();" )
|
1012
|
-
@conn.set_single_row_mode
|
1013
|
-
|
1014
|
-
first_result = nil
|
1015
|
-
expect do
|
1016
|
-
loop do
|
1017
|
-
res = @conn.get_result or break
|
1018
|
-
res.check
|
1019
|
-
first_result ||= res
|
1020
|
-
end
|
1021
|
-
end.to raise_error(PG::Error)
|
1022
|
-
expect( first_result.kind_of?(PG::Result) ).to be_truthy
|
1023
|
-
expect( first_result.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
1024
|
-
end
|
1165
|
+
end.to raise_error(PG::Error)
|
1166
|
+
expect( first_result.kind_of?(PG::Result) ).to be_truthy
|
1167
|
+
expect( first_result.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
1025
1168
|
end
|
1169
|
+
|
1026
1170
|
end
|
1027
1171
|
|
1028
1172
|
context "multinationalization support", :ruby_19 do
|
@@ -1082,55 +1226,164 @@ describe PG::Connection do
|
|
1082
1226
|
end
|
1083
1227
|
|
1084
1228
|
it "uses the client encoding for escaped string" do
|
1085
|
-
original = "
|
1229
|
+
original = "Möhre to\0 escape".encode( "utf-16be" )
|
1086
1230
|
@conn.set_client_encoding( "euc_jp" )
|
1087
1231
|
escaped = @conn.escape( original )
|
1088
1232
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1089
|
-
expect( escaped ).to eq( "
|
1233
|
+
expect( escaped ).to eq( "Möhre to".encode(Encoding::EUC_JP) )
|
1090
1234
|
end
|
1091
1235
|
|
1092
|
-
it "uses the client encoding for escaped literal"
|
1093
|
-
original = "
|
1236
|
+
it "uses the client encoding for escaped literal" do
|
1237
|
+
original = "Möhre to\0 escape".encode( "utf-16be" )
|
1094
1238
|
@conn.set_client_encoding( "euc_jp" )
|
1095
1239
|
escaped = @conn.escape_literal( original )
|
1096
1240
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1097
|
-
expect( escaped ).to eq( "'
|
1241
|
+
expect( escaped ).to eq( "'Möhre to'".encode(Encoding::EUC_JP) )
|
1098
1242
|
end
|
1099
1243
|
|
1100
|
-
it "uses the client encoding for escaped identifier"
|
1101
|
-
original = "
|
1244
|
+
it "uses the client encoding for escaped identifier" do
|
1245
|
+
original = "Möhre to\0 escape".encode( "utf-16le" )
|
1102
1246
|
@conn.set_client_encoding( "euc_jp" )
|
1103
1247
|
escaped = @conn.escape_identifier( original )
|
1104
1248
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1105
|
-
expect( escaped ).to eq( "\"
|
1249
|
+
expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
|
1106
1250
|
end
|
1107
1251
|
|
1108
1252
|
it "uses the client encoding for quote_ident" do
|
1109
|
-
original = "
|
1253
|
+
original = "Möhre to\0 escape".encode( "utf-16le" )
|
1110
1254
|
@conn.set_client_encoding( "euc_jp" )
|
1111
1255
|
escaped = @conn.quote_ident( original )
|
1112
1256
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1113
|
-
expect( escaped ).to eq( "\"
|
1257
|
+
expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
|
1114
1258
|
end
|
1115
1259
|
|
1116
1260
|
it "uses the previous string encoding for escaped string" do
|
1117
|
-
original = "
|
1261
|
+
original = "Möhre to\0 escape".encode( "iso-8859-1" )
|
1118
1262
|
@conn.set_client_encoding( "euc_jp" )
|
1119
1263
|
escaped = described_class.escape( original )
|
1120
1264
|
expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
|
1121
|
-
expect( escaped ).to eq( "
|
1265
|
+
expect( escaped ).to eq( "Möhre to".encode(Encoding::ISO8859_1) )
|
1122
1266
|
end
|
1123
1267
|
|
1124
1268
|
it "uses the previous string encoding for quote_ident" do
|
1125
|
-
original = "
|
1269
|
+
original = "Möhre to\0 escape".encode( "iso-8859-1" )
|
1126
1270
|
@conn.set_client_encoding( "euc_jp" )
|
1127
1271
|
escaped = described_class.quote_ident( original )
|
1128
1272
|
expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
|
1129
|
-
expect( escaped ).to eq( "\"
|
1273
|
+
expect( escaped.encode ).to eq( "\"Möhre to\"".encode(Encoding::ISO8859_1) )
|
1130
1274
|
end
|
1131
1275
|
|
1276
|
+
it "raises appropriate error if set_client_encoding is called with invalid arguments" do
|
1277
|
+
expect { @conn.set_client_encoding( "invalid" ) }.to raise_error(PG::Error, /invalid value/)
|
1278
|
+
expect { @conn.set_client_encoding( :invalid ) }.to raise_error(TypeError)
|
1279
|
+
expect { @conn.set_client_encoding( nil ) }.to raise_error(TypeError)
|
1280
|
+
end
|
1132
1281
|
end
|
1133
1282
|
|
1283
|
+
describe "respect and convert character encoding of input strings" do
|
1284
|
+
before :each do
|
1285
|
+
@conn.internal_encoding = __ENCODING__
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
it "should convert query string and parameters to #exec_params" do
|
1289
|
+
r = @conn.exec_params("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
|
1290
|
+
['grün'.encode('utf-16be'), 'grün'.encode('iso-8859-1')])
|
1291
|
+
expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1292
|
+
end
|
1293
|
+
|
1294
|
+
it "should convert query string and parameters to #async_exec" do
|
1295
|
+
r = @conn.async_exec("VALUES( $1, $2, $1=$2, 'grün')".encode("cp936"),
|
1296
|
+
['grün'.encode('cp850'), 'grün'.encode('utf-16le')])
|
1297
|
+
expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1298
|
+
end
|
1299
|
+
|
1300
|
+
it "should convert query string to #exec" do
|
1301
|
+
r = @conn.exec("SELECT 'grün'".encode("utf-16be"))
|
1302
|
+
expect( r.values ).to eq( [['grün']] )
|
1303
|
+
end
|
1304
|
+
|
1305
|
+
it "should convert query string to #async_exec" do
|
1306
|
+
r = @conn.async_exec("SELECT 'grün'".encode("utf-16le"))
|
1307
|
+
expect( r.values ).to eq( [['grün']] )
|
1308
|
+
end
|
1309
|
+
|
1310
|
+
it "should convert strings and parameters to #prepare and #exec_prepared" do
|
1311
|
+
@conn.prepare("weiß1".encode("utf-16be"), "VALUES( $1, $2, $1=$2, 'grün')".encode("cp850"))
|
1312
|
+
r = @conn.exec_prepared("weiß1".encode("utf-32le"),
|
1313
|
+
['grün'.encode('cp936'), 'grün'.encode('utf-16le')])
|
1314
|
+
expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1315
|
+
end
|
1316
|
+
|
1317
|
+
it "should convert strings to #describe_prepared" do
|
1318
|
+
@conn.prepare("weiß2", "VALUES(123)")
|
1319
|
+
r = @conn.describe_prepared("weiß2".encode("utf-16be"))
|
1320
|
+
expect( r.nfields ).to eq( 1 )
|
1321
|
+
end
|
1322
|
+
|
1323
|
+
it "should convert strings to #describe_portal" do
|
1324
|
+
@conn.exec "DECLARE cörsör CURSOR FOR VALUES(1,2,3)"
|
1325
|
+
r = @conn.describe_portal("cörsör".encode("utf-16le"))
|
1326
|
+
expect( r.nfields ).to eq( 3 )
|
1327
|
+
end
|
1328
|
+
|
1329
|
+
it "should convert query string to #send_query" do
|
1330
|
+
@conn.send_query("VALUES('grün')".encode("utf-16be"))
|
1331
|
+
expect( @conn.get_last_result.values ).to eq( [['grün']] )
|
1332
|
+
end
|
1333
|
+
|
1334
|
+
it "should convert query string and parameters to #send_query" do
|
1335
|
+
@conn.send_query("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
|
1336
|
+
['grün'.encode('utf-32be'), 'grün'.encode('iso-8859-1')])
|
1337
|
+
expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1338
|
+
end
|
1339
|
+
|
1340
|
+
it "should convert strings and parameters to #send_prepare and #send_query_prepared" do
|
1341
|
+
@conn.send_prepare("weiß3".encode("iso-8859-1"), "VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16be"))
|
1342
|
+
@conn.get_last_result
|
1343
|
+
@conn.send_query_prepared("weiß3".encode("utf-32le"),
|
1344
|
+
['grün'.encode('utf-16le'), 'grün'.encode('iso-8859-1')])
|
1345
|
+
expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
it "should convert strings to #send_describe_prepared" do
|
1349
|
+
@conn.prepare("weiß4", "VALUES(123)")
|
1350
|
+
@conn.send_describe_prepared("weiß4".encode("utf-16be"))
|
1351
|
+
expect( @conn.get_last_result.nfields ).to eq( 1 )
|
1352
|
+
end
|
1353
|
+
|
1354
|
+
it "should convert strings to #send_describe_portal" do
|
1355
|
+
@conn.exec "DECLARE cörsör CURSOR FOR VALUES(1,2,3)"
|
1356
|
+
@conn.send_describe_portal("cörsör".encode("utf-16le"))
|
1357
|
+
expect( @conn.get_last_result.nfields ).to eq( 3 )
|
1358
|
+
end
|
1359
|
+
|
1360
|
+
it "should convert error string to #put_copy_end" do
|
1361
|
+
@conn.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
|
1362
|
+
@conn.exec( "COPY copytable FROM STDIN" )
|
1363
|
+
@conn.put_copy_end("grün".encode("utf-16be"))
|
1364
|
+
expect( @conn.get_result.error_message ).to match(/grün/)
|
1365
|
+
@conn.get_result
|
1366
|
+
end
|
1367
|
+
end
|
1368
|
+
|
1369
|
+
it "can quote bigger strings with quote_ident" do
|
1370
|
+
original = "'01234567\"" * 100
|
1371
|
+
escaped = described_class.quote_ident( original + "\0afterzero" )
|
1372
|
+
expect( escaped ).to eq( "\"" + original.gsub("\"", "\"\"") + "\"" )
|
1373
|
+
end
|
1374
|
+
|
1375
|
+
it "can quote Arrays with quote_ident" do
|
1376
|
+
original = "'01234567\""
|
1377
|
+
escaped = described_class.quote_ident( [original]*3 )
|
1378
|
+
expected = ["\"" + original.gsub("\"", "\"\"") + "\""] * 3
|
1379
|
+
expect( escaped ).to eq( expected.join(".") )
|
1380
|
+
end
|
1381
|
+
|
1382
|
+
it "will raise a TypeError for invalid arguments to quote_ident" do
|
1383
|
+
expect{ described_class.quote_ident( nil ) }.to raise_error(TypeError)
|
1384
|
+
expect{ described_class.quote_ident( [nil] ) }.to raise_error(TypeError)
|
1385
|
+
expect{ described_class.quote_ident( [['a']] ) }.to raise_error(TypeError)
|
1386
|
+
end
|
1134
1387
|
|
1135
1388
|
describe "Ruby 1.9.x default_internal encoding" do
|
1136
1389
|
|
@@ -1143,12 +1396,12 @@ describe PG::Connection do
|
|
1143
1396
|
|
1144
1397
|
begin
|
1145
1398
|
prev_encoding = Encoding.default_internal
|
1146
|
-
Encoding.default_internal = Encoding::
|
1399
|
+
Encoding.default_internal = Encoding::ISO8859_2
|
1147
1400
|
|
1148
1401
|
conn = PG.connect( @conninfo )
|
1149
|
-
expect( conn.internal_encoding ).to eq( Encoding::
|
1402
|
+
expect( conn.internal_encoding ).to eq( Encoding::ISO8859_2 )
|
1150
1403
|
res = conn.exec( "SELECT foo FROM defaultinternaltest" )
|
1151
|
-
expect( res[0]['foo'].encoding ).to eq( Encoding::
|
1404
|
+
expect( res[0]['foo'].encoding ).to eq( Encoding::ISO8859_2 )
|
1152
1405
|
ensure
|
1153
1406
|
conn.exec( "DROP TABLE defaultinternaltest" )
|
1154
1407
|
conn.finish if conn
|
@@ -1193,7 +1446,7 @@ describe PG::Connection do
|
|
1193
1446
|
conn.finish if conn
|
1194
1447
|
end
|
1195
1448
|
|
1196
|
-
it "handles clearing result in or after set_notice_receiver"
|
1449
|
+
it "handles clearing result in or after set_notice_receiver" do
|
1197
1450
|
r = nil
|
1198
1451
|
@conn.set_notice_receiver do |result|
|
1199
1452
|
r = result
|
@@ -1208,7 +1461,7 @@ describe PG::Connection do
|
|
1208
1461
|
@conn.set_notice_receiver
|
1209
1462
|
end
|
1210
1463
|
|
1211
|
-
it "receives properly encoded messages in the notice callbacks"
|
1464
|
+
it "receives properly encoded messages in the notice callbacks" do
|
1212
1465
|
[:receiver, :processor].each do |kind|
|
1213
1466
|
notices = []
|
1214
1467
|
@conn.internal_encoding = 'utf-8'
|
@@ -1236,7 +1489,7 @@ describe PG::Connection do
|
|
1236
1489
|
end
|
1237
1490
|
end
|
1238
1491
|
|
1239
|
-
it "receives properly encoded text from wait_for_notify"
|
1492
|
+
it "receives properly encoded text from wait_for_notify" do
|
1240
1493
|
@conn.internal_encoding = 'utf-8'
|
1241
1494
|
@conn.exec( 'ROLLBACK' )
|
1242
1495
|
@conn.exec( 'LISTEN "Möhre"' )
|
@@ -1253,7 +1506,7 @@ describe PG::Connection do
|
|
1253
1506
|
expect( msg.encoding ).to eq( Encoding::UTF_8 )
|
1254
1507
|
end
|
1255
1508
|
|
1256
|
-
it "returns properly encoded text from notifies"
|
1509
|
+
it "returns properly encoded text from notifies" do
|
1257
1510
|
@conn.internal_encoding = 'utf-8'
|
1258
1511
|
@conn.exec( 'ROLLBACK' )
|
1259
1512
|
@conn.exec( 'LISTEN "Möhre"' )
|
@@ -1309,9 +1562,14 @@ describe PG::Connection do
|
|
1309
1562
|
end
|
1310
1563
|
|
1311
1564
|
it "shouldn't type map params unless requested" do
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1565
|
+
if @conn.server_version < 100000
|
1566
|
+
expect{
|
1567
|
+
@conn.exec_params( "SELECT $1", [5] )
|
1568
|
+
}.to raise_error(PG::IndeterminateDatatype)
|
1569
|
+
else
|
1570
|
+
# PostgreSQL-10 maps to TEXT type (OID 25)
|
1571
|
+
expect( @conn.exec_params( "SELECT $1", [5] ).ftype(0)).to eq(25)
|
1572
|
+
end
|
1315
1573
|
end
|
1316
1574
|
|
1317
1575
|
it "should raise an error on invalid encoder to put_copy_data" do
|
@@ -1378,15 +1636,15 @@ describe PG::Connection do
|
|
1378
1636
|
end
|
1379
1637
|
end
|
1380
1638
|
|
1381
|
-
it "can process #copy_data input queries with row encoder" do
|
1639
|
+
it "can process #copy_data input queries with row encoder and respects character encoding" do
|
1382
1640
|
@conn2.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
|
1383
1641
|
res2 = @conn2.copy_data( "COPY copytable FROM STDOUT" ) do |res|
|
1384
1642
|
@conn2.put_copy_data [1]
|
1385
|
-
@conn2.put_copy_data ["
|
1643
|
+
@conn2.put_copy_data ["Möhre".encode("utf-16le")]
|
1386
1644
|
end
|
1387
1645
|
|
1388
1646
|
res = @conn2.exec( "SELECT * FROM copytable ORDER BY col1" )
|
1389
|
-
expect( res.values ).to eq( [["1"], ["
|
1647
|
+
expect( res.values ).to eq( [["1"], ["Möhre"]] )
|
1390
1648
|
end
|
1391
1649
|
end
|
1392
1650
|
|
@@ -1428,14 +1686,16 @@ describe PG::Connection do
|
|
1428
1686
|
end
|
1429
1687
|
end
|
1430
1688
|
|
1431
|
-
it "can process #copy_data output with row decoder" do
|
1689
|
+
it "can process #copy_data output with row decoder and respects character encoding" do
|
1690
|
+
@conn2.internal_encoding = Encoding::ISO8859_1
|
1432
1691
|
rows = []
|
1433
|
-
res2 = @conn2.copy_data( "COPY (
|
1692
|
+
res2 = @conn2.copy_data( "COPY (VALUES('1'), ('Möhre')) TO STDOUT".encode("utf-16le") ) do |res|
|
1434
1693
|
while row=@conn2.get_copy_data
|
1435
1694
|
rows << row
|
1436
1695
|
end
|
1437
1696
|
end
|
1438
|
-
expect( rows ).to eq(
|
1697
|
+
expect( rows.last.last.encoding ).to eq( Encoding::ISO8859_1 )
|
1698
|
+
expect( rows ).to eq( [["1"], ["Möhre".encode("iso-8859-1")]] )
|
1439
1699
|
end
|
1440
1700
|
|
1441
1701
|
it "can type cast #copy_data output with explicit decoder" do
|