pg 0.18.0.pre20140820094244 → 0.18.0.pre20141017155815

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 (46) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +1573 -2
  5. data/History.rdoc +3 -11
  6. data/Manifest.txt +24 -0
  7. data/README.rdoc +51 -4
  8. data/Rakefile +20 -14
  9. data/Rakefile.cross +39 -32
  10. data/ext/extconf.rb +27 -26
  11. data/ext/pg.c +75 -21
  12. data/ext/pg.h +194 -6
  13. data/ext/pg_binary_decoder.c +160 -0
  14. data/ext/pg_binary_encoder.c +160 -0
  15. data/ext/pg_coder.c +454 -0
  16. data/ext/pg_connection.c +815 -518
  17. data/ext/pg_copy_coder.c +557 -0
  18. data/ext/pg_result.c +258 -103
  19. data/ext/pg_text_decoder.c +424 -0
  20. data/ext/pg_text_encoder.c +608 -0
  21. data/ext/pg_type_map.c +113 -0
  22. data/ext/pg_type_map_all_strings.c +113 -0
  23. data/ext/pg_type_map_by_column.c +254 -0
  24. data/ext/pg_type_map_by_mri_type.c +266 -0
  25. data/ext/pg_type_map_by_oid.c +341 -0
  26. data/ext/util.c +121 -0
  27. data/ext/util.h +63 -0
  28. data/lib/pg.rb +11 -1
  29. data/lib/pg/basic_type_mapping.rb +377 -0
  30. data/lib/pg/coder.rb +74 -0
  31. data/lib/pg/connection.rb +38 -5
  32. data/lib/pg/result.rb +13 -3
  33. data/lib/pg/text_decoder.rb +42 -0
  34. data/lib/pg/text_encoder.rb +27 -0
  35. data/lib/pg/type_map_by_column.rb +15 -0
  36. data/spec/helpers.rb +9 -1
  37. data/spec/pg/basic_type_mapping_spec.rb +251 -0
  38. data/spec/pg/connection_spec.rb +232 -13
  39. data/spec/pg/result_spec.rb +52 -0
  40. data/spec/pg/type_map_by_column_spec.rb +135 -0
  41. data/spec/pg/type_map_by_mri_type_spec.rb +122 -0
  42. data/spec/pg/type_map_by_oid_spec.rb +133 -0
  43. data/spec/pg/type_map_spec.rb +39 -0
  44. data/spec/pg/type_spec.rb +620 -0
  45. metadata +40 -4
  46. metadata.gz.sig +0 -0
@@ -229,6 +229,8 @@ describe PG::Connection do
229
229
  end
230
230
 
231
231
  it "can stop a thread that runs a blocking query with async_exec" do
232
+ pending "this does not work on Rubinius" if RUBY_ENGINE=='rbx'
233
+
232
234
  start = Time.now
233
235
  t = Thread.new do
234
236
  @conn.async_exec( 'select pg_sleep(10)' )
@@ -240,7 +242,7 @@ describe PG::Connection do
240
242
  expect( (Time.now - start) ).to be < 10
241
243
  end
242
244
 
243
- it "should work together with signal handlers" do
245
+ it "should work together with signal handlers", :unix do
244
246
  signal_received = false
245
247
  trap 'USR1' do
246
248
  signal_received = true
@@ -271,15 +273,19 @@ describe PG::Connection do
271
273
  res = nil
272
274
  @conn.exec( "CREATE TABLE pie ( flavor TEXT )" )
273
275
 
274
- expect {
275
- res = @conn.transaction do
276
- @conn.exec( "INSERT INTO pie VALUES ('rhubarb'), ('cherry'), ('schizophrenia')" )
277
- raise "Oh noes! All pie is gone!"
278
- end
279
- }.to raise_exception( RuntimeError, /all pie is gone/i )
276
+ begin
277
+ expect {
278
+ res = @conn.transaction do
279
+ @conn.exec( "INSERT INTO pie VALUES ('rhubarb'), ('cherry'), ('schizophrenia')" )
280
+ raise "Oh noes! All pie is gone!"
281
+ end
282
+ }.to raise_exception( RuntimeError, /all pie is gone/i )
280
283
 
281
- res = @conn.exec( "SELECT * FROM pie" )
282
- expect( res.ntuples ).to eq( 0 )
284
+ res = @conn.exec( "SELECT * FROM pie" )
285
+ expect( res.ntuples ).to eq( 0 )
286
+ ensure
287
+ @conn.exec( "DROP TABLE pie" )
288
+ end
283
289
  end
284
290
 
285
291
  it "returns the block result from Connection#transaction" do
@@ -324,6 +330,29 @@ describe PG::Connection do
324
330
  expect( res.values ).to eq( [ ['Wally'], ['Sally'] ] )
325
331
  end
326
332
 
333
+ it "supports hash form parameters for #exec_params" do
334
+ hash_param_bin = { value: ["00ff"].pack("H*"), type: 17, format: 1 }
335
+ hash_param_nil = { value: nil, type: 17, format: 1 }
336
+ res = @conn.exec_params( "SELECT $1, $2",
337
+ [ hash_param_bin, hash_param_nil ] )
338
+ expect( res.values ).to eq( [["\\x00ff", nil]] )
339
+ expect( result_typenames(res) ).to eq( ['bytea', 'bytea'] )
340
+ end
341
+
342
+ it "should work with arbitrary number of params" do
343
+ begin
344
+ 3.step( 12, 0.2 ) do |exp|
345
+ num_params = (2 ** exp).to_i
346
+ sql = num_params.times.map{|n| "$#{n+1}::INT" }.join(",")
347
+ params = num_params.times.to_a
348
+ res = @conn.exec_params( "SELECT #{sql}", params )
349
+ expect( res.nfields ).to eq( num_params )
350
+ expect( res.values ).to eq( [num_params.times.map(&:to_s)] )
351
+ end
352
+ rescue PG::ProgramLimitExceeded
353
+ # Stop silently if the server complains about too many params
354
+ end
355
+ end
327
356
 
328
357
  it "can wait for NOTIFY events" do
329
358
  @conn.exec( 'ROLLBACK' )
@@ -478,7 +507,7 @@ describe PG::Connection do
478
507
  expect( @conn ).to still_be_usable
479
508
  end
480
509
 
481
- it "can handle server errors in #copy_data for output" do
510
+ it "can handle server errors in #copy_data for output", :postgresql_90 do
482
511
  @conn.exec "ROLLBACK"
483
512
  @conn.transaction do
484
513
  @conn.exec( "CREATE FUNCTION errfunc() RETURNS int AS $$ BEGIN RAISE 'test-error'; END; $$ LANGUAGE plpgsql;" )
@@ -590,22 +619,35 @@ describe PG::Connection do
590
619
  expect( described_class.encrypt_password("postgres", "postgres") ).to match( /\S+/ )
591
620
  end
592
621
 
622
+ it "can return the default connection options" do
623
+ expect( described_class.conndefaults ).to be_a( Array )
624
+ expect( described_class.conndefaults ).to all( be_a(Hash) )
625
+ expect( described_class.conndefaults[0] ).to include( :keyword, :label, :dispchar, :dispsize )
626
+ expect( @conn.conndefaults ).to eq( described_class.conndefaults )
627
+ end
628
+
629
+ it "can return the default connection options as a Hash" do
630
+ expect( described_class.conndefaults_hash ).to be_a( Hash )
631
+ expect( described_class.conndefaults_hash ).to include( :user, :password, :dbname, :host, :port )
632
+ expect( described_class.conndefaults_hash[:port] ).to eq( '54321' )
633
+ expect( @conn.conndefaults_hash ).to eq( described_class.conndefaults_hash )
634
+ end
593
635
 
594
- it "can return the connection's connection options" do
636
+ it "can return the connection's connection options", :postgresql_93 do
595
637
  expect( @conn.conninfo ).to be_a( Array )
596
638
  expect( @conn.conninfo ).to all( be_a(Hash) )
597
639
  expect( @conn.conninfo[0] ).to include( :keyword, :label, :dispchar, :dispsize )
598
640
  end
599
641
 
600
642
 
601
- it "can return the connection's connection options as a Hash" do
643
+ it "can return the connection's connection options as a Hash", :postgresql_93 do
602
644
  expect( @conn.conninfo_hash ).to be_a( Hash )
603
645
  expect( @conn.conninfo_hash ).to include( :user, :password, :connect_timeout, :dbname, :host )
604
646
  expect( @conn.conninfo_hash[:dbname] ).to eq( 'test' )
605
647
  end
606
648
 
607
649
 
608
- it "honors the connect_timeout connection parameter" do
650
+ it "honors the connect_timeout connection parameter", :postgresql_93 do
609
651
  conn = PG.connect( port: @port, dbname: 'test', connect_timeout: 11 )
610
652
  begin
611
653
  expect( conn.conninfo_hash[:connect_timeout] ).to eq( "11" )
@@ -1097,6 +1139,7 @@ describe PG::Connection do
1097
1139
  res = conn.exec( "SELECT foo FROM defaultinternaltest" )
1098
1140
  expect( res[0]['foo'].encoding ).to eq( Encoding::UTF_8 )
1099
1141
  ensure
1142
+ conn.exec( "DROP TABLE defaultinternaltest" )
1100
1143
  conn.finish if conn
1101
1144
  Encoding.default_internal = prev_encoding
1102
1145
  end
@@ -1139,6 +1182,21 @@ describe PG::Connection do
1139
1182
  conn.finish if conn
1140
1183
  end
1141
1184
 
1185
+ it "handles clearing result in or after set_notice_receiver", :postgresql_90 do
1186
+ r = nil
1187
+ @conn.set_notice_receiver do |result|
1188
+ r = result
1189
+ expect( r.cleared? ).to eq(false)
1190
+ end
1191
+ @conn.exec "do $$ BEGIN RAISE NOTICE 'foo'; END; $$ LANGUAGE plpgsql;"
1192
+ sleep 0.2
1193
+ expect( r ).to be_a( PG::Result )
1194
+ expect( r.cleared? ).to eq(true)
1195
+ expect( r.autoclear? ).to eq(true)
1196
+ r.clear
1197
+ @conn.set_notice_receiver
1198
+ end
1199
+
1142
1200
  it "receives properly encoded messages in the notice callbacks", :postgresql_90 do
1143
1201
  [:receiver, :processor].each do |kind|
1144
1202
  notices = []
@@ -1226,4 +1284,165 @@ describe PG::Connection do
1226
1284
  t.join
1227
1285
  end
1228
1286
  end
1287
+
1288
+ describe "type casting" do
1289
+ it "should raise an error on invalid param mapping" do
1290
+ expect{
1291
+ @conn.exec_params( "SELECT 1", [], nil, :invalid )
1292
+ }.to raise_error(TypeError)
1293
+ end
1294
+
1295
+ it "should return nil if no type mapping is set" do
1296
+ expect( @conn.type_map_for_queries ).to be_nil
1297
+ expect( @conn.type_map_for_results ).to be_nil
1298
+ end
1299
+
1300
+ it "shouldn't type map params unless requested" do
1301
+ expect{
1302
+ @conn.exec_params( "SELECT $1", [5] )
1303
+ }.to raise_error(PG::IndeterminateDatatype)
1304
+ end
1305
+
1306
+ it "should raise an error on invalid encoder to put_copy_data" do
1307
+ expect{
1308
+ @conn.put_copy_data [1], :invalid
1309
+ }.to raise_error(TypeError)
1310
+ end
1311
+
1312
+ it "can type cast parameters to put_copy_data with explicit encoder" do
1313
+ tm = PG::TypeMapByColumn.new [nil]
1314
+ row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
1315
+
1316
+ @conn.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
1317
+ res2 = @conn.copy_data( "COPY copytable FROM STDOUT" ) do |res|
1318
+ @conn.put_copy_data [1], row_encoder
1319
+ @conn.put_copy_data ["2"], row_encoder
1320
+ end
1321
+
1322
+ res2 = @conn.copy_data( "COPY copytable FROM STDOUT", row_encoder ) do |res|
1323
+ @conn.put_copy_data [3]
1324
+ @conn.put_copy_data ["4"]
1325
+ end
1326
+
1327
+ res = @conn.exec( "SELECT * FROM copytable ORDER BY col1" )
1328
+ expect( res.values ).to eq( [["1"], ["2"], ["3"], ["4"]] )
1329
+ end
1330
+
1331
+ context "with default query type map" do
1332
+ before :each do
1333
+ @conn2 = described_class.new(@conninfo)
1334
+ tm = PG::TypeMapByMriType.new
1335
+ tm['T_FIXNUM'] = PG::TextEncoder::Integer.new oid: 20
1336
+ @conn2.type_map_for_queries = tm
1337
+
1338
+ row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
1339
+ @conn2.encoder_for_put_copy_data = row_encoder
1340
+ end
1341
+ after :each do
1342
+ @conn2.close
1343
+ end
1344
+
1345
+ it "should respect a type mapping for params and it's OID and format code" do
1346
+ res = @conn2.exec_params( "SELECT $1", [5] )
1347
+ expect( res.values ).to eq( [["5"]] )
1348
+ expect( res.ftype(0) ).to eq( 20 )
1349
+ end
1350
+
1351
+ it "should return the current type mapping" do
1352
+ expect( @conn2.type_map_for_queries ).to be_kind_of(PG::TypeMapByMriType)
1353
+ end
1354
+
1355
+ it "should work with arbitrary number of params in conjunction with type casting" do
1356
+ begin
1357
+ 3.step( 12, 0.2 ) do |exp|
1358
+ num_params = (2 ** exp).to_i
1359
+ sql = num_params.times.map{|n| "$#{n+1}" }.join(",")
1360
+ params = num_params.times.to_a
1361
+ res = @conn2.exec_params( "SELECT #{sql}", params )
1362
+ expect( res.nfields ).to eq( num_params )
1363
+ expect( res.values ).to eq( [num_params.times.map(&:to_s)] )
1364
+ end
1365
+ rescue PG::ProgramLimitExceeded
1366
+ # Stop silently as soon the server complains about too many params
1367
+ end
1368
+ end
1369
+
1370
+ it "can process #copy_data input queries with row encoder" do
1371
+ @conn2.exec( "CREATE TEMP TABLE copytable (col1 TEXT)" )
1372
+ res2 = @conn2.copy_data( "COPY copytable FROM STDOUT" ) do |res|
1373
+ @conn2.put_copy_data [1]
1374
+ @conn2.put_copy_data ["2"]
1375
+ end
1376
+
1377
+ res = @conn2.exec( "SELECT * FROM copytable ORDER BY col1" )
1378
+ expect( res.values ).to eq( [["1"], ["2"]] )
1379
+ end
1380
+ end
1381
+
1382
+ context "with default result type map" do
1383
+ before :each do
1384
+ @conn2 = described_class.new(@conninfo)
1385
+ tm = PG::TypeMapByOid.new
1386
+ tm.add_coder PG::TextDecoder::Integer.new oid: 23, format: 0
1387
+ @conn2.type_map_for_results = tm
1388
+
1389
+ row_decoder = PG::TextDecoder::CopyRow.new
1390
+ @conn2.decoder_for_get_copy_data = row_decoder
1391
+ end
1392
+ after :each do
1393
+ @conn2.close
1394
+ end
1395
+
1396
+ it "should respect a type mapping for result" do
1397
+ res = @conn2.exec_params( "SELECT $1::INT", ["5"] )
1398
+ expect( res.values ).to eq( [[5]] )
1399
+ end
1400
+
1401
+ it "should return the current type mapping" do
1402
+ expect( @conn2.type_map_for_results ).to be_kind_of(PG::TypeMapByOid)
1403
+ end
1404
+
1405
+ it "should work with arbitrary number of params in conjunction with type casting" do
1406
+ begin
1407
+ 3.step( 12, 0.2 ) do |exp|
1408
+ num_params = (2 ** exp).to_i
1409
+ sql = num_params.times.map{|n| "$#{n+1}::INT" }.join(",")
1410
+ params = num_params.times.to_a
1411
+ res = @conn2.exec_params( "SELECT #{sql}", params )
1412
+ expect( res.nfields ).to eq( num_params )
1413
+ expect( res.values ).to eq( [num_params.times.to_a] )
1414
+ end
1415
+ rescue PG::ProgramLimitExceeded
1416
+ # Stop silently as soon the server complains about too many params
1417
+ end
1418
+ end
1419
+
1420
+ it "can process #copy_data output with row decoder" do
1421
+ rows = []
1422
+ res2 = @conn2.copy_data( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT" ) do |res|
1423
+ while row=@conn2.get_copy_data
1424
+ rows << row
1425
+ end
1426
+ end
1427
+ expect( rows ).to eq( [["1"], ["2"]] )
1428
+ end
1429
+
1430
+ it "can type cast #copy_data output with explicit decoder" do
1431
+ tm = PG::TypeMapByColumn.new [PG::TextDecoder::Integer.new]
1432
+ row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
1433
+ rows = []
1434
+ @conn.copy_data( "COPY (SELECT 1 UNION ALL SELECT 2) TO STDOUT", row_decoder ) do |res|
1435
+ while row=@conn.get_copy_data
1436
+ rows << row
1437
+ end
1438
+ end
1439
+ @conn.copy_data( "COPY (SELECT 3 UNION ALL SELECT 4) TO STDOUT" ) do |res|
1440
+ while row=@conn.get_copy_data( false, row_decoder )
1441
+ rows << row
1442
+ end
1443
+ end
1444
+ expect( rows ).to eq( [[1], [2], [3], [4]] )
1445
+ end
1446
+ end
1447
+ end
1229
1448
  end
@@ -320,4 +320,56 @@ describe PG::Result do
320
320
  expect( error.result ).to eq( nil )
321
321
  }
322
322
  end
323
+
324
+ it "does not clear the result itself" do
325
+ r = @conn.exec "select 1"
326
+ expect( r.autoclear? ).to eq(false)
327
+ expect( r.cleared? ).to eq(false)
328
+ r.clear
329
+ expect( r.cleared? ).to eq(true)
330
+ end
331
+
332
+ context 'result value conversions with TypeMapByColumn' do
333
+ let!(:textdec_int){ PG::TextDecoder::Integer.new name: 'INT4', oid: 23 }
334
+ let!(:textdec_float){ PG::TextDecoder::Float.new name: 'FLOAT4', oid: 700 }
335
+
336
+ it "should allow reading, assigning and diabling type conversions" do
337
+ res = @conn.exec( "SELECT 123" )
338
+ expect( res.type_map ).to be_nil
339
+ res.type_map = PG::TypeMapByColumn.new [textdec_int]
340
+ expect( res.type_map ).to be_an_instance_of(PG::TypeMapByColumn)
341
+ expect( res.type_map.coders ).to eq( [textdec_int] )
342
+ res.type_map = PG::TypeMapByColumn.new [textdec_float]
343
+ expect( res.type_map.coders ).to eq( [textdec_float] )
344
+ res.type_map = nil
345
+ expect( res.type_map ).to be_nil
346
+ end
347
+
348
+ it "should be applied to all value retrieving methods" do
349
+ res = @conn.exec( "SELECT 123 as f" )
350
+ res.type_map = PG::TypeMapByColumn.new [textdec_int]
351
+ expect( res.values ).to eq( [[123]] )
352
+ expect( res.getvalue(0,0) ).to eq( 123 )
353
+ expect( res[0] ).to eq( {'f' => 123 } )
354
+ expect( res.enum_for(:each_row).to_a ).to eq( [[123]] )
355
+ expect( res.enum_for(:each).to_a ).to eq( [{'f' => 123}] )
356
+ expect( res.column_values(0) ).to eq( [123] )
357
+ expect( res.field_values('f') ).to eq( [123] )
358
+ end
359
+
360
+ it "should be usable for several querys" do
361
+ colmap = PG::TypeMapByColumn.new [textdec_int]
362
+ res = @conn.exec( "SELECT 123" )
363
+ res.type_map = colmap
364
+ expect( res.values ).to eq( [[123]] )
365
+ res = @conn.exec( "SELECT 456" )
366
+ res.type_map = colmap
367
+ expect( res.values ).to eq( [[456]] )
368
+ end
369
+
370
+ it "shouldn't allow invalid type maps" do
371
+ res = @conn.exec( "SELECT 1" )
372
+ expect{ res.type_map = 1 }.to raise_error(TypeError)
373
+ end
374
+ end
323
375
  end
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env rspec
2
+ # encoding: utf-8
3
+
4
+ require_relative '../helpers'
5
+
6
+ require 'pg'
7
+
8
+
9
+ describe PG::TypeMapByColumn do
10
+
11
+ let!(:textenc_int){ PG::TextEncoder::Integer.new name: 'INT4', oid: 23 }
12
+ let!(:textdec_int){ PG::TextDecoder::Integer.new name: 'INT4', oid: 23 }
13
+ let!(:textenc_float){ PG::TextEncoder::Float.new name: 'FLOAT4', oid: 700 }
14
+ let!(:textdec_float){ PG::TextDecoder::Float.new name: 'FLOAT4', oid: 700 }
15
+ let!(:textenc_string){ PG::TextEncoder::String.new name: 'TEXT', oid: 25 }
16
+ let!(:textdec_string){ PG::TextDecoder::String.new name: 'TEXT', oid: 25 }
17
+ let!(:textdec_bytea){ PG::TextDecoder::Bytea.new name: 'BYTEA', oid: 17 }
18
+ let!(:binaryenc_bytea){ PG::BinaryEncoder::Bytea.new name: 'BYTEA', oid: 17, format: 1 }
19
+ let!(:binarydec_bytea){ PG::BinaryDecoder::Bytea.new name: 'BYTEA', oid: 17, format: 1 }
20
+ let!(:pass_through_type) do
21
+ type = Class.new(PG::SimpleDecoder) do
22
+ def decode(*v)
23
+ v
24
+ end
25
+ end.new
26
+ type.oid = 123456
27
+ type.format = 1
28
+ type.name = 'pass_through'
29
+ type
30
+ end
31
+
32
+ it "should retrieve it's conversions" do
33
+ cm = PG::TypeMapByColumn.new( [textdec_int, textenc_string, textdec_float, pass_through_type, nil] )
34
+ expect( cm.coders ).to eq( [
35
+ textdec_int,
36
+ textenc_string,
37
+ textdec_float,
38
+ pass_through_type,
39
+ nil
40
+ ] )
41
+ expect( cm.inspect ).to eq( "#<PG::TypeMapByColumn INT4:0 TEXT:0 FLOAT4:0 pass_through:1 nil>" )
42
+ end
43
+
44
+ it "should retrieve it's oids" do
45
+ cm = PG::TypeMapByColumn.new( [textdec_int, textdec_string, textdec_float, pass_through_type, nil] )
46
+ expect( cm.oids ).to eq( [23, 25, 700, 123456, nil] )
47
+ end
48
+
49
+
50
+ #
51
+ # Encoding Examples
52
+ #
53
+
54
+ it "should encode integer params" do
55
+ col_map = PG::TypeMapByColumn.new( [textenc_int]*3 )
56
+ res = @conn.exec_params( "SELECT $1, $2, $3", [ 0, nil, "-999" ], 0, col_map )
57
+ expect( res.values ).to eq( [
58
+ [ "0", nil, "-999" ],
59
+ ] )
60
+ end
61
+
62
+ it "should encode bytea params" do
63
+ data = "'\u001F\\"
64
+ col_map = PG::TypeMapByColumn.new( [binaryenc_bytea]*2 )
65
+ res = @conn.exec_params( "SELECT $1, $2", [ data, nil ], 0, col_map )
66
+ res.type_map = PG::TypeMapByColumn.new( [textdec_bytea]*2 )
67
+ expect( res.values ).to eq( [
68
+ [ data, nil ],
69
+ ] )
70
+ end
71
+
72
+
73
+ it "should allow hash form parameters for default encoder" do
74
+ col_map = PG::TypeMapByColumn.new( [nil, nil] )
75
+ hash_param_bin = { value: ["00ff"].pack("H*"), type: 17, format: 1 }
76
+ hash_param_nil = { value: nil, type: 17, format: 1 }
77
+ res = @conn.exec_params( "SELECT $1, $2",
78
+ [ hash_param_bin, hash_param_nil ], 0, col_map )
79
+ expect( res.values ).to eq( [["\\x00ff", nil]] )
80
+ expect( result_typenames(res) ).to eq( ['bytea', 'bytea'] )
81
+ end
82
+
83
+ it "should convert hash form parameters to string when using string encoders" do
84
+ col_map = PG::TypeMapByColumn.new( [textenc_string, textenc_string] )
85
+ hash_param_bin = { value: ["00ff"].pack("H*"), type: 17, format: 1 }
86
+ hash_param_nil = { value: nil, type: 17, format: 1 }
87
+ res = @conn.exec_params( "SELECT $1::text, $2::text",
88
+ [ hash_param_bin, hash_param_nil ], 0, col_map )
89
+ expect( res.values ).to eq( [["{:value=>\"\\x00\\xFF\", :type=>17, :format=>1}", "{:value=>nil, :type=>17, :format=>1}"]] )
90
+ end
91
+
92
+ it "shouldn't allow param mappings with different number of fields" do
93
+ expect{
94
+ @conn.exec_params( "SELECT $1", [ 123 ], 0, PG::TypeMapByColumn.new([]) )
95
+ }.to raise_error(ArgumentError, /mapped columns/)
96
+ end
97
+
98
+ #
99
+ # Decoding Examples
100
+ #
101
+
102
+ class Exception_in_decode < PG::SimpleDecoder
103
+ def decode(res, tuple, field)
104
+ raise "no type decoder defined for tuple #{tuple} field #{field}"
105
+ end
106
+ end
107
+
108
+ it "should raise an error from decode method of type converter" do
109
+ res = @conn.exec( "SELECT now()" )
110
+ types = Array.new( res.nfields, Exception_in_decode.new )
111
+ res.type_map = PG::TypeMapByColumn.new( types )
112
+ expect{ res.values }.to raise_error(/no type decoder defined/)
113
+ end
114
+
115
+ it "should raise an error for invalid params" do
116
+ expect{ PG::TypeMapByColumn.new( :WrongType ) }.to raise_error(TypeError, /wrong argument type/)
117
+ expect{ PG::TypeMapByColumn.new( [123] ) }.to raise_error(ArgumentError, /invalid/)
118
+ end
119
+
120
+ it "shouldn't allow result mappings with different number of fields" do
121
+ res = @conn.exec( "SELECT 1" )
122
+ expect{ res.type_map = PG::TypeMapByColumn.new([]) }.to raise_error(ArgumentError, /mapped columns/)
123
+ end
124
+
125
+ #
126
+ # Decoding Examples text format
127
+ #
128
+
129
+ it "should allow mixed type conversions" do
130
+ res = @conn.exec( "SELECT 1, 'a', 2.0::FLOAT, '2013-06-30'::DATE, 3" )
131
+ res.type_map = PG::TypeMapByColumn.new( [textdec_int, textdec_string, textdec_float, pass_through_type, nil] )
132
+ expect( res.values ).to eq( [[1, 'a', 2.0, ['2013-06-30', 0, 3], '3' ]] )
133
+ end
134
+
135
+ end