pg 0.17.1 → 0.18.4
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/ChangeLog +2407 -2
- data/History.rdoc +68 -0
- data/Manifest.txt +29 -1
- data/README-Windows.rdoc +15 -26
- data/README.rdoc +52 -2
- data/Rakefile +56 -18
- data/Rakefile.cross +77 -49
- data/ext/extconf.rb +33 -26
- data/ext/pg.c +142 -21
- data/ext/pg.h +242 -6
- data/ext/pg_binary_decoder.c +162 -0
- data/ext/pg_binary_encoder.c +162 -0
- data/ext/pg_coder.c +479 -0
- data/ext/pg_connection.c +858 -553
- data/ext/pg_copy_coder.c +561 -0
- data/ext/pg_errors.c +6 -0
- data/ext/pg_result.c +479 -128
- data/ext/pg_text_decoder.c +421 -0
- data/ext/pg_text_encoder.c +663 -0
- data/ext/pg_type_map.c +159 -0
- data/ext/pg_type_map_all_strings.c +116 -0
- data/ext/pg_type_map_by_class.c +239 -0
- data/ext/pg_type_map_by_column.c +312 -0
- data/ext/pg_type_map_by_mri_type.c +284 -0
- data/ext/pg_type_map_by_oid.c +355 -0
- data/ext/pg_type_map_in_ruby.c +299 -0
- data/ext/util.c +149 -0
- data/ext/util.h +65 -0
- data/lib/pg/basic_type_mapping.rb +399 -0
- data/lib/pg/coder.rb +83 -0
- data/lib/pg/connection.rb +81 -29
- data/lib/pg/result.rb +13 -3
- data/lib/pg/text_decoder.rb +44 -0
- data/lib/pg/text_encoder.rb +27 -0
- data/lib/pg/type_map_by_column.rb +15 -0
- data/lib/pg.rb +12 -2
- data/spec/{lib/helpers.rb → helpers.rb} +101 -39
- data/spec/pg/basic_type_mapping_spec.rb +251 -0
- data/spec/pg/connection_spec.rb +516 -218
- data/spec/pg/result_spec.rb +216 -112
- data/spec/pg/type_map_by_class_spec.rb +138 -0
- data/spec/pg/type_map_by_column_spec.rb +222 -0
- data/spec/pg/type_map_by_mri_type_spec.rb +136 -0
- data/spec/pg/type_map_by_oid_spec.rb +149 -0
- data/spec/pg/type_map_in_ruby_spec.rb +164 -0
- data/spec/pg/type_map_spec.rb +22 -0
- data/spec/pg/type_spec.rb +697 -0
- data/spec/pg_spec.rb +24 -18
- data.tar.gz.sig +0 -0
- metadata +111 -45
- metadata.gz.sig +0 -0
@@ -0,0 +1,251 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require_relative '../helpers'
|
5
|
+
|
6
|
+
require 'pg'
|
7
|
+
|
8
|
+
describe 'Basic type mapping' do
|
9
|
+
|
10
|
+
describe PG::BasicTypeMapForQueries do
|
11
|
+
let!(:basic_type_mapping) do
|
12
|
+
PG::BasicTypeMapForQueries.new @conn
|
13
|
+
end
|
14
|
+
|
15
|
+
#
|
16
|
+
# Encoding Examples
|
17
|
+
#
|
18
|
+
|
19
|
+
it "should do basic param encoding", :ruby_19 do
|
20
|
+
res = @conn.exec_params( "SELECT $1::int8,$2::float,$3,$4::TEXT",
|
21
|
+
[1, 2.1, true, "b"], nil, basic_type_mapping )
|
22
|
+
|
23
|
+
expect( res.values ).to eq( [
|
24
|
+
[ "1", "2.1", "t", "b" ],
|
25
|
+
] )
|
26
|
+
|
27
|
+
expect( result_typenames(res) ).to eq( ['bigint', 'double precision', 'boolean', 'text'] )
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should do array param encoding" do
|
31
|
+
res = @conn.exec_params( "SELECT $1,$2,$3,$4", [
|
32
|
+
[1, 2, 3], [[1, 2], [3, nil]],
|
33
|
+
[1.11, 2.21],
|
34
|
+
['/,"'.gsub("/", "\\"), nil, 'abcäöü'],
|
35
|
+
], nil, basic_type_mapping )
|
36
|
+
|
37
|
+
expect( res.values ).to eq( [[
|
38
|
+
'{1,2,3}', '{{1,2},{3,NULL}}',
|
39
|
+
'{1.11,2.21}',
|
40
|
+
'{"//,/"",NULL,abcäöü}'.gsub("/", "\\"),
|
41
|
+
]] )
|
42
|
+
|
43
|
+
expect( result_typenames(res) ).to eq( ['bigint[]', 'bigint[]', 'double precision[]', 'text[]'] )
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
describe PG::BasicTypeMapForResults do
|
50
|
+
let!(:basic_type_mapping) do
|
51
|
+
PG::BasicTypeMapForResults.new @conn
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Decoding Examples
|
56
|
+
#
|
57
|
+
|
58
|
+
it "should do OID based type conversions", :ruby_19 do
|
59
|
+
res = @conn.exec( "SELECT 1, 'a', 2.0::FLOAT, TRUE, '2013-06-30'::DATE, generate_series(4,5)" )
|
60
|
+
expect( res.map_types!(basic_type_mapping).values ).to eq( [
|
61
|
+
[ 1, 'a', 2.0, true, Date.new(2013,6,30), 4 ],
|
62
|
+
[ 1, 'a', 2.0, true, Date.new(2013,6,30), 5 ],
|
63
|
+
] )
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Decoding Examples text+binary format converters
|
68
|
+
#
|
69
|
+
|
70
|
+
describe "connection wide type mapping" do
|
71
|
+
before :each do
|
72
|
+
@conn.type_map_for_results = basic_type_mapping
|
73
|
+
end
|
74
|
+
|
75
|
+
after :each do
|
76
|
+
@conn.type_map_for_results = PG::TypeMapAllStrings.new
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should do boolean type conversions" do
|
80
|
+
[1, 0].each do |format|
|
81
|
+
res = @conn.exec( "SELECT true::BOOLEAN, false::BOOLEAN, NULL::BOOLEAN", [], format )
|
82
|
+
expect( res.values ).to eq( [[true, false, nil]] )
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should do binary type conversions" do
|
87
|
+
[1, 0].each do |format|
|
88
|
+
res = @conn.exec( "SELECT E'\\\\000\\\\377'::BYTEA", [], format )
|
89
|
+
expect( res.values ).to eq( [[["00ff"].pack("H*")]] )
|
90
|
+
expect( res.values[0][0].encoding ).to eq( Encoding::ASCII_8BIT ) if Object.const_defined? :Encoding
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should do integer type conversions" do
|
95
|
+
[1, 0].each do |format|
|
96
|
+
res = @conn.exec( "SELECT -8999::INT2, -899999999::INT4, -8999999999999999999::INT8", [], format )
|
97
|
+
expect( res.values ).to eq( [[-8999, -899999999, -8999999999999999999]] )
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should do string type conversions" do
|
102
|
+
@conn.internal_encoding = 'utf-8' if Object.const_defined? :Encoding
|
103
|
+
[1, 0].each do |format|
|
104
|
+
res = @conn.exec( "SELECT 'abcäöü'::TEXT", [], format )
|
105
|
+
expect( res.values ).to eq( [['abcäöü']] )
|
106
|
+
expect( res.values[0][0].encoding ).to eq( Encoding::UTF_8 ) if Object.const_defined? :Encoding
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should do float type conversions" do
|
111
|
+
[1, 0].each do |format|
|
112
|
+
res = @conn.exec( "SELECT -8.999e3::FLOAT4,
|
113
|
+
8.999e10::FLOAT4,
|
114
|
+
-8999999999e-99::FLOAT8,
|
115
|
+
NULL::FLOAT4,
|
116
|
+
'NaN'::FLOAT4,
|
117
|
+
'Infinity'::FLOAT4,
|
118
|
+
'-Infinity'::FLOAT4
|
119
|
+
", [], format )
|
120
|
+
expect( res.getvalue(0,0) ).to be_within(1e-2).of(-8.999e3)
|
121
|
+
expect( res.getvalue(0,1) ).to be_within(1e5).of(8.999e10)
|
122
|
+
expect( res.getvalue(0,2) ).to be_within(1e-109).of(-8999999999e-99)
|
123
|
+
expect( res.getvalue(0,3) ).to be_nil
|
124
|
+
expect( res.getvalue(0,4) ).to be_nan
|
125
|
+
expect( res.getvalue(0,5) ).to eq( Float::INFINITY )
|
126
|
+
expect( res.getvalue(0,6) ).to eq( -Float::INFINITY )
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should do datetime without time zone type conversions" do
|
131
|
+
[0].each do |format|
|
132
|
+
res = @conn.exec( "SELECT CAST('2013-12-31 23:58:59+02' AS TIMESTAMP WITHOUT TIME ZONE),
|
133
|
+
CAST('1913-12-31 23:58:59.123-03' AS TIMESTAMP WITHOUT TIME ZONE),
|
134
|
+
CAST('infinity' AS TIMESTAMP WITHOUT TIME ZONE),
|
135
|
+
CAST('-infinity' AS TIMESTAMP WITHOUT TIME ZONE)", [], format )
|
136
|
+
expect( res.getvalue(0,0) ).to eq( Time.new(2013, 12, 31, 23, 58, 59) )
|
137
|
+
expect( res.getvalue(0,1) ).to be_within(1e-3).of(Time.new(1913, 12, 31, 23, 58, 59.123))
|
138
|
+
expect( res.getvalue(0,2) ).to eq( 'infinity' )
|
139
|
+
expect( res.getvalue(0,3) ).to eq( '-infinity' )
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should do datetime with time zone type conversions" do
|
144
|
+
[0].each do |format|
|
145
|
+
res = @conn.exec( "SELECT CAST('2013-12-31 23:58:59+02' AS TIMESTAMP WITH TIME ZONE),
|
146
|
+
CAST('1913-12-31 23:58:59.123-03' AS TIMESTAMP WITH TIME ZONE),
|
147
|
+
CAST('infinity' AS TIMESTAMP WITH TIME ZONE),
|
148
|
+
CAST('-infinity' AS TIMESTAMP WITH TIME ZONE)", [], format )
|
149
|
+
expect( res.getvalue(0,0) ).to eq( Time.new(2013, 12, 31, 23, 58, 59, "+02:00") )
|
150
|
+
expect( res.getvalue(0,1) ).to be_within(1e-3).of(Time.new(1913, 12, 31, 23, 58, 59.123, "-03:00"))
|
151
|
+
expect( res.getvalue(0,2) ).to eq( 'infinity' )
|
152
|
+
expect( res.getvalue(0,3) ).to eq( '-infinity' )
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should do date type conversions" do
|
157
|
+
[0].each do |format|
|
158
|
+
res = @conn.exec( "SELECT CAST('2113-12-31' AS DATE),
|
159
|
+
CAST('1913-12-31' AS DATE),
|
160
|
+
CAST('infinity' AS DATE),
|
161
|
+
CAST('-infinity' AS DATE)", [], format )
|
162
|
+
expect( res.getvalue(0,0) ).to eq( Date.new(2113, 12, 31) )
|
163
|
+
expect( res.getvalue(0,1) ).to eq( Date.new(1913, 12, 31) )
|
164
|
+
expect( res.getvalue(0,2) ).to eq( 'infinity' )
|
165
|
+
expect( res.getvalue(0,3) ).to eq( '-infinity' )
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should do array type conversions" do
|
170
|
+
[0].each do |format|
|
171
|
+
res = @conn.exec( "SELECT CAST('{1,2,3}' AS INT2[]), CAST('{{1,2},{3,4}}' AS INT2[][]),
|
172
|
+
CAST('{1,2,3}' AS INT4[]),
|
173
|
+
CAST('{1,2,3}' AS INT8[]),
|
174
|
+
CAST('{1,2,3}' AS TEXT[]),
|
175
|
+
CAST('{1,2,3}' AS VARCHAR[]),
|
176
|
+
CAST('{1,2,3}' AS FLOAT4[]),
|
177
|
+
CAST('{1,2,3}' AS FLOAT8[])
|
178
|
+
", [], format )
|
179
|
+
expect( res.getvalue(0,0) ).to eq( [1,2,3] )
|
180
|
+
expect( res.getvalue(0,1) ).to eq( [[1,2],[3,4]] )
|
181
|
+
expect( res.getvalue(0,2) ).to eq( [1,2,3] )
|
182
|
+
expect( res.getvalue(0,3) ).to eq( [1,2,3] )
|
183
|
+
expect( res.getvalue(0,4) ).to eq( ['1','2','3'] )
|
184
|
+
expect( res.getvalue(0,5) ).to eq( ['1','2','3'] )
|
185
|
+
expect( res.getvalue(0,6) ).to eq( [1.0,2.0,3.0] )
|
186
|
+
expect( res.getvalue(0,7) ).to eq( [1.0,2.0,3.0] )
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context "with usage of result oids for copy decoder selection" do
|
192
|
+
it "can type cast #copy_data output with explicit decoder" do
|
193
|
+
@conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
|
194
|
+
@conn.exec( "INSERT INTO copytable VALUES ('a', 123, '{5,4,3}'), ('b', 234, '{2,3}')" )
|
195
|
+
|
196
|
+
# Retrieve table OIDs per empty result.
|
197
|
+
res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
|
198
|
+
tm = basic_type_mapping.build_column_map( res )
|
199
|
+
row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
|
200
|
+
|
201
|
+
rows = []
|
202
|
+
@conn.copy_data( "COPY copytable TO STDOUT", row_decoder ) do |res|
|
203
|
+
while row=@conn.get_copy_data
|
204
|
+
rows << row
|
205
|
+
end
|
206
|
+
end
|
207
|
+
expect( rows ).to eq( [['a', 123, [5,4,3]], ['b', 234, [2,3]]] )
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
describe PG::BasicTypeMapBasedOnResult do
|
214
|
+
let!(:basic_type_mapping) do
|
215
|
+
PG::BasicTypeMapBasedOnResult.new @conn
|
216
|
+
end
|
217
|
+
|
218
|
+
context "with usage of result oids for bind params encoder selection" do
|
219
|
+
it "can type cast query params" do
|
220
|
+
@conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
|
221
|
+
|
222
|
+
# Retrieve table OIDs per empty result.
|
223
|
+
res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
|
224
|
+
tm = basic_type_mapping.build_column_map( res )
|
225
|
+
|
226
|
+
@conn.exec_params( "INSERT INTO copytable VALUES ($1, $2, $3)", ['a', 123, [5,4,3]], 0, tm )
|
227
|
+
@conn.exec_params( "INSERT INTO copytable VALUES ($1, $2, $3)", ['b', 234, [2,3]], 0, tm )
|
228
|
+
res = @conn.exec( "SELECT * FROM copytable" )
|
229
|
+
expect( res.values ).to eq( [['a', '123', '{5,4,3}'], ['b', '234', '{2,3}']] )
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context "with usage of result oids for copy encoder selection" do
|
234
|
+
it "can type cast #copy_data input with explicit encoder" do
|
235
|
+
@conn.exec( "CREATE TEMP TABLE copytable (t TEXT, i INT, ai INT[])" )
|
236
|
+
|
237
|
+
# Retrieve table OIDs per empty result set.
|
238
|
+
res = @conn.exec( "SELECT * FROM copytable LIMIT 0" )
|
239
|
+
tm = basic_type_mapping.build_column_map( res )
|
240
|
+
row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
|
241
|
+
|
242
|
+
@conn.copy_data( "COPY copytable FROM STDIN", row_encoder ) do |res|
|
243
|
+
@conn.put_copy_data ['a', 123, [5,4,3]]
|
244
|
+
@conn.put_copy_data ['b', 234, [2,3]]
|
245
|
+
end
|
246
|
+
res = @conn.exec( "SELECT * FROM copytable" )
|
247
|
+
expect( res.values ).to eq( [['a', '123', '{5,4,3}'], ['b', '234', '{2,3}']] )
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|