pg 0.18.0.pre20141017160319-x86-mingw32 → 0.18.0.pre20141117110243-x86-mingw32
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 +317 -235
- data/History.rdoc +15 -0
- data/Manifest.txt +2 -0
- data/README.rdoc +1 -1
- data/ext/extconf.rb +2 -0
- data/ext/pg.c +66 -0
- data/ext/pg.h +50 -13
- data/ext/pg_binary_decoder.c +3 -3
- data/ext/pg_coder.c +6 -0
- data/ext/pg_connection.c +49 -54
- data/ext/pg_copy_coder.c +13 -29
- data/ext/pg_errors.c +6 -0
- data/ext/pg_result.c +272 -76
- data/ext/pg_text_encoder.c +43 -0
- data/ext/pg_type_map.c +84 -13
- data/ext/pg_type_map_all_strings.c +15 -12
- data/ext/pg_type_map_by_class.c +239 -0
- data/ext/pg_type_map_by_column.c +80 -22
- data/ext/pg_type_map_by_mri_type.c +41 -23
- data/ext/pg_type_map_by_oid.c +51 -19
- data/lib/1.9/pg_ext.so +0 -0
- data/lib/2.0/pg_ext.so +0 -0
- data/lib/2.1/pg_ext.so +0 -0
- data/lib/i386-mingw32/libpq.dll +0 -0
- data/lib/pg.rb +2 -2
- data/lib/pg/basic_type_mapping.rb +13 -13
- data/lib/pg/coder.rb +9 -0
- data/spec/helpers.rb +5 -3
- data/spec/pg/basic_type_mapping_spec.rb +1 -1
- data/spec/pg/connection_spec.rb +16 -5
- data/spec/pg/result_spec.rb +77 -3
- data/spec/pg/type_map_by_class_spec.rb +138 -0
- data/spec/pg/type_map_by_column_spec.rb +87 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +14 -0
- data/spec/pg/type_map_by_oid_spec.rb +21 -0
- data/spec/pg/type_spec.rb +27 -7
- data/spec/pg_spec.rb +14 -0
- metadata +24 -21
- metadata.gz.sig +0 -0
data/lib/1.9/pg_ext.so
CHANGED
Binary file
|
data/lib/2.0/pg_ext.so
CHANGED
Binary file
|
data/lib/2.1/pg_ext.so
CHANGED
Binary file
|
data/lib/i386-mingw32/libpq.dll
CHANGED
Binary file
|
data/lib/pg.rb
CHANGED
@@ -10,7 +10,7 @@ rescue LoadError
|
|
10
10
|
|
11
11
|
# Set the PATH environment variable, so that libpq.dll can be found.
|
12
12
|
old_path = ENV['PATH']
|
13
|
-
ENV['PATH'] = "#{
|
13
|
+
ENV['PATH'] = "#{File.expand_path("../#{RUBY_PLATFORM}", __FILE__)};#{old_path}"
|
14
14
|
require "#{major_minor}/pg_ext"
|
15
15
|
ENV['PATH'] = old_path
|
16
16
|
else
|
@@ -24,7 +24,7 @@ end
|
|
24
24
|
module PG
|
25
25
|
|
26
26
|
# Library version
|
27
|
-
VERSION = '0.18.0.
|
27
|
+
VERSION = '0.18.0.pre20141117110243'
|
28
28
|
|
29
29
|
# VCS revision
|
30
30
|
REVISION = %q$Revision$
|
@@ -304,7 +304,7 @@ end
|
|
304
304
|
# # Execute a query. The Integer param value is typecasted internally by PG::BinaryEncoder::Int8.
|
305
305
|
# # The format of the parameter is set to 1 (binary) and the OID of this parameter is set to 20 (int8).
|
306
306
|
# res = conn.exec_params( "SELECT $1", [5] )
|
307
|
-
class PG::BasicTypeMapForQueries < PG::
|
307
|
+
class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
308
308
|
include PG::BasicTypeRegistry
|
309
309
|
|
310
310
|
def initialize(connection)
|
@@ -323,7 +323,7 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByMriType
|
|
323
323
|
end
|
324
324
|
|
325
325
|
def populate_encoder_list
|
326
|
-
DEFAULT_TYPE_MAP.each do |
|
326
|
+
DEFAULT_TYPE_MAP.each do |klass, selector|
|
327
327
|
if Array === selector
|
328
328
|
format, name, oid_name = selector
|
329
329
|
coder = coder_by_name(format, :encoder, name).dup
|
@@ -332,9 +332,9 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByMriType
|
|
332
332
|
else
|
333
333
|
coder.oid = 0
|
334
334
|
end
|
335
|
-
self[
|
335
|
+
self[klass] = coder
|
336
336
|
else
|
337
|
-
self[
|
337
|
+
self[klass] = selector
|
338
338
|
end
|
339
339
|
end
|
340
340
|
end
|
@@ -351,25 +351,25 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByMriType
|
|
351
351
|
while elem.kind_of?(Array)
|
352
352
|
elem = elem.first
|
353
353
|
end
|
354
|
-
@array_encoders_by_klass[elem.class] ||
|
354
|
+
@array_encoders_by_klass[elem.class] ||
|
355
|
+
elem.class.ancestors.lazy.map{|ancestor| @array_encoders_by_klass[ancestor] }.find{|a| a } ||
|
356
|
+
@anyarray_encoder
|
355
357
|
end
|
356
358
|
|
357
359
|
DEFAULT_TYPE_MAP = {
|
358
|
-
|
359
|
-
|
360
|
+
TrueClass => [1, 'bool', 'bool'],
|
361
|
+
FalseClass => [1, 'bool', 'bool'],
|
360
362
|
# We use text format and no type OID for numbers, because setting the OID can lead
|
361
363
|
# to unnecessary type conversions on server side.
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
'T_ARRAY'.freeze => :get_array_type,
|
364
|
+
Integer => [0, 'int8'],
|
365
|
+
Float => [0, 'float8'],
|
366
|
+
Array => :get_array_type,
|
366
367
|
}
|
367
368
|
|
368
369
|
DEFAULT_ARRAY_TYPE_MAP = {
|
369
370
|
TrueClass => [0, '_bool'],
|
370
371
|
FalseClass => [0, '_bool'],
|
371
|
-
|
372
|
-
Bignum => [0, '_int8'],
|
372
|
+
Integer => [0, '_int8'],
|
373
373
|
String => [0, '_text'],
|
374
374
|
Float => [0, '_float8'],
|
375
375
|
}
|
data/lib/pg/coder.rb
CHANGED
@@ -3,6 +3,15 @@
|
|
3
3
|
module PG
|
4
4
|
|
5
5
|
class Coder
|
6
|
+
|
7
|
+
module BinaryFormatting
|
8
|
+
Params = { format: 1 }
|
9
|
+
def initialize( params={} )
|
10
|
+
super(params.merge(Params))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
6
15
|
# Create a new coder object based on the attribute Hash.
|
7
16
|
def initialize(params={})
|
8
17
|
params.each do |key, val|
|
data/spec/helpers.rb
CHANGED
@@ -343,11 +343,13 @@ RSpec.configure do |config|
|
|
343
343
|
PG::Connection.instance_methods.map( &:to_sym ).include?( :escape_literal )
|
344
344
|
|
345
345
|
if !PG.respond_to?( :library_version )
|
346
|
-
config.filter_run_excluding( :postgresql_91, :postgresql_92, :postgresql_93 )
|
346
|
+
config.filter_run_excluding( :postgresql_91, :postgresql_92, :postgresql_93, :postgresql_94 )
|
347
347
|
elsif PG.library_version < 90200
|
348
|
-
config.filter_run_excluding( :postgresql_92, :postgresql_93 )
|
348
|
+
config.filter_run_excluding( :postgresql_92, :postgresql_93, :postgresql_94 )
|
349
349
|
elsif PG.library_version < 90300
|
350
|
-
config.filter_run_excluding( :postgresql_93 )
|
350
|
+
config.filter_run_excluding( :postgresql_93, :postgresql_94 )
|
351
|
+
elsif PG.library_version < 90400
|
352
|
+
config.filter_run_excluding( :postgresql_94 )
|
351
353
|
end
|
352
354
|
end
|
353
355
|
|
data/spec/pg/connection_spec.rb
CHANGED
@@ -157,6 +157,17 @@ describe PG::Connection do
|
|
157
157
|
expect( res[0]['n'] ).to eq( '1' )
|
158
158
|
end
|
159
159
|
|
160
|
+
it "can retrieve it's connection parameters for the established connection" do
|
161
|
+
expect( @conn.db ).to eq( "test" )
|
162
|
+
expect( @conn.user ).to be_a_kind_of( String )
|
163
|
+
expect( @conn.pass ).to eq( "" )
|
164
|
+
expect( @conn.host ).to eq( "localhost" )
|
165
|
+
# TODO: Not sure why libpq returns a NULL ptr instead of "127.0.0.1"
|
166
|
+
expect( @conn.hostaddr ).to eq( nil ) if @conn.server_version >= 9_04_00
|
167
|
+
expect( @conn.port ).to eq( 54321 )
|
168
|
+
expect( @conn.tty ).to eq( "" )
|
169
|
+
expect( @conn.options ).to eq( "" )
|
170
|
+
end
|
160
171
|
|
161
172
|
EXPECTED_TRACE_OUTPUT = %{
|
162
173
|
To backend> Msg Q
|
@@ -1293,8 +1304,8 @@ describe PG::Connection do
|
|
1293
1304
|
end
|
1294
1305
|
|
1295
1306
|
it "should return nil if no type mapping is set" do
|
1296
|
-
expect( @conn.type_map_for_queries ).to
|
1297
|
-
expect( @conn.type_map_for_results ).to
|
1307
|
+
expect( @conn.type_map_for_queries ).to be_kind_of(PG::TypeMapAllStrings)
|
1308
|
+
expect( @conn.type_map_for_results ).to be_kind_of(PG::TypeMapAllStrings)
|
1298
1309
|
end
|
1299
1310
|
|
1300
1311
|
it "shouldn't type map params unless requested" do
|
@@ -1331,8 +1342,8 @@ describe PG::Connection do
|
|
1331
1342
|
context "with default query type map" do
|
1332
1343
|
before :each do
|
1333
1344
|
@conn2 = described_class.new(@conninfo)
|
1334
|
-
tm = PG::
|
1335
|
-
tm[
|
1345
|
+
tm = PG::TypeMapByClass.new
|
1346
|
+
tm[Integer] = PG::TextEncoder::Integer.new oid: 20
|
1336
1347
|
@conn2.type_map_for_queries = tm
|
1337
1348
|
|
1338
1349
|
row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
|
@@ -1349,7 +1360,7 @@ describe PG::Connection do
|
|
1349
1360
|
end
|
1350
1361
|
|
1351
1362
|
it "should return the current type mapping" do
|
1352
|
-
expect( @conn2.type_map_for_queries ).to be_kind_of(PG::
|
1363
|
+
expect( @conn2.type_map_for_queries ).to be_kind_of(PG::TypeMapByClass)
|
1353
1364
|
end
|
1354
1365
|
|
1355
1366
|
it "should work with arbitrary number of params in conjunction with type casting" do
|
data/spec/pg/result_spec.rb
CHANGED
@@ -21,6 +21,80 @@ describe PG::Result do
|
|
21
21
|
expect( list ).to eq [['1', '2']]
|
22
22
|
end
|
23
23
|
|
24
|
+
it "yields a row as an Enumerator" do
|
25
|
+
res = @conn.exec("SELECT 1 AS a, 2 AS b")
|
26
|
+
e = res.each_row
|
27
|
+
expect( e ).to be_a_kind_of(Enumerator)
|
28
|
+
pending "Rubinius doesn't define RETURN_SIZED_ENUMERATOR()" if RUBY_ENGINE=='rbx'
|
29
|
+
expect( e.size ).to eq( 1 )
|
30
|
+
expect( e.to_a ).to eq [['1', '2']]
|
31
|
+
end
|
32
|
+
|
33
|
+
it "yields a row as an Enumerator of hashs" do
|
34
|
+
res = @conn.exec("SELECT 1 AS a, 2 AS b")
|
35
|
+
e = res.each
|
36
|
+
expect( e ).to be_a_kind_of(Enumerator)
|
37
|
+
pending "Rubinius doesn't define RETURN_SIZED_ENUMERATOR()" if RUBY_ENGINE=='rbx'
|
38
|
+
expect( e.size ).to eq( 1 )
|
39
|
+
expect( e.to_a ).to eq [{'a'=>'1', 'b'=>'2'}]
|
40
|
+
end
|
41
|
+
|
42
|
+
context "result streaming", :postgresql_92 do
|
43
|
+
it "can iterate over all tuples in single row mode" do
|
44
|
+
@conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
|
45
|
+
@conn.set_single_row_mode
|
46
|
+
expect(
|
47
|
+
@conn.get_result.stream_each.to_a
|
48
|
+
).to eq(
|
49
|
+
[{'a'=>"2"}, {'a'=>"3"}, {'a'=>"4"}]
|
50
|
+
)
|
51
|
+
expect(
|
52
|
+
@conn.get_result.enum_for(:stream_each).to_a
|
53
|
+
).to eq(
|
54
|
+
[{'b'=>"1", 'c'=>"5"}, {'b'=>"1", 'c'=>"6"}]
|
55
|
+
)
|
56
|
+
expect( @conn.get_result ).to be_nil
|
57
|
+
end
|
58
|
+
|
59
|
+
it "can iterate over all rows in single row mode" do
|
60
|
+
@conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
|
61
|
+
@conn.set_single_row_mode
|
62
|
+
expect(
|
63
|
+
@conn.get_result.enum_for(:stream_each_row).to_a
|
64
|
+
).to eq(
|
65
|
+
[["2"], ["3"], ["4"]]
|
66
|
+
)
|
67
|
+
expect(
|
68
|
+
@conn.get_result.stream_each_row.to_a
|
69
|
+
).to eq(
|
70
|
+
[["1", "5"], ["1", "6"]]
|
71
|
+
)
|
72
|
+
expect( @conn.get_result ).to be_nil
|
73
|
+
end
|
74
|
+
|
75
|
+
it "complains when not in single row mode" do
|
76
|
+
@conn.send_query( "SELECT generate_series(2,4)" )
|
77
|
+
expect{
|
78
|
+
@conn.get_result.stream_each_row.to_a
|
79
|
+
}.to raise_error(PG::InvalidResultStatus, /not in single row mode/)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "complains when intersected with get_result" do
|
83
|
+
@conn.send_query( "SELECT 1" )
|
84
|
+
@conn.set_single_row_mode
|
85
|
+
expect{
|
86
|
+
@conn.get_result.stream_each_row.each{ @conn.get_result }
|
87
|
+
}.to raise_error(PG::NoResultError, /no result received/)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "raises server errors" do
|
91
|
+
@conn.send_query( "SELECT 0/0" )
|
92
|
+
expect{
|
93
|
+
@conn.get_result.stream_each_row.to_a
|
94
|
+
}.to raise_error(PG::DivisionByZero)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
24
98
|
it "inserts nil AS NULL and return NULL as nil" do
|
25
99
|
res = @conn.exec("SELECT $1::int AS n", [nil])
|
26
100
|
expect( res[0]['n'] ).to be_nil()
|
@@ -335,14 +409,14 @@ describe PG::Result do
|
|
335
409
|
|
336
410
|
it "should allow reading, assigning and diabling type conversions" do
|
337
411
|
res = @conn.exec( "SELECT 123" )
|
338
|
-
expect( res.type_map ).to
|
412
|
+
expect( res.type_map ).to be_kind_of(PG::TypeMapAllStrings)
|
339
413
|
res.type_map = PG::TypeMapByColumn.new [textdec_int]
|
340
414
|
expect( res.type_map ).to be_an_instance_of(PG::TypeMapByColumn)
|
341
415
|
expect( res.type_map.coders ).to eq( [textdec_int] )
|
342
416
|
res.type_map = PG::TypeMapByColumn.new [textdec_float]
|
343
417
|
expect( res.type_map.coders ).to eq( [textdec_float] )
|
344
|
-
res.type_map =
|
345
|
-
expect( res.type_map ).to
|
418
|
+
res.type_map = PG::TypeMapAllStrings.new
|
419
|
+
expect( res.type_map ).to be_kind_of(PG::TypeMapAllStrings)
|
346
420
|
end
|
347
421
|
|
348
422
|
it "should be applied to all value retrieving methods" do
|
@@ -0,0 +1,138 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require_relative '../helpers'
|
5
|
+
|
6
|
+
require 'pg'
|
7
|
+
|
8
|
+
|
9
|
+
describe PG::TypeMapByClass do
|
10
|
+
|
11
|
+
let!(:textenc_int){ PG::TextEncoder::Integer.new name: 'INT4', oid: 23 }
|
12
|
+
let!(:textenc_float){ PG::TextEncoder::Float.new name: 'FLOAT8', oid: 701 }
|
13
|
+
let!(:textenc_string){ PG::TextEncoder::String.new name: 'TEXT', oid: 25 }
|
14
|
+
let!(:binaryenc_int){ PG::BinaryEncoder::Int8.new name: 'INT8', oid: 20, format: 1 }
|
15
|
+
let!(:pass_through_type) do
|
16
|
+
type = Class.new(PG::SimpleEncoder) do
|
17
|
+
def encode(*v)
|
18
|
+
v.inspect
|
19
|
+
end
|
20
|
+
end.new
|
21
|
+
type.oid = 25
|
22
|
+
type.format = 0
|
23
|
+
type.name = 'pass_through'
|
24
|
+
type
|
25
|
+
end
|
26
|
+
|
27
|
+
let!(:tm) do
|
28
|
+
tm = PG::TypeMapByClass.new
|
29
|
+
tm[Integer] = binaryenc_int
|
30
|
+
tm[Float] = textenc_float
|
31
|
+
tm[Symbol] = pass_through_type
|
32
|
+
tm
|
33
|
+
end
|
34
|
+
|
35
|
+
let!(:raise_class) do
|
36
|
+
Class.new
|
37
|
+
end
|
38
|
+
|
39
|
+
let!(:derived_tm) do
|
40
|
+
tm = Class.new(PG::TypeMapByClass) do
|
41
|
+
def array_type_map_for(value)
|
42
|
+
PG::TextEncoder::Array.new name: '_INT4', oid: 1007, elements_type: PG::TextEncoder::Integer.new
|
43
|
+
end
|
44
|
+
end.new
|
45
|
+
tm[Integer] = proc{|value| textenc_int }
|
46
|
+
tm[raise_class] = proc{|value| /invalid/ }
|
47
|
+
tm[Array] = :array_type_map_for
|
48
|
+
tm
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should retrieve all conversions" do
|
52
|
+
expect( tm.coders ).to eq( {
|
53
|
+
Integer => binaryenc_int,
|
54
|
+
Float => textenc_float,
|
55
|
+
Symbol => pass_through_type,
|
56
|
+
} )
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should retrieve particular conversions" do
|
60
|
+
expect( tm[Integer] ).to eq(binaryenc_int)
|
61
|
+
expect( tm[Float] ).to eq(textenc_float)
|
62
|
+
expect( tm[Bignum] ).to be_nil
|
63
|
+
expect( derived_tm[raise_class] ).to be_kind_of(Proc)
|
64
|
+
expect( derived_tm[Array] ).to eq(:array_type_map_for)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should allow deletion of coders" do
|
68
|
+
tm[Integer] = nil
|
69
|
+
expect( tm[Integer] ).to be_nil
|
70
|
+
expect( tm.coders ).to eq( {
|
71
|
+
Float => textenc_float,
|
72
|
+
Symbol => pass_through_type,
|
73
|
+
} )
|
74
|
+
end
|
75
|
+
|
76
|
+
it "forwards query param conversions to the #default_type_map" do
|
77
|
+
tm1 = PG::TypeMapByColumn.new( [textenc_int, nil, nil] )
|
78
|
+
|
79
|
+
tm2 = PG::TypeMapByClass.new
|
80
|
+
tm2[Integer] = PG::TextEncoder::Integer.new name: 'INT2', oid: 21
|
81
|
+
tm2.default_type_map = tm1
|
82
|
+
|
83
|
+
res = @conn.exec_params( "SELECT $1, $2, $3::TEXT", ['1', 2, 3], 0, tm2 )
|
84
|
+
|
85
|
+
expect( res.ftype(0) ).to eq( 23 ) # tm1
|
86
|
+
expect( res.ftype(1) ).to eq( 21 ) # tm2
|
87
|
+
expect( res.getvalue(0,2) ).to eq( "3" ) # TypeMapAllStrings
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Decoding Examples
|
92
|
+
#
|
93
|
+
|
94
|
+
it "should raise an error when used for results" do
|
95
|
+
res = @conn.exec_params( "SELECT 1", [], 1 )
|
96
|
+
expect{ res.type_map = tm }.to raise_error(NotImplementedError, /not suitable to map result values/)
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Encoding Examples
|
101
|
+
#
|
102
|
+
|
103
|
+
it "should allow mixed type conversions" do
|
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]']])
|
106
|
+
expect( res.ftype(0) ).to eq(20)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should expire the cache after changes to the coders" do
|
110
|
+
res = @conn.exec_params( "SELECT $1", [5], 0, tm )
|
111
|
+
expect( res.ftype(0) ).to eq(20)
|
112
|
+
|
113
|
+
tm[Integer] = textenc_int
|
114
|
+
|
115
|
+
res = @conn.exec_params( "SELECT $1", [5], 0, tm )
|
116
|
+
expect( res.ftype(0) ).to eq(23)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should allow mixed type conversions with derived type map" do
|
120
|
+
res = @conn.exec_params( "SELECT $1, $2", [6, [7]], 0, derived_tm )
|
121
|
+
expect( res.values ).to eq([['6', '{7}']])
|
122
|
+
expect( res.ftype(0) ).to eq(23)
|
123
|
+
expect( res.ftype(1) ).to eq(1007)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should raise TypeError with derived type map" do
|
127
|
+
expect{
|
128
|
+
@conn.exec_params( "SELECT $1", [raise_class.new], 0, derived_tm )
|
129
|
+
}.to raise_error(TypeError, /invalid type Regexp/)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should raise error on invalid coder object" do
|
133
|
+
tm[TrueClass] = "dummy"
|
134
|
+
expect{
|
135
|
+
res = @conn.exec_params( "SELECT $1", [true], 0, tm )
|
136
|
+
}.to raise_error(NoMethodError, /undefined method.*call.*dummy/)
|
137
|
+
end
|
138
|
+
end
|
@@ -46,6 +46,28 @@ describe PG::TypeMapByColumn do
|
|
46
46
|
expect( cm.oids ).to eq( [23, 25, 700, 123456, nil] )
|
47
47
|
end
|
48
48
|
|
49
|
+
it "should gracefully handle not initialized state" do
|
50
|
+
# PG::TypeMapByColumn is not initialized in allocate function, like other
|
51
|
+
# type maps, but in #initialize. So it might be not called by derived classes.
|
52
|
+
|
53
|
+
not_init = Class.new(PG::TypeMapByColumn) do
|
54
|
+
def initialize
|
55
|
+
# no super call
|
56
|
+
end
|
57
|
+
end.new
|
58
|
+
|
59
|
+
expect{ @conn.exec_params( "SELECT $1", [ 0 ], 0, not_init ) }.to raise_error(NotImplementedError)
|
60
|
+
|
61
|
+
res = @conn.exec( "SELECT 1" )
|
62
|
+
expect{ res.type_map = not_init }.to raise_error(NotImplementedError)
|
63
|
+
|
64
|
+
@conn.copy_data("COPY (SELECT 1) TO STDOUT") do
|
65
|
+
decoder = PG::TextDecoder::CopyRow.new(type_map: not_init)
|
66
|
+
expect{ @conn.get_copy_data(false, decoder) }.to raise_error(NotImplementedError)
|
67
|
+
@conn.get_copy_data
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
49
71
|
|
50
72
|
#
|
51
73
|
# Encoding Examples
|
@@ -95,6 +117,25 @@ describe PG::TypeMapByColumn do
|
|
95
117
|
}.to raise_error(ArgumentError, /mapped columns/)
|
96
118
|
end
|
97
119
|
|
120
|
+
it "should verify the default type map for query params as well" do
|
121
|
+
tm1 = PG::TypeMapByColumn.new([])
|
122
|
+
expect{
|
123
|
+
@conn.exec_params( "SELECT $1", [ 123 ], 0, PG::TypeMapByColumn.new([nil]).with_default_type_map(tm1) )
|
124
|
+
}.to raise_error(ArgumentError, /mapped columns/)
|
125
|
+
end
|
126
|
+
|
127
|
+
it "forwards query param conversions to the #default_type_map" do
|
128
|
+
tm1 = PG::TypeMapByClass.new
|
129
|
+
tm1[Integer] = PG::TextEncoder::Integer.new name: 'INT2', oid: 21
|
130
|
+
|
131
|
+
tm2 = PG::TypeMapByColumn.new( [textenc_int, nil, nil] ).with_default_type_map( tm1 )
|
132
|
+
res = @conn.exec_params( "SELECT $1, $2, $3::TEXT", [1, 2, :abc], 0, tm2 )
|
133
|
+
|
134
|
+
expect( res.ftype(0) ).to eq( 23 ) # tm2
|
135
|
+
expect( res.ftype(1) ).to eq( 21 ) # tm1
|
136
|
+
expect( res.getvalue(0,2) ).to eq( "abc" ) # TypeMapAllStrings
|
137
|
+
end
|
138
|
+
|
98
139
|
#
|
99
140
|
# Decoding Examples
|
100
141
|
#
|
@@ -122,6 +163,52 @@ describe PG::TypeMapByColumn do
|
|
122
163
|
expect{ res.type_map = PG::TypeMapByColumn.new([]) }.to raise_error(ArgumentError, /mapped columns/)
|
123
164
|
end
|
124
165
|
|
166
|
+
it "should verify the default type map for result values as well" do
|
167
|
+
res = @conn.exec( "SELECT 1" )
|
168
|
+
tm1 = PG::TypeMapByColumn.new([])
|
169
|
+
expect{
|
170
|
+
res.type_map = PG::TypeMapByColumn.new([nil]).with_default_type_map(tm1)
|
171
|
+
}.to raise_error(ArgumentError, /mapped columns/)
|
172
|
+
end
|
173
|
+
|
174
|
+
it "forwards result value conversions to a TypeMapByOid as #default_type_map" do
|
175
|
+
# One run with implicit built TypeMapByColumn and another with online lookup
|
176
|
+
[0, 10].each do |max_rows|
|
177
|
+
tm1 = PG::TypeMapByOid.new
|
178
|
+
tm1.add_coder PG::TextDecoder::Integer.new name: 'INT2', oid: 21
|
179
|
+
tm1.max_rows_for_online_lookup = max_rows
|
180
|
+
|
181
|
+
tm2 = PG::TypeMapByColumn.new( [textdec_int, nil, nil] ).with_default_type_map( tm1 )
|
182
|
+
res = @conn.exec( "SELECT '1'::INT4, '2'::INT2, '3'::INT8" ).map_types!( tm2 )
|
183
|
+
|
184
|
+
expect( res.getvalue(0,0) ).to eq( 1 ) # tm2
|
185
|
+
expect( res.getvalue(0,1) ).to eq( 2 ) # tm1
|
186
|
+
expect( res.getvalue(0,2) ).to eq( "3" ) # TypeMapAllStrings
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it "forwards get_copy_data conversions to another TypeMapByColumn as #default_type_map" do
|
191
|
+
tm1 = PG::TypeMapByColumn.new( [textdec_int, nil, nil] )
|
192
|
+
tm2 = PG::TypeMapByColumn.new( [nil, textdec_int, nil] ).with_default_type_map( tm1 )
|
193
|
+
decoder = PG::TextDecoder::CopyRow.new(type_map: tm2)
|
194
|
+
@conn.copy_data("COPY (SELECT 1, 2, 3) TO STDOUT", decoder) do
|
195
|
+
expect( @conn.get_copy_data ).to eq( [1, 2, '3'] )
|
196
|
+
@conn.get_copy_data
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
it "will deny copy queries with different column count" do
|
201
|
+
[[2, 2], [2, 3], [3, 2]].each do |cols1, cols2|
|
202
|
+
tm1 = PG::TypeMapByColumn.new( [textdec_int, nil, nil][0, cols1] )
|
203
|
+
tm2 = PG::TypeMapByColumn.new( [nil, textdec_int, nil][0, cols2] ).with_default_type_map( tm1 )
|
204
|
+
decoder = PG::TextDecoder::CopyRow.new(type_map: tm2)
|
205
|
+
@conn.copy_data("COPY (SELECT 1, 2, 3) TO STDOUT", decoder) do
|
206
|
+
expect{ @conn.get_copy_data }.to raise_error(ArgumentError, /number of copy fields/)
|
207
|
+
@conn.get_copy_data
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
125
212
|
#
|
126
213
|
# Decoding Examples text format
|
127
214
|
#
|