pg 1.1.0 → 1.2.2
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 +0 -6595
- data/History.rdoc +101 -1
- data/Manifest.txt +3 -2
- data/README-Windows.rdoc +4 -4
- data/README.ja.rdoc +1 -2
- data/README.rdoc +43 -8
- data/Rakefile +7 -5
- data/Rakefile.cross +46 -44
- data/ext/errorcodes.def +68 -0
- data/ext/errorcodes.txt +19 -2
- data/ext/extconf.rb +6 -6
- data/ext/pg.c +138 -99
- data/ext/pg.h +33 -26
- data/ext/pg_binary_decoder.c +20 -16
- data/ext/pg_binary_encoder.c +13 -12
- data/ext/pg_coder.c +5 -5
- data/ext/pg_connection.c +413 -321
- data/ext/pg_copy_coder.c +5 -3
- data/ext/pg_record_coder.c +490 -0
- data/ext/pg_result.c +282 -128
- data/ext/pg_text_decoder.c +14 -8
- data/ext/pg_text_encoder.c +180 -48
- data/ext/pg_tuple.c +14 -6
- data/ext/pg_type_map.c +1 -1
- data/ext/pg_type_map_all_strings.c +4 -4
- data/ext/pg_type_map_by_class.c +4 -3
- data/ext/pg_type_map_by_column.c +7 -6
- data/ext/pg_type_map_by_mri_type.c +1 -1
- data/ext/pg_type_map_by_oid.c +3 -2
- data/ext/pg_type_map_in_ruby.c +1 -1
- data/ext/{util.c → pg_util.c} +5 -5
- data/ext/{util.h → pg_util.h} +0 -0
- data/lib/pg.rb +5 -5
- data/lib/pg/basic_type_mapping.rb +79 -16
- data/lib/pg/binary_decoder.rb +1 -0
- data/lib/pg/coder.rb +22 -1
- data/lib/pg/connection.rb +2 -2
- data/lib/pg/constants.rb +1 -0
- data/lib/pg/exceptions.rb +1 -0
- data/lib/pg/result.rb +13 -1
- data/lib/pg/text_decoder.rb +2 -3
- data/lib/pg/text_encoder.rb +8 -18
- data/lib/pg/type_map_by_column.rb +2 -1
- data/spec/helpers.rb +18 -16
- data/spec/pg/basic_type_mapping_spec.rb +151 -14
- data/spec/pg/connection_spec.rb +162 -54
- data/spec/pg/result_spec.rb +193 -3
- data/spec/pg/tuple_spec.rb +55 -2
- data/spec/pg/type_map_by_column_spec.rb +5 -1
- data/spec/pg/type_spec.rb +180 -6
- metadata +39 -45
- metadata.gz.sig +0 -0
data/lib/pg/connection.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'pg' unless defined?( PG )
|
4
5
|
require 'uri'
|
@@ -47,7 +48,7 @@ class PG::Connection
|
|
47
48
|
|
48
49
|
if args.length == 1
|
49
50
|
case args.first
|
50
|
-
when URI, /\A#{URI
|
51
|
+
when URI, /\A#{URI::ABS_URI_REF}\z/
|
51
52
|
uri = URI(args.first)
|
52
53
|
options.merge!( Hash[URI.decode_www_form( uri.query )] ) if uri.query
|
53
54
|
when /=/
|
@@ -288,4 +289,3 @@ class PG::Connection
|
|
288
289
|
# pg-1.1.0+ defaults to libpq's async API for query related blocking methods
|
289
290
|
self.async_api = true
|
290
291
|
end # class PG::Connection
|
291
|
-
|
data/lib/pg/constants.rb
CHANGED
data/lib/pg/exceptions.rb
CHANGED
data/lib/pg/result.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'pg' unless defined?( PG )
|
4
5
|
|
@@ -9,12 +10,23 @@ class PG::Result
|
|
9
10
|
#
|
10
11
|
# +type_map+: a PG::TypeMap instance.
|
11
12
|
#
|
12
|
-
#
|
13
|
+
# This method is equal to #type_map= , but returns self, so that calls can be chained.
|
14
|
+
#
|
15
|
+
# See also PG::BasicTypeMapForResults
|
13
16
|
def map_types!(type_map)
|
14
17
|
self.type_map = type_map
|
15
18
|
return self
|
16
19
|
end
|
17
20
|
|
21
|
+
# Set the data type for all field name returning methods.
|
22
|
+
#
|
23
|
+
# +type+: a Symbol defining the field name type.
|
24
|
+
#
|
25
|
+
# This method is equal to #field_name_type= , but returns self, so that calls can be chained.
|
26
|
+
def field_names_as(type)
|
27
|
+
self.field_name_type = type
|
28
|
+
return self
|
29
|
+
end
|
18
30
|
|
19
31
|
### Return a String representation of the object suitable for debugging.
|
20
32
|
def inspect
|
data/lib/pg/text_decoder.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'date'
|
4
5
|
require 'json'
|
@@ -6,10 +7,8 @@ require 'json'
|
|
6
7
|
module PG
|
7
8
|
module TextDecoder
|
8
9
|
class Date < SimpleDecoder
|
9
|
-
ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
|
10
|
-
|
11
10
|
def decode(string, tuple=nil, field=nil)
|
12
|
-
if string =~
|
11
|
+
if string =~ /\A(\d{4})-(\d\d)-(\d\d)\z/
|
13
12
|
::Date.new $1.to_i, $2.to_i, $3.to_i
|
14
13
|
else
|
15
14
|
string
|
data/lib/pg/text_encoder.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'json'
|
4
5
|
require 'ipaddr'
|
@@ -6,36 +7,26 @@ require 'ipaddr'
|
|
6
7
|
module PG
|
7
8
|
module TextEncoder
|
8
9
|
class Date < SimpleEncoder
|
9
|
-
STRFTIME_ISO_DATE = "%Y-%m-%d".freeze
|
10
10
|
def encode(value)
|
11
|
-
value.respond_to?(:strftime) ? value.strftime(
|
11
|
+
value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d") : value
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
class TimestampWithoutTimeZone < SimpleEncoder
|
16
|
-
STRFTIME_ISO_DATETIME_WITHOUT_TIMEZONE = "%Y-%m-%d %H:%M:%S.%N".freeze
|
17
16
|
def encode(value)
|
18
|
-
value.respond_to?(:strftime) ? value.strftime(
|
17
|
+
value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S.%N") : value
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
22
21
|
class TimestampUtc < SimpleEncoder
|
23
|
-
STRFTIME_ISO_DATETIME_WITHOUT_TIMEZONE_UTC = "%Y-%m-%d %H:%M:%S.%N".freeze
|
24
22
|
def encode(value)
|
25
|
-
value.respond_to?(:utc) ? value.utc.strftime(
|
23
|
+
value.respond_to?(:utc) ? value.utc.strftime("%Y-%m-%d %H:%M:%S.%N") : value
|
26
24
|
end
|
27
25
|
end
|
28
26
|
|
29
27
|
class TimestampWithTimeZone < SimpleEncoder
|
30
|
-
STRFTIME_ISO_DATETIME_WITH_TIMEZONE = "%Y-%m-%d %H:%M:%S.%N %:z".freeze
|
31
28
|
def encode(value)
|
32
|
-
value.respond_to?(:strftime) ? value.strftime(
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
class Numeric < SimpleEncoder
|
37
|
-
def encode(value)
|
38
|
-
value.is_a?(BigDecimal) ? value.to_s('F') : value
|
29
|
+
value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S.%N %:z") : value
|
39
30
|
end
|
40
31
|
end
|
41
32
|
|
@@ -51,12 +42,12 @@ module PG
|
|
51
42
|
when IPAddr
|
52
43
|
default_prefix = (value.family == Socket::AF_INET ? 32 : 128)
|
53
44
|
s = value.to_s
|
54
|
-
|
45
|
+
if value.respond_to?(:prefix)
|
55
46
|
prefix = value.prefix
|
56
|
-
|
47
|
+
else
|
57
48
|
range = value.to_range
|
58
49
|
prefix = default_prefix - Math.log(((range.end.to_i - range.begin.to_i) + 1), 2).to_i
|
59
|
-
|
50
|
+
end
|
60
51
|
s << "/" << prefix.to_s if prefix != default_prefix
|
61
52
|
s
|
62
53
|
else
|
@@ -66,4 +57,3 @@ module PG
|
|
66
57
|
end
|
67
58
|
end
|
68
59
|
end # module PG
|
69
|
-
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'pg' unless defined?( PG )
|
4
5
|
|
@@ -9,7 +10,7 @@ class PG::TypeMapByColumn
|
|
9
10
|
end
|
10
11
|
|
11
12
|
def inspect
|
12
|
-
type_strings = coders.map{|c| c ?
|
13
|
+
type_strings = coders.map{|c| c ? c.inspect_short : 'nil' }
|
13
14
|
"#<#{self.class} #{type_strings.join(' ')}>"
|
14
15
|
end
|
15
16
|
end
|
data/spec/helpers.rb
CHANGED
@@ -22,6 +22,7 @@ module PG::TestingHelpers
|
|
22
22
|
|
23
23
|
mod.around( :each ) do |example|
|
24
24
|
begin
|
25
|
+
@conn.set_default_encoding
|
25
26
|
@conn.exec( 'BEGIN' ) unless example.metadata[:without_transaction]
|
26
27
|
desc = example.source_location.join(':')
|
27
28
|
@conn.exec %Q{SET application_name TO '%s'} %
|
@@ -171,18 +172,18 @@ module PG::TestingHelpers
|
|
171
172
|
datadir = testdir + 'data'
|
172
173
|
pidfile = datadir + 'postmaster.pid'
|
173
174
|
if pidfile.exist? && pid = pidfile.read.chomp.to_i
|
174
|
-
|
175
|
+
trace "pidfile (%p) exists: %d" % [ pidfile, pid ]
|
175
176
|
begin
|
176
177
|
Process.kill( 0, pid )
|
177
178
|
rescue Errno::ESRCH
|
178
|
-
|
179
|
+
trace "No postmaster running for %s" % [ datadir ]
|
179
180
|
# Process isn't alive, so don't try to stop it
|
180
181
|
else
|
181
|
-
|
182
|
+
trace "Stopping lingering database at PID %d" % [ pid ]
|
182
183
|
run 'pg_ctl', '-D', datadir.to_s, '-m', 'fast', 'stop'
|
183
184
|
end
|
184
185
|
else
|
185
|
-
|
186
|
+
trace "No pidfile (%p)" % [ pidfile ]
|
186
187
|
end
|
187
188
|
end
|
188
189
|
end
|
@@ -193,12 +194,12 @@ module PG::TestingHelpers
|
|
193
194
|
require 'pg'
|
194
195
|
stop_existing_postmasters()
|
195
196
|
|
196
|
-
|
197
|
+
trace "Setting up test database for #{description}"
|
197
198
|
@test_pgdata = TEST_DIRECTORY + 'data'
|
198
199
|
@test_pgdata.mkpath
|
199
200
|
|
200
|
-
|
201
|
-
ENV['PGPORT']
|
201
|
+
ENV['PGPORT'] ||= "54321"
|
202
|
+
@port = ENV['PGPORT'].to_i
|
202
203
|
ENV['PGHOST'] = 'localhost'
|
203
204
|
@conninfo = "host=localhost port=#{@port} dbname=test"
|
204
205
|
|
@@ -208,7 +209,7 @@ module PG::TestingHelpers
|
|
208
209
|
begin
|
209
210
|
unless (@test_pgdata+"postgresql.conf").exist?
|
210
211
|
FileUtils.rm_rf( @test_pgdata, :verbose => $DEBUG )
|
211
|
-
|
212
|
+
trace "Running initdb"
|
212
213
|
log_and_run @logfile, 'initdb', '-E', 'UTF8', '--no-locale', '-D', @test_pgdata.to_s
|
213
214
|
end
|
214
215
|
|
@@ -217,7 +218,7 @@ module PG::TestingHelpers
|
|
217
218
|
'-D', @test_pgdata.to_s, 'start'
|
218
219
|
sleep 2
|
219
220
|
|
220
|
-
|
221
|
+
trace "Creating the test DB"
|
221
222
|
log_and_run @logfile, 'psql', '-e', '-c', 'DROP DATABASE IF EXISTS test', 'postgres'
|
222
223
|
log_and_run @logfile, 'createdb', '-e', 'test'
|
223
224
|
|
@@ -238,7 +239,7 @@ module PG::TestingHelpers
|
|
238
239
|
|
239
240
|
|
240
241
|
def teardown_testing_db( conn )
|
241
|
-
|
242
|
+
trace "Tearing down test database"
|
242
243
|
|
243
244
|
if conn
|
244
245
|
check_for_lingering_connections( conn )
|
@@ -318,20 +319,19 @@ module PG::TestingHelpers
|
|
318
319
|
return ConnStillUsableMatcher.new
|
319
320
|
end
|
320
321
|
|
321
|
-
def wait_for_polling_ok(conn)
|
322
|
-
|
323
|
-
status = conn.connect_poll
|
322
|
+
def wait_for_polling_ok(conn, meth = :connect_poll)
|
323
|
+
status = conn.send(meth)
|
324
324
|
|
325
325
|
while status != PG::PGRES_POLLING_OK
|
326
326
|
if status == PG::PGRES_POLLING_READING
|
327
|
-
select( [
|
327
|
+
select( [conn.socket_io], [], [], 5.0 ) or
|
328
328
|
raise "Asynchronous connection timed out!"
|
329
329
|
|
330
330
|
elsif status == PG::PGRES_POLLING_WRITING
|
331
|
-
select( [], [
|
331
|
+
select( [], [conn.socket_io], [], 5.0 ) or
|
332
332
|
raise "Asynchronous connection timed out!"
|
333
333
|
end
|
334
|
-
status = conn.
|
334
|
+
status = conn.send(meth)
|
335
335
|
end
|
336
336
|
end
|
337
337
|
|
@@ -376,5 +376,7 @@ RSpec.configure do |config|
|
|
376
376
|
config.filter_run_excluding( :postgresql_93 ) if PG.library_version < 90300
|
377
377
|
config.filter_run_excluding( :postgresql_94 ) if PG.library_version < 90400
|
378
378
|
config.filter_run_excluding( :postgresql_95 ) if PG.library_version < 90500
|
379
|
+
config.filter_run_excluding( :postgresql_96 ) if PG.library_version < 90600
|
379
380
|
config.filter_run_excluding( :postgresql_10 ) if PG.library_version < 100000
|
381
|
+
config.filter_run_excluding( :postgresql_12 ) if PG.library_version < 120000
|
380
382
|
end
|
@@ -21,6 +21,14 @@ ensure
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
def expect_to_typecase_result_value_warning
|
25
|
+
warning = 'Warning: no type cast defined for type "name" with oid 19. '\
|
26
|
+
"Please cast this type explicitly to TEXT to be safe for future changes.\n"\
|
27
|
+
'Warning: no type cast defined for type "regproc" with oid 24. '\
|
28
|
+
"Please cast this type explicitly to TEXT to be safe for future changes.\n"
|
29
|
+
expect { yield }.to output(warning).to_stderr
|
30
|
+
end
|
31
|
+
|
24
32
|
describe 'Basic type mapping' do
|
25
33
|
|
26
34
|
describe PG::BasicTypeMapForQueries do
|
@@ -32,8 +40,8 @@ describe 'Basic type mapping' do
|
|
32
40
|
# Encoding Examples
|
33
41
|
#
|
34
42
|
|
35
|
-
it "should do basic param encoding"
|
36
|
-
res = @conn.exec_params( "SELECT $1::int8
|
43
|
+
it "should do basic param encoding" do
|
44
|
+
res = @conn.exec_params( "SELECT $1::int8, $2::float, $3, $4::TEXT",
|
37
45
|
[1, 2.1, true, "b"], nil, basic_type_mapping )
|
38
46
|
|
39
47
|
expect( res.values ).to eq( [
|
@@ -43,26 +51,132 @@ describe 'Basic type mapping' do
|
|
43
51
|
expect( result_typenames(res) ).to eq( ['bigint', 'double precision', 'boolean', 'text'] )
|
44
52
|
end
|
45
53
|
|
46
|
-
it "should do
|
47
|
-
res = @conn.exec_params( "SELECT $1
|
48
|
-
|
49
|
-
|
50
|
-
|
54
|
+
it "should do basic Time encoding" do
|
55
|
+
res = @conn.exec_params( "SELECT $1 AT TIME ZONE '-02'",
|
56
|
+
[Time.new(2019, 12, 8, 20, 38, 12.123, "-01:00")], nil, basic_type_mapping )
|
57
|
+
|
58
|
+
expect( res.values ).to eq( [[ "2019-12-08 23:38:12.123" ]] )
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should do basic param encoding of various float values" do
|
62
|
+
res = @conn.exec_params( "SELECT $1::float, $2::float, $3::float, $4::float, $5::float, $6::float, $7::float, $8::float, $9::float, $10::float, $11::float, $12::float",
|
63
|
+
[0, 7, 9, 0.1, 0.9, -0.11, 10.11,
|
64
|
+
9876543210987654321e-400,
|
65
|
+
9876543210987654321e400,
|
66
|
+
-1.234567890123456789e-280,
|
67
|
+
-1.234567890123456789e280,
|
68
|
+
9876543210987654321e280
|
69
|
+
], nil, basic_type_mapping )
|
70
|
+
|
71
|
+
expect( res.values[0][0, 9] ).to eq(
|
72
|
+
[ "0", "7", "9", "0.1", "0.9", "-0.11", "10.11", "0", "Infinity" ]
|
73
|
+
)
|
74
|
+
|
75
|
+
expect( res.values[0][9] ).to match( /^-1\.2345678901234\d*e\-280$/ )
|
76
|
+
expect( res.values[0][10] ).to match( /^-1\.2345678901234\d*e\+280$/ )
|
77
|
+
expect( res.values[0][11] ).to match( /^9\.8765432109876\d*e\+298$/ )
|
78
|
+
|
79
|
+
expect( result_typenames(res) ).to eq( ['double precision'] * 12 )
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should do default array-as-array param encoding" do
|
83
|
+
expect( basic_type_mapping.encode_array_as).to eq(:array)
|
84
|
+
res = @conn.exec_params( "SELECT $1,$2,$3,$4,$5,$6", [
|
85
|
+
[1, 2, 3], # Integer -> bigint[]
|
86
|
+
[[1, 2], [3, nil]], # Integer two dimensions -> bigint[]
|
87
|
+
[1.11, 2.21], # Float -> double precision[]
|
88
|
+
['/,"'.gsub("/", "\\"), nil, 'abcäöü'], # String -> text[]
|
89
|
+
[BigDecimal("123.45")], # BigDecimal -> numeric[]
|
90
|
+
[IPAddr.new('1234::5678')], # IPAddr -> inet[]
|
51
91
|
], nil, basic_type_mapping )
|
52
92
|
|
53
93
|
expect( res.values ).to eq( [[
|
54
|
-
'{1,2,3}',
|
94
|
+
'{1,2,3}',
|
95
|
+
'{{1,2},{3,NULL}}',
|
55
96
|
'{1.11,2.21}',
|
56
97
|
'{"//,/"",NULL,abcäöü}'.gsub("/", "\\"),
|
98
|
+
'{123.45}',
|
99
|
+
'{1234::5678}',
|
57
100
|
]] )
|
58
101
|
|
59
|
-
expect( result_typenames(res) ).to eq( ['bigint[]', 'bigint[]', 'double precision[]', 'text[]'] )
|
102
|
+
expect( result_typenames(res) ).to eq( ['bigint[]', 'bigint[]', 'double precision[]', 'text[]', 'numeric[]', 'inet[]'] )
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should do default array-as-array param encoding with Time objects" do
|
106
|
+
res = @conn.exec_params( "SELECT $1", [
|
107
|
+
[Time.new(2019, 12, 8, 20, 38, 12.123, "-01:00")], # Time -> timestamptz[]
|
108
|
+
], nil, basic_type_mapping )
|
109
|
+
|
110
|
+
expect( res.values[0][0] ).to match( /\{\"2019-12-08 \d\d:38:12.123[+-]\d\d\"\}/ )
|
111
|
+
expect( result_typenames(res) ).to eq( ['timestamp with time zone[]'] )
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should do array-as-json encoding" do
|
115
|
+
basic_type_mapping.encode_array_as = :json
|
116
|
+
expect( basic_type_mapping.encode_array_as).to eq(:json)
|
117
|
+
|
118
|
+
res = @conn.exec_params( "SELECT $1::JSON, $2::JSON", [
|
119
|
+
[1, {a: 5}, true, ["a", 2], [3.4, nil]],
|
120
|
+
['/,"'.gsub("/", "\\"), nil, 'abcäöü'],
|
121
|
+
], nil, basic_type_mapping )
|
122
|
+
|
123
|
+
expect( res.values ).to eq( [[
|
124
|
+
'[1,{"a":5},true,["a",2],[3.4,null]]',
|
125
|
+
'["//,/"",null,"abcäöü"]'.gsub("/", "\\"),
|
126
|
+
]] )
|
127
|
+
|
128
|
+
expect( result_typenames(res) ).to eq( ['json', 'json'] )
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should do hash-as-json encoding" do
|
132
|
+
res = @conn.exec_params( "SELECT $1::JSON, $2::JSON", [
|
133
|
+
{a: 5, b: ["a", 2], c: nil},
|
134
|
+
{qu: '/,"'.gsub("/", "\\"), ni: nil, uml: 'abcäöü'},
|
135
|
+
], nil, basic_type_mapping )
|
136
|
+
|
137
|
+
expect( res.values ).to eq( [[
|
138
|
+
'{"a":5,"b":["a",2],"c":null}',
|
139
|
+
'{"qu":"//,/"","ni":null,"uml":"abcäöü"}'.gsub("/", "\\"),
|
140
|
+
]] )
|
141
|
+
|
142
|
+
expect( result_typenames(res) ).to eq( ['json', 'json'] )
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "Record encoding" do
|
146
|
+
before :all do
|
147
|
+
@conn.exec("CREATE TYPE test_record1 AS (i int, d float, t text)")
|
148
|
+
@conn.exec("CREATE TYPE test_record2 AS (i int, r test_record1)")
|
149
|
+
end
|
150
|
+
|
151
|
+
after :all do
|
152
|
+
@conn.exec("DROP TYPE IF EXISTS test_record2 CASCADE")
|
153
|
+
@conn.exec("DROP TYPE IF EXISTS test_record1 CASCADE")
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should do array-as-record encoding" do
|
157
|
+
basic_type_mapping.encode_array_as = :record
|
158
|
+
expect( basic_type_mapping.encode_array_as).to eq(:record)
|
159
|
+
|
160
|
+
res = @conn.exec_params( "SELECT $1::test_record1, $2::test_record2, $3::text", [
|
161
|
+
[5, 3.4, "txt"],
|
162
|
+
[1, [2, 4.5, "bcd"]],
|
163
|
+
[4, 5, 6],
|
164
|
+
], nil, basic_type_mapping )
|
165
|
+
|
166
|
+
expect( res.values ).to eq( [[
|
167
|
+
'(5,3.4,txt)',
|
168
|
+
'(1,"(2,4.5,bcd)")',
|
169
|
+
'("4","5","6")',
|
170
|
+
]] )
|
171
|
+
|
172
|
+
expect( result_typenames(res) ).to eq( ['test_record1', 'test_record2', 'text'] )
|
173
|
+
end
|
60
174
|
end
|
61
175
|
|
62
176
|
it "should do bigdecimal param encoding" do
|
63
177
|
large = ('123456790'*10) << '.' << ('012345679')
|
64
178
|
res = @conn.exec_params( "SELECT $1::numeric,$2::numeric",
|
65
|
-
[BigDecimal
|
179
|
+
[BigDecimal('1'), BigDecimal(large)], nil, basic_type_mapping )
|
66
180
|
|
67
181
|
expect( res.values ).to eq( [
|
68
182
|
[ "1.0", large ],
|
@@ -82,6 +196,23 @@ describe 'Basic type mapping' do
|
|
82
196
|
expect( result_typenames(res) ).to eq( ['inet', 'inet', 'cidr', 'cidr'] )
|
83
197
|
end
|
84
198
|
|
199
|
+
it "should do array of string encoding on unknown classes" do
|
200
|
+
iv = Class.new do
|
201
|
+
def to_s
|
202
|
+
"abc"
|
203
|
+
end
|
204
|
+
end.new
|
205
|
+
res = @conn.exec_params( "SELECT $1", [
|
206
|
+
[iv, iv], # Unknown -> text[]
|
207
|
+
], nil, basic_type_mapping )
|
208
|
+
|
209
|
+
expect( res.values ).to eq( [[
|
210
|
+
'{abc,abc}',
|
211
|
+
]] )
|
212
|
+
|
213
|
+
expect( result_typenames(res) ).to eq( ['text[]'] )
|
214
|
+
end
|
215
|
+
|
85
216
|
end
|
86
217
|
|
87
218
|
|
@@ -95,7 +226,7 @@ describe 'Basic type mapping' do
|
|
95
226
|
# Decoding Examples
|
96
227
|
#
|
97
228
|
|
98
|
-
it "should do OID based type conversions"
|
229
|
+
it "should do OID based type conversions" do
|
99
230
|
res = @conn.exec( "SELECT 1, 'a', 2.0::FLOAT, TRUE, '2013-06-30'::DATE, generate_series(4,5)" )
|
100
231
|
expect( res.map_types!(basic_type_mapping).values ).to eq( [
|
101
232
|
[ 1, 'a', 2.0, true, Date.new(2013,6,30), 4 ],
|
@@ -187,7 +318,9 @@ describe 'Basic type mapping' do
|
|
187
318
|
it "should convert format #{format} timestamps per TimestampUtc" do
|
188
319
|
restore_type("timestamp") do
|
189
320
|
PG::BasicTypeRegistry.register_type 0, 'timestamp', nil, PG::TextDecoder::TimestampUtc
|
190
|
-
|
321
|
+
expect_to_typecase_result_value_warning do
|
322
|
+
@conn.type_map_for_results = PG::BasicTypeMapForResults.new(@conn)
|
323
|
+
end
|
191
324
|
res = @conn.exec_params( "SELECT CAST('2013-07-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
|
192
325
|
CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
|
193
326
|
CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
|
@@ -209,7 +342,9 @@ describe 'Basic type mapping' do
|
|
209
342
|
restore_type("timestamp") do
|
210
343
|
PG::BasicTypeRegistry.register_type 0, 'timestamp', nil, PG::TextDecoder::TimestampUtcToLocal
|
211
344
|
PG::BasicTypeRegistry.register_type 1, 'timestamp', nil, PG::BinaryDecoder::TimestampUtcToLocal
|
212
|
-
|
345
|
+
expect_to_typecase_result_value_warning do
|
346
|
+
@conn.type_map_for_results = PG::BasicTypeMapForResults.new(@conn)
|
347
|
+
end
|
213
348
|
res = @conn.exec_params( "SELECT CAST('2013-07-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
|
214
349
|
CAST('1913-12-31 23:58:59.1231-03' AS TIMESTAMP WITHOUT TIME ZONE),
|
215
350
|
CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
|
@@ -231,7 +366,9 @@ describe 'Basic type mapping' do
|
|
231
366
|
restore_type("timestamp") do
|
232
367
|
PG::BasicTypeRegistry.register_type 0, 'timestamp', nil, PG::TextDecoder::TimestampLocal
|
233
368
|
PG::BasicTypeRegistry.register_type 1, 'timestamp', nil, PG::BinaryDecoder::TimestampLocal
|
234
|
-
|
369
|
+
expect_to_typecase_result_value_warning do
|
370
|
+
@conn.type_map_for_results = PG::BasicTypeMapForResults.new(@conn)
|
371
|
+
end
|
235
372
|
res = @conn.exec_params( "SELECT CAST('2013-07-31 23:58:59' AS TIMESTAMP WITHOUT TIME ZONE),
|
236
373
|
CAST('1913-12-31 23:58:59.1231' AS TIMESTAMP WITHOUT TIME ZONE),
|
237
374
|
CAST('4714-11-24 23:58:59.1231-03 BC' AS TIMESTAMP WITHOUT TIME ZONE),
|