pg 0.18.4 → 0.19.0.pre20160409114042

Sign up to get free protection for your applications and to get access to all the features.
@@ -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