pg 0.18.4 → 0.19.0.pre20160409114042

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.
@@ -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
 
@@ -224,8 +245,27 @@ class PG::Connection
224
245
  end
225
246
  end
226
247
 
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
265
+
227
266
  end # class PG::Connection
228
267
 
268
+ # :stopdoc:
229
269
  # Backward-compatible alias
230
270
  PGconn = PG::Connection
231
271
 
@@ -12,15 +12,19 @@ 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
22
  str[-1,0] = " status=#{res_status(result_status)} ntuples=#{ntuples} nfields=#{nfields} cmd_tuples=#{cmd_tuples}"
21
- str
23
+ return str
22
24
  end
25
+
23
26
  end # class PG::Result
24
27
 
28
+ # :stopdoc:
25
29
  # Backward-compatible alias
26
30
  PGresult = PG::Result
@@ -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
 
@@ -684,13 +684,13 @@ describe PG::Connection do
684
684
  end
685
685
 
686
686
  it "described_class#block should allow a timeout" do
687
- @conn.send_query( "select pg_sleep(3)" )
687
+ @conn.send_query( "select pg_sleep(1)" )
688
688
 
689
689
  start = Time.now
690
- @conn.block( 0.1 )
690
+ @conn.block( 0.3 )
691
691
  finish = Time.now
692
692
 
693
- expect( (finish - start) ).to be_within( 0.05 ).of( 0.1 )
693
+ expect( (finish - start) ).to be_within( 0.2 ).of( 0.3 )
694
694
  end
695
695
 
696
696
 
@@ -725,6 +725,25 @@ describe PG::Connection do
725
725
  expect( @conn.conninfo_hash[:dbname] ).to eq( 'test' )
726
726
  end
727
727
 
728
+ describe "connection information related to SSL" do
729
+
730
+ it "can retrieve connection's ssl state", :postgresql_95 do
731
+ expect( @conn.ssl_in_use? ).to be false
732
+ end
733
+
734
+ it "can retrieve connection's ssl attribute_names", :postgresql_95 do
735
+ expect( @conn.ssl_attribute_names ).to be_a(Array)
736
+ end
737
+
738
+ it "can retrieve a single ssl connection attribute", :postgresql_95 do
739
+ expect( @conn.ssl_attribute('dbname') ).to eq( nil )
740
+ end
741
+
742
+ it "can retrieve all connection's ssl attributes", :postgresql_95 do
743
+ expect( @conn.ssl_attributes ).to be_a_kind_of( Hash )
744
+ end
745
+ end
746
+
728
747
 
729
748
  it "honors the connect_timeout connection parameter", :postgresql_93 do
730
749
  conn = PG.connect( port: @port, dbname: 'test', connect_timeout: 11 )
@@ -1070,8 +1089,8 @@ describe PG::Connection do
1070
1089
  res.check
1071
1090
  first_row_time = Time.now unless first_row_time
1072
1091
  end
1073
- expect( (Time.now - start_time) ).to be >= 1.0
1074
- expect( (first_row_time - start_time) ).to be < 1.0
1092
+ expect( (Time.now - start_time) ).to be >= 0.9
1093
+ expect( (first_row_time - start_time) ).to be < 0.9
1075
1094
  end
1076
1095
 
1077
1096
  it "should receive rows before entire query fails" do
@@ -1150,51 +1169,137 @@ describe PG::Connection do
1150
1169
  end
1151
1170
 
1152
1171
  it "uses the client encoding for escaped string" do
1153
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1172
+ original = "Möhre to\0 escape".encode( "utf-16be" )
1154
1173
  @conn.set_client_encoding( "euc_jp" )
1155
1174
  escaped = @conn.escape( original )
1156
1175
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1157
- expect( escaped ).to eq( "string to" )
1176
+ expect( escaped ).to eq( "Möhre to".encode(Encoding::EUC_JP) )
1158
1177
  end
1159
1178
 
1160
1179
  it "uses the client encoding for escaped literal", :postgresql_90 do
1161
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1180
+ original = "Möhre to\0 escape".encode( "utf-16be" )
1162
1181
  @conn.set_client_encoding( "euc_jp" )
1163
1182
  escaped = @conn.escape_literal( original )
1164
1183
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1165
- expect( escaped ).to eq( "'string to'" )
1184
+ expect( escaped ).to eq( "'Möhre to'".encode(Encoding::EUC_JP) )
1166
1185
  end
1167
1186
 
1168
1187
  it "uses the client encoding for escaped identifier", :postgresql_90 do
1169
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1188
+ original = "Möhre to\0 escape".encode( "utf-16le" )
1170
1189
  @conn.set_client_encoding( "euc_jp" )
1171
1190
  escaped = @conn.escape_identifier( original )
1172
1191
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1173
- expect( escaped ).to eq( "\"string to\"" )
1192
+ expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
1174
1193
  end
1175
1194
 
1176
1195
  it "uses the client encoding for quote_ident" do
1177
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1196
+ original = "Möhre to\0 escape".encode( "utf-16le" )
1178
1197
  @conn.set_client_encoding( "euc_jp" )
1179
1198
  escaped = @conn.quote_ident( original )
1180
1199
  expect( escaped.encoding ).to eq( Encoding::EUC_JP )
1181
- expect( escaped ).to eq( "\"string to\"" )
1200
+ expect( escaped ).to eq( "\"Möhre to\"".encode(Encoding::EUC_JP) )
1182
1201
  end
1183
1202
 
1184
1203
  it "uses the previous string encoding for escaped string" do
1185
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1204
+ original = "Möhre to\0 escape".encode( "iso-8859-1" )
1186
1205
  @conn.set_client_encoding( "euc_jp" )
1187
1206
  escaped = described_class.escape( original )
1188
1207
  expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
1189
- expect( escaped ).to eq( "string to" )
1208
+ expect( escaped ).to eq( "Möhre to".encode(Encoding::ISO8859_1) )
1190
1209
  end
1191
1210
 
1192
1211
  it "uses the previous string encoding for quote_ident" do
1193
- original = "string to\0 escape".force_encoding( "iso8859-1" )
1212
+ original = "Möhre to\0 escape".encode( "iso-8859-1" )
1194
1213
  @conn.set_client_encoding( "euc_jp" )
1195
1214
  escaped = described_class.quote_ident( original )
1196
1215
  expect( escaped.encoding ).to eq( Encoding::ISO8859_1 )
1197
- expect( escaped ).to eq( "\"string to\"" )
1216
+ expect( escaped.encode ).to eq( "\"Möhre to\"".encode(Encoding::ISO8859_1) )
1217
+ end
1218
+ end
1219
+
1220
+ describe "respect and convert character encoding of input strings" do
1221
+ before :each do
1222
+ @conn.internal_encoding = __ENCODING__
1223
+ end
1224
+
1225
+ it "should convert query string and parameters to #exec_params" do
1226
+ r = @conn.exec_params("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
1227
+ ['grün'.encode('utf-16be'), 'grün'.encode('iso-8859-1')])
1228
+ expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1229
+ end
1230
+
1231
+ it "should convert query string and parameters to #async_exec" do
1232
+ r = @conn.async_exec("VALUES( $1, $2, $1=$2, 'grün')".encode("cp936"),
1233
+ ['grün'.encode('cp850'), 'grün'.encode('utf-16le')])
1234
+ expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1235
+ end
1236
+
1237
+ it "should convert query string to #exec" do
1238
+ r = @conn.exec("SELECT 'grün'".encode("utf-16be"))
1239
+ expect( r.values ).to eq( [['grün']] )
1240
+ end
1241
+
1242
+ it "should convert query string to #async_exec" do
1243
+ r = @conn.async_exec("SELECT 'grün'".encode("utf-16le"))
1244
+ expect( r.values ).to eq( [['grün']] )
1245
+ end
1246
+
1247
+ it "should convert strings and parameters to #prepare and #exec_prepared" do
1248
+ @conn.prepare("weiß1".encode("utf-16be"), "VALUES( $1, $2, $1=$2, 'grün')".encode("cp850"))
1249
+ r = @conn.exec_prepared("weiß1".encode("utf-32le"),
1250
+ ['grün'.encode('cp936'), 'grün'.encode('utf-16le')])
1251
+ expect( r.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1252
+ end
1253
+
1254
+ it "should convert strings to #describe_prepared" do
1255
+ @conn.prepare("weiß2", "VALUES(123)")
1256
+ r = @conn.describe_prepared("weiß2".encode("utf-16be"))
1257
+ expect( r.nfields ).to eq( 1 )
1258
+ end
1259
+
1260
+ it "should convert strings to #describe_portal" do
1261
+ @conn.exec "DECLARE cörsör CURSOR FOR VALUES(1,2,3)"
1262
+ r = @conn.describe_portal("cörsör".encode("utf-16le"))
1263
+ expect( r.nfields ).to eq( 3 )
1264
+ end
1265
+
1266
+ it "should convert query string to #send_query" do
1267
+ @conn.send_query("VALUES('grün')".encode("utf-16be"))
1268
+ expect( @conn.get_last_result.values ).to eq( [['grün']] )
1269
+ end
1270
+
1271
+ it "should convert query string and parameters to #send_query" do
1272
+ @conn.send_query("VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16le"),
1273
+ ['grün'.encode('utf-32be'), 'grün'.encode('iso-8859-1')])
1274
+ expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1275
+ end
1276
+
1277
+ it "should convert strings and parameters to #send_prepare and #send_query_prepared" do
1278
+ @conn.send_prepare("weiß3".encode("iso-8859-1"), "VALUES( $1, $2, $1=$2, 'grün')".encode("utf-16be"))
1279
+ @conn.get_last_result
1280
+ @conn.send_query_prepared("weiß3".encode("utf-32le"),
1281
+ ['grün'.encode('utf-16le'), 'grün'.encode('iso-8859-1')])
1282
+ expect( @conn.get_last_result.values ).to eq( [['grün', 'grün', 't', 'grün']] )
1283
+ end
1284
+
1285
+ it "should convert strings to #send_describe_prepared" do
1286
+ @conn.prepare("weiß4", "VALUES(123)")
1287
+ @conn.send_describe_prepared("weiß4".encode("utf-16be"))
1288
+ expect( @conn.get_last_result.nfields ).to eq( 1 )
1289
+ end
1290
+
1291
+ it "should convert strings to #send_describe_portal" do
1292
+ @conn.exec "DECLARE cörsör CURSOR FOR VALUES(1,2,3)"
1293
+ @conn.send_describe_portal("cörsör".encode("utf-16le"))
1294
+ expect( @conn.get_last_result.nfields ).to eq( 3 )
1295
+ end
1296
+
1297
+ it "should convert error string to #put_copy_end" do
1298
+ @conn.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
1299
+ @conn.exec( "COPY copytable FROM STDIN" )
1300
+ @conn.put_copy_end("grün".encode("utf-16be"))
1301
+ expect( @conn.get_result.error_message ).to match(/grün/)
1302
+ @conn.get_result
1198
1303
  end
1199
1304
  end
1200
1305
 
@@ -1463,15 +1568,15 @@ describe PG::Connection do
1463
1568
  end
1464
1569
  end
1465
1570
 
1466
- it "can process #copy_data input queries with row encoder" do
1571
+ it "can process #copy_data input queries with row encoder and respects character encoding" do
1467
1572
  @conn2.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
1468
1573
  res2 = @conn2.copy_data( "COPY copytable FROM STDOUT" ) do |res|
1469
1574
  @conn2.put_copy_data [1]
1470
- @conn2.put_copy_data ["2"]
1575
+ @conn2.put_copy_data ["Möhre".encode("utf-16le")]
1471
1576
  end
1472
1577
 
1473
1578
  res = @conn2.exec( "SELECT * FROM copytable ORDER BY col1" )
1474
- expect( res.values ).to eq( [["1"], ["2"]] )
1579
+ expect( res.values ).to eq( [["1"], ["Möhre"]] )
1475
1580
  end
1476
1581
  end
1477
1582
 
@@ -1513,14 +1618,16 @@ describe PG::Connection do
1513
1618
  end
1514
1619
  end
1515
1620
 
1516
- it "can process #copy_data output with row decoder" do
1621
+ it "can process #copy_data output with row decoder and respects character encoding" do
1622
+ @conn2.internal_encoding = Encoding::ISO8859_1
1517
1623
  rows = []
1518
- res2 = @conn2.copy_data( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT" ) do |res|
1624
+ res2 = @conn2.copy_data( "COPY (VALUES('1'), ('Möhre')) TO STDOUT".encode("utf-16le") ) do |res|
1519
1625
  while row=@conn2.get_copy_data
1520
1626
  rows << row
1521
1627
  end
1522
1628
  end
1523
- expect( rows ).to eq( [["1"], ["2"]] )
1629
+ expect( rows.last.last.encoding ).to eq( Encoding::ISO8859_1 )
1630
+ expect( rows ).to eq( [["1"], ["Möhre".encode("iso-8859-1")]] )
1524
1631
  end
1525
1632
 
1526
1633
  it "can type cast #copy_data output with explicit decoder" do
@@ -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
 
@@ -116,7 +116,7 @@ describe PG::TypeMapByMriType do
116
116
 
117
117
  it "should allow mixed type conversions" do
118
118
  res = @conn.exec_params( "SELECT $1, $2, $3", [5, 1.23, :TestSymbol], 0, tm )
119
- expect( res.values ).to eq([['5', '1.23', '[:TestSymbol]']])
119
+ expect( res.values ).to eq([['5', '1.23', "[:TestSymbol, #{@conn.internal_encoding.inspect}]"]])
120
120
  expect( res.ftype(0) ).to eq(20)
121
121
  end
122
122
 
@@ -39,6 +39,14 @@ describe "PG::Type derivations" do
39
39
  end.new
40
40
  end
41
41
 
42
+ let!(:intenc_incrementer_with_encoding) do
43
+ Class.new(PG::SimpleEncoder) do
44
+ def encode(value, encoding)
45
+ r = (value.to_i + 1).to_s + " #{encoding}"
46
+ r.encode!(encoding)
47
+ end
48
+ end.new
49
+ end
42
50
  let!(:intenc_incrementer_with_int_result) do
43
51
  Class.new(PG::SimpleEncoder) do
44
52
  def encode(value)
@@ -127,6 +135,13 @@ describe "PG::Type derivations" do
127
135
  expect( quoted_type.decode(%[a.b]) ).to eq( ['a','b'] )
128
136
  expect( quoted_type.decode(%[a]) ).to eq( ['a'] )
129
137
  end
138
+
139
+ it 'should split identifier string with correct character encoding' do
140
+ quoted_type = PG::TextDecoder::Identifier.new
141
+ v = quoted_type.decode(%[Héllo].encode("iso-8859-1")).first
142
+ expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
143
+ expect( v ).to eq( %[Héllo].encode(Encoding::ISO_8859_1) )
144
+ end
130
145
  end
131
146
 
132
147
  it "should raise when decode method is called with wrong args" do
@@ -209,6 +224,13 @@ describe "PG::Type derivations" do
209
224
  expect( quoted_type.encode( nil ) ).to be_nil
210
225
  end
211
226
 
227
+ it 'should quote identifiers with correct character encoding' do
228
+ quoted_type = PG::TextEncoder::Identifier.new
229
+ v = quoted_type.encode(['Héllo'], "iso-8859-1")
230
+ expect( v ).to eq( %["Héllo"].encode(Encoding::ISO_8859_1) )
231
+ expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
232
+ end
233
+
212
234
  it "will raise a TypeError for invalid arguments to quote_ident" do
213
235
  quoted_type = PG::TextEncoder::Identifier.new
214
236
  expect{ quoted_type.encode( [nil] ) }.to raise_error(TypeError)
@@ -220,6 +242,12 @@ describe "PG::Type derivations" do
220
242
  expect( intenc_incrementer.encode(3) ).to eq( "4 " )
221
243
  end
222
244
 
245
+ it "should encode with ruby encoder and given character encoding" do
246
+ r = intenc_incrementer_with_encoding.encode(3, Encoding::CP850)
247
+ expect( r ).to eq( "4 CP850" )
248
+ expect( r.encoding ).to eq( Encoding::CP850 )
249
+ end
250
+
223
251
  it "should return when ruby encoder returns non string values" do
224
252
  expect( intenc_incrementer_with_int_result.encode(3) ).to eq( 4 )
225
253
  end
@@ -447,9 +475,18 @@ describe "PG::Type derivations" do
447
475
  end
448
476
 
449
477
  context 'array of types with encoder in ruby space' do
450
- it 'encodes with quotation' do
478
+ it 'encodes with quotation and default character encoding' do
479
+ array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer, needs_quotation: true
480
+ r = array_type.encode([3,4])
481
+ expect( r ).to eq( %[{"4 ","5 "}] )
482
+ expect( r.encoding ).to eq( Encoding::ASCII_8BIT )
483
+ end
484
+
485
+ it 'encodes with quotation and given character encoding' do
451
486
  array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer, needs_quotation: true
452
- expect( array_type.encode([3,4]) ).to eq( %[{"4 ","5 "}] )
487
+ r = array_type.encode([3,4], Encoding::CP850)
488
+ expect( r ).to eq( %[{"4 ","5 "}] )
489
+ expect( r.encoding ).to eq( Encoding::CP850 )
453
490
  end
454
491
 
455
492
  it 'encodes without quotation' do
@@ -457,6 +494,20 @@ describe "PG::Type derivations" do
457
494
  expect( array_type.encode([3,4]) ).to eq( %[{4 ,5 }] )
458
495
  end
459
496
 
497
+ it 'encodes with default character encoding' do
498
+ array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_encoding
499
+ r = array_type.encode([3,4])
500
+ expect( r ).to eq( %[{"4 ASCII-8BIT","5 ASCII-8BIT"}] )
501
+ expect( r.encoding ).to eq( Encoding::ASCII_8BIT )
502
+ end
503
+
504
+ it 'encodes with given character encoding' do
505
+ array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_encoding
506
+ r = array_type.encode([3,4], Encoding::CP850)
507
+ expect( r ).to eq( %[{"4 CP850","5 CP850"}] )
508
+ expect( r.encoding ).to eq( Encoding::CP850 )
509
+ end
510
+
460
511
  it "should raise when ruby encoder returns non string values" do
461
512
  array_type = PG::TextEncoder::Array.new elements_type: intenc_incrementer_with_int_result, needs_quotation: false
462
513
  expect{ array_type.encode([3,4]) }.to raise_error(TypeError)
@@ -473,6 +524,13 @@ describe "PG::Type derivations" do
473
524
  quoted_type = PG::TextEncoder::QuotedLiteral.new elements_type: textenc_string_array
474
525
  expect( quoted_type.encode(["'A\",","\\B'"]) ).to eq( %['{"''A\\",","\\\\B''"}'] )
475
526
  end
527
+
528
+ it 'should quote literals with correct character encoding' do
529
+ quoted_type = PG::TextEncoder::QuotedLiteral.new elements_type: textenc_string_array
530
+ v = quoted_type.encode(["Héllo"], "iso-8859-1")
531
+ expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
532
+ expect( v ).to eq( %['{Héllo}'].encode(Encoding::ISO_8859_1) )
533
+ end
476
534
  end
477
535
  end
478
536
 
@@ -522,9 +580,19 @@ describe "PG::Type derivations" do
522
580
  expect( e.encode("(\xFBm") ).to eq("KPtt")
523
581
  end
524
582
 
583
+ it 'should encode Strings as base64 with correct character encoding' do
584
+ e = PG::TextEncoder::ToBase64.new
585
+ v = e.encode("Héllo".encode("utf-16le"), "iso-8859-1")
586
+ expect( v ).to eq("SOlsbG8=")
587
+ expect( v.encoding ).to eq(Encoding::ISO_8859_1)
588
+ end
589
+
525
590
  it "should encode Strings as base64 in BinaryDecoder" do
526
591
  e = PG::BinaryDecoder::ToBase64.new
527
592
  expect( e.decode("x") ).to eq("eA==")
593
+ v = e.decode("Héllo".encode("utf-16le"))
594
+ expect( v ).to eq("SADpAGwAbABvAA==")
595
+ expect( v.encoding ).to eq(Encoding::ASCII_8BIT)
528
596
  end
529
597
 
530
598
  it "should encode Integers as base64" do
@@ -611,6 +679,12 @@ describe "PG::Type derivations" do
611
679
  expect( encoder.encode([:xyz, 123, 2456, 34567, 456789, 5678901, [1,2,3], 12.1, "abcdefg", nil]) ).
612
680
  to eq("xyz\t123\t2456\t34567\t456789\t5678901\t[1, 2, 3]\t12.1\tabcdefg\t\\N\n")
613
681
  end
682
+
683
+ it 'should output a string with correct character encoding' do
684
+ v = encoder.encode(["Héllo"], "iso-8859-1")
685
+ expect( v.encoding ).to eq( Encoding::ISO_8859_1 )
686
+ expect( v ).to eq( "Héllo\n".encode(Encoding::ISO_8859_1) )
687
+ end
614
688
  end
615
689
 
616
690
  context "with TypeMapByClass" do
@@ -675,6 +749,12 @@ describe "PG::Type derivations" do
675
749
  it "should decode different types of Ruby objects" do
676
750
  expect( decoder.decode("123\t \0#\t#\n#\r#\\ \t234\t#\x01#\002\n".gsub("#", "\\"))).to eq( ["123", " \0\t\n\r\\ ", "234", "\x01\x02"] )
677
751
  end
752
+
753
+ it 'should respect input character encoding' do
754
+ v = decoder.decode("Héllo\n".encode("iso-8859-1")).first
755
+ expect( v.encoding ).to eq(Encoding::ISO_8859_1)
756
+ expect( v ).to eq("Héllo".encode("iso-8859-1"))
757
+ end
678
758
  end
679
759
  end
680
760