pg 0.18.0 → 1.0.0

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