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.
Files changed (74) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/ChangeLog +218 -1
  4. data/History.rdoc +106 -0
  5. data/Manifest.txt +5 -18
  6. data/README.rdoc +15 -5
  7. data/Rakefile +8 -9
  8. data/Rakefile.cross +19 -22
  9. data/ext/errorcodes.def +17 -0
  10. data/ext/errorcodes.rb +1 -1
  11. data/ext/errorcodes.txt +11 -1
  12. data/ext/extconf.rb +14 -32
  13. data/ext/gvl_wrappers.c +4 -0
  14. data/ext/gvl_wrappers.h +23 -39
  15. data/ext/pg.c +19 -48
  16. data/ext/pg.h +46 -81
  17. data/ext/pg_binary_decoder.c +69 -6
  18. data/ext/pg_coder.c +53 -4
  19. data/ext/pg_connection.c +401 -253
  20. data/ext/pg_copy_coder.c +10 -5
  21. data/ext/pg_result.c +359 -131
  22. data/ext/pg_text_decoder.c +597 -37
  23. data/ext/pg_text_encoder.c +6 -7
  24. data/ext/pg_tuple.c +541 -0
  25. data/ext/pg_type_map.c +14 -7
  26. data/ext/util.c +6 -6
  27. data/ext/util.h +2 -2
  28. data/lib/pg/basic_type_mapping.rb +40 -7
  29. data/lib/pg/binary_decoder.rb +22 -0
  30. data/lib/pg/coder.rb +1 -1
  31. data/lib/pg/connection.rb +27 -7
  32. data/lib/pg/constants.rb +1 -1
  33. data/lib/pg/exceptions.rb +1 -1
  34. data/lib/pg/result.rb +6 -5
  35. data/lib/pg/text_decoder.rb +19 -23
  36. data/lib/pg/text_encoder.rb +36 -2
  37. data/lib/pg/tuple.rb +30 -0
  38. data/lib/pg/type_map_by_column.rb +1 -1
  39. data/lib/pg.rb +21 -11
  40. data/spec/helpers.rb +47 -19
  41. data/spec/pg/basic_type_mapping_spec.rb +230 -27
  42. data/spec/pg/connection_spec.rb +402 -275
  43. data/spec/pg/connection_sync_spec.rb +41 -0
  44. data/spec/pg/result_spec.rb +59 -17
  45. data/spec/pg/tuple_spec.rb +280 -0
  46. data/spec/pg/type_map_by_class_spec.rb +2 -2
  47. data/spec/pg/type_map_by_column_spec.rb +1 -1
  48. data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
  49. data/spec/pg/type_map_by_oid_spec.rb +1 -1
  50. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  51. data/spec/pg/type_map_spec.rb +1 -1
  52. data/spec/pg/type_spec.rb +184 -12
  53. data/spec/pg_spec.rb +2 -2
  54. data.tar.gz.sig +0 -0
  55. metadata +47 -53
  56. metadata.gz.sig +0 -0
  57. data/sample/array_insert.rb +0 -20
  58. data/sample/async_api.rb +0 -106
  59. data/sample/async_copyto.rb +0 -39
  60. data/sample/async_mixed.rb +0 -56
  61. data/sample/check_conn.rb +0 -21
  62. data/sample/copyfrom.rb +0 -81
  63. data/sample/copyto.rb +0 -19
  64. data/sample/cursor.rb +0 -21
  65. data/sample/disk_usage_report.rb +0 -186
  66. data/sample/issue-119.rb +0 -94
  67. data/sample/losample.rb +0 -69
  68. data/sample/minimal-testcase.rb +0 -17
  69. data/sample/notify_wait.rb +0 -72
  70. data/sample/pg_statistics.rb +0 -294
  71. data/sample/replication_monitor.rb +0 -231
  72. data/sample/test_binary_values.rb +0 -33
  73. data/sample/wal_shipper.rb +0 -434
  74. data/sample/warehouse_partitions.rb +0 -320
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env rspec
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", :postgresql_90 do
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
- expected_trace_output = EXPECTED_TRACE_OUTPUT.dup
286
- # For PostgreSQL < 9.0, the output will be different:
287
- # -From backend (#4)> 13
288
- # -From backend> "SELECT 1"
289
- # +From backend (#4)> 11
290
- # +From backend> "SELECT"
291
- if @conn.server_version < 90000
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( expected_trace_output )
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.exec( "INSERT INTO students VALUES( $1, $2 )", ['Wally', 8] )
405
- @conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Sally', 6] )
406
- @conn.exec( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
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", :postgresql_90 do
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", :postgresql_90 do
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
- it "raises an appropriate error if either of the required arguments for encrypt_password " +
784
- "is not valid" do
785
- expect {
786
- described_class.encrypt_password( nil, nil )
787
- }.to raise_error( TypeError )
788
- expect {
789
- described_class.encrypt_password( "postgres", nil )
790
- }.to raise_error( TypeError )
791
- expect {
792
- described_class.encrypt_password( nil, "postgres" )
793
- }.to raise_error( TypeError )
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 "can connect asynchronously", :socket_io do
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 (if any) before waiting on an #async_exec"
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 "calls the block if one is provided to #async_exec" do
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.async_exec( "select 47 as one" ) do |pg_res|
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, /can't get socket descriptor/)
942
+ expect{ conn.block }.to raise_error(PG::ConnectionBad, /connection not open/)
890
943
  end
891
944
 
892
- context "under PostgreSQL 9", :postgresql_90 do
945
+ it "sets the fallback_application_name on new connections" do
946
+ conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
893
947
 
894
- before( :each ) do
895
- pending "only works with a PostgreSQL >= 9.0 server" if @conn.server_version < 9_00_00
896
- end
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
- it "sets the fallback_application_name on new connections" do
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
- it "sets a shortened fallback_application_name on new connections" do
908
- old_0 = $0
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
- @conn.exec( 'ROLLBACK' )
925
- @conn.exec( 'LISTEN knees' )
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
- event, pid, msg = nil
932
- @conn.wait_for_notify( 10 ) do |*args|
933
- event, pid, msg = *args
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
- expect( event ).to eq( 'knees' )
938
- expect( pid ).to be_a_kind_of( Integer )
939
- expect( msg ).to eq( 'skirt and boots' )
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
- it "accepts nil as the timeout in #wait_for_notify " do
943
- @conn.exec( 'ROLLBACK' )
944
- @conn.exec( 'LISTEN knees' )
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
- conn = described_class.connect( @conninfo )
947
- conn.exec( %Q{NOTIFY knees} )
948
- conn.finish
989
+ it "accepts nil as the timeout in #wait_for_notify " do
990
+ @conn.exec( 'ROLLBACK' )
991
+ @conn.exec( 'LISTEN knees' )
949
992
 
950
- event, pid = nil
951
- @conn.wait_for_notify( nil ) do |*args|
952
- event, pid = *args
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
- expect( event ).to eq( 'knees' )
957
- expect( pid ).to be_a_kind_of( Integer )
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
- it "sends nil as the payload if the notification wasn't given one" do
961
- @conn.exec( 'ROLLBACK' )
962
- @conn.exec( 'LISTEN knees' )
1003
+ expect( event ).to eq( 'knees' )
1004
+ expect( pid ).to be_a_kind_of( Integer )
1005
+ end
963
1006
 
964
- conn = described_class.connect( @conninfo )
965
- conn.exec( %Q{NOTIFY knees} )
966
- conn.finish
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
- payload = :notnil
969
- @conn.wait_for_notify( nil ) do |*args|
970
- payload = args[ 2 ]
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
- expect( payload ).to be_nil()
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
- it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
978
- "two arguments" do
1021
+ expect( payload ).to be_nil()
1022
+ end
979
1023
 
980
- @conn.exec( 'ROLLBACK' )
981
- @conn.exec( 'LISTEN knees' )
1024
+ it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
1025
+ "two arguments" do
982
1026
 
983
- conn = described_class.connect( @conninfo )
984
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
985
- conn.finish
1027
+ @conn.exec( 'ROLLBACK' )
1028
+ @conn.exec( 'LISTEN knees' )
986
1029
 
987
- event, pid, msg = nil
988
- @conn.wait_for_notify( 10 ) do |arg1, arg2|
989
- event, pid, msg = arg1, arg2
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
- expect( event ).to eq( 'knees' )
994
- expect( pid ).to be_a_kind_of( Integer )
995
- expect( msg ).to be_nil()
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
- it "calls the block supplied to wait_for_notify with the notify payload if it " +
999
- "doesn't accept arguments" do
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
- @conn.exec( 'ROLLBACK' )
1002
- @conn.exec( 'LISTEN knees' )
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
- conn = described_class.connect( @conninfo )
1005
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
1006
- conn.finish
1048
+ @conn.exec( 'ROLLBACK' )
1049
+ @conn.exec( 'LISTEN knees' )
1007
1050
 
1008
- notification_received = false
1009
- @conn.wait_for_notify( 10 ) do
1010
- notification_received = true
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
- expect( notification_received ).to be_truthy()
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
- it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
1018
- "three arguments" do
1061
+ expect( notification_received ).to be_truthy()
1062
+ end
1019
1063
 
1020
- @conn.exec( 'ROLLBACK' )
1021
- @conn.exec( 'LISTEN knees' )
1064
+ it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
1065
+ "three arguments" do
1022
1066
 
1023
- conn = described_class.connect( @conninfo )
1024
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
1025
- conn.finish
1067
+ @conn.exec( 'ROLLBACK' )
1068
+ @conn.exec( 'LISTEN knees' )
1026
1069
 
1027
- event, pid, msg = nil
1028
- @conn.wait_for_notify( 10 ) do |arg1, arg2, arg3|
1029
- event, pid, msg = arg1, arg2, arg3
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
- expect( event ).to eq( 'knees' )
1034
- expect( pid ).to be_a_kind_of( Integer )
1035
- expect( msg ).to eq( 'skirt and boots' )
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 "under PostgreSQL 9.1 client library", :postgresql_91, :without_transaction do
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
- context "under PostgreSQL 9.2 client library", :postgresql_92 do
1077
- describe "set_single_row_mode" do
1120
+ describe "set_single_row_mode" do
1078
1121
 
1079
- it "raises an error when called at the wrong time" do
1080
- expect {
1081
- @conn.set_single_row_mode
1082
- }.to raise_error(PG::Error)
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
- it "should work in single row mode" do
1086
- @conn.send_query( "SELECT generate_series(1,10)" )
1087
- @conn.set_single_row_mode
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
- results = []
1090
- loop do
1091
- @conn.block
1092
- res = @conn.get_result or break
1093
- results << res
1094
- end
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
- it "should receive rows before entire query is finished" do
1107
- @conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, pg_sleep(1);" )
1108
- @conn.set_single_row_mode
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
- start_time = Time.now
1111
- first_row_time = nil
1169
+ first_result = nil
1170
+ expect do
1112
1171
  loop do
1113
1172
  res = @conn.get_result or break
1114
1173
  res.check
1115
- first_row_time = Time.now unless first_row_time
1174
+ first_result ||= res
1116
1175
  end
1117
- expect( (Time.now - start_time) ).to be >= 0.9
1118
- expect( (first_row_time - start_time) ).to be < 0.9
1119
- end
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.exec("VALUES ('fantasia')", [], 0)
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.exec("VALUES ('世界線航跡蔵')", [], 0)
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.exec(stmt, [], 0)
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.exec(stmt, [], 0)
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\0 escape".encode( "utf-16be" )
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", :postgresql_90 do
1205
- original = "Möhre to\0 escape".encode( "utf-16be" )
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", :postgresql_90 do
1213
- original = "Möhre to\0 escape".encode( "utf-16le" )
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\0 escape".encode( "utf-16le" )
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\0 escape".encode( "iso-8859-1" )
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\0 escape".encode( "iso-8859-1" )
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 #send_query" do
1297
- @conn.send_query("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
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 + "\0afterzero" )
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", :postgresql_90 do
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", :postgresql_90 do
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", :postgresql_90 do
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", :postgresql_90 do
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
- expect{
1528
- @conn.exec_params( "SELECT $1", [5] )
1529
- }.to raise_error(PG::IndeterminateDatatype)
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