pg 0.18.4 → 0.21.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 (57) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/BSDL +2 -2
  4. data/ChangeLog +689 -5
  5. data/History.rdoc +56 -0
  6. data/Manifest.txt +1 -18
  7. data/README.rdoc +13 -9
  8. data/Rakefile +15 -17
  9. data/Rakefile.cross +8 -7
  10. data/ext/errorcodes.def +25 -0
  11. data/ext/errorcodes.txt +13 -1
  12. data/ext/extconf.rb +9 -1
  13. data/ext/gvl_wrappers.h +4 -0
  14. data/ext/pg.c +4 -3
  15. data/ext/pg.h +6 -3
  16. data/ext/pg_binary_encoder.c +8 -8
  17. data/ext/pg_coder.c +31 -10
  18. data/ext/pg_connection.c +252 -98
  19. data/ext/pg_copy_coder.c +34 -4
  20. data/ext/pg_result.c +20 -14
  21. data/ext/pg_text_encoder.c +62 -42
  22. data/ext/pg_type_map.c +14 -7
  23. data/lib/pg/basic_type_mapping.rb +35 -8
  24. data/lib/pg/connection.rb +46 -10
  25. data/lib/pg/deprecated_constants.rb +21 -0
  26. data/lib/pg/result.rb +10 -5
  27. data/lib/pg/text_decoder.rb +7 -0
  28. data/lib/pg/text_encoder.rb +8 -0
  29. data/lib/pg.rb +21 -9
  30. data/spec/helpers.rb +6 -9
  31. data/spec/pg/basic_type_mapping_spec.rb +54 -0
  32. data/spec/pg/connection_spec.rb +158 -26
  33. data/spec/pg/result_spec.rb +11 -4
  34. data/spec/pg/type_map_by_class_spec.rb +2 -2
  35. data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
  36. data/spec/pg/type_spec.rb +82 -2
  37. data.tar.gz.sig +0 -0
  38. metadata +50 -64
  39. metadata.gz.sig +0 -0
  40. data/sample/array_insert.rb +0 -20
  41. data/sample/async_api.rb +0 -106
  42. data/sample/async_copyto.rb +0 -39
  43. data/sample/async_mixed.rb +0 -56
  44. data/sample/check_conn.rb +0 -21
  45. data/sample/copyfrom.rb +0 -81
  46. data/sample/copyto.rb +0 -19
  47. data/sample/cursor.rb +0 -21
  48. data/sample/disk_usage_report.rb +0 -186
  49. data/sample/issue-119.rb +0 -94
  50. data/sample/losample.rb +0 -69
  51. data/sample/minimal-testcase.rb +0 -17
  52. data/sample/notify_wait.rb +0 -72
  53. data/sample/pg_statistics.rb +0 -294
  54. data/sample/replication_monitor.rb +0 -231
  55. data/sample/test_binary_values.rb +0 -33
  56. data/sample/wal_shipper.rb +0 -434
  57. data/sample/warehouse_partitions.rb +0 -320
data/lib/pg/connection.rb CHANGED
@@ -110,12 +110,20 @@ class PG::Connection
110
110
  # of #put_copy_data, #get_copy_data and #put_copy_end.
111
111
  #
112
112
  # Example with CSV input format:
113
- # conn.exec "create table my_table (a text,b text,c text,d text,e text)"
113
+ # conn.exec "create table my_table (a text,b text,c text,d text)"
114
114
  # conn.copy_data "COPY my_table FROM STDIN CSV" do
115
- # conn.put_copy_data "some,csv,data,to,copy\n"
116
- # conn.put_copy_data "more,csv,data,to,copy\n"
115
+ # conn.put_copy_data "some,data,to,copy\n"
116
+ # conn.put_copy_data "more,data,to,copy\n"
117
+ # end
118
+ # This creates +my_table+ and inserts two CSV rows.
119
+ #
120
+ # The same with text format encoder PG::TextEncoder::CopyRow
121
+ # and Array input:
122
+ # enco = PG::TextEncoder::CopyRow.new
123
+ # conn.copy_data "COPY my_table FROM STDIN", enco do
124
+ # conn.put_copy_data ['some', 'data', 'to', 'copy']
125
+ # conn.put_copy_data ['more', 'data', 'to', 'copy']
117
126
  # end
118
- # This creates +my_table+ and inserts two rows.
119
127
  #
120
128
  # Example with CSV output format:
121
129
  # conn.copy_data "COPY my_table TO STDOUT CSV" do
@@ -124,8 +132,21 @@ class PG::Connection
124
132
  # end
125
133
  # end
126
134
  # This prints all rows of +my_table+ to stdout:
127
- # "some,csv,data,to,copy\n"
128
- # "more,csv,data,to,copy\n"
135
+ # "some,data,to,copy\n"
136
+ # "more,data,to,copy\n"
137
+ #
138
+ # The same with text format decoder PG::TextDecoder::CopyRow
139
+ # and Array output:
140
+ # deco = PG::TextDecoder::CopyRow.new
141
+ # conn.copy_data "COPY my_table TO STDOUT", deco do
142
+ # while row=conn.get_copy_data
143
+ # p row
144
+ # end
145
+ # end
146
+ # This receives all rows of +my_table+ as ruby array:
147
+ # ["some", "data", "to", "copy"]
148
+ # ["more", "data", "to", "copy"]
149
+
129
150
  def copy_data( sql, coder=nil )
130
151
  res = exec( sql )
131
152
 
@@ -165,7 +186,7 @@ class PG::Connection
165
186
  raise
166
187
  else
167
188
  res = get_last_result
168
- if res.result_status != PGRES_COMMAND_OK
189
+ if !res || res.result_status != PGRES_COMMAND_OK
169
190
  while get_copy_data
170
191
  end
171
192
  while get_result
@@ -224,8 +245,23 @@ class PG::Connection
224
245
  end
225
246
  end
226
247
 
227
- end # class PG::Connection
248
+ # Method 'ssl_attribute' was introduced in PostgreSQL 9.5.
249
+ if self.instance_methods.find{|m| m.to_sym == :ssl_attribute }
250
+ # call-seq:
251
+ # conn.ssl_attributes -> Hash<String,String>
252
+ #
253
+ # Returns SSL-related information about the connection as key/value pairs
254
+ #
255
+ # The available attributes varies depending on the SSL library being used,
256
+ # and the type of connection.
257
+ #
258
+ # See also #ssl_attribute
259
+ def ssl_attributes
260
+ ssl_attribute_names.each.with_object({}) do |n,h|
261
+ h[n] = ssl_attribute(n)
262
+ end
263
+ end
264
+ end
228
265
 
229
- # Backward-compatible alias
230
- PGconn = PG::Connection
266
+ end # class PG::Connection
231
267
 
@@ -0,0 +1,21 @@
1
+ # -*- ruby -*-
2
+ #encoding: utf-8
3
+
4
+ # Warn about use of deprecated constants when this is autoloaded
5
+ callsite = caller(3).first
6
+
7
+ warn <<END_OF_WARNING
8
+ The PGconn, PGresult, and PGError constants are deprecated, and will be
9
+ removed as of version 1.0.
10
+
11
+ You should use PG::Connection, PG::Result, and PG::Error instead, respectively.
12
+
13
+ Called from #{callsite}
14
+ END_OF_WARNING
15
+
16
+
17
+
18
+ PGconn = PG::Connection
19
+ PGresult = PG::Result
20
+ PGError = PG::Error
21
+
data/lib/pg/result.rb CHANGED
@@ -12,15 +12,20 @@ class PG::Result
12
12
  # See PG::BasicTypeMapForResults
13
13
  def map_types!(type_map)
14
14
  self.type_map = type_map
15
- self
15
+ return self
16
16
  end
17
17
 
18
+
19
+ ### Return a String representation of the object suitable for debugging.
18
20
  def inspect
19
21
  str = self.to_s
20
- str[-1,0] = " status=#{res_status(result_status)} ntuples=#{ntuples} nfields=#{nfields} cmd_tuples=#{cmd_tuples}"
21
- str
22
+ str[-1,0] = if cleared?
23
+ " cleared"
24
+ else
25
+ " status=#{res_status(result_status)} ntuples=#{ntuples} nfields=#{nfields} cmd_tuples=#{cmd_tuples}"
26
+ end
27
+ return str
22
28
  end
29
+
23
30
  end # class PG::Result
24
31
 
25
- # Backward-compatible alias
26
- PGresult = PG::Result
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'date'
4
+ require 'json'
4
5
 
5
6
  module PG
6
7
  module TextDecoder
@@ -39,6 +40,12 @@ module PG
39
40
  end
40
41
  end
41
42
  end
43
+
44
+ class JSON < SimpleDecoder
45
+ def decode(string, tuple=nil, field=nil)
46
+ ::JSON.parse(string, quirks_mode: true)
47
+ end
48
+ end
42
49
  end
43
50
  end # module PG
44
51
 
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'json'
4
+
3
5
  module PG
4
6
  module TextEncoder
5
7
  class Date < SimpleEncoder
@@ -22,6 +24,12 @@ module PG
22
24
  value.respond_to?(:strftime) ? value.strftime(STRFTIME_ISO_DATETIME_WITH_TIMEZONE) : value
23
25
  end
24
26
  end
27
+
28
+ class JSON < SimpleEncoder
29
+ def encode(value)
30
+ ::JSON.generate(value, quirks_mode: true)
31
+ end
32
+ end
25
33
  end
26
34
  end # module PG
27
35
 
data/lib/pg.rb CHANGED
@@ -8,11 +8,22 @@ rescue LoadError
8
8
  major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
9
9
  raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
10
10
 
11
- # Set the PATH environment variable, so that libpq.dll can be found.
12
- old_path = ENV['PATH']
13
- ENV['PATH'] = "#{File.expand_path("../#{RUBY_PLATFORM}", __FILE__)};#{old_path}"
14
- require "#{major_minor}/pg_ext"
15
- ENV['PATH'] = old_path
11
+ add_dll_path = proc do |path, &block|
12
+ begin
13
+ require 'ruby_installer/runtime'
14
+ RubyInstaller::Runtime.add_dll_directory(path, &block)
15
+ rescue LoadError
16
+ old_path = ENV['PATH']
17
+ ENV['PATH'] = "#{path};#{old_path}"
18
+ block.call
19
+ ENV['PATH'] = old_path
20
+ end
21
+ end
22
+
23
+ # Temporary add this directory for DLL search, so that libpq.dll can be found.
24
+ add_dll_path.call(__dir__) do
25
+ require "#{major_minor}/pg_ext"
26
+ end
16
27
  else
17
28
  raise
18
29
  end
@@ -24,10 +35,10 @@ end
24
35
  module PG
25
36
 
26
37
  # Library version
27
- VERSION = '0.18.4'
38
+ VERSION = '0.21.0'
28
39
 
29
40
  # VCS revision
30
- REVISION = %q$Revision: da42b972b5ab $
41
+ REVISION = %q$Revision: f6063a34ae2b $
31
42
 
32
43
  class NotAllCopyDataRetrieved < PG::Error
33
44
  end
@@ -59,6 +70,7 @@ module PG
59
70
  end # module PG
60
71
 
61
72
 
62
- # Backward-compatible aliase
63
- PGError = PG::Error
73
+ autoload :PGError, 'pg/deprecated_constants'
74
+ autoload :PGconn, 'pg/deprecated_constants'
75
+ autoload :PGresult, 'pg/deprecated_constants'
64
76
 
data/spec/helpers.rb CHANGED
@@ -339,17 +339,14 @@ RSpec.configure do |config|
339
339
  config.filter_run_excluding :socket_io unless
340
340
  PG::Connection.instance_methods.map( &:to_sym ).include?( :socket_io )
341
341
 
342
- config.filter_run_excluding :postgresql_90 unless
343
- PG::Connection.instance_methods.map( &:to_sym ).include?( :escape_literal )
344
-
345
- if !PG.respond_to?( :library_version )
346
- config.filter_run_excluding( :postgresql_91, :postgresql_92, :postgresql_93, :postgresql_94 )
347
- elsif PG.library_version < 90200
348
- config.filter_run_excluding( :postgresql_92, :postgresql_93, :postgresql_94 )
342
+ if PG.library_version < 90200
343
+ config.filter_run_excluding( :postgresql_92, :postgresql_93, :postgresql_94, :postgresql_95 )
349
344
  elsif PG.library_version < 90300
350
- config.filter_run_excluding( :postgresql_93, :postgresql_94 )
345
+ config.filter_run_excluding( :postgresql_93, :postgresql_94, :postgresql_95 )
351
346
  elsif PG.library_version < 90400
352
- config.filter_run_excluding( :postgresql_94 )
347
+ config.filter_run_excluding( :postgresql_94, :postgresql_95 )
348
+ elsif PG.library_version < 90500
349
+ config.filter_run_excluding( :postgresql_95 )
353
350
  end
354
351
  end
355
352
 
@@ -166,6 +166,27 @@ describe 'Basic type mapping' do
166
166
  end
167
167
  end
168
168
 
169
+ it "should do JSON conversions", :postgresql_94 do
170
+ [0].each do |format|
171
+ ['JSON', 'JSONB'].each do |type|
172
+ res = @conn.exec( "SELECT CAST('123' AS #{type}),
173
+ CAST('12.3' AS #{type}),
174
+ CAST('true' AS #{type}),
175
+ CAST('false' AS #{type}),
176
+ CAST('null' AS #{type}),
177
+ CAST('[1, \"a\", null]' AS #{type}),
178
+ CAST('{\"b\" : [2,3]}' AS #{type})", [], format )
179
+ expect( res.getvalue(0,0) ).to eq( 123 )
180
+ expect( res.getvalue(0,1) ).to be_within(0.1).of( 12.3 )
181
+ expect( res.getvalue(0,2) ).to eq( true )
182
+ expect( res.getvalue(0,3) ).to eq( false )
183
+ expect( res.getvalue(0,4) ).to eq( nil )
184
+ expect( res.getvalue(0,5) ).to eq( [1, "a", nil] )
185
+ expect( res.getvalue(0,6) ).to eq( {"b" => [2, 3]} )
186
+ end
187
+ end
188
+ end
189
+
169
190
  it "should do array type conversions" do
170
191
  [0].each do |format|
171
192
  res = @conn.exec( "SELECT CAST('{1,2,3}' AS INT2[]), CAST('{{1,2},{3,4}}' AS INT2[][]),
@@ -228,6 +249,39 @@ describe 'Basic type mapping' do
228
249
  res = @conn.exec( "SELECT * FROM copytable" )
229
250
  expect( res.values ).to eq( [['a', '123', '{5,4,3}'], ['b', '234', '{2,3}']] )
230
251
  end
252
+
253
+ it "can do JSON conversions", :postgresql_94 do
254
+ ['JSON', 'JSONB'].each do |type|
255
+ sql = "SELECT CAST('123' AS #{type}),
256
+ CAST('12.3' AS #{type}),
257
+ CAST('true' AS #{type}),
258
+ CAST('false' AS #{type}),
259
+ CAST('null' AS #{type}),
260
+ CAST('[1, \"a\", null]' AS #{type}),
261
+ CAST('{\"b\" : [2,3]}' AS #{type})"
262
+
263
+ tm = basic_type_mapping.build_column_map( @conn.exec( sql ) )
264
+ expect( tm.coders.map(&:name) ).to eq( [type.downcase] * 7 )
265
+
266
+ res = @conn.exec_params( "SELECT $1, $2, $3, $4, $5, $6, $7",
267
+ [ 123,
268
+ 12.3,
269
+ true,
270
+ false,
271
+ nil,
272
+ [1, "a", nil],
273
+ {"b" => [2, 3]},
274
+ ], 0, tm )
275
+
276
+ expect( res.getvalue(0,0) ).to eq( "123" )
277
+ expect( res.getvalue(0,1) ).to eq( "12.3" )
278
+ expect( res.getvalue(0,2) ).to eq( "true" )
279
+ expect( res.getvalue(0,3) ).to eq( "false" )
280
+ expect( res.getvalue(0,4) ).to eq( nil )
281
+ expect( res.getvalue(0,5).gsub(" ","") ).to eq( "[1,\"a\",null]" )
282
+ expect( res.getvalue(0,6).gsub(" ","") ).to eq( "{\"b\":[2,3]}" )
283
+ end
284
+ end
231
285
  end
232
286
 
233
287
  context "with usage of result oids for copy encoder selection" do
@@ -643,6 +643,31 @@ describe PG::Connection do
643
643
  expect( @conn ).to still_be_usable
644
644
  end
645
645
 
646
+ it "gracefully handle SQL statements while in #copy_data for input" do
647
+ @conn.exec "ROLLBACK"
648
+ @conn.transaction do
649
+ @conn.exec( "CREATE TEMP TABLE copytable (col1 INT)" )
650
+ expect {
651
+ @conn.copy_data( "COPY copytable FROM STDOUT" ) do |res|
652
+ @conn.exec "SELECT 1"
653
+ end
654
+ }.to raise_error(PG::Error, /no COPY in progress/)
655
+ end
656
+ expect( @conn ).to still_be_usable
657
+ end
658
+
659
+ it "gracefully handle SQL statements while in #copy_data for output" do
660
+ @conn.exec "ROLLBACK"
661
+ @conn.transaction do
662
+ expect {
663
+ @conn.copy_data( "COPY (VALUES(1), (2)) TO STDOUT" ) do |res|
664
+ @conn.exec "SELECT 3"
665
+ end
666
+ }.to raise_error(PG::Error, /no COPY in progress/)
667
+ end
668
+ expect( @conn ).to still_be_usable
669
+ end
670
+
646
671
  it "should raise an error for non copy statements in #copy_data" do
647
672
  expect {
648
673
  @conn.copy_data( "SELECT 1" ){}
@@ -684,13 +709,13 @@ describe PG::Connection do
684
709
  end
685
710
 
686
711
  it "described_class#block should allow a timeout" do
687
- @conn.send_query( "select pg_sleep(3)" )
712
+ @conn.send_query( "select pg_sleep(1)" )
688
713
 
689
714
  start = Time.now
690
- @conn.block( 0.1 )
715
+ @conn.block( 0.3 )
691
716
  finish = Time.now
692
717
 
693
- expect( (finish - start) ).to be_within( 0.05 ).of( 0.1 )
718
+ expect( (finish - start) ).to be_within( 0.2 ).of( 0.3 )
694
719
  end
695
720
 
696
721
 
@@ -725,6 +750,25 @@ describe PG::Connection do
725
750
  expect( @conn.conninfo_hash[:dbname] ).to eq( 'test' )
726
751
  end
727
752
 
753
+ describe "connection information related to SSL" do
754
+
755
+ it "can retrieve connection's ssl state", :postgresql_95 do
756
+ expect( @conn.ssl_in_use? ).to be false
757
+ end
758
+
759
+ it "can retrieve connection's ssl attribute_names", :postgresql_95 do
760
+ expect( @conn.ssl_attribute_names ).to be_a(Array)
761
+ end
762
+
763
+ it "can retrieve a single ssl connection attribute", :postgresql_95 do
764
+ expect( @conn.ssl_attribute('dbname') ).to eq( nil )
765
+ end
766
+
767
+ it "can retrieve all connection's ssl attributes", :postgresql_95 do
768
+ expect( @conn.ssl_attributes ).to be_a_kind_of( Hash )
769
+ end
770
+ end
771
+
728
772
 
729
773
  it "honors the connect_timeout connection parameter", :postgresql_93 do
730
774
  conn = PG.connect( port: @port, dbname: 'test', connect_timeout: 11 )
@@ -1070,8 +1114,8 @@ describe PG::Connection do
1070
1114
  res.check
1071
1115
  first_row_time = Time.now unless first_row_time
1072
1116
  end
1073
- expect( (Time.now - start_time) ).to be >= 1.0
1074
- expect( (first_row_time - start_time) ).to be < 1.0
1117
+ expect( (Time.now - start_time) ).to be >= 0.9
1118
+ expect( (first_row_time - start_time) ).to be < 0.9
1075
1119
  end
1076
1120
 
1077
1121
  it "should receive rows before entire query fails" do
@@ -1150,51 +1194,137 @@ describe PG::Connection do
1150
1194
  end
1151
1195
 
1152
1196
  it "uses the client encoding for escaped string" do
1153
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1197
+ original = "Möhre to\0 escape".encode( "utf-16be" )
1154
1198
  @conn.set_client_encoding( "euc_jp" )
1155
1199
  escaped = @conn.escape( original )
1156
1200
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1157
- expect( escaped ).to eq( "string to" )
1201
+ expect( escaped ).to eq( "Möhre to".encode(Encoding::EUC_JP) )
1158
1202
  end
1159
1203
 
1160
1204
  it "uses the client encoding for escaped literal", :postgresql_90 do
1161
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1205
+ original = "Möhre to\0 escape".encode( "utf-16be" )
1162
1206
  @conn.set_client_encoding( "euc_jp" )
1163
1207
  escaped = @conn.escape_literal( original )
1164
1208
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1165
- expect( escaped ).to eq( "'string to'" )
1209
+ expect( escaped ).to eq( "'Möhre to'".encode(Encoding::EUC_JP) )
1166
1210
  end
1167
1211
 
1168
1212
  it "uses the client encoding for escaped identifier", :postgresql_90 do
1169
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1213
+ original = "Möhre to\0 escape".encode( "utf-16le" )
1170
1214
  @conn.set_client_encoding( "euc_jp" )
1171
1215
  escaped = @conn.escape_identifier( original )
1172
1216
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1173
- expect( escaped ).to eq( "\"string to\"" )
1217
+ expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
1174
1218
  end
1175
1219
 
1176
1220
  it "uses the client encoding for quote_ident" do
1177
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1221
+ original = "Möhre to\0 escape".encode( "utf-16le" )
1178
1222
  @conn.set_client_encoding( "euc_jp" )
1179
1223
  escaped = @conn.quote_ident( original )
1180
1224
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1181
- expect( escaped ).to eq( "\"string to\"" )
1225
+ expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
1182
1226
  end
1183
1227
 
1184
1228
  it "uses the previous string encoding for escaped string" do
1185
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1229
+ original = "Möhre to\0 escape".encode( "iso-8859-1" )
1186
1230
  @conn.set_client_encoding( "euc_jp" )
1187
1231
  escaped = described_class.escape( original )
1188
1232
  expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
1189
- expect( escaped ).to eq( "string to" )
1233
+ expect( escaped ).to eq( "Möhre to".encode(Encoding::ISO8859_1) )
1190
1234
  end
1191
1235
 
1192
1236
  it "uses the previous string encoding for quote_ident" do
1193
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1237
+ original = "Möhre to\0 escape".encode( "iso-8859-1" )
1194
1238
  @conn.set_client_encoding( "euc_jp" )
1195
1239
  escaped = described_class.quote_ident( original )
1196
1240
  expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
1197
- expect( escaped ).to eq( "\"string to\"" )
1241
+ expect( escaped.encode ).to eq( "\"Möhre to\"".encode(Encoding::ISO8859_1) )
1242
+ end
1243
+ end
1244
+
1245
+ describe "respect and convert character encoding of input strings" do
1246
+ before :each do
1247
+ @conn.internal_encoding = __ENCODING__
1248
+ end
1249
+
1250
+ it "should convert query string and parameters to #exec_params" do
1251
+ r = @conn.exec_params("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
1252
+ ['grün'.encode('utf-16be'), 'grün'.encode('iso-8859-1')])
1253
+ expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1254
+ end
1255
+
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
+ it "should convert query string to #exec" do
1263
+ r = @conn.exec("SELECT 'grün'".encode("utf-16be"))
1264
+ expect( r.values ).to eq( [['grün']] )
1265
+ end
1266
+
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
+ it "should convert strings and parameters to #prepare and #exec_prepared" do
1273
+ @conn.prepare("weiß1".encode("utf-16be"), "VALUES( $1, $2, $1=$2, 'grün')".encode("cp850"))
1274
+ r = @conn.exec_prepared("weiß1".encode("utf-32le"),
1275
+ ['grün'.encode('cp936'), 'grün'.encode('utf-16le')])
1276
+ expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1277
+ end
1278
+
1279
+ it "should convert strings to #describe_prepared" do
1280
+ @conn.prepare("weiß2", "VALUES(123)")
1281
+ r = @conn.describe_prepared("weiß2".encode("utf-16be"))
1282
+ expect( r.nfields ).to eq( 1 )
1283
+ end
1284
+
1285
+ it "should convert strings to #describe_portal" do
1286
+ @conn.exec "DECLARE cörsör CURSOR FOR VALUES(1,2,3)"
1287
+ r = @conn.describe_portal("cörsör".encode("utf-16le"))
1288
+ expect( r.nfields ).to eq( 3 )
1289
+ end
1290
+
1291
+ it "should convert query string to #send_query" do
1292
+ @conn.send_query("VALUES('grün')".encode("utf-16be"))
1293
+ expect( @conn.get_last_result.values ).to eq( [['grün']] )
1294
+ end
1295
+
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"),
1298
+ ['grün'.encode('utf-32be'), 'grün'.encode('iso-8859-1')])
1299
+ expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1300
+ end
1301
+
1302
+ it "should convert strings and parameters to #send_prepare and #send_query_prepared" do
1303
+ @conn.send_prepare("weiß3".encode("iso-8859-1"), "VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16be"))
1304
+ @conn.get_last_result
1305
+ @conn.send_query_prepared("weiß3".encode("utf-32le"),
1306
+ ['grün'.encode('utf-16le'), 'grün'.encode('iso-8859-1')])
1307
+ expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1308
+ end
1309
+
1310
+ it "should convert strings to #send_describe_prepared" do
1311
+ @conn.prepare("weiß4", "VALUES(123)")
1312
+ @conn.send_describe_prepared("weiß4".encode("utf-16be"))
1313
+ expect( @conn.get_last_result.nfields ).to eq( 1 )
1314
+ end
1315
+
1316
+ it "should convert strings to #send_describe_portal" do
1317
+ @conn.exec "DECLARE cörsör CURSOR FOR VALUES(1,2,3)"
1318
+ @conn.send_describe_portal("cörsör".encode("utf-16le"))
1319
+ expect( @conn.get_last_result.nfields ).to eq( 3 )
1320
+ end
1321
+
1322
+ it "should convert error string to #put_copy_end" do
1323
+ @conn.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
1324
+ @conn.exec( "COPY copytable FROM STDIN" )
1325
+ @conn.put_copy_end("grün".encode("utf-16be"))
1326
+ expect( @conn.get_result.error_message ).to match(/grün/)
1327
+ @conn.get_result
1198
1328
  end
1199
1329
  end
1200
1330
 
@@ -1228,12 +1358,12 @@ describe PG::Connection do
1228
1358
 
1229
1359
  begin
1230
1360
  prev_encoding = Encoding.default_internal
1231
- Encoding.default_internal = Encoding::UTF_8
1361
+ Encoding.default_internal = Encoding::ISO8859_2
1232
1362
 
1233
1363
  conn = PG.connect( @conninfo )
1234
- expect( conn.internal_encoding ).to eq( Encoding::UTF_8 )
1364
+ expect( conn.internal_encoding ).to eq( Encoding::ISO8859_2 )
1235
1365
  res = conn.exec( "SELECT foo FROM defaultinternaltest" )
1236
- expect( res[0]['foo'].encoding ).to eq( Encoding::UTF_8 )
1366
+ expect( res[0]['foo'].encoding ).to eq( Encoding::ISO8859_2 )
1237
1367
  ensure
1238
1368
  conn.exec( "DROP TABLE defaultinternaltest" )
1239
1369
  conn.finish if conn
@@ -1463,15 +1593,15 @@ describe PG::Connection do
1463
1593
  end
1464
1594
  end
1465
1595
 
1466
- it "can process #copy_data input queries with row encoder" do
1596
+ it "can process #copy_data input queries with row encoder and respects character encoding" do
1467
1597
  @conn2.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
1468
1598
  res2 = @conn2.copy_data( "COPY copytable FROM STDOUT" ) do |res|
1469
1599
  @conn2.put_copy_data [1]
1470
- @conn2.put_copy_data ["2"]
1600
+ @conn2.put_copy_data ["Möhre".encode("utf-16le")]
1471
1601
  end
1472
1602
 
1473
1603
  res = @conn2.exec( "SELECT * FROM copytable ORDER BY col1" )
1474
- expect( res.values ).to eq( [["1"], ["2"]] )
1604
+ expect( res.values ).to eq( [["1"], ["Möhre"]] )
1475
1605
  end
1476
1606
  end
1477
1607
 
@@ -1513,14 +1643,16 @@ describe PG::Connection do
1513
1643
  end
1514
1644
  end
1515
1645
 
1516
- it "can process #copy_data output with row decoder" do
1646
+ it "can process #copy_data output with row decoder and respects character encoding" do
1647
+ @conn2.internal_encoding = Encoding::ISO8859_1
1517
1648
  rows = []
1518
- res2 = @conn2.copy_data( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT" ) do |res|
1649
+ res2 = @conn2.copy_data( "COPY (VALUES('1'), ('Möhre')) TO STDOUT".encode("utf-16le") ) do |res|
1519
1650
  while row=@conn2.get_copy_data
1520
1651
  rows << row
1521
1652
  end
1522
1653
  end
1523
- expect( rows ).to eq( [["1"], ["2"]] )
1654
+ expect( rows.last.last.encoding ).to eq( Encoding::ISO8859_1 )
1655
+ expect( rows ).to eq( [["1"], ["Möhre".encode("iso-8859-1")]] )
1524
1656
  end
1525
1657
 
1526
1658
  it "can type cast #copy_data output with explicit decoder" do
@@ -100,11 +100,11 @@ describe PG::Result do
100
100
  expect( res[0]['n'] ).to be_nil()
101
101
  end
102
102
 
103
- it "encapsulates errors in a PGError object" do
103
+ it "encapsulates errors in a PG::Error object" do
104
104
  exception = nil
105
105
  begin
106
106
  @conn.exec( "SELECT * FROM nonexistant_table" )
107
- rescue PGError => err
107
+ rescue PG::Error => err
108
108
  exception = err
109
109
  end
110
110
 
@@ -136,7 +136,7 @@ describe PG::Result do
136
136
  exception = nil
137
137
  begin
138
138
  @conn.exec( "INSERT INTO integrity VALUES (NULL)" )
139
- rescue PGError => err
139
+ rescue PG::Error => err
140
140
  exception = err
141
141
  end
142
142
  result = exception.result
@@ -152,7 +152,7 @@ describe PG::Result do
152
152
  sqlstate = nil
153
153
  begin
154
154
  res = @conn.exec("SELECT 1/0")
155
- rescue PGError => e
155
+ rescue PG::Error => e
156
156
  sqlstate = e.result.result_error_field( PG::PG_DIAG_SQLSTATE ).to_i
157
157
  end
158
158
  expect( sqlstate ).to eq( 22012 )
@@ -403,6 +403,13 @@ describe PG::Result do
403
403
  expect( r.cleared? ).to eq(true)
404
404
  end
405
405
 
406
+ it "can be inspected before and after clear" do
407
+ r = @conn.exec "select 1"
408
+ expect( r.inspect ).to match(/status=PGRES_TUPLES_OK/)
409
+ r.clear
410
+ expect( r.inspect ).to match(/cleared/)
411
+ end
412
+
406
413
  context 'result value conversions with TypeMapByColumn' do
407
414
  let!(:textdec_int){ PG::TextDecoder::Integer.new name: 'INT4', oid: 23 }
408
415
  let!(:textdec_float){ PG::TextDecoder::Float.new name: 'FLOAT4', oid: 700 }
@@ -59,7 +59,7 @@ describe PG::TypeMapByClass do
59
59
  it "should retrieve particular conversions" do
60
60
  expect( tm[Integer] ).to eq(binaryenc_int)
61
61
  expect( tm[Float] ).to eq(textenc_float)
62
- expect( tm[Bignum] ).to be_nil
62
+ expect( tm[Range] ).to be_nil
63
63
  expect( derived_tm[raise_class] ).to be_kind_of(Proc)
64
64
  expect( derived_tm[Array] ).to eq(:array_type_map_for)
65
65
  end
@@ -102,7 +102,7 @@ describe PG::TypeMapByClass do
102
102
 
103
103
  it "should allow mixed type conversions" do
104
104
  res = @conn.exec_params( "SELECT $1, $2, $3", [5, 1.23, :TestSymbol], 0, tm )
105
- expect( res.values ).to eq([['5', '1.23', '[:TestSymbol]']])
105
+ expect( res.values ).to eq([['5', '1.23', "[:TestSymbol, #{@conn.internal_encoding.inspect}]"]])
106
106
  expect( res.ftype(0) ).to eq(20)
107
107
  end
108
108