pg 0.18.0 → 1.1.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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data/BSDL +2 -2
- data/ChangeLog +1221 -4
- data/History.rdoc +200 -0
- data/Manifest.txt +5 -18
- data/README-Windows.rdoc +15 -26
- data/README.rdoc +27 -10
- data/Rakefile +33 -24
- data/Rakefile.cross +57 -39
- data/ext/errorcodes.def +37 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +16 -1
- data/ext/extconf.rb +29 -35
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +27 -39
- data/ext/pg.c +27 -53
- data/ext/pg.h +66 -83
- data/ext/pg_binary_decoder.c +75 -6
- data/ext/pg_binary_encoder.c +14 -12
- data/ext/pg_coder.c +83 -13
- data/ext/pg_connection.c +627 -351
- data/ext/pg_copy_coder.c +44 -9
- data/ext/pg_result.c +364 -134
- data/ext/pg_text_decoder.c +605 -46
- data/ext/pg_text_encoder.c +95 -76
- data/ext/pg_tuple.c +541 -0
- data/ext/pg_type_map.c +20 -13
- data/ext/pg_type_map_by_column.c +7 -7
- data/ext/pg_type_map_by_mri_type.c +2 -2
- data/ext/pg_type_map_in_ruby.c +4 -7
- data/ext/util.c +7 -7
- data/ext/util.h +3 -3
- data/lib/pg/basic_type_mapping.rb +105 -45
- data/lib/pg/binary_decoder.rb +22 -0
- data/lib/pg/coder.rb +1 -1
- data/lib/pg/connection.rb +109 -39
- data/lib/pg/constants.rb +1 -1
- data/lib/pg/exceptions.rb +1 -1
- data/lib/pg/result.rb +11 -6
- data/lib/pg/text_decoder.rb +25 -20
- data/lib/pg/text_encoder.rb +43 -1
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +1 -1
- data/lib/pg.rb +21 -11
- data/spec/helpers.rb +50 -25
- data/spec/pg/basic_type_mapping_spec.rb +287 -30
- data/spec/pg/connection_spec.rb +695 -282
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +59 -17
- data/spec/pg/tuple_spec.rb +280 -0
- data/spec/pg/type_map_by_class_spec.rb +3 -3
- data/spec/pg/type_map_by_column_spec.rb +1 -1
- data/spec/pg/type_map_by_mri_type_spec.rb +2 -2
- data/spec/pg/type_map_by_oid_spec.rb +1 -1
- data/spec/pg/type_map_in_ruby_spec.rb +1 -1
- data/spec/pg/type_map_spec.rb +1 -1
- data/spec/pg/type_spec.rb +319 -35
- data/spec/pg_spec.rb +2 -2
- data.tar.gz.sig +0 -0
- metadata +68 -68
- metadata.gz.sig +0 -0
- data/sample/array_insert.rb +0 -20
- data/sample/async_api.rb +0 -106
- data/sample/async_copyto.rb +0 -39
- data/sample/async_mixed.rb +0 -56
- data/sample/check_conn.rb +0 -21
- data/sample/copyfrom.rb +0 -81
- data/sample/copyto.rb +0 -19
- data/sample/cursor.rb +0 -21
- data/sample/disk_usage_report.rb +0 -186
- data/sample/issue-119.rb +0 -94
- data/sample/losample.rb +0 -69
- data/sample/minimal-testcase.rb +0 -17
- data/sample/notify_wait.rb +0 -72
- data/sample/pg_statistics.rb +0 -294
- data/sample/replication_monitor.rb +0 -231
- data/sample/test_binary_values.rb +0 -33
- data/sample/wal_shipper.rb +0 -434
- data/sample/warehouse_partitions.rb +0 -320
data/ext/util.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*
|
2
2
|
* util.c - Utils for ruby-pg
|
3
|
-
* $Id: util.c,v
|
3
|
+
* $Id: util.c,v fc1c4deb1398 2018/06/25 12:02:09 kanis $
|
4
4
|
*
|
5
5
|
*/
|
6
6
|
|
@@ -15,9 +15,9 @@ static const char base64_encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk
|
|
15
15
|
* in-place (with _out_ == _in_).
|
16
16
|
*/
|
17
17
|
void
|
18
|
-
base64_encode( char *out, char *in, int len)
|
18
|
+
base64_encode( char *out, const char *in, int len)
|
19
19
|
{
|
20
|
-
char *in_ptr = in + len;
|
20
|
+
const unsigned char *in_ptr = (const unsigned char *)in + len;
|
21
21
|
char *out_ptr = out + BASE64_ENCODED_SIZE(len);
|
22
22
|
int part_len = len % 3;
|
23
23
|
|
@@ -72,12 +72,12 @@ static const unsigned char base64_decode_table[] =
|
|
72
72
|
* It is possible to decode a string in-place (with _out_ == _in_).
|
73
73
|
*/
|
74
74
|
int
|
75
|
-
base64_decode( char *out, char *in, unsigned int len)
|
75
|
+
base64_decode( char *out, const char *in, unsigned int len)
|
76
76
|
{
|
77
77
|
unsigned char a, b, c, d;
|
78
|
-
unsigned char *in_ptr = (unsigned char *)in;
|
78
|
+
const unsigned char *in_ptr = (const unsigned char *)in;
|
79
79
|
unsigned char *out_ptr = (unsigned char *)out;
|
80
|
-
unsigned char *iend_ptr = (unsigned char *)in + len;
|
80
|
+
const unsigned char *iend_ptr = (unsigned char *)in + len;
|
81
81
|
|
82
82
|
for(;;){
|
83
83
|
if( in_ptr+3 < iend_ptr &&
|
@@ -124,7 +124,7 @@ base64_decode( char *out, char *in, unsigned int len)
|
|
124
124
|
* At most n bytes will be examined from each string.
|
125
125
|
*/
|
126
126
|
int
|
127
|
-
|
127
|
+
rbpg_strncasecmp(const char *s1, const char *s2, size_t n)
|
128
128
|
{
|
129
129
|
while (n-- > 0)
|
130
130
|
{
|
data/ext/util.h
CHANGED
@@ -57,9 +57,9 @@
|
|
57
57
|
#define BASE64_ENCODED_SIZE(strlen) (((strlen) + 2) / 3 * 4)
|
58
58
|
#define BASE64_DECODED_SIZE(base64len) (((base64len) + 3) / 4 * 3)
|
59
59
|
|
60
|
-
void base64_encode( char *out, char *in, int len);
|
61
|
-
int base64_decode( char *out, char *in, unsigned int len);
|
60
|
+
void base64_encode( char *out, const char *in, int len);
|
61
|
+
int base64_decode( char *out, const char *in, unsigned int len);
|
62
62
|
|
63
|
-
int
|
63
|
+
int rbpg_strncasecmp(const char *s1, const char *s2, size_t n);
|
64
64
|
|
65
65
|
#endif /* end __utils_h */
|
@@ -1,7 +1,27 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
2
|
|
3
3
|
require 'pg' unless defined?( PG )
|
4
4
|
|
5
|
+
# This module defines the mapping between OID and encoder/decoder classes for PG::BasicTypeMapForResults, PG::BasicTypeMapForQueries and PG::BasicTypeMapBasedOnResult.
|
6
|
+
#
|
7
|
+
# Additional types can be added like so:
|
8
|
+
#
|
9
|
+
# require 'pg'
|
10
|
+
# require 'ipaddr'
|
11
|
+
#
|
12
|
+
# class InetDecoder < PG::SimpleDecoder
|
13
|
+
# def decode(string, tuple=nil, field=nil)
|
14
|
+
# IPAddr.new(string)
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# class InetEncoder < PG::SimpleEncoder
|
18
|
+
# def encode(ip_addr)
|
19
|
+
# ip_addr.to_s
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # 0 if for text format, can also be 1 for binary
|
24
|
+
# PG::BasicTypeRegistry.register_type(0, 'inet', InetEncoder, InetDecoder)
|
5
25
|
module PG::BasicTypeRegistry
|
6
26
|
# An instance of this class stores the coders that should be used for a given wire format (text or binary)
|
7
27
|
# and type cast direction (encoder or decoder).
|
@@ -25,9 +45,9 @@ module PG::BasicTypeRegistry
|
|
25
45
|
|
26
46
|
# populate the enum types
|
27
47
|
_enums, leaves = leaves.partition { |row| row['typinput'] == 'enum_in' }
|
28
|
-
|
29
|
-
|
30
|
-
|
48
|
+
# enums.each do |row|
|
49
|
+
# coder_map[row['oid'].to_i] = OID::Enum.new
|
50
|
+
# end
|
31
51
|
|
32
52
|
# populate the base types
|
33
53
|
leaves.find_all { |row| coders_by_name.key?(row['typname']) }.each do |row|
|
@@ -41,9 +61,9 @@ module PG::BasicTypeRegistry
|
|
41
61
|
_records_by_oid = result.group_by { |row| row['oid'] }
|
42
62
|
|
43
63
|
# populate composite types
|
44
|
-
|
45
|
-
|
46
|
-
|
64
|
+
# nodes.each do |row|
|
65
|
+
# add_oid row, records_by_oid, coder_map
|
66
|
+
# end
|
47
67
|
|
48
68
|
if arraycoder
|
49
69
|
# populate array types
|
@@ -62,11 +82,11 @@ module PG::BasicTypeRegistry
|
|
62
82
|
end
|
63
83
|
|
64
84
|
# populate range types
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
85
|
+
# ranges.find_all { |row| coder_map.key? row['rngsubtype'].to_i }.each do |row|
|
86
|
+
# subcoder = coder_map[row['rngsubtype'].to_i]
|
87
|
+
# range = OID::Range.new subcoder
|
88
|
+
# coder_map[row['oid'].to_i] = range
|
89
|
+
# end
|
70
90
|
|
71
91
|
@coders = coder_map.values
|
72
92
|
@coders_by_name = @coders.inject({}){|h, t| h[t.name] = t; h }
|
@@ -137,6 +157,7 @@ module PG::BasicTypeRegistry
|
|
137
157
|
# Register an OID type named +name+ with a typecasting encoder and decoder object in
|
138
158
|
# +type+. +name+ should correspond to the `typname` column in
|
139
159
|
# the `pg_type` table.
|
160
|
+
# +format+ can be 0 for text format and 1 for binary.
|
140
161
|
def self.register_type(format, name, encoder_class, decoder_class)
|
141
162
|
CODERS_BY_NAME[format] ||= { encoder: {}, decoder: {} }
|
142
163
|
CODERS_BY_NAME[format][:encoder][name] = encoder_class.new(name: name, format: format) if encoder_class
|
@@ -145,8 +166,14 @@ module PG::BasicTypeRegistry
|
|
145
166
|
|
146
167
|
# Alias the +old+ type to the +new+ type.
|
147
168
|
def self.alias_type(format, new, old)
|
148
|
-
|
149
|
-
|
169
|
+
[:encoder, :decoder].each do |ende|
|
170
|
+
enc = CODERS_BY_NAME[format][ende][old]
|
171
|
+
if enc
|
172
|
+
CODERS_BY_NAME[format][ende][new] = enc
|
173
|
+
else
|
174
|
+
CODERS_BY_NAME[format][ende].delete(new)
|
175
|
+
end
|
176
|
+
end
|
150
177
|
end
|
151
178
|
|
152
179
|
register_type 0, 'int2', PG::TextEncoder::Integer, PG::TextDecoder::Integer
|
@@ -154,46 +181,47 @@ module PG::BasicTypeRegistry
|
|
154
181
|
alias_type 0, 'int8', 'int2'
|
155
182
|
alias_type 0, 'oid', 'int2'
|
156
183
|
|
157
|
-
|
184
|
+
register_type 0, 'numeric', PG::TextEncoder::Numeric, PG::TextDecoder::Numeric
|
158
185
|
register_type 0, 'text', PG::TextEncoder::String, PG::TextDecoder::String
|
159
186
|
alias_type 0, 'varchar', 'text'
|
160
187
|
alias_type 0, 'char', 'text'
|
161
188
|
alias_type 0, 'bpchar', 'text'
|
162
189
|
alias_type 0, 'xml', 'text'
|
163
190
|
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
#
|
170
|
-
#
|
191
|
+
# FIXME: why are we keeping these types as strings?
|
192
|
+
# alias_type 'tsvector', 'text'
|
193
|
+
# alias_type 'interval', 'text'
|
194
|
+
# alias_type 'macaddr', 'text'
|
195
|
+
# alias_type 'uuid', 'text'
|
196
|
+
#
|
197
|
+
# register_type 'money', OID::Money.new
|
171
198
|
# There is no PG::TextEncoder::Bytea, because it's simple and more efficient to send bytea-data
|
172
199
|
# in binary format, either with PG::BinaryEncoder::Bytea or in Hash param format.
|
173
200
|
register_type 0, 'bytea', nil, PG::TextDecoder::Bytea
|
174
201
|
register_type 0, 'bool', PG::TextEncoder::Boolean, PG::TextDecoder::Boolean
|
175
|
-
#
|
176
|
-
#
|
177
|
-
|
202
|
+
# register_type 'bit', OID::Bit.new
|
203
|
+
# register_type 'varbit', OID::Bit.new
|
204
|
+
|
178
205
|
register_type 0, 'float4', PG::TextEncoder::Float, PG::TextDecoder::Float
|
179
206
|
alias_type 0, 'float8', 'float4'
|
180
207
|
|
181
208
|
register_type 0, 'timestamp', PG::TextEncoder::TimestampWithoutTimeZone, PG::TextDecoder::TimestampWithoutTimeZone
|
182
209
|
register_type 0, 'timestamptz', PG::TextEncoder::TimestampWithTimeZone, PG::TextDecoder::TimestampWithTimeZone
|
183
210
|
register_type 0, 'date', PG::TextEncoder::Date, PG::TextDecoder::Date
|
184
|
-
#
|
185
|
-
#
|
186
|
-
#
|
187
|
-
#
|
188
|
-
#
|
189
|
-
#
|
190
|
-
#
|
191
|
-
|
192
|
-
|
193
|
-
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
|
211
|
+
# register_type 'time', OID::Time.new
|
212
|
+
#
|
213
|
+
# register_type 'path', OID::Text.new
|
214
|
+
# register_type 'point', OID::Point.new
|
215
|
+
# register_type 'polygon', OID::Text.new
|
216
|
+
# register_type 'circle', OID::Text.new
|
217
|
+
# register_type 'hstore', OID::Hstore.new
|
218
|
+
register_type 0, 'json', PG::TextEncoder::JSON, PG::TextDecoder::JSON
|
219
|
+
alias_type 0, 'jsonb', 'json'
|
220
|
+
# register_type 'citext', OID::Text.new
|
221
|
+
# register_type 'ltree', OID::Text.new
|
222
|
+
#
|
223
|
+
register_type 0, 'inet', PG::TextEncoder::Inet, PG::TextDecoder::Inet
|
224
|
+
alias_type 0, 'cidr', 'inet'
|
197
225
|
|
198
226
|
|
199
227
|
|
@@ -212,6 +240,8 @@ module PG::BasicTypeRegistry
|
|
212
240
|
register_type 1, 'bool', PG::BinaryEncoder::Boolean, PG::BinaryDecoder::Boolean
|
213
241
|
register_type 1, 'float4', nil, PG::BinaryDecoder::Float
|
214
242
|
register_type 1, 'float8', nil, PG::BinaryDecoder::Float
|
243
|
+
register_type 1, 'timestamp', nil, PG::BinaryDecoder::TimestampUtc
|
244
|
+
register_type 1, 'timestamptz', nil, PG::BinaryDecoder::TimestampUtcToLocal
|
215
245
|
end
|
216
246
|
|
217
247
|
# Simple set of rules for type casting common PostgreSQL types to Ruby.
|
@@ -226,8 +256,8 @@ end
|
|
226
256
|
#
|
227
257
|
# Example:
|
228
258
|
# conn = PG::Connection.new
|
229
|
-
# # Assign a default ruleset for type casts of
|
230
|
-
# conn.
|
259
|
+
# # Assign a default ruleset for type casts of output values.
|
260
|
+
# conn.type_map_for_results = PG::BasicTypeMapForResults.new(conn)
|
231
261
|
# # Execute a query.
|
232
262
|
# res = conn.exec_params( "SELECT $1::INT", ['5'] )
|
233
263
|
# # Retrieve and cast the result value. Value format is 0 (text) and OID is 20. Therefore typecasting
|
@@ -236,8 +266,28 @@ end
|
|
236
266
|
#
|
237
267
|
# PG::TypeMapByOid#fit_to_result(result, false) can be used to generate
|
238
268
|
# a result independent PG::TypeMapByColumn type map, which can subsequently be used
|
239
|
-
# to cast #get_copy_data fields
|
269
|
+
# to cast #get_copy_data fields:
|
270
|
+
#
|
271
|
+
# For the following table:
|
272
|
+
# conn.exec( "CREATE TABLE copytable AS VALUES('a', 123, '{5,4,3}'::INT[])" )
|
240
273
|
#
|
274
|
+
# # Retrieve table OIDs per empty result set.
|
275
|
+
# res = conn.exec( "SELECT * FROM copytable LIMIT 0" )
|
276
|
+
# # Build a type map for common database to ruby type decoders.
|
277
|
+
# btm = PG::BasicTypeMapForResults.new(conn)
|
278
|
+
# # Build a PG::TypeMapByColumn with decoders suitable for copytable.
|
279
|
+
# tm = btm.build_column_map( res )
|
280
|
+
# row_decoder = PG::TextDecoder::CopyRow.new type_map: tm
|
281
|
+
#
|
282
|
+
# conn.copy_data( "COPY copytable TO STDOUT", row_decoder ) do |res|
|
283
|
+
# while row=conn.get_copy_data
|
284
|
+
# p row
|
285
|
+
# end
|
286
|
+
# end
|
287
|
+
# This prints the rows with type casted columns:
|
288
|
+
# ["a", 123, [5, 4, 3]]
|
289
|
+
#
|
290
|
+
# See also PG::BasicTypeMapBasedOnResult for the encoder direction and PG::BasicTypeRegistry for the definition of additional types.
|
241
291
|
class PG::BasicTypeMapForResults < PG::TypeMapByOid
|
242
292
|
include PG::BasicTypeRegistry
|
243
293
|
|
@@ -290,12 +340,17 @@ end
|
|
290
340
|
#
|
291
341
|
# # Retrieve table OIDs per empty result set.
|
292
342
|
# res = conn.exec( "SELECT * FROM copytable LIMIT 0" )
|
293
|
-
#
|
343
|
+
# # Build a type map for common ruby to database type encoders.
|
344
|
+
# btm = PG::BasicTypeMapBasedOnResult.new(conn)
|
345
|
+
# # Build a PG::TypeMapByColumn with encoders suitable for copytable.
|
346
|
+
# tm = btm.build_column_map( res )
|
294
347
|
# row_encoder = PG::TextEncoder::CopyRow.new type_map: tm
|
295
348
|
#
|
296
349
|
# conn.copy_data( "COPY copytable FROM STDIN", row_encoder ) do |res|
|
297
350
|
# conn.put_copy_data ['a', 123, [5,4,3]]
|
298
351
|
# end
|
352
|
+
# This inserts a single row into copytable with type casts from ruby to
|
353
|
+
# database types.
|
299
354
|
class PG::BasicTypeMapBasedOnResult < PG::TypeMapByOid
|
300
355
|
include PG::BasicTypeRegistry
|
301
356
|
|
@@ -314,15 +369,16 @@ end
|
|
314
369
|
# OIDs of supported type casts are not hard-coded in the sources, but are retrieved from the
|
315
370
|
# PostgreSQL's pg_type table in PG::BasicTypeMapForQueries.new .
|
316
371
|
#
|
317
|
-
# Query params are type casted based on the
|
372
|
+
# Query params are type casted based on the class of the given value.
|
318
373
|
#
|
319
374
|
# Higher level libraries will most likely not make use of this class, but use their
|
320
|
-
# own set of rules to choose suitable
|
375
|
+
# own derivation of PG::TypeMapByClass or another set of rules to choose suitable
|
376
|
+
# encoders and decoders for the values to be sent.
|
321
377
|
#
|
322
378
|
# Example:
|
323
379
|
# conn = PG::Connection.new
|
324
380
|
# # Assign a default ruleset for type casts of input and output values.
|
325
|
-
# conn.
|
381
|
+
# conn.type_map_for_queries = PG::BasicTypeMapForQueries.new(conn)
|
326
382
|
# # Execute a query. The Integer param value is typecasted internally by PG::BinaryEncoder::Int8.
|
327
383
|
# # The format of the parameter is set to 1 (binary) and the OID of this parameter is set to 20 (int8).
|
328
384
|
# res = conn.exec_params( "SELECT $1", [5] )
|
@@ -385,6 +441,10 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
385
441
|
# to unnecessary type conversions on server side.
|
386
442
|
Integer => [0, 'int8'],
|
387
443
|
Float => [0, 'float8'],
|
444
|
+
BigDecimal => [0, 'numeric'],
|
445
|
+
# We use text format and no type OID for IPAddr, because setting the OID can lead
|
446
|
+
# to unnecessary inet/cidr conversions on the server side.
|
447
|
+
IPAddr => [0, 'inet'],
|
388
448
|
Array => :get_array_type,
|
389
449
|
}
|
390
450
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
module PG
|
4
|
+
module BinaryDecoder
|
5
|
+
# Convenience classes for timezone options
|
6
|
+
class TimestampUtc < Timestamp
|
7
|
+
def initialize(params={})
|
8
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_UTC))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
class TimestampUtcToLocal < Timestamp
|
12
|
+
def initialize(params={})
|
13
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
class TimestampLocal < Timestamp
|
17
|
+
def initialize(params={})
|
18
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_LOCAL | PG::Coder::TIMESTAMP_APP_LOCAL))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end # module PG
|
data/lib/pg/coder.rb
CHANGED
data/lib/pg/connection.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
2
|
|
3
3
|
require 'pg' unless defined?( PG )
|
4
|
+
require 'uri'
|
4
5
|
|
5
6
|
# The PostgreSQL connection class. The interface for this class is based on
|
6
7
|
# {libpq}[http://www.postgresql.org/docs/9.2/interactive/libpq.html], the C
|
@@ -34,48 +35,57 @@ class PG::Connection
|
|
34
35
|
def self::parse_connect_args( *args )
|
35
36
|
return '' if args.empty?
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
hash_arg = args.last.is_a?( Hash ) ? args.pop : {}
|
39
|
+
option_string = ''
|
40
|
+
options = {}
|
40
41
|
|
41
42
|
# Parameter 'fallback_application_name' was introduced in PostgreSQL 9.0
|
42
43
|
# together with PQescapeLiteral().
|
43
|
-
if PG::Connection.instance_methods.find{|m| m.to_sym == :escape_literal }
|
44
|
-
|
45
|
-
appname = PG::Connection.quote_connstr( appname )
|
46
|
-
connopts = ["fallback_application_name=#{appname}"]
|
47
|
-
else
|
48
|
-
connopts = []
|
44
|
+
if PG::Connection.instance_methods.find {|m| m.to_sym == :escape_literal }
|
45
|
+
options[:fallback_application_name] = $0.sub( /^(.{30}).{4,}(.{30})$/ ){ $1+"..."+$2 }
|
49
46
|
end
|
50
47
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
48
|
+
if args.length == 1
|
49
|
+
case args.first
|
50
|
+
when URI, /\A#{URI.regexp}\z/
|
51
|
+
uri = URI(args.first)
|
52
|
+
options.merge!( Hash[URI.decode_www_form( uri.query )] ) if uri.query
|
53
|
+
when /=/
|
54
|
+
# Option string style
|
55
|
+
option_string = args.first.to_s
|
56
|
+
else
|
57
|
+
# Positional parameters
|
58
|
+
options[CONNECT_ARGUMENT_ORDER.first.to_sym] = args.first
|
56
59
|
end
|
57
|
-
end
|
58
|
-
|
59
|
-
# Option string style
|
60
|
-
if args.length == 1 && args.first.to_s.index( '=' )
|
61
|
-
connopts.unshift( args.first )
|
62
|
-
|
63
|
-
# Append positional parameters
|
64
60
|
else
|
65
|
-
|
66
|
-
|
61
|
+
max = CONNECT_ARGUMENT_ORDER.length
|
62
|
+
raise ArgumentError,
|
63
|
+
"Extra positional parameter %d: %p" % [ max + 1, args[max] ] if args.length > max
|
67
64
|
|
68
|
-
|
69
|
-
|
70
|
-
connopts.push( "%s=%s" % [key, PG::Connection.quote_connstr(val.to_s)] )
|
65
|
+
CONNECT_ARGUMENT_ORDER.zip( args ) do |(k,v)|
|
66
|
+
options[ k.to_sym ] = v if v
|
71
67
|
end
|
72
68
|
end
|
73
69
|
|
74
|
-
|
70
|
+
options.merge!( hash_arg )
|
71
|
+
|
72
|
+
if uri
|
73
|
+
uri.host = nil if options[:host]
|
74
|
+
uri.port = nil if options[:port]
|
75
|
+
uri.user = nil if options[:user]
|
76
|
+
uri.password = nil if options[:password]
|
77
|
+
uri.path = '' if options[:dbname]
|
78
|
+
uri.query = URI.encode_www_form( options )
|
79
|
+
return uri.to_s.sub( /^#{uri.scheme}:(?!\/\/)/, "#{uri.scheme}://" )
|
80
|
+
else
|
81
|
+
option_string += ' ' unless option_string.empty? && options.empty?
|
82
|
+
return option_string + options.map { |k,v| "#{k}=#{quote_connstr(v)}" }.join( ' ' )
|
83
|
+
end
|
75
84
|
end
|
76
85
|
|
86
|
+
|
77
87
|
# call-seq:
|
78
|
-
# conn.copy_data( sql ) {|sql_result| ... } -> PG::Result
|
88
|
+
# conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
|
79
89
|
#
|
80
90
|
# Execute a copy process for transfering data to or from the server.
|
81
91
|
#
|
@@ -99,13 +109,26 @@ class PG::Connection
|
|
99
109
|
# of blocking mode of operation, #copy_data is preferred to raw calls
|
100
110
|
# of #put_copy_data, #get_copy_data and #put_copy_end.
|
101
111
|
#
|
112
|
+
# _coder_ can be a PG::Coder derivation
|
113
|
+
# (typically PG::TextEncoder::CopyRow or PG::TextDecoder::CopyRow).
|
114
|
+
# This enables encoding of data fields given to #put_copy_data
|
115
|
+
# or decoding of fields received by #get_copy_data.
|
116
|
+
#
|
102
117
|
# Example with CSV input format:
|
103
|
-
# conn.exec "create table my_table (a text,b text,c text,d text
|
118
|
+
# conn.exec "create table my_table (a text,b text,c text,d text)"
|
104
119
|
# conn.copy_data "COPY my_table FROM STDIN CSV" do
|
105
|
-
# conn.put_copy_data "some,
|
106
|
-
# conn.put_copy_data "more,
|
120
|
+
# conn.put_copy_data "some,data,to,copy\n"
|
121
|
+
# conn.put_copy_data "more,data,to,copy\n"
|
122
|
+
# end
|
123
|
+
# This creates +my_table+ and inserts two CSV rows.
|
124
|
+
#
|
125
|
+
# The same with text format encoder PG::TextEncoder::CopyRow
|
126
|
+
# and Array input:
|
127
|
+
# enco = PG::TextEncoder::CopyRow.new
|
128
|
+
# conn.copy_data "COPY my_table FROM STDIN", enco do
|
129
|
+
# conn.put_copy_data ['some', 'data', 'to', 'copy']
|
130
|
+
# conn.put_copy_data ['more', 'data', 'to', 'copy']
|
107
131
|
# end
|
108
|
-
# This creates +my_table+ and inserts two rows.
|
109
132
|
#
|
110
133
|
# Example with CSV output format:
|
111
134
|
# conn.copy_data "COPY my_table TO STDOUT CSV" do
|
@@ -114,8 +137,21 @@ class PG::Connection
|
|
114
137
|
# end
|
115
138
|
# end
|
116
139
|
# This prints all rows of +my_table+ to stdout:
|
117
|
-
# "some,
|
118
|
-
# "more,
|
140
|
+
# "some,data,to,copy\n"
|
141
|
+
# "more,data,to,copy\n"
|
142
|
+
#
|
143
|
+
# The same with text format decoder PG::TextDecoder::CopyRow
|
144
|
+
# and Array output:
|
145
|
+
# deco = PG::TextDecoder::CopyRow.new
|
146
|
+
# conn.copy_data "COPY my_table TO STDOUT", deco do
|
147
|
+
# while row=conn.get_copy_data
|
148
|
+
# p row
|
149
|
+
# end
|
150
|
+
# end
|
151
|
+
# This receives all rows of +my_table+ as ruby array:
|
152
|
+
# ["some", "data", "to", "copy"]
|
153
|
+
# ["more", "data", "to", "copy"]
|
154
|
+
|
119
155
|
def copy_data( sql, coder=nil )
|
120
156
|
res = exec( sql )
|
121
157
|
|
@@ -155,7 +191,7 @@ class PG::Connection
|
|
155
191
|
raise
|
156
192
|
else
|
157
193
|
res = get_last_result
|
158
|
-
if res.result_status != PGRES_COMMAND_OK
|
194
|
+
if !res || res.result_status != PGRES_COMMAND_OK
|
159
195
|
while get_copy_data
|
160
196
|
end
|
161
197
|
while get_result
|
@@ -214,8 +250,42 @@ class PG::Connection
|
|
214
250
|
end
|
215
251
|
end
|
216
252
|
|
217
|
-
|
253
|
+
# Method 'ssl_attribute' was introduced in PostgreSQL 9.5.
|
254
|
+
if self.instance_methods.find{|m| m.to_sym == :ssl_attribute }
|
255
|
+
# call-seq:
|
256
|
+
# conn.ssl_attributes -> Hash<String,String>
|
257
|
+
#
|
258
|
+
# Returns SSL-related information about the connection as key/value pairs
|
259
|
+
#
|
260
|
+
# The available attributes varies depending on the SSL library being used,
|
261
|
+
# and the type of connection.
|
262
|
+
#
|
263
|
+
# See also #ssl_attribute
|
264
|
+
def ssl_attributes
|
265
|
+
ssl_attribute_names.each.with_object({}) do |n,h|
|
266
|
+
h[n] = ssl_attribute(n)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
REDIRECT_METHODS = {
|
272
|
+
:exec => [:async_exec, :sync_exec],
|
273
|
+
:query => [:async_exec, :sync_exec],
|
274
|
+
:exec_params => [:async_exec_params, :sync_exec_params],
|
275
|
+
:prepare => [:async_prepare, :sync_prepare],
|
276
|
+
:exec_prepared => [:async_exec_prepared, :sync_exec_prepared],
|
277
|
+
:describe_portal => [:async_describe_portal, :sync_describe_portal],
|
278
|
+
:describe_prepared => [:async_describe_prepared, :sync_describe_prepared],
|
279
|
+
}
|
280
|
+
|
281
|
+
def self.async_api=(enable)
|
282
|
+
REDIRECT_METHODS.each do |ali, (async, sync)|
|
283
|
+
remove_method(ali) if method_defined?(ali)
|
284
|
+
alias_method( ali, enable ? async : sync )
|
285
|
+
end
|
286
|
+
end
|
218
287
|
|
219
|
-
#
|
220
|
-
|
288
|
+
# pg-1.1.0+ defaults to libpq's async API for query related blocking methods
|
289
|
+
self.async_api = true
|
290
|
+
end # class PG::Connection
|
221
291
|
|
data/lib/pg/constants.rb
CHANGED
data/lib/pg/exceptions.rb
CHANGED
data/lib/pg/result.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
2
|
|
3
3
|
require 'pg' unless defined?( PG )
|
4
4
|
|
@@ -12,15 +12,20 @@ class PG::Result
|
|
12
12
|
# See PG::BasicTypeMapForResults
|
13
13
|
def map_types!(type_map)
|
14
14
|
self.type_map = type_map
|
15
|
-
self
|
15
|
+
return self
|
16
16
|
end
|
17
17
|
|
18
|
+
|
19
|
+
### Return a String representation of the object suitable for debugging.
|
18
20
|
def inspect
|
19
21
|
str = self.to_s
|
20
|
-
str[-1,0] =
|
21
|
-
|
22
|
+
str[-1,0] = if cleared?
|
23
|
+
" cleared"
|
24
|
+
else
|
25
|
+
" status=#{res_status(result_status)} ntuples=#{ntuples} nfields=#{nfields} cmd_tuples=#{cmd_tuples}"
|
26
|
+
end
|
27
|
+
return str
|
22
28
|
end
|
29
|
+
|
23
30
|
end # class PG::Result
|
24
31
|
|
25
|
-
# Backward-compatible alias
|
26
|
-
PGresult = PG::Result
|
data/lib/pg/text_decoder.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'json'
|
2
5
|
|
3
6
|
module PG
|
4
7
|
module TextDecoder
|
@@ -7,36 +10,38 @@ module PG
|
|
7
10
|
|
8
11
|
def decode(string, tuple=nil, field=nil)
|
9
12
|
if string =~ ISO_DATE
|
10
|
-
|
13
|
+
::Date.new $1.to_i, $2.to_i, $3.to_i
|
11
14
|
else
|
12
15
|
string
|
13
16
|
end
|
14
17
|
end
|
15
18
|
end
|
16
19
|
|
17
|
-
class
|
18
|
-
ISO_DATETIME_WITHOUT_TIMEZONE = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
|
19
|
-
|
20
|
+
class JSON < SimpleDecoder
|
20
21
|
def decode(string, tuple=nil, field=nil)
|
21
|
-
|
22
|
-
Time.new $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, "#{$6}#{$7}".to_r
|
23
|
-
else
|
24
|
-
string
|
25
|
-
end
|
22
|
+
::JSON.parse(string, quirks_mode: true)
|
26
23
|
end
|
27
24
|
end
|
28
25
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
26
|
+
# Convenience classes for timezone options
|
27
|
+
class TimestampUtc < Timestamp
|
28
|
+
def initialize(params={})
|
29
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_UTC))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
class TimestampUtcToLocal < Timestamp
|
33
|
+
def initialize(params={})
|
34
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL))
|
38
35
|
end
|
39
36
|
end
|
37
|
+
class TimestampLocal < Timestamp
|
38
|
+
def initialize(params={})
|
39
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_LOCAL | PG::Coder::TIMESTAMP_APP_LOCAL))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# For backward compatibility:
|
44
|
+
TimestampWithoutTimeZone = TimestampLocal
|
45
|
+
TimestampWithTimeZone = Timestamp
|
40
46
|
end
|
41
47
|
end # module PG
|
42
|
-
|