pg 0.21.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  * pg_result.c - PG::Result class extension
3
- * $Id: pg_result.c,v c4a1abc36c47 2017/06/02 01:00:09 ged $
3
+ * $Id: pg_result.c,v 7c5d9608349f 2018/01/03 16:11:52 lars $
4
4
  *
5
5
  */
6
6
 
@@ -92,12 +92,8 @@ pg_result_check( VALUE self )
92
92
  case PGRES_TUPLES_OK:
93
93
  case PGRES_COPY_OUT:
94
94
  case PGRES_COPY_IN:
95
- #ifdef HAVE_CONST_PGRES_COPY_BOTH
96
95
  case PGRES_COPY_BOTH:
97
- #endif
98
- #ifdef HAVE_CONST_PGRES_SINGLE_TUPLE
99
96
  case PGRES_SINGLE_TUPLE:
100
- #endif
101
97
  case PGRES_EMPTY_QUERY:
102
98
  case PGRES_COMMAND_OK:
103
99
  return self;
@@ -1056,7 +1052,6 @@ pgresult_type_map_get(VALUE self)
1056
1052
  return this->typemap;
1057
1053
  }
1058
1054
 
1059
- #ifdef HAVE_PQSETSINGLEROWMODE
1060
1055
  /*
1061
1056
  * call-seq:
1062
1057
  * res.stream_each{ |tuple| ... }
@@ -1087,6 +1082,8 @@ pgresult_type_map_get(VALUE self)
1087
1082
  * # do something with the received row of the second query
1088
1083
  * end
1089
1084
  * conn.get_result # => nil (no more results)
1085
+ *
1086
+ * Available since PostgreSQL-9.2
1090
1087
  */
1091
1088
  static VALUE
1092
1089
  pgresult_stream_each(VALUE self)
@@ -1150,6 +1147,8 @@ pgresult_stream_each(VALUE self)
1150
1147
  *
1151
1148
  * This method works equally to #stream_each , but yields an Array of
1152
1149
  * values.
1150
+ *
1151
+ * Available since PostgreSQL-9.2
1153
1152
  */
1154
1153
  static VALUE
1155
1154
  pgresult_stream_each_row(VALUE self)
@@ -1210,7 +1209,6 @@ pgresult_stream_each_row(VALUE self)
1210
1209
  /* never reached */
1211
1210
  return self;
1212
1211
  }
1213
- #endif
1214
1212
 
1215
1213
 
1216
1214
  void
@@ -1267,11 +1265,9 @@ init_pg_result()
1267
1265
  rb_define_method(rb_cPGresult, "type_map=", pgresult_type_map_set, 1);
1268
1266
  rb_define_method(rb_cPGresult, "type_map", pgresult_type_map_get, 0);
1269
1267
 
1270
- #ifdef HAVE_PQSETSINGLEROWMODE
1271
1268
  /****** PG::Result INSTANCE METHODS: streaming ******/
1272
1269
  rb_define_method(rb_cPGresult, "stream_each", pgresult_stream_each, 0);
1273
1270
  rb_define_method(rb_cPGresult, "stream_each_row", pgresult_stream_each_row, 0);
1274
- #endif
1275
1271
  }
1276
1272
 
1277
1273
 
data/lib/pg.rb CHANGED
@@ -35,10 +35,10 @@ end
35
35
  module PG
36
36
 
37
37
  # Library version
38
- VERSION = '0.21.0'
38
+ VERSION = '1.0.0'
39
39
 
40
40
  # VCS revision
41
- REVISION = %q$Revision: f6063a34ae2b $
41
+ REVISION = %q$Revision: fef434914848 $
42
42
 
43
43
  class NotAllCopyDataRetrieved < PG::Error
44
44
  end
@@ -70,7 +70,3 @@ module PG
70
70
  end # module PG
71
71
 
72
72
 
73
- autoload :PGError, 'pg/deprecated_constants'
74
- autoload :PGconn, 'pg/deprecated_constants'
75
- autoload :PGresult, 'pg/deprecated_constants'
76
-
@@ -47,7 +47,7 @@ class PG::Connection
47
47
 
48
48
  if args.length == 1
49
49
  case args.first
50
- when URI, URI.regexp
50
+ when URI, /\A#{URI.regexp}\z/
51
51
  uri = URI(args.first)
52
52
  options.merge!( Hash[URI.decode_www_form( uri.query )] ) if uri.query
53
53
  when /=/
@@ -85,7 +85,7 @@ class PG::Connection
85
85
 
86
86
 
87
87
  # call-seq:
88
- # conn.copy_data( sql ) {|sql_result| ... } -> PG::Result
88
+ # conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
89
89
  #
90
90
  # Execute a copy process for transfering data to or from the server.
91
91
  #
@@ -109,6 +109,11 @@ class PG::Connection
109
109
  # of blocking mode of operation, #copy_data is preferred to raw calls
110
110
  # of #put_copy_data, #get_copy_data and #put_copy_end.
111
111
  #
112
+ # _coder_ can be a PG::Coder derivation
113
+ # (typically PG::TextEncoder::CopyRow or PG::TextDecoder::CopyRow).
114
+ # This enables encoding of data fields given to #put_copy_data
115
+ # or decoding of fields received by #get_copy_data.
116
+ #
112
117
  # Example with CSV input format:
113
118
  # conn.exec "create table my_table (a text,b text,c text,d text)"
114
119
  # conn.copy_data "COPY my_table FROM STDIN CSV" do
@@ -5,7 +5,9 @@ require 'rspec'
5
5
  require 'shellwords'
6
6
  require 'pg'
7
7
 
8
- TEST_DIRECTORY = Pathname.getwd + "tmp_test_specs"
8
+ DEFAULT_TEST_DIR_STR = File.join(Dir.pwd, "tmp_test_specs")
9
+ TEST_DIR_STR = ENV['RUBY_PG_TEST_DIR'] || DEFAULT_TEST_DIR_STR
10
+ TEST_DIRECTORY = Pathname.new(TEST_DIR_STR)
9
11
 
10
12
  module PG::TestingHelpers
11
13
 
@@ -251,7 +253,7 @@ module PG::TestingHelpers
251
253
 
252
254
  def check_for_lingering_connections( conn )
253
255
  conn.exec( "SELECT * FROM pg_stat_activity" ) do |res|
254
- conns = res.find_all {|row| row['pid'].to_i != conn.backend_pid }
256
+ conns = res.find_all {|row| row['pid'].to_i != conn.backend_pid && ["client backend", nil].include?(row["backend_type"]) }
255
257
  unless conns.empty?
256
258
  puts "Lingering connections remain:"
257
259
  conns.each do |row|
@@ -339,14 +341,8 @@ RSpec.configure do |config|
339
341
  config.filter_run_excluding :socket_io unless
340
342
  PG::Connection.instance_methods.map( &:to_sym ).include?( :socket_io )
341
343
 
342
- if PG.library_version < 90200
343
- config.filter_run_excluding( :postgresql_92, :postgresql_93, :postgresql_94, :postgresql_95 )
344
- elsif PG.library_version < 90300
345
- config.filter_run_excluding( :postgresql_93, :postgresql_94, :postgresql_95 )
346
- elsif PG.library_version < 90400
347
- config.filter_run_excluding( :postgresql_94, :postgresql_95 )
348
- elsif PG.library_version < 90500
349
- config.filter_run_excluding( :postgresql_95 )
350
- end
344
+ config.filter_run_excluding( :postgresql_93 ) if PG.library_version < 90300
345
+ config.filter_run_excluding( :postgresql_94 ) if PG.library_version < 90400
346
+ config.filter_run_excluding( :postgresql_95 ) if PG.library_version < 90500
347
+ config.filter_run_excluding( :postgresql_10 ) if PG.library_version < 100000
351
348
  end
352
-
@@ -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,
@@ -219,7 +232,7 @@ describe PG::Connection do
219
232
  described_class.connect(@conninfo).finish
220
233
  sleep 0.5
221
234
  res = @conn.exec(%[SELECT COUNT(*) AS n FROM pg_stat_activity
222
- WHERE usename IS NOT NULL])
235
+ WHERE usename IS NOT NULL AND application_name != ''])
223
236
  # there's still the global @conn, but should be no more
224
237
  expect( res[0]['n'] ).to eq( '1' )
225
238
  end
@@ -524,7 +537,7 @@ describe PG::Connection do
524
537
  @conn.exec( 'UNLISTEN woo' )
525
538
  end
526
539
 
527
- 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
528
541
  notices = []
529
542
  @conn.set_notice_processor do |msg|
530
543
  notices << [msg, Time.now]
@@ -586,7 +599,7 @@ describe PG::Connection do
586
599
  expect( @conn ).to still_be_usable
587
600
  end
588
601
 
589
- 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
590
603
  @conn.exec "ROLLBACK"
591
604
  @conn.transaction do
592
605
  @conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
@@ -718,11 +731,6 @@ describe PG::Connection do
718
731
  expect( (finish - start) ).to be_within( 0.2 ).of( 0.3 )
719
732
  end
720
733
 
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
734
  it "can return the default connection options" do
727
735
  expect( described_class.conndefaults ).to be_a( Array )
728
736
  expect( described_class.conndefaults ).to all( be_a(Hash) )
@@ -779,18 +787,52 @@ describe PG::Connection do
779
787
  end
780
788
  end
781
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
782
794
 
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 )
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
794
836
  end
795
837
 
796
838
 
@@ -889,155 +931,147 @@ describe PG::Connection do
889
931
  expect{ conn.block }.to raise_error(PG::ConnectionBad, /can't get socket descriptor/)
890
932
  end
891
933
 
892
- 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' )
893
936
 
894
- before( :each ) do
895
- pending "only works with a PostgreSQL >= 9.0 server" if @conn.server_version < 9_00_00
896
- 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
897
942
 
898
- 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"
899
947
  conn_string = PG::Connection.parse_connect_args( 'dbname=test' )
900
-
901
948
  conn_name = conn_string[ /application_name='(.*?)'/, 1 ]
902
949
  expect( conn_name ).to include( $0[0..10] )
903
950
  expect( conn_name ).to include( $0[-10..-1] )
904
951
  expect( conn_name.length ).to be <= 64
952
+ ensure
953
+ $0 = old_0
905
954
  end
955
+ end
906
956
 
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' )
957
+ it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
958
+ "any number of arguments" do
926
959
 
927
- conn = described_class.connect( @conninfo )
928
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
929
- conn.finish
960
+ @conn.exec( 'ROLLBACK' )
961
+ @conn.exec( 'LISTEN knees' )
930
962
 
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' )
963
+ conn = described_class.connect( @conninfo )
964
+ conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
965
+ conn.finish
936
966
 
937
- expect( event ).to eq( 'knees' )
938
- expect( pid ).to be_a_kind_of( Integer )
939
- 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
940
970
  end
971
+ @conn.exec( 'UNLISTEN knees' )
941
972
 
942
- it "accepts nil as the timeout in #wait_for_notify " do
943
- @conn.exec( 'ROLLBACK' )
944
- @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
945
977
 
946
- conn = described_class.connect( @conninfo )
947
- conn.exec( %Q{NOTIFY knees} )
948
- conn.finish
978
+ it "accepts nil as the timeout in #wait_for_notify " do
979
+ @conn.exec( 'ROLLBACK' )
980
+ @conn.exec( 'LISTEN knees' )
949
981
 
950
- event, pid = nil
951
- @conn.wait_for_notify( nil ) do |*args|
952
- event, pid = *args
953
- end
954
- @conn.exec( 'UNLISTEN knees' )
982
+ conn = described_class.connect( @conninfo )
983
+ conn.exec( %Q{NOTIFY knees} )
984
+ conn.finish
955
985
 
956
- expect( event ).to eq( 'knees' )
957
- expect( pid ).to be_a_kind_of( Integer )
986
+ event, pid = nil
987
+ @conn.wait_for_notify( nil ) do |*args|
988
+ event, pid = *args
958
989
  end
990
+ @conn.exec( 'UNLISTEN knees' )
959
991
 
960
- it "sends nil as the payload if the notification wasn't given one" do
961
- @conn.exec( 'ROLLBACK' )
962
- @conn.exec( 'LISTEN knees' )
992
+ expect( event ).to eq( 'knees' )
993
+ expect( pid ).to be_a_kind_of( Integer )
994
+ end
963
995
 
964
- conn = described_class.connect( @conninfo )
965
- conn.exec( %Q{NOTIFY knees} )
966
- 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' )
967
999
 
968
- payload = :notnil
969
- @conn.wait_for_notify( nil ) do |*args|
970
- payload = args[ 2 ]
971
- end
972
- @conn.exec( 'UNLISTEN knees' )
1000
+ conn = described_class.connect( @conninfo )
1001
+ conn.exec( %Q{NOTIFY knees} )
1002
+ conn.finish
973
1003
 
974
- expect( payload ).to be_nil()
1004
+ payload = :notnil
1005
+ @conn.wait_for_notify( nil ) do |*args|
1006
+ payload = args[ 2 ]
975
1007
  end
1008
+ @conn.exec( 'UNLISTEN knees' )
976
1009
 
977
- it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
978
- "two arguments" do
1010
+ expect( payload ).to be_nil()
1011
+ end
979
1012
 
980
- @conn.exec( 'ROLLBACK' )
981
- @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
982
1015
 
983
- conn = described_class.connect( @conninfo )
984
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
985
- conn.finish
1016
+ @conn.exec( 'ROLLBACK' )
1017
+ @conn.exec( 'LISTEN knees' )
986
1018
 
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' )
1019
+ conn = described_class.connect( @conninfo )
1020
+ conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
1021
+ conn.finish
992
1022
 
993
- expect( event ).to eq( 'knees' )
994
- expect( pid ).to be_a_kind_of( Integer )
995
- 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
996
1026
  end
1027
+ @conn.exec( 'UNLISTEN knees' )
997
1028
 
998
- it "calls the block supplied to wait_for_notify with the notify payload if it " +
999
- "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
1000
1033
 
1001
- @conn.exec( 'ROLLBACK' )
1002
- @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
1003
1036
 
1004
- conn = described_class.connect( @conninfo )
1005
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
1006
- conn.finish
1037
+ @conn.exec( 'ROLLBACK' )
1038
+ @conn.exec( 'LISTEN knees' )
1007
1039
 
1008
- notification_received = false
1009
- @conn.wait_for_notify( 10 ) do
1010
- notification_received = true
1011
- end
1012
- @conn.exec( 'UNLISTEN knees' )
1040
+ conn = described_class.connect( @conninfo )
1041
+ conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
1042
+ conn.finish
1013
1043
 
1014
- expect( notification_received ).to be_truthy()
1044
+ notification_received = false
1045
+ @conn.wait_for_notify( 10 ) do
1046
+ notification_received = true
1015
1047
  end
1048
+ @conn.exec( 'UNLISTEN knees' )
1016
1049
 
1017
- it "calls the block supplied to wait_for_notify with the notify payload if it accepts " +
1018
- "three arguments" do
1050
+ expect( notification_received ).to be_truthy()
1051
+ end
1019
1052
 
1020
- @conn.exec( 'ROLLBACK' )
1021
- @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
1022
1055
 
1023
- conn = described_class.connect( @conninfo )
1024
- conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
1025
- conn.finish
1056
+ @conn.exec( 'ROLLBACK' )
1057
+ @conn.exec( 'LISTEN knees' )
1026
1058
 
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' )
1059
+ conn = described_class.connect( @conninfo )
1060
+ conn.exec( %Q{NOTIFY knees, 'skirt and boots'} )
1061
+ conn.finish
1032
1062
 
1033
- expect( event ).to eq( 'knees' )
1034
- expect( pid ).to be_a_kind_of( Integer )
1035
- 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
1036
1066
  end
1067
+ @conn.exec( 'UNLISTEN knees' )
1037
1068
 
1069
+ expect( event ).to eq( 'knees' )
1070
+ expect( pid ).to be_a_kind_of( Integer )
1071
+ expect( msg ).to eq( 'skirt and boots' )
1038
1072
  end
1039
1073
 
1040
- context "under PostgreSQL 9.1 client library", :postgresql_91, :without_transaction do
1074
+ context "server ping", :without_transaction do
1041
1075
 
1042
1076
  it "pings successfully with connection string" do
1043
1077
  ping = described_class.ping(@conninfo)
@@ -1070,71 +1104,69 @@ describe PG::Connection do
1070
1104
  expect( ping ).to eq( PG::PQPING_NO_ATTEMPT )
1071
1105
  end
1072
1106
 
1073
-
1074
1107
  end
1075
1108
 
1076
- context "under PostgreSQL 9.2 client library", :postgresql_92 do
1077
- describe "set_single_row_mode" do
1109
+ describe "set_single_row_mode" do
1078
1110
 
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)
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
1083
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
1133
+ end
1134
+ expect( results.last.result_status ).to eq( PG::PGRES_TUPLES_OK )
1135
+ expect( results.last.ntuples ).to eq( 0 )
1136
+ end
1084
1137
 
1085
- it "should work in single row mode" do
1086
- @conn.send_query( "SELECT generate_series(1,10)" )
1087
- @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
1088
1141
 
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 )
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
1104
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
1105
1152
 
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
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
1109
1157
 
1110
- start_time = Time.now
1111
- first_row_time = nil
1158
+ first_result = nil
1159
+ expect do
1112
1160
  loop do
1113
1161
  res = @conn.get_result or break
1114
1162
  res.check
1115
- first_row_time = Time.now unless first_row_time
1163
+ first_result ||= res
1116
1164
  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
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 )
1137
1168
  end
1169
+
1138
1170
  end
1139
1171
 
1140
1172
  context "multinationalization support", :ruby_19 do
@@ -1201,7 +1233,7 @@ describe PG::Connection do
1201
1233
  expect( escaped ).to eq( "Möhre to".encode(Encoding::EUC_JP) )
1202
1234
  end
1203
1235
 
1204
- it "uses the client encoding for escaped literal", :postgresql_90 do
1236
+ it "uses the client encoding for escaped literal" do
1205
1237
  original = "Möhre to\0 escape".encode( "utf-16be" )
1206
1238
  @conn.set_client_encoding( "euc_jp" )
1207
1239
  escaped = @conn.escape_literal( original )
@@ -1209,7 +1241,7 @@ describe PG::Connection do
1209
1241
  expect( escaped ).to eq( "'Möhre to'".encode(Encoding::EUC_JP) )
1210
1242
  end
1211
1243
 
1212
- it "uses the client encoding for escaped identifier", :postgresql_90 do
1244
+ it "uses the client encoding for escaped identifier" do
1213
1245
  original = "Möhre to\0 escape".encode( "utf-16le" )
1214
1246
  @conn.set_client_encoding( "euc_jp" )
1215
1247
  escaped = @conn.escape_identifier( original )
@@ -1240,6 +1272,12 @@ describe PG::Connection do
1240
1272
  expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
1241
1273
  expect( escaped.encode ).to eq( "\"Möhre to\"".encode(Encoding::ISO8859_1) )
1242
1274
  end
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
1243
1281
  end
1244
1282
 
1245
1283
  describe "respect and convert character encoding of input strings" do
@@ -1408,7 +1446,7 @@ describe PG::Connection do
1408
1446
  conn.finish if conn
1409
1447
  end
1410
1448
 
1411
- 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
1412
1450
  r = nil
1413
1451
  @conn.set_notice_receiver do |result|
1414
1452
  r = result
@@ -1423,7 +1461,7 @@ describe PG::Connection do
1423
1461
  @conn.set_notice_receiver
1424
1462
  end
1425
1463
 
1426
- it "receives properly encoded messages in the notice callbacks", :postgresql_90 do
1464
+ it "receives properly encoded messages in the notice callbacks" do
1427
1465
  [:receiver, :processor].each do |kind|
1428
1466
  notices = []
1429
1467
  @conn.internal_encoding = 'utf-8'
@@ -1451,7 +1489,7 @@ describe PG::Connection do
1451
1489
  end
1452
1490
  end
1453
1491
 
1454
- it "receives properly encoded text from wait_for_notify", :postgresql_90 do
1492
+ it "receives properly encoded text from wait_for_notify" do
1455
1493
  @conn.internal_encoding = 'utf-8'
1456
1494
  @conn.exec( 'ROLLBACK' )
1457
1495
  @conn.exec( 'LISTEN "Möhre"' )
@@ -1468,7 +1506,7 @@ describe PG::Connection do
1468
1506
  expect( msg.encoding ).to eq( Encoding::UTF_8 )
1469
1507
  end
1470
1508
 
1471
- it "returns properly encoded text from notifies", :postgresql_90 do
1509
+ it "returns properly encoded text from notifies" do
1472
1510
  @conn.internal_encoding = 'utf-8'
1473
1511
  @conn.exec( 'ROLLBACK' )
1474
1512
  @conn.exec( 'LISTEN "Möhre"' )
@@ -1524,9 +1562,14 @@ describe PG::Connection do
1524
1562
  end
1525
1563
 
1526
1564
  it "shouldn't type map params unless requested" do
1527
- expect{
1528
- @conn.exec_params( "SELECT $1", [5] )
1529
- }.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
1530
1573
  end
1531
1574
 
1532
1575
  it "should raise an error on invalid encoder to put_copy_data" do