pg 0.19.0 → 1.1.0
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/ChangeLog +218 -1
- data/History.rdoc +106 -0
- data/Manifest.txt +5 -18
- data/README.rdoc +15 -5
- data/Rakefile +8 -9
- data/Rakefile.cross +19 -22
- data/ext/errorcodes.def +17 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +11 -1
- data/ext/extconf.rb +14 -32
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -39
- data/ext/pg.c +19 -48
- data/ext/pg.h +46 -81
- data/ext/pg_binary_decoder.c +69 -6
- data/ext/pg_coder.c +53 -4
- data/ext/pg_connection.c +401 -253
- data/ext/pg_copy_coder.c +10 -5
- data/ext/pg_result.c +359 -131
- data/ext/pg_text_decoder.c +597 -37
- data/ext/pg_text_encoder.c +6 -7
- data/ext/pg_tuple.c +541 -0
- data/ext/pg_type_map.c +14 -7
- data/ext/util.c +6 -6
- data/ext/util.h +2 -2
- data/lib/pg/basic_type_mapping.rb +40 -7
- data/lib/pg/binary_decoder.rb +22 -0
- data/lib/pg/coder.rb +1 -1
- data/lib/pg/connection.rb +27 -7
- data/lib/pg/constants.rb +1 -1
- data/lib/pg/exceptions.rb +1 -1
- data/lib/pg/result.rb +6 -5
- data/lib/pg/text_decoder.rb +19 -23
- data/lib/pg/text_encoder.rb +36 -2
- 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 +47 -19
- data/spec/pg/basic_type_mapping_spec.rb +230 -27
- data/spec/pg/connection_spec.rb +402 -275
- 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 +2 -2
- data/spec/pg/type_map_by_column_spec.rb +1 -1
- data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
- 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 +184 -12
- data/spec/pg_spec.rb +2 -2
- data.tar.gz.sig +0 -0
- metadata +47 -53
- 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
@@ -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).
|
@@ -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,7 +181,7 @@ 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'
|
@@ -193,8 +220,8 @@ module PG::BasicTypeRegistry
|
|
193
220
|
# register_type 'citext', OID::Text.new
|
194
221
|
# register_type 'ltree', OID::Text.new
|
195
222
|
#
|
196
|
-
|
197
|
-
|
223
|
+
register_type 0, 'inet', PG::TextEncoder::Inet, PG::TextDecoder::Inet
|
224
|
+
alias_type 0, 'cidr', 'inet'
|
198
225
|
|
199
226
|
|
200
227
|
|
@@ -213,6 +240,8 @@ module PG::BasicTypeRegistry
|
|
213
240
|
register_type 1, 'bool', PG::BinaryEncoder::Boolean, PG::BinaryDecoder::Boolean
|
214
241
|
register_type 1, 'float4', nil, PG::BinaryDecoder::Float
|
215
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
|
216
245
|
end
|
217
246
|
|
218
247
|
# Simple set of rules for type casting common PostgreSQL types to Ruby.
|
@@ -258,7 +287,7 @@ end
|
|
258
287
|
# This prints the rows with type casted columns:
|
259
288
|
# ["a", 123, [5, 4, 3]]
|
260
289
|
#
|
261
|
-
# See also PG::BasicTypeMapBasedOnResult for the encoder direction.
|
290
|
+
# See also PG::BasicTypeMapBasedOnResult for the encoder direction and PG::BasicTypeRegistry for the definition of additional types.
|
262
291
|
class PG::BasicTypeMapForResults < PG::TypeMapByOid
|
263
292
|
include PG::BasicTypeRegistry
|
264
293
|
|
@@ -412,6 +441,10 @@ class PG::BasicTypeMapForQueries < PG::TypeMapByClass
|
|
412
441
|
# to unnecessary type conversions on server side.
|
413
442
|
Integer => [0, 'int8'],
|
414
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'],
|
415
448
|
Array => :get_array_type,
|
416
449
|
}
|
417
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,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
2
|
|
3
3
|
require 'pg' unless defined?( PG )
|
4
4
|
require 'uri'
|
@@ -47,7 +47,7 @@ class PG::Connection
|
|
47
47
|
|
48
48
|
if args.length == 1
|
49
49
|
case args.first
|
50
|
-
when URI, URI.regexp
|
50
|
+
when URI, /\A#{URI.regexp}\z/
|
51
51
|
uri = URI(args.first)
|
52
52
|
options.merge!( Hash[URI.decode_www_form( uri.query )] ) if uri.query
|
53
53
|
when /=/
|
@@ -85,7 +85,7 @@ class PG::Connection
|
|
85
85
|
|
86
86
|
|
87
87
|
# call-seq:
|
88
|
-
# conn.copy_data( sql ) {|sql_result| ... } -> PG::Result
|
88
|
+
# conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
|
89
89
|
#
|
90
90
|
# Execute a copy process for transfering data to or from the server.
|
91
91
|
#
|
@@ -109,6 +109,11 @@ class PG::Connection
|
|
109
109
|
# of blocking mode of operation, #copy_data is preferred to raw calls
|
110
110
|
# of #put_copy_data, #get_copy_data and #put_copy_end.
|
111
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
|
+
#
|
112
117
|
# Example with CSV input format:
|
113
118
|
# conn.exec "create table my_table (a text,b text,c text,d text)"
|
114
119
|
# conn.copy_data "COPY my_table FROM STDIN CSV" do
|
@@ -263,9 +268,24 @@ class PG::Connection
|
|
263
268
|
end
|
264
269
|
end
|
265
270
|
|
266
|
-
|
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
|
267
287
|
|
268
|
-
#
|
269
|
-
|
270
|
-
|
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
|
271
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
|
|
@@ -19,12 +19,13 @@ class PG::Result
|
|
19
19
|
### Return a String representation of the object suitable for debugging.
|
20
20
|
def inspect
|
21
21
|
str = self.to_s
|
22
|
-
str[-1,0] =
|
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
|
23
27
|
return str
|
24
28
|
end
|
25
29
|
|
26
30
|
end # class PG::Result
|
27
31
|
|
28
|
-
# :stopdoc:
|
29
|
-
# Backward-compatible alias
|
30
|
-
PGresult = PG::Result
|
data/lib/pg/text_decoder.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
2
|
|
3
3
|
require 'date'
|
4
4
|
require 'json'
|
@@ -17,35 +17,31 @@ module PG
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
class
|
21
|
-
ISO_DATETIME_WITHOUT_TIMEZONE = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
|
22
|
-
|
20
|
+
class JSON < SimpleDecoder
|
23
21
|
def decode(string, tuple=nil, field=nil)
|
24
|
-
|
25
|
-
Time.new $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, "#{$6}#{$7}".to_r
|
26
|
-
else
|
27
|
-
string
|
28
|
-
end
|
22
|
+
::JSON.parse(string, quirks_mode: true)
|
29
23
|
end
|
30
24
|
end
|
31
25
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
if string =~ ISO_DATETIME_WITH_TIMEZONE
|
37
|
-
Time.new $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, "#{$6}#{$7}".to_r, "#{$8}:#{$9 || '00'}:#{$10 || '00'}"
|
38
|
-
else
|
39
|
-
string
|
40
|
-
end
|
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))
|
41
30
|
end
|
42
31
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
::JSON.load(string)
|
32
|
+
class TimestampUtcToLocal < Timestamp
|
33
|
+
def initialize(params={})
|
34
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL))
|
47
35
|
end
|
48
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
|
49
46
|
end
|
50
47
|
end # module PG
|
51
|
-
|
data/lib/pg/text_encoder.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
2
|
|
3
3
|
require 'json'
|
4
|
+
require 'ipaddr'
|
4
5
|
|
5
6
|
module PG
|
6
7
|
module TextEncoder
|
@@ -18,6 +19,13 @@ module PG
|
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
22
|
+
class TimestampUtc < SimpleEncoder
|
23
|
+
STRFTIME_ISO_DATETIME_WITHOUT_TIMEZONE_UTC = "%Y-%m-%d %H:%M:%S.%N".freeze
|
24
|
+
def encode(value)
|
25
|
+
value.respond_to?(:utc) ? value.utc.strftime(STRFTIME_ISO_DATETIME_WITHOUT_TIMEZONE_UTC) : value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
21
29
|
class TimestampWithTimeZone < SimpleEncoder
|
22
30
|
STRFTIME_ISO_DATETIME_WITH_TIMEZONE = "%Y-%m-%d %H:%M:%S.%N %:z".freeze
|
23
31
|
def encode(value)
|
@@ -25,9 +33,35 @@ module PG
|
|
25
33
|
end
|
26
34
|
end
|
27
35
|
|
36
|
+
class Numeric < SimpleEncoder
|
37
|
+
def encode(value)
|
38
|
+
value.is_a?(BigDecimal) ? value.to_s('F') : value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
28
42
|
class JSON < SimpleEncoder
|
29
43
|
def encode(value)
|
30
|
-
::JSON.
|
44
|
+
::JSON.generate(value, quirks_mode: true)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Inet < SimpleEncoder
|
49
|
+
def encode(value)
|
50
|
+
case value
|
51
|
+
when IPAddr
|
52
|
+
default_prefix = (value.family == Socket::AF_INET ? 32 : 128)
|
53
|
+
s = value.to_s
|
54
|
+
if value.respond_to?(:prefix)
|
55
|
+
prefix = value.prefix
|
56
|
+
else
|
57
|
+
range = value.to_range
|
58
|
+
prefix = default_prefix - Math.log(((range.end.to_i - range.begin.to_i) + 1), 2).to_i
|
59
|
+
end
|
60
|
+
s << "/" << prefix.to_s if prefix != default_prefix
|
61
|
+
s
|
62
|
+
else
|
63
|
+
value
|
64
|
+
end
|
31
65
|
end
|
32
66
|
end
|
33
67
|
end
|
data/lib/pg/tuple.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'pg' unless defined?( PG )
|
5
|
+
|
6
|
+
|
7
|
+
class PG::Tuple
|
8
|
+
|
9
|
+
### Return a String representation of the object suitable for debugging.
|
10
|
+
def inspect
|
11
|
+
"#<#{self.class} #{self.map{|k,v| "#{k}: #{v.inspect}" }.join(", ") }>"
|
12
|
+
end
|
13
|
+
|
14
|
+
def has_key?(key)
|
15
|
+
field_map.has_key?(key)
|
16
|
+
end
|
17
|
+
alias key? has_key?
|
18
|
+
|
19
|
+
def keys
|
20
|
+
field_names || field_map.keys.freeze
|
21
|
+
end
|
22
|
+
|
23
|
+
def each_key(&block)
|
24
|
+
if fn=field_names
|
25
|
+
fn.each(&block)
|
26
|
+
else
|
27
|
+
field_map.each_key(&block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/pg.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
2
|
|
3
3
|
begin
|
4
4
|
require 'pg_ext'
|
@@ -8,11 +8,22 @@ rescue LoadError
|
|
8
8
|
major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
|
9
9
|
raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
add_dll_path = proc do |path, &block|
|
12
|
+
begin
|
13
|
+
require 'ruby_installer/runtime'
|
14
|
+
RubyInstaller::Runtime.add_dll_directory(path, &block)
|
15
|
+
rescue LoadError
|
16
|
+
old_path = ENV['PATH']
|
17
|
+
ENV['PATH'] = "#{path};#{old_path}"
|
18
|
+
block.call
|
19
|
+
ENV['PATH'] = old_path
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Temporary add this directory for DLL search, so that libpq.dll can be found.
|
24
|
+
add_dll_path.call(__dir__) do
|
25
|
+
require "#{major_minor}/pg_ext"
|
26
|
+
end
|
16
27
|
else
|
17
28
|
raise
|
18
29
|
end
|
@@ -24,10 +35,10 @@ end
|
|
24
35
|
module PG
|
25
36
|
|
26
37
|
# Library version
|
27
|
-
VERSION = '
|
38
|
+
VERSION = '1.1.0'
|
28
39
|
|
29
40
|
# VCS revision
|
30
|
-
REVISION = %q$Revision:
|
41
|
+
REVISION = %q$Revision: ca83074366ac $
|
31
42
|
|
32
43
|
class NotAllCopyDataRetrieved < PG::Error
|
33
44
|
end
|
@@ -49,16 +60,15 @@ module PG
|
|
49
60
|
require 'pg/exceptions'
|
50
61
|
require 'pg/constants'
|
51
62
|
require 'pg/coder'
|
63
|
+
require 'pg/binary_decoder'
|
52
64
|
require 'pg/text_encoder'
|
53
65
|
require 'pg/text_decoder'
|
54
66
|
require 'pg/basic_type_mapping'
|
55
67
|
require 'pg/type_map_by_column'
|
56
68
|
require 'pg/connection'
|
57
69
|
require 'pg/result'
|
70
|
+
require 'pg/tuple'
|
58
71
|
|
59
72
|
end # module PG
|
60
73
|
|
61
74
|
|
62
|
-
# Backward-compatible aliase
|
63
|
-
PGError = PG::Error
|
64
|
-
|
data/spec/helpers.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
2
|
|
3
3
|
require 'pathname'
|
4
4
|
require 'rspec'
|
5
5
|
require 'shellwords'
|
6
6
|
require 'pg'
|
7
7
|
|
8
|
-
|
8
|
+
DEFAULT_TEST_DIR_STR = File.join(Dir.pwd, "tmp_test_specs")
|
9
|
+
TEST_DIR_STR = ENV['RUBY_PG_TEST_DIR'] || DEFAULT_TEST_DIR_STR
|
10
|
+
TEST_DIRECTORY = Pathname.new(TEST_DIR_STR)
|
9
11
|
|
10
12
|
module PG::TestingHelpers
|
11
13
|
|
@@ -21,11 +23,9 @@ module PG::TestingHelpers
|
|
21
23
|
mod.around( :each ) do |example|
|
22
24
|
begin
|
23
25
|
@conn.exec( 'BEGIN' ) unless example.metadata[:without_transaction]
|
24
|
-
|
25
|
-
|
26
|
-
@conn.
|
27
|
-
[@conn.escape_string(desc.slice(-60))]
|
28
|
-
end
|
26
|
+
desc = example.source_location.join(':')
|
27
|
+
@conn.exec %Q{SET application_name TO '%s'} %
|
28
|
+
[@conn.escape_string(desc.slice(-60))]
|
29
29
|
example.run
|
30
30
|
ensure
|
31
31
|
@conn.exec( 'ROLLBACK' ) unless example.metadata[:without_transaction]
|
@@ -251,7 +251,7 @@ module PG::TestingHelpers
|
|
251
251
|
|
252
252
|
def check_for_lingering_connections( conn )
|
253
253
|
conn.exec( "SELECT * FROM pg_stat_activity" ) do |res|
|
254
|
-
conns = res.find_all {|row| row['pid'].to_i != conn.backend_pid }
|
254
|
+
conns = res.find_all {|row| row['pid'].to_i != conn.backend_pid && ["client backend", nil].include?(row["backend_type"]) }
|
255
255
|
unless conns.empty?
|
256
256
|
puts "Lingering connections remain:"
|
257
257
|
conns.each do |row|
|
@@ -264,7 +264,7 @@ module PG::TestingHelpers
|
|
264
264
|
|
265
265
|
# Retrieve the names of the column types of a given result set.
|
266
266
|
def result_typenames(res)
|
267
|
-
@conn.
|
267
|
+
@conn.exec_params( "SELECT " + res.nfields.times.map{|i| "format_type($#{i*2+1},$#{i*2+2})"}.join(","),
|
268
268
|
res.nfields.times.map{|i| [res.ftype(i), res.fmod(i)] }.flatten ).
|
269
269
|
values[0]
|
270
270
|
end
|
@@ -318,6 +318,40 @@ module PG::TestingHelpers
|
|
318
318
|
return ConnStillUsableMatcher.new
|
319
319
|
end
|
320
320
|
|
321
|
+
def wait_for_polling_ok(conn)
|
322
|
+
socket = conn.socket_io
|
323
|
+
status = conn.connect_poll
|
324
|
+
|
325
|
+
while status != PG::PGRES_POLLING_OK
|
326
|
+
if status == PG::PGRES_POLLING_READING
|
327
|
+
select( [socket], [], [], 5.0 ) or
|
328
|
+
raise "Asynchronous connection timed out!"
|
329
|
+
|
330
|
+
elsif status == PG::PGRES_POLLING_WRITING
|
331
|
+
select( [], [socket], [], 5.0 ) or
|
332
|
+
raise "Asynchronous connection timed out!"
|
333
|
+
end
|
334
|
+
status = conn.connect_poll
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def wait_for_query_result(conn)
|
339
|
+
result = nil
|
340
|
+
loop do
|
341
|
+
# Buffer any incoming data on the socket until a full result is ready.
|
342
|
+
conn.consume_input
|
343
|
+
while conn.is_busy
|
344
|
+
select( [conn.socket_io], nil, nil, 5.0 ) or
|
345
|
+
raise "Timeout waiting for query response."
|
346
|
+
conn.consume_input
|
347
|
+
end
|
348
|
+
|
349
|
+
# Fetch the next result. If there isn't one, the query is finished
|
350
|
+
result = conn.get_result || break
|
351
|
+
end
|
352
|
+
result
|
353
|
+
end
|
354
|
+
|
321
355
|
end
|
322
356
|
|
323
357
|
|
@@ -339,14 +373,8 @@ RSpec.configure do |config|
|
|
339
373
|
config.filter_run_excluding :socket_io unless
|
340
374
|
PG::Connection.instance_methods.map( &:to_sym ).include?( :socket_io )
|
341
375
|
|
342
|
-
if PG.library_version <
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
elsif PG.library_version < 90400
|
347
|
-
config.filter_run_excluding( :postgresql_94, :postgresql_95 )
|
348
|
-
elsif PG.library_version < 90500
|
349
|
-
config.filter_run_excluding( :postgresql_95 )
|
350
|
-
end
|
376
|
+
config.filter_run_excluding( :postgresql_93 ) if PG.library_version < 90300
|
377
|
+
config.filter_run_excluding( :postgresql_94 ) if PG.library_version < 90400
|
378
|
+
config.filter_run_excluding( :postgresql_95 ) if PG.library_version < 90500
|
379
|
+
config.filter_run_excluding( :postgresql_10 ) if PG.library_version < 100000
|
351
380
|
end
|
352
|
-
|