pg 0.19.0 → 1.1.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/ChangeLog +218 -1
- data/History.rdoc +106 -0
- data/Manifest.txt +5 -18
- data/README.rdoc +15 -5
- data/Rakefile +8 -9
- data/Rakefile.cross +19 -22
- data/ext/errorcodes.def +17 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +11 -1
- data/ext/extconf.rb +14 -32
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -39
- data/ext/pg.c +19 -48
- data/ext/pg.h +46 -81
- data/ext/pg_binary_decoder.c +69 -6
- data/ext/pg_coder.c +53 -4
- data/ext/pg_connection.c +401 -253
- data/ext/pg_copy_coder.c +10 -5
- data/ext/pg_result.c +359 -131
- data/ext/pg_text_decoder.c +597 -37
- data/ext/pg_text_encoder.c +6 -7
- data/ext/pg_tuple.c +541 -0
- data/ext/pg_type_map.c +14 -7
- data/ext/util.c +6 -6
- data/ext/util.h +2 -2
- data/lib/pg/basic_type_mapping.rb +40 -7
- data/lib/pg/binary_decoder.rb +22 -0
- data/lib/pg/coder.rb +1 -1
- data/lib/pg/connection.rb +27 -7
- data/lib/pg/constants.rb +1 -1
- data/lib/pg/exceptions.rb +1 -1
- data/lib/pg/result.rb +6 -5
- data/lib/pg/text_decoder.rb +19 -23
- data/lib/pg/text_encoder.rb +36 -2
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +1 -1
- data/lib/pg.rb +21 -11
- data/spec/helpers.rb +47 -19
- data/spec/pg/basic_type_mapping_spec.rb +230 -27
- data/spec/pg/connection_spec.rb +402 -275
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +59 -17
- data/spec/pg/tuple_spec.rb +280 -0
- data/spec/pg/type_map_by_class_spec.rb +2 -2
- data/spec/pg/type_map_by_column_spec.rb +1 -1
- data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
- data/spec/pg/type_map_by_oid_spec.rb +1 -1
- data/spec/pg/type_map_in_ruby_spec.rb +1 -1
- data/spec/pg/type_map_spec.rb +1 -1
- data/spec/pg/type_spec.rb +184 -12
- data/spec/pg_spec.rb +2 -2
- data.tar.gz.sig +0 -0
- metadata +47 -53
- 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
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# -*- rspec -*-
|
2
2
|
#encoding: utf-8
|
3
3
|
|
4
4
|
require_relative '../helpers'
|
@@ -118,6 +118,19 @@ describe PG::Connection do
|
|
118
118
|
expect( described_class.parse_connect_args ).to eq( '' )
|
119
119
|
end
|
120
120
|
|
121
|
+
it "connects successfully with connection string" do
|
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
|
+
|
121
134
|
it "connects successfully with connection string" do
|
122
135
|
tmpconn = described_class.connect( @conninfo )
|
123
136
|
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
@@ -139,7 +152,7 @@ describe PG::Connection do
|
|
139
152
|
tmpconn.finish
|
140
153
|
end
|
141
154
|
|
142
|
-
it "connects using a hash of optional connection parameters"
|
155
|
+
it "connects using a hash of optional connection parameters" do
|
143
156
|
tmpconn = described_class.connect(
|
144
157
|
:host => 'localhost',
|
145
158
|
:port => @port,
|
@@ -163,21 +176,8 @@ describe PG::Connection do
|
|
163
176
|
it "can connect asynchronously", :socket_io do
|
164
177
|
tmpconn = described_class.connect_start( @conninfo )
|
165
178
|
expect( tmpconn ).to be_a( described_class )
|
166
|
-
socket = tmpconn.socket_io
|
167
|
-
status = tmpconn.connect_poll
|
168
|
-
|
169
|
-
while status != PG::PGRES_POLLING_OK
|
170
|
-
if status == PG::PGRES_POLLING_READING
|
171
|
-
select( [socket], [], [], 5.0 ) or
|
172
|
-
raise "Asynchronous connection timed out!"
|
173
|
-
|
174
|
-
elsif status == PG::PGRES_POLLING_WRITING
|
175
|
-
select( [], [socket], [], 5.0 ) or
|
176
|
-
raise "Asynchronous connection timed out!"
|
177
|
-
end
|
178
|
-
status = tmpconn.connect_poll
|
179
|
-
end
|
180
179
|
|
180
|
+
wait_for_polling_ok(tmpconn)
|
181
181
|
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
182
182
|
tmpconn.finish
|
183
183
|
end
|
@@ -188,28 +188,48 @@ describe PG::Connection do
|
|
188
188
|
described_class.connect_start(@conninfo) do |tmpconn|
|
189
189
|
expect( tmpconn ).to be_a( described_class )
|
190
190
|
conn = tmpconn
|
191
|
-
socket = tmpconn.socket_io
|
192
|
-
status = tmpconn.connect_poll
|
193
|
-
|
194
|
-
while status != PG::PGRES_POLLING_OK
|
195
|
-
if status == PG::PGRES_POLLING_READING
|
196
|
-
if(not select([socket],[],[],5.0))
|
197
|
-
raise "Asynchronous connection timed out!"
|
198
|
-
end
|
199
|
-
elsif(status == PG::PGRES_POLLING_WRITING)
|
200
|
-
if(not select([],[socket],[],5.0))
|
201
|
-
raise "Asynchronous connection timed out!"
|
202
|
-
end
|
203
|
-
end
|
204
|
-
status = tmpconn.connect_poll
|
205
|
-
end
|
206
191
|
|
192
|
+
wait_for_polling_ok(tmpconn)
|
207
193
|
expect( tmpconn.status ).to eq( PG::CONNECTION_OK )
|
208
194
|
end
|
209
195
|
|
210
196
|
expect( conn ).to be_finished()
|
211
197
|
end
|
212
198
|
|
199
|
+
context "with async established connection", :socket_io do
|
200
|
+
before :each do
|
201
|
+
@conn2 = described_class.connect_start( @conninfo )
|
202
|
+
wait_for_polling_ok(@conn2)
|
203
|
+
expect( @conn2 ).to still_be_usable
|
204
|
+
end
|
205
|
+
|
206
|
+
after :each do
|
207
|
+
expect( @conn2 ).to still_be_usable
|
208
|
+
@conn2.close
|
209
|
+
end
|
210
|
+
|
211
|
+
it "conn.send_query and IO.select work" do
|
212
|
+
@conn2.send_query("SELECT 1")
|
213
|
+
res = wait_for_query_result(@conn2)
|
214
|
+
expect( res.values ).to eq([["1"]])
|
215
|
+
end
|
216
|
+
|
217
|
+
it "conn.send_query and conn.block work" do
|
218
|
+
@conn2.send_query("SELECT 2")
|
219
|
+
@conn2.block
|
220
|
+
res = @conn2.get_last_result
|
221
|
+
expect( res.values ).to eq([["2"]])
|
222
|
+
end
|
223
|
+
|
224
|
+
it "conn.async_query works" do
|
225
|
+
res = @conn2.async_query("SELECT 3")
|
226
|
+
expect( res.values ).to eq([["3"]])
|
227
|
+
expect( @conn2 ).to still_be_usable
|
228
|
+
|
229
|
+
res = @conn2.query("SELECT 4")
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
213
233
|
it "raises proper error when sending fails" do
|
214
234
|
conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
|
215
235
|
expect{ conn.exec 'SELECT 1' }.to raise_error(PG::UnableToSend, /no connection/)
|
@@ -219,7 +239,7 @@ describe PG::Connection do
|
|
219
239
|
described_class.connect(@conninfo).finish
|
220
240
|
sleep 0.5
|
221
241
|
res = @conn.exec(%[SELECT COUNT(*) AS n FROM pg_stat_activity
|
222
|
-
WHERE usename IS NOT NULL])
|
242
|
+
WHERE usename IS NOT NULL AND application_name != ''])
|
223
243
|
# there's still the global @conn, but should be no more
|
224
244
|
expect( res[0]['n'] ).to eq( '1' )
|
225
245
|
end
|
@@ -282,18 +302,15 @@ describe PG::Connection do
|
|
282
302
|
|
283
303
|
trace_data = trace_file.read
|
284
304
|
|
285
|
-
|
286
|
-
#
|
287
|
-
#
|
288
|
-
#
|
289
|
-
# +From backend (#4)>
|
290
|
-
#
|
291
|
-
|
292
|
-
expected_trace_output.sub!( /From backend \(#4\)> 13/, 'From backend (#4)> 11' )
|
293
|
-
expected_trace_output.sub!( /From backend> "SELECT 1"/, 'From backend> "SELECT"' )
|
294
|
-
end
|
305
|
+
# For async_exec the output will be different:
|
306
|
+
# From backend> Z
|
307
|
+
# From backend (#4)> 5
|
308
|
+
# +From backend> Z
|
309
|
+
# +From backend (#4)> 5
|
310
|
+
# From backend> T
|
311
|
+
trace_data.sub!( /(From backend> Z\nFrom backend \(#4\)> 5\n){3}/m, '\\1\\1' )
|
295
312
|
|
296
|
-
expect( trace_data ).to eq(
|
313
|
+
expect( trace_data ).to eq( EXPECTED_TRACE_OUTPUT )
|
297
314
|
end
|
298
315
|
|
299
316
|
it "allows a query to be cancelled" do
|
@@ -388,22 +405,11 @@ describe PG::Connection do
|
|
388
405
|
end
|
389
406
|
end
|
390
407
|
|
391
|
-
|
392
|
-
it "supports parameters passed to #exec (backward compatibility)" do
|
393
|
-
@conn.exec( "CREATE TABLE students ( name TEXT, age INTEGER )" )
|
394
|
-
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Wally', 8] )
|
395
|
-
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Sally', 6] )
|
396
|
-
@conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
|
397
|
-
|
398
|
-
res = @conn.exec( "SELECT name FROM students WHERE age >= $1", [6] )
|
399
|
-
expect( res.values ).to eq( [ ['Wally'], ['Sally'] ] )
|
400
|
-
end
|
401
|
-
|
402
408
|
it "supports explicitly calling #exec_params" do
|
403
409
|
@conn.exec( "CREATE TABLE students ( name TEXT, age INTEGER )" )
|
404
|
-
@conn.
|
405
|
-
@conn.
|
406
|
-
@conn.
|
410
|
+
@conn.exec_params( "INSERT INTO students VALUES( $1, $2 )", ['Wally', 8] )
|
411
|
+
@conn.exec_params( "INSERT INTO students VALUES( $1, $2 )", ['Sally', 6] )
|
412
|
+
@conn.exec_params( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
|
407
413
|
|
408
414
|
res = @conn.exec_params( "SELECT name FROM students WHERE age >= $1", [6] )
|
409
415
|
expect( res.values ).to eq( [ ['Wally'], ['Sally'] ] )
|
@@ -524,7 +530,7 @@ describe PG::Connection do
|
|
524
530
|
@conn.exec( 'UNLISTEN woo' )
|
525
531
|
end
|
526
532
|
|
527
|
-
it "can receive notices while waiting for NOTIFY without exceeding the timeout"
|
533
|
+
it "can receive notices while waiting for NOTIFY without exceeding the timeout" do
|
528
534
|
notices = []
|
529
535
|
@conn.set_notice_processor do |msg|
|
530
536
|
notices << [msg, Time.now]
|
@@ -586,7 +592,7 @@ describe PG::Connection do
|
|
586
592
|
expect( @conn ).to still_be_usable
|
587
593
|
end
|
588
594
|
|
589
|
-
it "can handle server errors in #copy_data for output"
|
595
|
+
it "can handle server errors in #copy_data for output" do
|
590
596
|
@conn.exec "ROLLBACK"
|
591
597
|
@conn.transaction do
|
592
598
|
@conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
|
@@ -718,11 +724,6 @@ describe PG::Connection do
|
|
718
724
|
expect( (finish - start) ).to be_within( 0.2 ).of( 0.3 )
|
719
725
|
end
|
720
726
|
|
721
|
-
|
722
|
-
it "can encrypt a string given a password and username" do
|
723
|
-
expect( described_class.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
|
724
|
-
end
|
725
|
-
|
726
727
|
it "can return the default connection options" do
|
727
728
|
expect( described_class.conndefaults ).to be_a( Array )
|
728
729
|
expect( described_class.conndefaults ).to all( be_a(Hash) )
|
@@ -779,18 +780,52 @@ describe PG::Connection do
|
|
779
780
|
end
|
780
781
|
end
|
781
782
|
|
783
|
+
describe "deprecated password encryption method" do
|
784
|
+
it "can encrypt password for a given user" do
|
785
|
+
expect( described_class.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
|
786
|
+
end
|
782
787
|
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
788
|
+
it "raises an appropriate error if either of the required arguments is not valid" do
|
789
|
+
expect {
|
790
|
+
described_class.encrypt_password( nil, nil )
|
791
|
+
}.to raise_error( TypeError )
|
792
|
+
expect {
|
793
|
+
described_class.encrypt_password( "postgres", nil )
|
794
|
+
}.to raise_error( TypeError )
|
795
|
+
expect {
|
796
|
+
described_class.encrypt_password( nil, "postgres" )
|
797
|
+
}.to raise_error( TypeError )
|
798
|
+
end
|
799
|
+
end
|
800
|
+
|
801
|
+
describe "password encryption method", :postgresql_10 do
|
802
|
+
it "can encrypt without algorithm" do
|
803
|
+
expect( @conn.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
|
804
|
+
expect( @conn.encrypt_password("postgres", "postgres", nil) ).to match( /\S+/ )
|
805
|
+
end
|
806
|
+
|
807
|
+
it "can encrypt with algorithm" do
|
808
|
+
expect( @conn.encrypt_password("postgres", "postgres", "md5") ).to match( /md5\S+/i )
|
809
|
+
expect( @conn.encrypt_password("postgres", "postgres", "scram-sha-256") ).to match( /SCRAM-SHA-256\S+/i )
|
810
|
+
end
|
811
|
+
|
812
|
+
it "raises an appropriate error if either of the required arguments is not valid" do
|
813
|
+
expect {
|
814
|
+
@conn.encrypt_password( nil, nil )
|
815
|
+
}.to raise_error( TypeError )
|
816
|
+
expect {
|
817
|
+
@conn.encrypt_password( "postgres", nil )
|
818
|
+
}.to raise_error( TypeError )
|
819
|
+
expect {
|
820
|
+
@conn.encrypt_password( nil, "postgres" )
|
821
|
+
}.to raise_error( TypeError )
|
822
|
+
expect {
|
823
|
+
@conn.encrypt_password( "postgres", "postgres", :invalid )
|
824
|
+
}.to raise_error( TypeError )
|
825
|
+
expect {
|
826
|
+
@conn.encrypt_password( "postgres", "postgres", "invalid" )
|
827
|
+
}.to raise_error( PG::Error, /unrecognized/ )
|
828
|
+
end
|
794
829
|
end
|
795
830
|
|
796
831
|
|
@@ -832,7 +867,7 @@ describe PG::Connection do
|
|
832
867
|
end
|
833
868
|
|
834
869
|
|
835
|
-
it "
|
870
|
+
it "handles server close while asynchronous connect", :socket_io do
|
836
871
|
serv = TCPServer.new( '127.0.0.1', 54320 )
|
837
872
|
conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
|
838
873
|
expect( [PG::PGRES_POLLING_WRITING, PG::CONNECTION_OK] ).to include conn.connect_poll
|
@@ -844,11 +879,29 @@ describe PG::Connection do
|
|
844
879
|
expect( conn.connect_poll ).to eq( PG::PGRES_POLLING_FAILED )
|
845
880
|
end
|
846
881
|
|
847
|
-
it "discards previous results
|
882
|
+
it "discards previous results at #discard_results" do
|
883
|
+
@conn.send_query( "select 1" )
|
884
|
+
@conn.discard_results
|
885
|
+
@conn.send_query( "select 41 as one" )
|
886
|
+
res = @conn.get_last_result
|
887
|
+
expect( res.to_a ).to eq( [{ 'one' => '41' }] )
|
888
|
+
end
|
889
|
+
|
890
|
+
it "discards previous results (if any) before waiting on #exec" do
|
891
|
+
@conn.send_query( "select 1" )
|
892
|
+
res = @conn.exec( "select 42 as one" )
|
893
|
+
expect( res.to_a ).to eq( [{ 'one' => '42' }] )
|
894
|
+
end
|
848
895
|
|
849
|
-
it "
|
896
|
+
it "discards previous errors before waiting on #exec", :without_transaction do
|
897
|
+
@conn.send_query( "ERROR" )
|
898
|
+
res = @conn.exec( "select 43 as one" )
|
899
|
+
expect( res.to_a ).to eq( [{ 'one' => '43' }] )
|
900
|
+
end
|
901
|
+
|
902
|
+
it "calls the block if one is provided to #exec" do
|
850
903
|
result = nil
|
851
|
-
@conn.
|
904
|
+
@conn.exec( "select 47 as one" ) do |pg_res|
|
852
905
|
result = pg_res[0]
|
853
906
|
end
|
854
907
|
expect( result ).to eq( { 'one' => '47' } )
|
@@ -886,158 +939,150 @@ describe PG::Connection do
|
|
886
939
|
end
|
887
940
|
serv.close
|
888
941
|
expect{ conn.block }.to raise_error(PG::ConnectionBad, /server closed the connection unexpectedly/)
|
889
|
-
expect{ conn.block }.to raise_error(PG::ConnectionBad, /
|
942
|
+
expect{ conn.block }.to raise_error(PG::ConnectionBad, /connection not open/)
|
890
943
|
end
|
891
944
|
|
892
|
-
|
945
|
+
it "sets the fallback_application_name on new connections" do
|
946
|
+
conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
|
893
947
|
|
894
|
-
|
895
|
-
|
896
|
-
|
948
|
+
conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
|
949
|
+
expect( conn_name ).to include( $0[0..10] )
|
950
|
+
expect( conn_name ).to include( $0[-10..-1] )
|
951
|
+
expect( conn_name.length ).to be <= 64
|
952
|
+
end
|
897
953
|
|
898
|
-
|
954
|
+
it "sets a shortened fallback_application_name on new connections" do
|
955
|
+
old_0 = $0
|
956
|
+
begin
|
957
|
+
$0 = "/this/is/a/very/long/path/with/many/directories/to/our/beloved/ruby"
|
899
958
|
conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
|
900
|
-
|
901
959
|
conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
|
902
960
|
expect( conn_name ).to include( $0[0..10] )
|
903
961
|
expect( conn_name ).to include( $0[-10..-1] )
|
904
962
|
expect( conn_name.length ).to be <= 64
|
963
|
+
ensure
|
964
|
+
$0 = old_0
|
905
965
|
end
|
966
|
+
end
|
906
967
|
|
907
|
-
|
908
|
-
|
909
|
-
begin
|
910
|
-
$0 = "/this/is/a/very/long/path/with/many/directories/to/our/beloved/ruby"
|
911
|
-
conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
|
912
|
-
conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
|
913
|
-
expect( conn_name ).to include( $0[0..10] )
|
914
|
-
expect( conn_name ).to include( $0[-10..-1] )
|
915
|
-
expect( conn_name.length ).to be <= 64
|
916
|
-
ensure
|
917
|
-
$0 = old_0
|
918
|
-
end
|
919
|
-
end
|
920
|
-
|
921
|
-
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
922
|
-
"any number of arguments" do
|
968
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
969
|
+
"any number of arguments" do
|
923
970
|
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
conn = described_class.connect( @conninfo )
|
928
|
-
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
929
|
-
conn.finish
|
971
|
+
@conn.exec( 'ROLLBACK' )
|
972
|
+
@conn.exec( 'LISTEN knees' )
|
930
973
|
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
end
|
935
|
-
@conn.exec( 'UNLISTEN knees' )
|
974
|
+
conn = described_class.connect( @conninfo )
|
975
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
976
|
+
conn.finish
|
936
977
|
|
937
|
-
|
938
|
-
|
939
|
-
|
978
|
+
event, pid, msg = nil
|
979
|
+
@conn.wait_for_notify( 10 ) do |*args|
|
980
|
+
event, pid, msg = *args
|
940
981
|
end
|
982
|
+
@conn.exec( 'UNLISTEN knees' )
|
941
983
|
|
942
|
-
|
943
|
-
|
944
|
-
|
984
|
+
expect( event ).to eq( 'knees' )
|
985
|
+
expect( pid ).to be_a_kind_of( Integer )
|
986
|
+
expect( msg ).to eq( 'skirt and boots' )
|
987
|
+
end
|
945
988
|
|
946
|
-
|
947
|
-
|
948
|
-
|
989
|
+
it "accepts nil as the timeout in #wait_for_notify " do
|
990
|
+
@conn.exec( 'ROLLBACK' )
|
991
|
+
@conn.exec( 'LISTEN knees' )
|
949
992
|
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
end
|
954
|
-
@conn.exec( 'UNLISTEN knees' )
|
993
|
+
conn = described_class.connect( @conninfo )
|
994
|
+
conn.exec( %Q{NOTIFY knees} )
|
995
|
+
conn.finish
|
955
996
|
|
956
|
-
|
957
|
-
|
997
|
+
event, pid = nil
|
998
|
+
@conn.wait_for_notify( nil ) do |*args|
|
999
|
+
event, pid = *args
|
958
1000
|
end
|
1001
|
+
@conn.exec( 'UNLISTEN knees' )
|
959
1002
|
|
960
|
-
|
961
|
-
|
962
|
-
|
1003
|
+
expect( event ).to eq( 'knees' )
|
1004
|
+
expect( pid ).to be_a_kind_of( Integer )
|
1005
|
+
end
|
963
1006
|
|
964
|
-
|
965
|
-
|
966
|
-
|
1007
|
+
it "sends nil as the payload if the notification wasn't given one" do
|
1008
|
+
@conn.exec( 'ROLLBACK' )
|
1009
|
+
@conn.exec( 'LISTEN knees' )
|
967
1010
|
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
end
|
972
|
-
@conn.exec( 'UNLISTEN knees' )
|
1011
|
+
conn = described_class.connect( @conninfo )
|
1012
|
+
conn.exec( %Q{NOTIFY knees} )
|
1013
|
+
conn.finish
|
973
1014
|
|
974
|
-
|
1015
|
+
payload = :notnil
|
1016
|
+
@conn.wait_for_notify( nil ) do |*args|
|
1017
|
+
payload = args[ 2 ]
|
975
1018
|
end
|
1019
|
+
@conn.exec( 'UNLISTEN knees' )
|
976
1020
|
|
977
|
-
|
978
|
-
|
1021
|
+
expect( payload ).to be_nil()
|
1022
|
+
end
|
979
1023
|
|
980
|
-
|
981
|
-
|
1024
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
1025
|
+
"two arguments" do
|
982
1026
|
|
983
|
-
|
984
|
-
|
985
|
-
conn.finish
|
1027
|
+
@conn.exec( 'ROLLBACK' )
|
1028
|
+
@conn.exec( 'LISTEN knees' )
|
986
1029
|
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
end
|
991
|
-
@conn.exec( 'UNLISTEN knees' )
|
1030
|
+
conn = described_class.connect( @conninfo )
|
1031
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1032
|
+
conn.finish
|
992
1033
|
|
993
|
-
|
994
|
-
|
995
|
-
|
1034
|
+
event, pid, msg = nil
|
1035
|
+
@conn.wait_for_notify( 10 ) do |arg1, arg2|
|
1036
|
+
event, pid, msg = arg1, arg2
|
996
1037
|
end
|
1038
|
+
@conn.exec( 'UNLISTEN knees' )
|
997
1039
|
|
998
|
-
|
999
|
-
|
1040
|
+
expect( event ).to eq( 'knees' )
|
1041
|
+
expect( pid ).to be_a_kind_of( Integer )
|
1042
|
+
expect( msg ).to be_nil()
|
1043
|
+
end
|
1000
1044
|
|
1001
|
-
|
1002
|
-
|
1045
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it " +
|
1046
|
+
"doesn't accept arguments" do
|
1003
1047
|
|
1004
|
-
|
1005
|
-
|
1006
|
-
conn.finish
|
1048
|
+
@conn.exec( 'ROLLBACK' )
|
1049
|
+
@conn.exec( 'LISTEN knees' )
|
1007
1050
|
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
end
|
1012
|
-
@conn.exec( 'UNLISTEN knees' )
|
1051
|
+
conn = described_class.connect( @conninfo )
|
1052
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1053
|
+
conn.finish
|
1013
1054
|
|
1014
|
-
|
1055
|
+
notification_received = false
|
1056
|
+
@conn.wait_for_notify( 10 ) do
|
1057
|
+
notification_received = true
|
1015
1058
|
end
|
1059
|
+
@conn.exec( 'UNLISTEN knees' )
|
1016
1060
|
|
1017
|
-
|
1018
|
-
|
1061
|
+
expect( notification_received ).to be_truthy()
|
1062
|
+
end
|
1019
1063
|
|
1020
|
-
|
1021
|
-
|
1064
|
+
it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
|
1065
|
+
"three arguments" do
|
1022
1066
|
|
1023
|
-
|
1024
|
-
|
1025
|
-
conn.finish
|
1067
|
+
@conn.exec( 'ROLLBACK' )
|
1068
|
+
@conn.exec( 'LISTEN knees' )
|
1026
1069
|
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
end
|
1031
|
-
@conn.exec( 'UNLISTEN knees' )
|
1070
|
+
conn = described_class.connect( @conninfo )
|
1071
|
+
conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
|
1072
|
+
conn.finish
|
1032
1073
|
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1074
|
+
event, pid, msg = nil
|
1075
|
+
@conn.wait_for_notify( 10 ) do |arg1, arg2, arg3|
|
1076
|
+
event, pid, msg = arg1, arg2, arg3
|
1036
1077
|
end
|
1078
|
+
@conn.exec( 'UNLISTEN knees' )
|
1037
1079
|
|
1080
|
+
expect( event ).to eq( 'knees' )
|
1081
|
+
expect( pid ).to be_a_kind_of( Integer )
|
1082
|
+
expect( msg ).to eq( 'skirt and boots' )
|
1038
1083
|
end
|
1039
1084
|
|
1040
|
-
context "
|
1085
|
+
context "server ping", :without_transaction do
|
1041
1086
|
|
1042
1087
|
it "pings successfully with connection string" do
|
1043
1088
|
ping = described_class.ping(@conninfo)
|
@@ -1070,71 +1115,69 @@ describe PG::Connection do
|
|
1070
1115
|
expect( ping ).to eq( PG::PQPING_NO_ATTEMPT )
|
1071
1116
|
end
|
1072
1117
|
|
1073
|
-
|
1074
1118
|
end
|
1075
1119
|
|
1076
|
-
|
1077
|
-
describe "set_single_row_mode" do
|
1120
|
+
describe "set_single_row_mode" do
|
1078
1121
|
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1122
|
+
it "raises an error when called at the wrong time" do
|
1123
|
+
expect {
|
1124
|
+
@conn.set_single_row_mode
|
1125
|
+
}.to raise_error(PG::Error)
|
1126
|
+
end
|
1127
|
+
|
1128
|
+
it "should work in single row mode" do
|
1129
|
+
@conn.send_query( "SELECT generate_series(1,10)" )
|
1130
|
+
@conn.set_single_row_mode
|
1131
|
+
|
1132
|
+
results = []
|
1133
|
+
loop do
|
1134
|
+
@conn.block
|
1135
|
+
res = @conn.get_result or break
|
1136
|
+
results << res
|
1137
|
+
end
|
1138
|
+
expect( results.length ).to eq( 11 )
|
1139
|
+
results[0..-2].each do |res|
|
1140
|
+
expect( res.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
1141
|
+
values = res.field_values('generate_series')
|
1142
|
+
expect( values.length ).to eq( 1 )
|
1143
|
+
expect( values.first.to_i ).to be > 0
|
1083
1144
|
end
|
1145
|
+
expect( results.last.result_status ).to eq( PG::PGRES_TUPLES_OK )
|
1146
|
+
expect( results.last.ntuples ).to eq( 0 )
|
1147
|
+
end
|
1084
1148
|
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1149
|
+
it "should receive rows before entire query is finished" do
|
1150
|
+
@conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, pg_sleep(1);" )
|
1151
|
+
@conn.set_single_row_mode
|
1088
1152
|
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
expect( results.length ).to eq( 11 )
|
1096
|
-
results[0..-2].each do |res|
|
1097
|
-
expect( res.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
1098
|
-
values = res.field_values('generate_series')
|
1099
|
-
expect( values.length ).to eq( 1 )
|
1100
|
-
expect( values.first.to_i ).to be > 0
|
1101
|
-
end
|
1102
|
-
expect( results.last.result_status ).to eq( PG::PGRES_TUPLES_OK )
|
1103
|
-
expect( results.last.ntuples ).to eq( 0 )
|
1153
|
+
start_time = Time.now
|
1154
|
+
first_row_time = nil
|
1155
|
+
loop do
|
1156
|
+
res = @conn.get_result or break
|
1157
|
+
res.check
|
1158
|
+
first_row_time = Time.now unless first_row_time
|
1104
1159
|
end
|
1160
|
+
expect( (Time.now - start_time) ).to be >= 0.9
|
1161
|
+
expect( (first_row_time - start_time) ).to be < 0.9
|
1162
|
+
end
|
1105
1163
|
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1164
|
+
it "should receive rows before entire query fails" do
|
1165
|
+
@conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
|
1166
|
+
@conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, errfunc();" )
|
1167
|
+
@conn.set_single_row_mode
|
1109
1168
|
|
1110
|
-
|
1111
|
-
|
1169
|
+
first_result = nil
|
1170
|
+
expect do
|
1112
1171
|
loop do
|
1113
1172
|
res = @conn.get_result or break
|
1114
1173
|
res.check
|
1115
|
-
|
1174
|
+
first_result ||= res
|
1116
1175
|
end
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
it "should receive rows before entire query fails" do
|
1122
|
-
@conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
|
1123
|
-
@conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, errfunc();" )
|
1124
|
-
@conn.set_single_row_mode
|
1125
|
-
|
1126
|
-
first_result = nil
|
1127
|
-
expect do
|
1128
|
-
loop do
|
1129
|
-
res = @conn.get_result or break
|
1130
|
-
res.check
|
1131
|
-
first_result ||= res
|
1132
|
-
end
|
1133
|
-
end.to raise_error(PG::Error)
|
1134
|
-
expect( first_result.kind_of?(PG::Result) ).to be_truthy
|
1135
|
-
expect( first_result.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
1136
|
-
end
|
1176
|
+
end.to raise_error(PG::Error)
|
1177
|
+
expect( first_result.kind_of?(PG::Result) ).to be_truthy
|
1178
|
+
expect( first_result.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
|
1137
1179
|
end
|
1180
|
+
|
1138
1181
|
end
|
1139
1182
|
|
1140
1183
|
context "multinationalization support", :ruby_19 do
|
@@ -1144,7 +1187,7 @@ describe PG::Connection do
|
|
1144
1187
|
out_string = nil
|
1145
1188
|
@conn.transaction do |conn|
|
1146
1189
|
conn.internal_encoding = 'iso8859-1'
|
1147
|
-
res = conn.
|
1190
|
+
res = conn.exec_params("VALUES ('fantasia')", [], 0)
|
1148
1191
|
out_string = res[0]['column1']
|
1149
1192
|
end
|
1150
1193
|
expect( out_string ).to eq( 'fantasia' )
|
@@ -1155,7 +1198,7 @@ describe PG::Connection do
|
|
1155
1198
|
out_string = nil
|
1156
1199
|
@conn.transaction do |conn|
|
1157
1200
|
conn.internal_encoding = 'utf-8'
|
1158
|
-
res = conn.
|
1201
|
+
res = conn.exec_params("VALUES ('世界線航跡蔵')", [], 0)
|
1159
1202
|
out_string = res[0]['column1']
|
1160
1203
|
end
|
1161
1204
|
expect( out_string ).to eq( '世界線航跡蔵' )
|
@@ -1167,7 +1210,7 @@ describe PG::Connection do
|
|
1167
1210
|
@conn.transaction do |conn|
|
1168
1211
|
conn.internal_encoding = 'EUC-JP'
|
1169
1212
|
stmt = "VALUES ('世界線航跡蔵')".encode('EUC-JP')
|
1170
|
-
res = conn.
|
1213
|
+
res = conn.exec_params(stmt, [], 0)
|
1171
1214
|
out_string = res[0]['column1']
|
1172
1215
|
end
|
1173
1216
|
expect( out_string ).to eq( '世界線航跡蔵'.encode('EUC-JP') )
|
@@ -1180,7 +1223,7 @@ describe PG::Connection do
|
|
1180
1223
|
@conn.transaction do |conn|
|
1181
1224
|
conn.internal_encoding = 'EUC-JP'
|
1182
1225
|
stmt = "VALUES ('世界線航跡蔵')".encode('EUC-JP')
|
1183
|
-
res = conn.
|
1226
|
+
res = conn.exec_params(stmt, [], 0)
|
1184
1227
|
conn.internal_encoding = 'utf-8'
|
1185
1228
|
out_string = res[0]['column1']
|
1186
1229
|
end
|
@@ -1194,51 +1237,57 @@ describe PG::Connection do
|
|
1194
1237
|
end
|
1195
1238
|
|
1196
1239
|
it "uses the client encoding for escaped string" do
|
1197
|
-
original = "Möhre to
|
1240
|
+
original = "Möhre to 'scape".encode( "utf-16be" )
|
1198
1241
|
@conn.set_client_encoding( "euc_jp" )
|
1199
1242
|
escaped = @conn.escape( original )
|
1200
1243
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1201
|
-
expect( escaped ).to eq( "Möhre to".encode(Encoding::EUC_JP) )
|
1244
|
+
expect( escaped ).to eq( "Möhre to ''scape".encode(Encoding::EUC_JP) )
|
1202
1245
|
end
|
1203
1246
|
|
1204
|
-
it "uses the client encoding for escaped literal"
|
1205
|
-
original = "Möhre to
|
1247
|
+
it "uses the client encoding for escaped literal" do
|
1248
|
+
original = "Möhre to 'scape".encode( "utf-16be" )
|
1206
1249
|
@conn.set_client_encoding( "euc_jp" )
|
1207
1250
|
escaped = @conn.escape_literal( original )
|
1208
1251
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1209
|
-
expect( escaped ).to eq( "'Möhre to'".encode(Encoding::EUC_JP) )
|
1252
|
+
expect( escaped ).to eq( "'Möhre to ''scape'".encode(Encoding::EUC_JP) )
|
1210
1253
|
end
|
1211
1254
|
|
1212
|
-
it "uses the client encoding for escaped identifier"
|
1213
|
-
original = "Möhre to
|
1255
|
+
it "uses the client encoding for escaped identifier" do
|
1256
|
+
original = "Möhre to 'scape".encode( "utf-16le" )
|
1214
1257
|
@conn.set_client_encoding( "euc_jp" )
|
1215
1258
|
escaped = @conn.escape_identifier( original )
|
1216
1259
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1217
|
-
expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
|
1260
|
+
expect( escaped ).to eq( "\"Möhre to 'scape\"".encode(Encoding::EUC_JP) )
|
1218
1261
|
end
|
1219
1262
|
|
1220
1263
|
it "uses the client encoding for quote_ident" do
|
1221
|
-
original = "Möhre to
|
1264
|
+
original = "Möhre to 'scape".encode( "utf-16le" )
|
1222
1265
|
@conn.set_client_encoding( "euc_jp" )
|
1223
1266
|
escaped = @conn.quote_ident( original )
|
1224
1267
|
expect( escaped.encoding ).to eq( Encoding::EUC_JP )
|
1225
|
-
expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
|
1268
|
+
expect( escaped ).to eq( "\"Möhre to 'scape\"".encode(Encoding::EUC_JP) )
|
1226
1269
|
end
|
1227
1270
|
|
1228
1271
|
it "uses the previous string encoding for escaped string" do
|
1229
|
-
original = "Möhre to
|
1272
|
+
original = "Möhre to 'scape".encode( "iso-8859-1" )
|
1230
1273
|
@conn.set_client_encoding( "euc_jp" )
|
1231
1274
|
escaped = described_class.escape( original )
|
1232
1275
|
expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
|
1233
|
-
expect( escaped ).to eq( "Möhre to".encode(Encoding::ISO8859_1) )
|
1276
|
+
expect( escaped ).to eq( "Möhre to ''scape".encode(Encoding::ISO8859_1) )
|
1234
1277
|
end
|
1235
1278
|
|
1236
1279
|
it "uses the previous string encoding for quote_ident" do
|
1237
|
-
original = "Möhre to
|
1280
|
+
original = "Möhre to 'scape".encode( "iso-8859-1" )
|
1238
1281
|
@conn.set_client_encoding( "euc_jp" )
|
1239
1282
|
escaped = described_class.quote_ident( original )
|
1240
1283
|
expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
|
1241
|
-
expect( escaped.encode ).to eq( "\"Möhre to\"".encode(Encoding::ISO8859_1) )
|
1284
|
+
expect( escaped.encode ).to eq( "\"Möhre to 'scape\"".encode(Encoding::ISO8859_1) )
|
1285
|
+
end
|
1286
|
+
|
1287
|
+
it "raises appropriate error if set_client_encoding is called with invalid arguments" do
|
1288
|
+
expect { @conn.set_client_encoding( "invalid" ) }.to raise_error(PG::Error, /invalid value/)
|
1289
|
+
expect { @conn.set_client_encoding( :invalid ) }.to raise_error(TypeError)
|
1290
|
+
expect { @conn.set_client_encoding( nil ) }.to raise_error(TypeError)
|
1242
1291
|
end
|
1243
1292
|
end
|
1244
1293
|
|
@@ -1253,22 +1302,11 @@ describe PG::Connection do
|
|
1253
1302
|
expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1254
1303
|
end
|
1255
1304
|
|
1256
|
-
it "should convert query string and parameters to #async_exec" do
|
1257
|
-
r = @conn.async_exec("VALUES( $1, $2, $1=$2, 'grün')".encode("cp936"),
|
1258
|
-
['grün'.encode('cp850'), 'grün'.encode('utf-16le')])
|
1259
|
-
expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1260
|
-
end
|
1261
|
-
|
1262
1305
|
it "should convert query string to #exec" do
|
1263
1306
|
r = @conn.exec("SELECT 'grün'".encode("utf-16be"))
|
1264
1307
|
expect( r.values ).to eq( [['grün']] )
|
1265
1308
|
end
|
1266
1309
|
|
1267
|
-
it "should convert query string to #async_exec" do
|
1268
|
-
r = @conn.async_exec("SELECT 'grün'".encode("utf-16le"))
|
1269
|
-
expect( r.values ).to eq( [['grün']] )
|
1270
|
-
end
|
1271
|
-
|
1272
1310
|
it "should convert strings and parameters to #prepare and #exec_prepared" do
|
1273
1311
|
@conn.prepare("weiß1".encode("utf-16be"), "VALUES( $1, $2, $1=$2, 'grün')".encode("cp850"))
|
1274
1312
|
r = @conn.exec_prepared("weiß1".encode("utf-32le"),
|
@@ -1293,8 +1331,8 @@ describe PG::Connection do
|
|
1293
1331
|
expect( @conn.get_last_result.values ).to eq( [['grün']] )
|
1294
1332
|
end
|
1295
1333
|
|
1296
|
-
it "should convert query string and parameters to #
|
1297
|
-
@conn.
|
1334
|
+
it "should convert query string and parameters to #send_query_params" do
|
1335
|
+
@conn.send_query_params("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
|
1298
1336
|
['grün'.encode('utf-32be'), 'grün'.encode('iso-8859-1')])
|
1299
1337
|
expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
|
1300
1338
|
end
|
@@ -1328,9 +1366,54 @@ describe PG::Connection do
|
|
1328
1366
|
end
|
1329
1367
|
end
|
1330
1368
|
|
1369
|
+
it "rejects command strings with zero bytes" do
|
1370
|
+
expect{ @conn.exec( "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1371
|
+
expect{ @conn.exec_params( "SELECT 1;\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
|
1372
|
+
expect{ @conn.prepare( "abc\x00", "SELECT 1;" ) }.to raise_error(ArgumentError, /null byte/)
|
1373
|
+
expect{ @conn.prepare( "abc", "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1374
|
+
expect{ @conn.exec_prepared( "abc\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
|
1375
|
+
expect{ @conn.describe_prepared( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1376
|
+
expect{ @conn.describe_portal( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1377
|
+
expect{ @conn.send_query( "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1378
|
+
expect{ @conn.send_query_params( "SELECT 1;\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
|
1379
|
+
expect{ @conn.send_prepare( "abc\x00", "SELECT 1;" ) }.to raise_error(ArgumentError, /null byte/)
|
1380
|
+
expect{ @conn.send_prepare( "abc", "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1381
|
+
expect{ @conn.send_query_prepared( "abc\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
|
1382
|
+
expect{ @conn.send_describe_prepared( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1383
|
+
expect{ @conn.send_describe_portal( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
|
1384
|
+
end
|
1385
|
+
|
1386
|
+
it "rejects query params with zero bytes" do
|
1387
|
+
expect{ @conn.exec_params( "SELECT 1;\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
|
1388
|
+
expect{ @conn.exec_prepared( "abc\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
|
1389
|
+
expect{ @conn.send_query_params( "SELECT 1;\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
|
1390
|
+
expect{ @conn.send_query_prepared( "abc\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
|
1391
|
+
end
|
1392
|
+
|
1393
|
+
it "rejects string with zero bytes in escape" do
|
1394
|
+
expect{ @conn.escape( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
|
1395
|
+
end
|
1396
|
+
|
1397
|
+
it "rejects string with zero bytes in escape_literal" do
|
1398
|
+
expect{ @conn.escape_literal( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
|
1399
|
+
end
|
1400
|
+
|
1401
|
+
it "rejects string with zero bytes in escape_identifier" do
|
1402
|
+
expect{ @conn.escape_identifier( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
|
1403
|
+
end
|
1404
|
+
|
1405
|
+
it "rejects string with zero bytes in quote_ident" do
|
1406
|
+
expect{ described_class.quote_ident( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
|
1407
|
+
end
|
1408
|
+
|
1409
|
+
it "rejects Array with string with zero bytes" do
|
1410
|
+
original = ["xyz", "2\x00"]
|
1411
|
+
expect{ described_class.quote_ident( original ) }.to raise_error(ArgumentError, /null byte/)
|
1412
|
+
end
|
1413
|
+
|
1331
1414
|
it "can quote bigger strings with quote_ident" do
|
1332
1415
|
original = "'01234567\"" * 100
|
1333
|
-
escaped = described_class.quote_ident( original
|
1416
|
+
escaped = described_class.quote_ident( original )
|
1334
1417
|
expect( escaped ).to eq( "\"" + original.gsub("\"", "\"\"") + "\"" )
|
1335
1418
|
end
|
1336
1419
|
|
@@ -1408,7 +1491,7 @@ describe PG::Connection do
|
|
1408
1491
|
conn.finish if conn
|
1409
1492
|
end
|
1410
1493
|
|
1411
|
-
it "handles clearing result in or after set_notice_receiver"
|
1494
|
+
it "handles clearing result in or after set_notice_receiver" do
|
1412
1495
|
r = nil
|
1413
1496
|
@conn.set_notice_receiver do |result|
|
1414
1497
|
r = result
|
@@ -1423,7 +1506,7 @@ describe PG::Connection do
|
|
1423
1506
|
@conn.set_notice_receiver
|
1424
1507
|
end
|
1425
1508
|
|
1426
|
-
it "receives properly encoded messages in the notice callbacks"
|
1509
|
+
it "receives properly encoded messages in the notice callbacks" do
|
1427
1510
|
[:receiver, :processor].each do |kind|
|
1428
1511
|
notices = []
|
1429
1512
|
@conn.internal_encoding = 'utf-8'
|
@@ -1451,7 +1534,7 @@ describe PG::Connection do
|
|
1451
1534
|
end
|
1452
1535
|
end
|
1453
1536
|
|
1454
|
-
it "receives properly encoded text from wait_for_notify"
|
1537
|
+
it "receives properly encoded text from wait_for_notify" do
|
1455
1538
|
@conn.internal_encoding = 'utf-8'
|
1456
1539
|
@conn.exec( 'ROLLBACK' )
|
1457
1540
|
@conn.exec( 'LISTEN "Möhre"' )
|
@@ -1468,7 +1551,7 @@ describe PG::Connection do
|
|
1468
1551
|
expect( msg.encoding ).to eq( Encoding::UTF_8 )
|
1469
1552
|
end
|
1470
1553
|
|
1471
|
-
it "returns properly encoded text from notifies"
|
1554
|
+
it "returns properly encoded text from notifies" do
|
1472
1555
|
@conn.internal_encoding = 'utf-8'
|
1473
1556
|
@conn.exec( 'ROLLBACK' )
|
1474
1557
|
@conn.exec( 'LISTEN "Möhre"' )
|
@@ -1524,9 +1607,14 @@ describe PG::Connection do
|
|
1524
1607
|
end
|
1525
1608
|
|
1526
1609
|
it "shouldn't type map params unless requested" do
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1610
|
+
if @conn.server_version < 100000
|
1611
|
+
expect{
|
1612
|
+
@conn.exec_params( "SELECT $1", [5] )
|
1613
|
+
}.to raise_error(PG::IndeterminateDatatype)
|
1614
|
+
else
|
1615
|
+
# PostgreSQL-10 maps to TEXT type (OID 25)
|
1616
|
+
expect( @conn.exec_params( "SELECT $1", [5] ).ftype(0)).to eq(25)
|
1617
|
+
end
|
1530
1618
|
end
|
1531
1619
|
|
1532
1620
|
it "should raise an error on invalid encoder to put_copy_data" do
|
@@ -1673,4 +1761,43 @@ describe PG::Connection do
|
|
1673
1761
|
end
|
1674
1762
|
end
|
1675
1763
|
end
|
1764
|
+
|
1765
|
+
describe "deprecated forms of methods" do
|
1766
|
+
it "should forward exec to exec_params" do
|
1767
|
+
res = @conn.exec("VALUES($1::INT)", [7]).values
|
1768
|
+
expect(res).to eq( [["7"]] )
|
1769
|
+
res = @conn.exec("VALUES($1::INT)", [7], 1).values
|
1770
|
+
expect(res).to eq( [[[7].pack("N")]] )
|
1771
|
+
res = @conn.exec("VALUES(8)", [], 1).values
|
1772
|
+
expect(res).to eq( [[[8].pack("N")]] )
|
1773
|
+
end
|
1774
|
+
|
1775
|
+
it "should forward exec_params to exec" do
|
1776
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)").values
|
1777
|
+
expect(res).to eq( [["4"]] )
|
1778
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)", nil).values
|
1779
|
+
expect(res).to eq( [["4"]] )
|
1780
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)", nil, nil).values
|
1781
|
+
expect(res).to eq( [["4"]] )
|
1782
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)", nil, 1).values
|
1783
|
+
expect(res).to eq( [["4"]] )
|
1784
|
+
res = @conn.exec_params("VALUES(3); VALUES(4)", nil, nil, nil).values
|
1785
|
+
expect(res).to eq( [["4"]] )
|
1786
|
+
expect{
|
1787
|
+
@conn.exec_params("VALUES(3); VALUES(4)", nil, nil, nil, nil).values
|
1788
|
+
}.to raise_error(ArgumentError)
|
1789
|
+
end
|
1790
|
+
|
1791
|
+
it "should forward send_query to send_query_params" do
|
1792
|
+
@conn.send_query("VALUES($1)", [5])
|
1793
|
+
expect(@conn.get_last_result.values).to eq( [["5"]] )
|
1794
|
+
end
|
1795
|
+
|
1796
|
+
it "shouldn't forward send_query_params to send_query" do
|
1797
|
+
expect{ @conn.send_query_params("VALUES(4)").values }
|
1798
|
+
.to raise_error(ArgumentError)
|
1799
|
+
expect{ @conn.send_query_params("VALUES(4)", nil).values }
|
1800
|
+
.to raise_error(TypeError)
|
1801
|
+
end
|
1802
|
+
end
|
1676
1803
|
end
|