pg 0.18.0.pre20140820094244 → 0.18.0.pre20141017155815
Sign up to get free protection for your applications and to get access to all the features.
- 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
|