pg 0.21.0 → 1.1.4

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 (55) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/History.rdoc +98 -0
  5. data/Manifest.txt +5 -1
  6. data/README.rdoc +14 -4
  7. data/Rakefile +4 -5
  8. data/Rakefile.cross +17 -21
  9. data/ext/errorcodes.def +12 -0
  10. data/ext/errorcodes.rb +1 -1
  11. data/ext/errorcodes.txt +4 -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 +23 -50
  16. data/ext/pg.h +51 -81
  17. data/ext/pg_binary_decoder.c +73 -6
  18. data/ext/pg_coder.c +52 -3
  19. data/ext/pg_connection.c +369 -219
  20. data/ext/pg_copy_coder.c +10 -5
  21. data/ext/pg_result.c +343 -119
  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/util.c +6 -6
  26. data/ext/util.h +2 -2
  27. data/lib/pg.rb +5 -7
  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 -3
  32. data/lib/pg/constants.rb +1 -1
  33. data/lib/pg/exceptions.rb +1 -1
  34. data/lib/pg/result.rb +1 -1
  35. data/lib/pg/text_decoder.rb +19 -23
  36. data/lib/pg/text_encoder.rb +35 -1
  37. data/lib/pg/tuple.rb +30 -0
  38. data/lib/pg/type_map_by_column.rb +1 -1
  39. data/spec/helpers.rb +49 -21
  40. data/spec/pg/basic_type_mapping_spec.rb +230 -27
  41. data/spec/pg/connection_spec.rb +473 -277
  42. data/spec/pg/connection_sync_spec.rb +41 -0
  43. data/spec/pg/result_spec.rb +48 -13
  44. data/spec/pg/tuple_spec.rb +280 -0
  45. data/spec/pg/type_map_by_class_spec.rb +1 -1
  46. data/spec/pg/type_map_by_column_spec.rb +1 -1
  47. data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
  48. data/spec/pg/type_map_by_oid_spec.rb +1 -1
  49. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  50. data/spec/pg/type_map_spec.rb +1 -1
  51. data/spec/pg/type_spec.rb +184 -12
  52. data/spec/pg_spec.rb +2 -2
  53. metadata +37 -33
  54. metadata.gz.sig +0 -0
  55. data/lib/pg/deprecated_constants.rb +0 -21
@@ -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,79 @@ 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
+
232
+ it "can use conn.reset_start to restart the connection" do
233
+ ios = IO.pipe
234
+ conn = described_class.connect_start( @conninfo )
235
+ wait_for_polling_ok(conn)
236
+
237
+ # Close the two pipe file descriptors, so that the file descriptor of
238
+ # newly established connection is probably distinct from the previous one.
239
+ ios.each(&:close)
240
+ conn.reset_start
241
+ wait_for_polling_ok(conn, :reset_poll)
242
+
243
+ # The new connection should work even when the file descriptor has changed.
244
+ conn.send_query("SELECT 1")
245
+ res = wait_for_query_result(conn)
246
+ expect( res.values ).to eq([["1"]])
247
+
248
+ conn.close
249
+ end
250
+
251
+ it "should properly close a socket IO when GC'ed" do
252
+ # This results in
253
+ # Errno::ENOTSOCK: An operation was attempted on something that is not a socket.
254
+ # on Windows when rb_w32_unwrap_io_handle() isn't called in pgconn_gc_free().
255
+ 5.times do
256
+ conn = described_class.connect( @conninfo )
257
+ conn.socket_io.close
258
+ end
259
+ GC.start
260
+ IO.pipe.each(&:close)
261
+ end
262
+ end
263
+
213
264
  it "raises proper error when sending fails" do
214
265
  conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
215
266
  expect{ conn.exec 'SELECT 1' }.to raise_error(PG::UnableToSend, /no connection/)
@@ -219,7 +270,7 @@ describe PG::Connection do
219
270
  described_class.connect(@conninfo).finish
220
271
  sleep 0.5
221
272
  res = @conn.exec(%[SELECT COUNT(*) AS n FROM pg_stat_activity
222
- WHERE usename IS NOT NULL])
273
+ WHERE usename IS NOT NULL AND application_name != ''])
223
274
  # there's still the global @conn, but should be no more
224
275
  expect( res[0]['n'] ).to eq( '1' )
225
276
  end
@@ -228,7 +279,7 @@ describe PG::Connection do
228
279
  expect( @conn.db ).to eq( "test" )
229
280
  expect( @conn.user ).to be_a_kind_of( String )
230
281
  expect( @conn.pass ).to eq( "" )
231
- expect( @conn.port ).to eq( 54321 )
282
+ expect( @conn.port ).to eq( @port )
232
283
  expect( @conn.tty ).to eq( "" )
233
284
  expect( @conn.options ).to eq( "" )
234
285
  end
@@ -282,18 +333,15 @@ describe PG::Connection do
282
333
 
283
334
  trace_data = trace_file.read
284
335
 
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
336
+ # For async_exec the output will be different:
337
+ # From backend> Z
338
+ # From backend (#4)> 5
339
+ # +From backend> Z
340
+ # +From backend (#4)> 5
341
+ # From backend> T
342
+ trace_data.sub!( /(From backend> Z\nFrom backend \(#4\)> 5\n){3}/m, '\\1\\1' )
295
343
 
296
- expect( trace_data ).to eq( expected_trace_output )
344
+ expect( trace_data ).to eq( EXPECTED_TRACE_OUTPUT )
297
345
  end
298
346
 
299
347
  it "allows a query to be cancelled" do
@@ -388,22 +436,11 @@ describe PG::Connection do
388
436
  end
389
437
  end
390
438
 
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
439
  it "supports explicitly calling #exec_params" do
403
440
  @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] )
441
+ @conn.exec_params( "INSERT INTO students VALUES( $1, $2 )", ['Wally', 8] )
442
+ @conn.exec_params( "INSERT INTO students VALUES( $1, $2 )", ['Sally', 6] )
443
+ @conn.exec_params( "INSERT INTO students VALUES( $1, $2 )", ['Dorothy', 4] )
407
444
 
408
445
  res = @conn.exec_params( "SELECT name FROM students WHERE age >= $1", [6] )
409
446
  expect( res.values ).to eq( [ ['Wally'], ['Sally'] ] )
@@ -524,7 +561,7 @@ describe PG::Connection do
524
561
  @conn.exec( 'UNLISTEN woo' )
525
562
  end
526
563
 
527
- it "can receive notices while waiting for NOTIFY without exceeding the timeout", :postgresql_90 do
564
+ it "can receive notices while waiting for NOTIFY without exceeding the timeout" do
528
565
  notices = []
529
566
  @conn.set_notice_processor do |msg|
530
567
  notices << [msg, Time.now]
@@ -586,7 +623,7 @@ describe PG::Connection do
586
623
  expect( @conn ).to still_be_usable
587
624
  end
588
625
 
589
- it "can handle server errors in #copy_data for output", :postgresql_90 do
626
+ it "can handle server errors in #copy_data for output" do
590
627
  @conn.exec "ROLLBACK"
591
628
  @conn.transaction do
592
629
  @conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
@@ -718,11 +755,6 @@ describe PG::Connection do
718
755
  expect( (finish - start) ).to be_within( 0.2 ).of( 0.3 )
719
756
  end
720
757
 
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
758
  it "can return the default connection options" do
727
759
  expect( described_class.conndefaults ).to be_a( Array )
728
760
  expect( described_class.conndefaults ).to all( be_a(Hash) )
@@ -733,7 +765,7 @@ describe PG::Connection do
733
765
  it "can return the default connection options as a Hash" do
734
766
  expect( described_class.conndefaults_hash ).to be_a( Hash )
735
767
  expect( described_class.conndefaults_hash ).to include( :user, :password, :dbname, :host, :port )
736
- expect( ['5432', '54321'] ).to include( described_class.conndefaults_hash[:port] )
768
+ expect( ['5432', '54321', @port.to_s] ).to include( described_class.conndefaults_hash[:port] )
737
769
  expect( @conn.conndefaults_hash ).to eq( described_class.conndefaults_hash )
738
770
  end
739
771
 
@@ -779,18 +811,52 @@ describe PG::Connection do
779
811
  end
780
812
  end
781
813
 
814
+ describe "deprecated password encryption method" do
815
+ it "can encrypt password for a given user" do
816
+ expect( described_class.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
817
+ end
782
818
 
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 )
819
+ it "raises an appropriate error if either of the required arguments is not valid" do
820
+ expect {
821
+ described_class.encrypt_password( nil, nil )
822
+ }.to raise_error( TypeError )
823
+ expect {
824
+ described_class.encrypt_password( "postgres", nil )
825
+ }.to raise_error( TypeError )
826
+ expect {
827
+ described_class.encrypt_password( nil, "postgres" )
828
+ }.to raise_error( TypeError )
829
+ end
830
+ end
831
+
832
+ describe "password encryption method", :postgresql_10 do
833
+ it "can encrypt without algorithm" do
834
+ expect( @conn.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
835
+ expect( @conn.encrypt_password("postgres", "postgres", nil) ).to match( /\S+/ )
836
+ end
837
+
838
+ it "can encrypt with algorithm" do
839
+ expect( @conn.encrypt_password("postgres", "postgres", "md5") ).to match( /md5\S+/i )
840
+ expect( @conn.encrypt_password("postgres", "postgres", "scram-sha-256") ).to match( /SCRAM-SHA-256\S+/i )
841
+ end
842
+
843
+ it "raises an appropriate error if either of the required arguments is not valid" do
844
+ expect {
845
+ @conn.encrypt_password( nil, nil )
846
+ }.to raise_error( TypeError )
847
+ expect {
848
+ @conn.encrypt_password( "postgres", nil )
849
+ }.to raise_error( TypeError )
850
+ expect {
851
+ @conn.encrypt_password( nil, "postgres" )
852
+ }.to raise_error( TypeError )
853
+ expect {
854
+ @conn.encrypt_password( "postgres", "postgres", :invalid )
855
+ }.to raise_error( TypeError )
856
+ expect {
857
+ @conn.encrypt_password( "postgres", "postgres", "invalid" )
858
+ }.to raise_error( PG::Error, /unrecognized/ )
859
+ end
794
860
  end
795
861
 
796
862
 
@@ -832,7 +898,7 @@ describe PG::Connection do
832
898
  end
833
899
 
834
900
 
835
- it "can connect asynchronously", :socket_io do
901
+ it "handles server close while asynchronous connect", :socket_io do
836
902
  serv = TCPServer.new( '127.0.0.1', 54320 )
837
903
  conn = described_class.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
838
904
  expect( [PG::PGRES_POLLING_WRITING, PG::CONNECTION_OK] ).to include conn.connect_poll
@@ -844,11 +910,29 @@ describe PG::Connection do
844
910
  expect( conn.connect_poll ).to eq( PG::PGRES_POLLING_FAILED )
845
911
  end
846
912
 
847
- it "discards previous results (if any) before waiting on an #async_exec"
913
+ it "discards previous results at #discard_results" do
914
+ @conn.send_query( "select 1" )
915
+ @conn.discard_results
916
+ @conn.send_query( "select 41 as one" )
917
+ res = @conn.get_last_result
918
+ expect( res.to_a ).to eq( [{ 'one' => '41' }] )
919
+ end
920
+
921
+ it "discards previous results (if any) before waiting on #exec" do
922
+ @conn.send_query( "select 1" )
923
+ res = @conn.exec( "select 42 as one" )
924
+ expect( res.to_a ).to eq( [{ 'one' => '42' }] )
925
+ end
848
926
 
849
- it "calls the block if one is provided to #async_exec" do
927
+ it "discards previous errors before waiting on #exec", :without_transaction do
928
+ @conn.send_query( "ERROR" )
929
+ res = @conn.exec( "select 43 as one" )
930
+ expect( res.to_a ).to eq( [{ 'one' => '43' }] )
931
+ end
932
+
933
+ it "calls the block if one is provided to #exec" do
850
934
  result = nil
851
- @conn.async_exec( "select 47 as one" ) do |pg_res|
935
+ @conn.exec( "select 47 as one" ) do |pg_res|
852
936
  result = pg_res[0]
853
937
  end
854
938
  expect( result ).to eq( { 'one' => '47' } )
@@ -861,6 +945,20 @@ describe PG::Connection do
861
945
  expect { conn.finish }.to raise_error( PG::ConnectionBad, /connection is closed/i )
862
946
  end
863
947
 
948
+ it "can use conn.reset to restart the connection" do
949
+ ios = IO.pipe
950
+ conn = PG.connect( @conninfo )
951
+
952
+ # Close the two pipe file descriptors, so that the file descriptor of
953
+ # newly established connection is probably distinct from the previous one.
954
+ ios.each(&:close)
955
+ conn.reset
956
+
957
+ # The new connection should work even when the file descriptor has changed.
958
+ expect( conn.exec("SELECT 1").values ).to eq([["1"]])
959
+ conn.close
960
+ end
961
+
864
962
  it "closes the IO fetched from #socket_io when the connection is closed", :without_transaction, :socket_io do
865
963
  conn = PG.connect( @conninfo )
866
964
  io = conn.socket_io
@@ -889,155 +987,147 @@ describe PG::Connection do
889
987
  expect{ conn.block }.to raise_error(PG::ConnectionBad, /can't get socket descriptor/)
890
988
  end
891
989
 
892
- context "under PostgreSQL 9", :postgresql_90 do
990
+ it "sets the fallback_application_name on new connections" do
991
+ conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
893
992
 
894
- before( :each ) do
895
- pending "only works with a PostgreSQL >= 9.0 server" if @conn.server_version < 9_00_00
896
- end
993
+ conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
994
+ expect( conn_name ).to include( $0[0..10] )
995
+ expect( conn_name ).to include( $0[-10..-1] )
996
+ expect( conn_name.length ).to be <= 64
997
+ end
897
998
 
898
- it "sets the fallback_application_name on new connections" do
999
+ it "sets a shortened fallback_application_name on new connections" do
1000
+ old_0 = $0
1001
+ begin
1002
+ $0 = "/this/is/a/very/long/path/with/many/directories/to/our/beloved/ruby"
899
1003
  conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
900
-
901
1004
  conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
902
1005
  expect( conn_name ).to include( $0[0..10] )
903
1006
  expect( conn_name ).to include( $0[-10..-1] )
904
1007
  expect( conn_name.length ).to be <= 64
1008
+ ensure
1009
+ $0 = old_0
905
1010
  end
1011
+ end
906
1012
 
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
923
-
924
- @conn.exec( 'ROLLBACK' )
925
- @conn.exec( 'LISTEN knees' )
1013
+ it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
1014
+ "any number of arguments" do
926
1015
 
927
- conn = described_class.connect( @conninfo )
928
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
929
- conn.finish
1016
+ @conn.exec( 'ROLLBACK' )
1017
+ @conn.exec( 'LISTEN knees' )
930
1018
 
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' )
1019
+ conn = described_class.connect( @conninfo )
1020
+ conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
1021
+ conn.finish
936
1022
 
937
- expect( event ).to eq( 'knees' )
938
- expect( pid ).to be_a_kind_of( Integer )
939
- expect( msg ).to eq( 'skirt and boots' )
1023
+ event, pid, msg = nil
1024
+ @conn.wait_for_notify( 10 ) do |*args|
1025
+ event, pid, msg = *args
940
1026
  end
1027
+ @conn.exec( 'UNLISTEN knees' )
941
1028
 
942
- it "accepts nil as the timeout in #wait_for_notify " do
943
- @conn.exec( 'ROLLBACK' )
944
- @conn.exec( 'LISTEN knees' )
1029
+ expect( event ).to eq( 'knees' )
1030
+ expect( pid ).to be_a_kind_of( Integer )
1031
+ expect( msg ).to eq( 'skirt and boots' )
1032
+ end
945
1033
 
946
- conn = described_class.connect( @conninfo )
947
- conn.exec( %Q{NOTIFY knees} )
948
- conn.finish
1034
+ it "accepts nil as the timeout in #wait_for_notify " do
1035
+ @conn.exec( 'ROLLBACK' )
1036
+ @conn.exec( 'LISTEN knees' )
949
1037
 
950
- event, pid = nil
951
- @conn.wait_for_notify( nil ) do |*args|
952
- event, pid = *args
953
- end
954
- @conn.exec( 'UNLISTEN knees' )
1038
+ conn = described_class.connect( @conninfo )
1039
+ conn.exec( %Q{NOTIFY knees} )
1040
+ conn.finish
955
1041
 
956
- expect( event ).to eq( 'knees' )
957
- expect( pid ).to be_a_kind_of( Integer )
1042
+ event, pid = nil
1043
+ @conn.wait_for_notify( nil ) do |*args|
1044
+ event, pid = *args
958
1045
  end
1046
+ @conn.exec( 'UNLISTEN knees' )
959
1047
 
960
- it "sends nil as the payload if the notification wasn't given one" do
961
- @conn.exec( 'ROLLBACK' )
962
- @conn.exec( 'LISTEN knees' )
1048
+ expect( event ).to eq( 'knees' )
1049
+ expect( pid ).to be_a_kind_of( Integer )
1050
+ end
963
1051
 
964
- conn = described_class.connect( @conninfo )
965
- conn.exec( %Q{NOTIFY knees} )
966
- conn.finish
1052
+ it "sends nil as the payload if the notification wasn't given one" do
1053
+ @conn.exec( 'ROLLBACK' )
1054
+ @conn.exec( 'LISTEN knees' )
967
1055
 
968
- payload = :notnil
969
- @conn.wait_for_notify( nil ) do |*args|
970
- payload = args[ 2 ]
971
- end
972
- @conn.exec( 'UNLISTEN knees' )
1056
+ conn = described_class.connect( @conninfo )
1057
+ conn.exec( %Q{NOTIFY knees} )
1058
+ conn.finish
973
1059
 
974
- expect( payload ).to be_nil()
1060
+ payload = :notnil
1061
+ @conn.wait_for_notify( nil ) do |*args|
1062
+ payload = args[ 2 ]
975
1063
  end
1064
+ @conn.exec( 'UNLISTEN knees' )
976
1065
 
977
- it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
978
- "two arguments" do
1066
+ expect( payload ).to be_nil()
1067
+ end
979
1068
 
980
- @conn.exec( 'ROLLBACK' )
981
- @conn.exec( 'LISTEN knees' )
1069
+ it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
1070
+ "two arguments" do
982
1071
 
983
- conn = described_class.connect( @conninfo )
984
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
985
- conn.finish
1072
+ @conn.exec( 'ROLLBACK' )
1073
+ @conn.exec( 'LISTEN knees' )
986
1074
 
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' )
1075
+ conn = described_class.connect( @conninfo )
1076
+ conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
1077
+ conn.finish
992
1078
 
993
- expect( event ).to eq( 'knees' )
994
- expect( pid ).to be_a_kind_of( Integer )
995
- expect( msg ).to be_nil()
1079
+ event, pid, msg = nil
1080
+ @conn.wait_for_notify( 10 ) do |arg1, arg2|
1081
+ event, pid, msg = arg1, arg2
996
1082
  end
1083
+ @conn.exec( 'UNLISTEN knees' )
997
1084
 
998
- it "calls the block supplied to wait_for_notify with the notify payload if it " +
999
- "doesn't accept arguments" do
1085
+ expect( event ).to eq( 'knees' )
1086
+ expect( pid ).to be_a_kind_of( Integer )
1087
+ expect( msg ).to be_nil()
1088
+ end
1000
1089
 
1001
- @conn.exec( 'ROLLBACK' )
1002
- @conn.exec( 'LISTEN knees' )
1090
+ it "calls the block supplied to wait_for_notify with the notify payload if it " +
1091
+ "doesn't accept arguments" do
1003
1092
 
1004
- conn = described_class.connect( @conninfo )
1005
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
1006
- conn.finish
1093
+ @conn.exec( 'ROLLBACK' )
1094
+ @conn.exec( 'LISTEN knees' )
1007
1095
 
1008
- notification_received = false
1009
- @conn.wait_for_notify( 10 ) do
1010
- notification_received = true
1011
- end
1012
- @conn.exec( 'UNLISTEN knees' )
1096
+ conn = described_class.connect( @conninfo )
1097
+ conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
1098
+ conn.finish
1013
1099
 
1014
- expect( notification_received ).to be_truthy()
1100
+ notification_received = false
1101
+ @conn.wait_for_notify( 10 ) do
1102
+ notification_received = true
1015
1103
  end
1104
+ @conn.exec( 'UNLISTEN knees' )
1016
1105
 
1017
- it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
1018
- "three arguments" do
1106
+ expect( notification_received ).to be_truthy()
1107
+ end
1019
1108
 
1020
- @conn.exec( 'ROLLBACK' )
1021
- @conn.exec( 'LISTEN knees' )
1109
+ it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
1110
+ "three arguments" do
1022
1111
 
1023
- conn = described_class.connect( @conninfo )
1024
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
1025
- conn.finish
1112
+ @conn.exec( 'ROLLBACK' )
1113
+ @conn.exec( 'LISTEN knees' )
1026
1114
 
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' )
1115
+ conn = described_class.connect( @conninfo )
1116
+ conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
1117
+ conn.finish
1032
1118
 
1033
- expect( event ).to eq( 'knees' )
1034
- expect( pid ).to be_a_kind_of( Integer )
1035
- expect( msg ).to eq( 'skirt and boots' )
1119
+ event, pid, msg = nil
1120
+ @conn.wait_for_notify( 10 ) do |arg1, arg2, arg3|
1121
+ event, pid, msg = arg1, arg2, arg3
1036
1122
  end
1123
+ @conn.exec( 'UNLISTEN knees' )
1037
1124
 
1125
+ expect( event ).to eq( 'knees' )
1126
+ expect( pid ).to be_a_kind_of( Integer )
1127
+ expect( msg ).to eq( 'skirt and boots' )
1038
1128
  end
1039
1129
 
1040
- context "under PostgreSQL 9.1 client library", :postgresql_91, :without_transaction do
1130
+ context "server ping", :without_transaction do
1041
1131
 
1042
1132
  it "pings successfully with connection string" do
1043
1133
  ping = described_class.ping(@conninfo)
@@ -1065,76 +1155,82 @@ describe PG::Connection do
1065
1155
  expect( ping ).to eq( PG::PQPING_NO_RESPONSE )
1066
1156
  end
1067
1157
 
1068
- it "returns correct response when ping connection arguments are wrong" do
1158
+ it "returns error when ping connection arguments are wrong" do
1069
1159
  ping = described_class.ping('localhost', 'localhost', nil, nil, :test, nil, nil)
1070
- expect( ping ).to eq( PG::PQPING_NO_ATTEMPT )
1160
+ expect( ping ).to_not eq( PG::PQPING_OK )
1071
1161
  end
1072
1162
 
1163
+ it "returns correct response when ping connection arguments are wrong" do
1164
+ ping = described_class.ping(
1165
+ :host => 'localhost',
1166
+ :invalid_option => 9999,
1167
+ :dbname => :test)
1168
+ expect( ping ).to eq( PG::PQPING_NO_ATTEMPT )
1169
+ end
1073
1170
 
1074
1171
  end
1075
1172
 
1076
- context "under PostgreSQL 9.2 client library", :postgresql_92 do
1077
- describe "set_single_row_mode" do
1173
+ describe "set_single_row_mode" do
1078
1174
 
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)
1175
+ it "raises an error when called at the wrong time" do
1176
+ expect {
1177
+ @conn.set_single_row_mode
1178
+ }.to raise_error(PG::Error)
1179
+ end
1180
+
1181
+ it "should work in single row mode" do
1182
+ @conn.send_query( "SELECT generate_series(1,10)" )
1183
+ @conn.set_single_row_mode
1184
+
1185
+ results = []
1186
+ loop do
1187
+ @conn.block
1188
+ res = @conn.get_result or break
1189
+ results << res
1190
+ end
1191
+ expect( results.length ).to eq( 11 )
1192
+ results[0..-2].each do |res|
1193
+ expect( res.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
1194
+ values = res.field_values('generate_series')
1195
+ expect( values.length ).to eq( 1 )
1196
+ expect( values.first.to_i ).to be > 0
1083
1197
  end
1198
+ expect( results.last.result_status ).to eq( PG::PGRES_TUPLES_OK )
1199
+ expect( results.last.ntuples ).to eq( 0 )
1200
+ end
1084
1201
 
1085
- it "should work in single row mode" do
1086
- @conn.send_query( "SELECT generate_series(1,10)" )
1087
- @conn.set_single_row_mode
1202
+ it "should receive rows before entire query is finished" do
1203
+ @conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, pg_sleep(1);" )
1204
+ @conn.set_single_row_mode
1088
1205
 
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 )
1206
+ start_time = Time.now
1207
+ first_row_time = nil
1208
+ loop do
1209
+ res = @conn.get_result or break
1210
+ res.check
1211
+ first_row_time = Time.now unless first_row_time
1104
1212
  end
1213
+ expect( (Time.now - start_time) ).to be >= 0.9
1214
+ expect( (first_row_time - start_time) ).to be < 0.9
1215
+ end
1105
1216
 
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
1217
+ it "should receive rows before entire query fails" do
1218
+ @conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
1219
+ @conn.send_query( "SELECT generate_series(0,999), NULL UNION ALL SELECT 1000, errfunc();" )
1220
+ @conn.set_single_row_mode
1109
1221
 
1110
- start_time = Time.now
1111
- first_row_time = nil
1222
+ first_result = nil
1223
+ expect do
1112
1224
  loop do
1113
1225
  res = @conn.get_result or break
1114
1226
  res.check
1115
- first_row_time = Time.now unless first_row_time
1227
+ first_result ||= res
1116
1228
  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
1229
+ end.to raise_error(PG::Error)
1230
+ expect( first_result.kind_of?(PG::Result) ).to be_truthy
1231
+ expect( first_result.result_status ).to eq( PG::PGRES_SINGLE_TUPLE )
1137
1232
  end
1233
+
1138
1234
  end
1139
1235
 
1140
1236
  context "multinationalization support", :ruby_19 do
@@ -1144,7 +1240,7 @@ describe PG::Connection do
1144
1240
  out_string = nil
1145
1241
  @conn.transaction do |conn|
1146
1242
  conn.internal_encoding = 'iso8859-1'
1147
- res = conn.exec("VALUES ('fantasia')", [], 0)
1243
+ res = conn.exec_params("VALUES ('fantasia')", [], 0)
1148
1244
  out_string = res[0]['column1']
1149
1245
  end
1150
1246
  expect( out_string ).to eq( 'fantasia' )
@@ -1155,7 +1251,7 @@ describe PG::Connection do
1155
1251
  out_string = nil
1156
1252
  @conn.transaction do |conn|
1157
1253
  conn.internal_encoding = 'utf-8'
1158
- res = conn.exec("VALUES ('世界線航跡蔵')", [], 0)
1254
+ res = conn.exec_params("VALUES ('世界線航跡蔵')", [], 0)
1159
1255
  out_string = res[0]['column1']
1160
1256
  end
1161
1257
  expect( out_string ).to eq( '世界線航跡蔵' )
@@ -1167,7 +1263,7 @@ describe PG::Connection do
1167
1263
  @conn.transaction do |conn|
1168
1264
  conn.internal_encoding = 'EUC-JP'
1169
1265
  stmt = "VALUES ('世界線航跡蔵')".encode('EUC-JP')
1170
- res = conn.exec(stmt, [], 0)
1266
+ res = conn.exec_params(stmt, [], 0)
1171
1267
  out_string = res[0]['column1']
1172
1268
  end
1173
1269
  expect( out_string ).to eq( '世界線航跡蔵'.encode('EUC-JP') )
@@ -1180,7 +1276,7 @@ describe PG::Connection do
1180
1276
  @conn.transaction do |conn|
1181
1277
  conn.internal_encoding = 'EUC-JP'
1182
1278
  stmt = "VALUES ('世界線航跡蔵')".encode('EUC-JP')
1183
- res = conn.exec(stmt, [], 0)
1279
+ res = conn.exec_params(stmt, [], 0)
1184
1280
  conn.internal_encoding = 'utf-8'
1185
1281
  out_string = res[0]['column1']
1186
1282
  end
@@ -1193,52 +1289,74 @@ describe PG::Connection do
1193
1289
  expect( @conn.internal_encoding ).to eq( Encoding::ASCII_8BIT )
1194
1290
  end
1195
1291
 
1292
+ it "the connection should use JOHAB dummy encoding when it's set to JOHAB" do
1293
+ @conn.set_client_encoding "JOHAB"
1294
+ val = @conn.exec("SELECT chr(x'3391'::int)").values[0][0]
1295
+ expect( val.encoding.name ).to eq( "JOHAB" )
1296
+ expect( val.unpack("H*")[0] ).to eq( "dc65" )
1297
+ end
1298
+
1299
+ it "can retrieve server encoding as text" do
1300
+ enc = @conn.parameter_status "server_encoding"
1301
+ expect( enc ).to eq( "UTF8" )
1302
+ end
1303
+
1304
+ it "can retrieve server encoding as ruby encoding" do
1305
+ expect( @conn.external_encoding ).to eq( Encoding::UTF_8 )
1306
+ end
1307
+
1196
1308
  it "uses the client encoding for escaped string" do
1197
- original = "Möhre to\0 escape".encode( "utf-16be" )
1309
+ original = "Möhre to 'scape".encode( "utf-16be" )
1198
1310
  @conn.set_client_encoding( "euc_jp" )
1199
1311
  escaped = @conn.escape( original )
1200
1312
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1201
- expect( escaped ).to eq( "Möhre to".encode(Encoding::EUC_JP) )
1313
+ expect( escaped ).to eq( "Möhre to ''scape".encode(Encoding::EUC_JP) )
1202
1314
  end
1203
1315
 
1204
- it "uses the client encoding for escaped literal", :postgresql_90 do
1205
- original = "Möhre to\0 escape".encode( "utf-16be" )
1316
+ it "uses the client encoding for escaped literal" do
1317
+ original = "Möhre to 'scape".encode( "utf-16be" )
1206
1318
  @conn.set_client_encoding( "euc_jp" )
1207
1319
  escaped = @conn.escape_literal( original )
1208
1320
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1209
- expect( escaped ).to eq( "'Möhre to'".encode(Encoding::EUC_JP) )
1321
+ expect( escaped ).to eq( "'Möhre to ''scape'".encode(Encoding::EUC_JP) )
1210
1322
  end
1211
1323
 
1212
- it "uses the client encoding for escaped identifier", :postgresql_90 do
1213
- original = "Möhre to\0 escape".encode( "utf-16le" )
1324
+ it "uses the client encoding for escaped identifier" do
1325
+ original = "Möhre to 'scape".encode( "utf-16le" )
1214
1326
  @conn.set_client_encoding( "euc_jp" )
1215
1327
  escaped = @conn.escape_identifier( original )
1216
1328
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1217
- expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
1329
+ expect( escaped ).to eq( "\"Möhre to 'scape\"".encode(Encoding::EUC_JP) )
1218
1330
  end
1219
1331
 
1220
1332
  it "uses the client encoding for quote_ident" do
1221
- original = "Möhre to\0 escape".encode( "utf-16le" )
1333
+ original = "Möhre to 'scape".encode( "utf-16le" )
1222
1334
  @conn.set_client_encoding( "euc_jp" )
1223
1335
  escaped = @conn.quote_ident( original )
1224
1336
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1225
- expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
1337
+ expect( escaped ).to eq( "\"Möhre to 'scape\"".encode(Encoding::EUC_JP) )
1226
1338
  end
1227
1339
 
1228
1340
  it "uses the previous string encoding for escaped string" do
1229
- original = "Möhre to\0 escape".encode( "iso-8859-1" )
1341
+ original = "Möhre to 'scape".encode( "iso-8859-1" )
1230
1342
  @conn.set_client_encoding( "euc_jp" )
1231
1343
  escaped = described_class.escape( original )
1232
1344
  expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
1233
- expect( escaped ).to eq( "Möhre to".encode(Encoding::ISO8859_1) )
1345
+ expect( escaped ).to eq( "Möhre to ''scape".encode(Encoding::ISO8859_1) )
1234
1346
  end
1235
1347
 
1236
1348
  it "uses the previous string encoding for quote_ident" do
1237
- original = "Möhre to\0 escape".encode( "iso-8859-1" )
1349
+ original = "Möhre to 'scape".encode( "iso-8859-1" )
1238
1350
  @conn.set_client_encoding( "euc_jp" )
1239
1351
  escaped = described_class.quote_ident( original )
1240
1352
  expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
1241
- expect( escaped.encode ).to eq( "\"Möhre to\"".encode(Encoding::ISO8859_1) )
1353
+ expect( escaped.encode ).to eq( "\"Möhre to 'scape\"".encode(Encoding::ISO8859_1) )
1354
+ end
1355
+
1356
+ it "raises appropriate error if set_client_encoding is called with invalid arguments" do
1357
+ expect { @conn.set_client_encoding( "invalid" ) }.to raise_error(PG::Error, /invalid value/)
1358
+ expect { @conn.set_client_encoding( :invalid ) }.to raise_error(TypeError)
1359
+ expect { @conn.set_client_encoding( nil ) }.to raise_error(TypeError)
1242
1360
  end
1243
1361
  end
1244
1362
 
@@ -1253,22 +1371,11 @@ describe PG::Connection do
1253
1371
  expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1254
1372
  end
1255
1373
 
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
1374
  it "should convert query string to #exec" do
1263
1375
  r = @conn.exec("SELECT 'grün'".encode("utf-16be"))
1264
1376
  expect( r.values ).to eq( [['grün']] )
1265
1377
  end
1266
1378
 
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
1379
  it "should convert strings and parameters to #prepare and #exec_prepared" do
1273
1380
  @conn.prepare("weiß1".encode("utf-16be"), "VALUES( $1, $2, $1=$2, 'grün')".encode("cp850"))
1274
1381
  r = @conn.exec_prepared("weiß1".encode("utf-32le"),
@@ -1293,8 +1400,8 @@ describe PG::Connection do
1293
1400
  expect( @conn.get_last_result.values ).to eq( [['grün']] )
1294
1401
  end
1295
1402
 
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"),
1403
+ it "should convert query string and parameters to #send_query_params" do
1404
+ @conn.send_query_params("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
1298
1405
  ['grün'.encode('utf-32be'), 'grün'.encode('iso-8859-1')])
1299
1406
  expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1300
1407
  end
@@ -1328,9 +1435,54 @@ describe PG::Connection do
1328
1435
  end
1329
1436
  end
1330
1437
 
1438
+ it "rejects command strings with zero bytes" do
1439
+ expect{ @conn.exec( "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
1440
+ expect{ @conn.exec_params( "SELECT 1;\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
1441
+ expect{ @conn.prepare( "abc\x00", "SELECT 1;" ) }.to raise_error(ArgumentError, /null byte/)
1442
+ expect{ @conn.prepare( "abc", "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
1443
+ expect{ @conn.exec_prepared( "abc\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
1444
+ expect{ @conn.describe_prepared( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
1445
+ expect{ @conn.describe_portal( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
1446
+ expect{ @conn.send_query( "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
1447
+ expect{ @conn.send_query_params( "SELECT 1;\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
1448
+ expect{ @conn.send_prepare( "abc\x00", "SELECT 1;" ) }.to raise_error(ArgumentError, /null byte/)
1449
+ expect{ @conn.send_prepare( "abc", "SELECT 1;\x00" ) }.to raise_error(ArgumentError, /null byte/)
1450
+ expect{ @conn.send_query_prepared( "abc\x00", [] ) }.to raise_error(ArgumentError, /null byte/)
1451
+ expect{ @conn.send_describe_prepared( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
1452
+ expect{ @conn.send_describe_portal( "abc\x00" ) }.to raise_error(ArgumentError, /null byte/)
1453
+ end
1454
+
1455
+ it "rejects query params with zero bytes" do
1456
+ expect{ @conn.exec_params( "SELECT 1;\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
1457
+ expect{ @conn.exec_prepared( "abc\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
1458
+ expect{ @conn.send_query_params( "SELECT 1;\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
1459
+ expect{ @conn.send_query_prepared( "abc\x00", ["ab\x00"] ) }.to raise_error(ArgumentError, /null byte/)
1460
+ end
1461
+
1462
+ it "rejects string with zero bytes in escape" do
1463
+ expect{ @conn.escape( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
1464
+ end
1465
+
1466
+ it "rejects string with zero bytes in escape_literal" do
1467
+ expect{ @conn.escape_literal( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
1468
+ end
1469
+
1470
+ it "rejects string with zero bytes in escape_identifier" do
1471
+ expect{ @conn.escape_identifier( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
1472
+ end
1473
+
1474
+ it "rejects string with zero bytes in quote_ident" do
1475
+ expect{ described_class.quote_ident( "ab\x00cd" ) }.to raise_error(ArgumentError, /null byte/)
1476
+ end
1477
+
1478
+ it "rejects Array with string with zero bytes" do
1479
+ original = ["xyz", "2\x00"]
1480
+ expect{ described_class.quote_ident( original ) }.to raise_error(ArgumentError, /null byte/)
1481
+ end
1482
+
1331
1483
  it "can quote bigger strings with quote_ident" do
1332
1484
  original = "'01234567\"" * 100
1333
- escaped = described_class.quote_ident( original + "\0afterzero" )
1485
+ escaped = described_class.quote_ident( original )
1334
1486
  expect( escaped ).to eq( "\"" + original.gsub("\"", "\"\"") + "\"" )
1335
1487
  end
1336
1488
 
@@ -1408,7 +1560,7 @@ describe PG::Connection do
1408
1560
  conn.finish if conn
1409
1561
  end
1410
1562
 
1411
- it "handles clearing result in or after set_notice_receiver", :postgresql_90 do
1563
+ it "handles clearing result in or after set_notice_receiver" do
1412
1564
  r = nil
1413
1565
  @conn.set_notice_receiver do |result|
1414
1566
  r = result
@@ -1423,7 +1575,7 @@ describe PG::Connection do
1423
1575
  @conn.set_notice_receiver
1424
1576
  end
1425
1577
 
1426
- it "receives properly encoded messages in the notice callbacks", :postgresql_90 do
1578
+ it "receives properly encoded messages in the notice callbacks" do
1427
1579
  [:receiver, :processor].each do |kind|
1428
1580
  notices = []
1429
1581
  @conn.internal_encoding = 'utf-8'
@@ -1451,7 +1603,7 @@ describe PG::Connection do
1451
1603
  end
1452
1604
  end
1453
1605
 
1454
- it "receives properly encoded text from wait_for_notify", :postgresql_90 do
1606
+ it "receives properly encoded text from wait_for_notify" do
1455
1607
  @conn.internal_encoding = 'utf-8'
1456
1608
  @conn.exec( 'ROLLBACK' )
1457
1609
  @conn.exec( 'LISTEN "Möhre"' )
@@ -1468,7 +1620,7 @@ describe PG::Connection do
1468
1620
  expect( msg.encoding ).to eq( Encoding::UTF_8 )
1469
1621
  end
1470
1622
 
1471
- it "returns properly encoded text from notifies", :postgresql_90 do
1623
+ it "returns properly encoded text from notifies" do
1472
1624
  @conn.internal_encoding = 'utf-8'
1473
1625
  @conn.exec( 'ROLLBACK' )
1474
1626
  @conn.exec( 'LISTEN "Möhre"' )
@@ -1524,9 +1676,14 @@ describe PG::Connection do
1524
1676
  end
1525
1677
 
1526
1678
  it "shouldn't type map params unless requested" do
1527
- expect{
1528
- @conn.exec_params( "SELECT $1", [5] )
1529
- }.to raise_error(PG::IndeterminateDatatype)
1679
+ if @conn.server_version < 100000
1680
+ expect{
1681
+ @conn.exec_params( "SELECT $1", [5] )
1682
+ }.to raise_error(PG::IndeterminateDatatype)
1683
+ else
1684
+ # PostgreSQL-10 maps to TEXT type (OID 25)
1685
+ expect( @conn.exec_params( "SELECT $1", [5] ).ftype(0)).to eq(25)
1686
+ end
1530
1687
  end
1531
1688
 
1532
1689
  it "should raise an error on invalid encoder to put_copy_data" do
@@ -1673,4 +1830,43 @@ describe PG::Connection do
1673
1830
  end
1674
1831
  end
1675
1832
  end
1833
+
1834
+ describe "deprecated forms of methods" do
1835
+ it "should forward exec to exec_params" do
1836
+ res = @conn.exec("VALUES($1::INT)", [7]).values
1837
+ expect(res).to eq( [["7"]] )
1838
+ res = @conn.exec("VALUES($1::INT)", [7], 1).values
1839
+ expect(res).to eq( [[[7].pack("N")]] )
1840
+ res = @conn.exec("VALUES(8)", [], 1).values
1841
+ expect(res).to eq( [[[8].pack("N")]] )
1842
+ end
1843
+
1844
+ it "should forward exec_params to exec" do
1845
+ res = @conn.exec_params("VALUES(3); VALUES(4)").values
1846
+ expect(res).to eq( [["4"]] )
1847
+ res = @conn.exec_params("VALUES(3); VALUES(4)", nil).values
1848
+ expect(res).to eq( [["4"]] )
1849
+ res = @conn.exec_params("VALUES(3); VALUES(4)", nil, nil).values
1850
+ expect(res).to eq( [["4"]] )
1851
+ res = @conn.exec_params("VALUES(3); VALUES(4)", nil, 1).values
1852
+ expect(res).to eq( [["4"]] )
1853
+ res = @conn.exec_params("VALUES(3); VALUES(4)", nil, nil, nil).values
1854
+ expect(res).to eq( [["4"]] )
1855
+ expect{
1856
+ @conn.exec_params("VALUES(3); VALUES(4)", nil, nil, nil, nil).values
1857
+ }.to raise_error(ArgumentError)
1858
+ end
1859
+
1860
+ it "should forward send_query to send_query_params" do
1861
+ @conn.send_query("VALUES($1)", [5])
1862
+ expect(@conn.get_last_result.values).to eq( [["5"]] )
1863
+ end
1864
+
1865
+ it "shouldn't forward send_query_params to send_query" do
1866
+ expect{ @conn.send_query_params("VALUES(4)").values }
1867
+ .to raise_error(ArgumentError)
1868
+ expect{ @conn.send_query_params("VALUES(4)", nil).values }
1869
+ .to raise_error(TypeError)
1870
+ end
1871
+ end
1676
1872
  end