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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +1573 -2
- data/History.rdoc +3 -11
- data/Manifest.txt +24 -0
- data/README.rdoc +51 -4
- data/Rakefile +20 -14
- data/Rakefile.cross +39 -32
- data/ext/extconf.rb +27 -26
- data/ext/pg.c +75 -21
- data/ext/pg.h +194 -6
- data/ext/pg_binary_decoder.c +160 -0
- data/ext/pg_binary_encoder.c +160 -0
- data/ext/pg_coder.c +454 -0
- data/ext/pg_connection.c +815 -518
- data/ext/pg_copy_coder.c +557 -0
- data/ext/pg_result.c +258 -103
- data/ext/pg_text_decoder.c +424 -0
- data/ext/pg_text_encoder.c +608 -0
- data/ext/pg_type_map.c +113 -0
- data/ext/pg_type_map_all_strings.c +113 -0
- data/ext/pg_type_map_by_column.c +254 -0
- data/ext/pg_type_map_by_mri_type.c +266 -0
- data/ext/pg_type_map_by_oid.c +341 -0
- data/ext/util.c +121 -0
- data/ext/util.h +63 -0
- data/lib/pg.rb +11 -1
- data/lib/pg/basic_type_mapping.rb +377 -0
- data/lib/pg/coder.rb +74 -0
- data/lib/pg/connection.rb +38 -5
- data/lib/pg/result.rb +13 -3
- data/lib/pg/text_decoder.rb +42 -0
- data/lib/pg/text_encoder.rb +27 -0
- data/lib/pg/type_map_by_column.rb +15 -0
- data/spec/helpers.rb +9 -1
- data/spec/pg/basic_type_mapping_spec.rb +251 -0
- data/spec/pg/connection_spec.rb +232 -13
- data/spec/pg/result_spec.rb +52 -0
- data/spec/pg/type_map_by_column_spec.rb +135 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +122 -0
- data/spec/pg/type_map_by_oid_spec.rb +133 -0
- data/spec/pg/type_map_spec.rb +39 -0
- data/spec/pg/type_spec.rb +620 -0
- metadata +40 -4
- metadata.gz.sig +0 -0
data/spec/pg/connection_spec.rb
CHANGED
@@ -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
|
-
|
275
|
-
|
276
|
-
@conn.
|
277
|
-
|
278
|
-
|
279
|
-
|
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
|
-
|
282
|
-
|
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
|
data/spec/pg/result_spec.rb
CHANGED
@@ -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
|