pg 0.21.0 → 1.2.3
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.tar.gz.sig +0 -0
- data/ChangeLog +0 -6595
- data/History.rdoc +184 -0
- data/Manifest.txt +8 -3
- data/README-Windows.rdoc +4 -4
- data/README.ja.rdoc +1 -2
- data/README.rdoc +58 -13
- data/Rakefile +10 -9
- data/Rakefile.cross +68 -71
- data/ext/errorcodes.def +76 -0
- data/ext/errorcodes.rb +1 -1
- data/ext/errorcodes.txt +21 -2
- data/ext/extconf.rb +18 -36
- data/ext/gvl_wrappers.c +4 -0
- data/ext/gvl_wrappers.h +23 -39
- data/ext/pg.c +154 -144
- data/ext/pg.h +68 -95
- data/ext/pg_binary_decoder.c +82 -15
- data/ext/pg_binary_encoder.c +13 -12
- data/ext/pg_coder.c +73 -12
- data/ext/pg_connection.c +699 -459
- data/ext/pg_copy_coder.c +16 -8
- data/ext/pg_record_coder.c +491 -0
- data/ext/pg_result.c +571 -195
- data/ext/pg_text_decoder.c +606 -40
- data/ext/pg_text_encoder.c +185 -54
- data/ext/pg_tuple.c +549 -0
- 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 +9 -4
- 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} +10 -10
- data/ext/{util.h → pg_util.h} +2 -2
- data/lib/pg.rb +8 -10
- data/lib/pg/basic_type_mapping.rb +121 -25
- data/lib/pg/binary_decoder.rb +23 -0
- data/lib/pg/coder.rb +23 -2
- data/lib/pg/connection.rb +28 -4
- data/lib/pg/constants.rb +2 -1
- data/lib/pg/exceptions.rb +2 -1
- data/lib/pg/result.rb +14 -2
- data/lib/pg/text_decoder.rb +21 -26
- data/lib/pg/text_encoder.rb +32 -8
- data/lib/pg/tuple.rb +30 -0
- data/lib/pg/type_map_by_column.rb +3 -2
- data/spec/helpers.rb +61 -33
- data/spec/pg/basic_type_mapping_spec.rb +362 -37
- data/spec/pg/connection_spec.rb +602 -329
- data/spec/pg/connection_sync_spec.rb +41 -0
- data/spec/pg/result_spec.rb +242 -17
- data/spec/pg/tuple_spec.rb +333 -0
- data/spec/pg/type_map_by_class_spec.rb +2 -2
- data/spec/pg/type_map_by_column_spec.rb +6 -2
- data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
- data/spec/pg/type_map_by_oid_spec.rb +3 -3
- 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 +364 -18
- data/spec/pg_spec.rb +2 -2
- metadata +48 -43
- metadata.gz.sig +0 -0
- data/lib/pg/deprecated_constants.rb +0 -21
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module PG
|
5
|
+
module BinaryDecoder
|
6
|
+
# Convenience classes for timezone options
|
7
|
+
class TimestampUtc < Timestamp
|
8
|
+
def initialize(params={})
|
9
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_UTC))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
class TimestampUtcToLocal < Timestamp
|
13
|
+
def initialize(params={})
|
14
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
class TimestampLocal < Timestamp
|
18
|
+
def initialize(params={})
|
19
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_LOCAL | PG::Coder::TIMESTAMP_APP_LOCAL))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end # module PG
|
data/lib/pg/coder.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module PG
|
4
5
|
|
@@ -28,6 +29,7 @@ module PG
|
|
28
29
|
{
|
29
30
|
oid: oid,
|
30
31
|
format: format,
|
32
|
+
flags: flags,
|
31
33
|
name: name,
|
32
34
|
}
|
33
35
|
end
|
@@ -52,6 +54,18 @@ module PG
|
|
52
54
|
str[-1,0] = "#{name_str} #{oid_str}#{format_str}"
|
53
55
|
str
|
54
56
|
end
|
57
|
+
|
58
|
+
def inspect_short
|
59
|
+
str = case format
|
60
|
+
when 0 then "T"
|
61
|
+
when 1 then "B"
|
62
|
+
else format.to_s
|
63
|
+
end
|
64
|
+
str += "E" if respond_to?(:encode)
|
65
|
+
str += "D" if respond_to?(:decode)
|
66
|
+
|
67
|
+
"#{name || self.class.name}:#{str}"
|
68
|
+
end
|
55
69
|
end
|
56
70
|
|
57
71
|
class CompositeCoder < Coder
|
@@ -79,5 +93,12 @@ module PG
|
|
79
93
|
})
|
80
94
|
end
|
81
95
|
end
|
82
|
-
end # module PG
|
83
96
|
|
97
|
+
class RecordCoder < Coder
|
98
|
+
def to_h
|
99
|
+
super.merge!({
|
100
|
+
type_map: type_map,
|
101
|
+
})
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end # module PG
|
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, 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 /=/
|
@@ -85,7 +86,7 @@ class PG::Connection
|
|
85
86
|
|
86
87
|
|
87
88
|
# call-seq:
|
88
|
-
# conn.copy_data( sql ) {|sql_result| ... } -> PG::Result
|
89
|
+
# conn.copy_data( sql [, coder] ) {|sql_result| ... } -> PG::Result
|
89
90
|
#
|
90
91
|
# Execute a copy process for transfering data to or from the server.
|
91
92
|
#
|
@@ -109,6 +110,11 @@ class PG::Connection
|
|
109
110
|
# of blocking mode of operation, #copy_data is preferred to raw calls
|
110
111
|
# of #put_copy_data, #get_copy_data and #put_copy_end.
|
111
112
|
#
|
113
|
+
# _coder_ can be a PG::Coder derivation
|
114
|
+
# (typically PG::TextEncoder::CopyRow or PG::TextDecoder::CopyRow).
|
115
|
+
# This enables encoding of data fields given to #put_copy_data
|
116
|
+
# or decoding of fields received by #get_copy_data.
|
117
|
+
#
|
112
118
|
# Example with CSV input format:
|
113
119
|
# conn.exec "create table my_table (a text,b text,c text,d text)"
|
114
120
|
# conn.copy_data "COPY my_table FROM STDIN CSV" do
|
@@ -263,5 +269,23 @@ class PG::Connection
|
|
263
269
|
end
|
264
270
|
end
|
265
271
|
|
266
|
-
|
272
|
+
REDIRECT_METHODS = {
|
273
|
+
:exec => [:async_exec, :sync_exec],
|
274
|
+
:query => [:async_exec, :sync_exec],
|
275
|
+
:exec_params => [:async_exec_params, :sync_exec_params],
|
276
|
+
:prepare => [:async_prepare, :sync_prepare],
|
277
|
+
:exec_prepared => [:async_exec_prepared, :sync_exec_prepared],
|
278
|
+
:describe_portal => [:async_describe_portal, :sync_describe_portal],
|
279
|
+
:describe_prepared => [:async_describe_prepared, :sync_describe_prepared],
|
280
|
+
}
|
281
|
+
|
282
|
+
def self.async_api=(enable)
|
283
|
+
REDIRECT_METHODS.each do |ali, (async, sync)|
|
284
|
+
remove_method(ali) if method_defined?(ali)
|
285
|
+
alias_method( ali, enable ? async : sync )
|
286
|
+
end
|
287
|
+
end
|
267
288
|
|
289
|
+
# pg-1.1.0+ defaults to libpq's async API for query related blocking methods
|
290
|
+
self.async_api = true
|
291
|
+
end # class PG::Connection
|
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
|
@@ -17,35 +16,31 @@ module PG
|
|
17
16
|
end
|
18
17
|
end
|
19
18
|
|
20
|
-
class
|
21
|
-
ISO_DATETIME_WITHOUT_TIMEZONE = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
|
22
|
-
|
19
|
+
class JSON < SimpleDecoder
|
23
20
|
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
|
21
|
+
::JSON.parse(string, quirks_mode: true)
|
29
22
|
end
|
30
23
|
end
|
31
24
|
|
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
|
25
|
+
# Convenience classes for timezone options
|
26
|
+
class TimestampUtc < Timestamp
|
27
|
+
def initialize(params={})
|
28
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_UTC))
|
41
29
|
end
|
42
30
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
31
|
+
class TimestampUtcToLocal < Timestamp
|
32
|
+
def initialize(params={})
|
33
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_UTC | PG::Coder::TIMESTAMP_APP_LOCAL))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
class TimestampLocal < Timestamp
|
37
|
+
def initialize(params={})
|
38
|
+
super(params.merge(flags: PG::Coder::TIMESTAMP_DB_LOCAL | PG::Coder::TIMESTAMP_APP_LOCAL))
|
47
39
|
end
|
48
40
|
end
|
41
|
+
|
42
|
+
# For backward compatibility:
|
43
|
+
TimestampWithoutTimeZone = TimestampLocal
|
44
|
+
TimestampWithTimeZone = Timestamp
|
49
45
|
end
|
50
46
|
end # module PG
|
51
|
-
|
data/lib/pg/text_encoder.rb
CHANGED
@@ -1,27 +1,32 @@
|
|
1
|
-
|
1
|
+
# -*- ruby -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'json'
|
5
|
+
require 'ipaddr'
|
4
6
|
|
5
7
|
module PG
|
6
8
|
module TextEncoder
|
7
9
|
class Date < SimpleEncoder
|
8
|
-
STRFTIME_ISO_DATE = "%Y-%m-%d".freeze
|
9
10
|
def encode(value)
|
10
|
-
value.respond_to?(:strftime) ? value.strftime(
|
11
|
+
value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d") : value
|
11
12
|
end
|
12
13
|
end
|
13
14
|
|
14
15
|
class TimestampWithoutTimeZone < SimpleEncoder
|
15
|
-
STRFTIME_ISO_DATETIME_WITHOUT_TIMEZONE = "%Y-%m-%d %H:%M:%S.%N".freeze
|
16
16
|
def encode(value)
|
17
|
-
value.respond_to?(:strftime) ? value.strftime(
|
17
|
+
value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S.%N") : value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class TimestampUtc < SimpleEncoder
|
22
|
+
def encode(value)
|
23
|
+
value.respond_to?(:utc) ? value.utc.strftime("%Y-%m-%d %H:%M:%S.%N") : value
|
18
24
|
end
|
19
25
|
end
|
20
26
|
|
21
27
|
class TimestampWithTimeZone < SimpleEncoder
|
22
|
-
STRFTIME_ISO_DATETIME_WITH_TIMEZONE = "%Y-%m-%d %H:%M:%S.%N %:z".freeze
|
23
28
|
def encode(value)
|
24
|
-
value.respond_to?(:strftime) ? value.strftime(
|
29
|
+
value.respond_to?(:strftime) ? value.strftime("%Y-%m-%d %H:%M:%S.%N %:z") : value
|
25
30
|
end
|
26
31
|
end
|
27
32
|
|
@@ -30,6 +35,25 @@ module PG
|
|
30
35
|
::JSON.generate(value, quirks_mode: true)
|
31
36
|
end
|
32
37
|
end
|
38
|
+
|
39
|
+
class Inet < SimpleEncoder
|
40
|
+
def encode(value)
|
41
|
+
case value
|
42
|
+
when IPAddr
|
43
|
+
default_prefix = (value.family == Socket::AF_INET ? 32 : 128)
|
44
|
+
s = value.to_s
|
45
|
+
if value.respond_to?(:prefix)
|
46
|
+
prefix = value.prefix
|
47
|
+
else
|
48
|
+
range = value.to_range
|
49
|
+
prefix = default_prefix - Math.log(((range.end.to_i - range.begin.to_i) + 1), 2).to_i
|
50
|
+
end
|
51
|
+
s << "/" << prefix.to_s if prefix != default_prefix
|
52
|
+
s
|
53
|
+
else
|
54
|
+
value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
33
58
|
end
|
34
59
|
end # module PG
|
35
|
-
|
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
|
@@ -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
@@ -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
|
|
@@ -20,12 +22,11 @@ module PG::TestingHelpers
|
|
20
22
|
|
21
23
|
mod.around( :each ) do |example|
|
22
24
|
begin
|
25
|
+
@conn.set_default_encoding
|
23
26
|
@conn.exec( 'BEGIN' ) unless example.metadata[:without_transaction]
|
24
|
-
|
25
|
-
|
26
|
-
@conn.
|
27
|
-
[@conn.escape_string(desc.slice(-60))]
|
28
|
-
end
|
27
|
+
desc = example.source_location.join(':')
|
28
|
+
@conn.exec %Q{SET application_name TO '%s'} %
|
29
|
+
[@conn.escape_string(desc.slice(-60))]
|
29
30
|
example.run
|
30
31
|
ensure
|
31
32
|
@conn.exec( 'ROLLBACK' ) unless example.metadata[:without_transaction]
|
@@ -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,14 +218,14 @@ 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
|
|
224
225
|
rescue => err
|
225
226
|
$stderr.puts "%p during test setup: %s" % [ err.class, err.message ]
|
226
227
|
$stderr.puts "See #{@logfile} for details."
|
227
|
-
$stderr.puts
|
228
|
+
$stderr.puts err.backtrace if $DEBUG
|
228
229
|
fail
|
229
230
|
end
|
230
231
|
|
@@ -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 )
|
@@ -251,7 +252,7 @@ module PG::TestingHelpers
|
|
251
252
|
|
252
253
|
def check_for_lingering_connections( conn )
|
253
254
|
conn.exec( "SELECT * FROM pg_stat_activity" ) do |res|
|
254
|
-
conns = res.find_all {|row| row['pid'].to_i != conn.backend_pid }
|
255
|
+
conns = res.find_all {|row| row['pid'].to_i != conn.backend_pid && ["client backend", nil].include?(row["backend_type"]) }
|
255
256
|
unless conns.empty?
|
256
257
|
puts "Lingering connections remain:"
|
257
258
|
conns.each do |row|
|
@@ -264,7 +265,7 @@ module PG::TestingHelpers
|
|
264
265
|
|
265
266
|
# Retrieve the names of the column types of a given result set.
|
266
267
|
def result_typenames(res)
|
267
|
-
@conn.
|
268
|
+
@conn.exec_params( "SELECT " + res.nfields.times.map{|i| "format_type($#{i*2+1},$#{i*2+2})"}.join(","),
|
268
269
|
res.nfields.times.map{|i| [res.ftype(i), res.fmod(i)] }.flatten ).
|
269
270
|
values[0]
|
270
271
|
end
|
@@ -318,6 +319,39 @@ module PG::TestingHelpers
|
|
318
319
|
return ConnStillUsableMatcher.new
|
319
320
|
end
|
320
321
|
|
322
|
+
def wait_for_polling_ok(conn, meth = :connect_poll)
|
323
|
+
status = conn.send(meth)
|
324
|
+
|
325
|
+
while status != PG::PGRES_POLLING_OK
|
326
|
+
if status == PG::PGRES_POLLING_READING
|
327
|
+
select( [conn.socket_io], [], [], 5.0 ) or
|
328
|
+
raise "Asynchronous connection timed out!"
|
329
|
+
|
330
|
+
elsif status == PG::PGRES_POLLING_WRITING
|
331
|
+
select( [], [conn.socket_io], [], 5.0 ) or
|
332
|
+
raise "Asynchronous connection timed out!"
|
333
|
+
end
|
334
|
+
status = conn.send(meth)
|
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
|
|
@@ -336,17 +370,11 @@ RSpec.configure do |config|
|
|
336
370
|
else
|
337
371
|
config.filter_run_excluding :windows
|
338
372
|
end
|
339
|
-
config.filter_run_excluding :socket_io unless
|
340
|
-
PG::Connection.instance_methods.map( &:to_sym ).include?( :socket_io )
|
341
|
-
|
342
|
-
if PG.library_version < 90200
|
343
|
-
config.filter_run_excluding( :postgresql_92, :postgresql_93, :postgresql_94, :postgresql_95 )
|
344
|
-
elsif PG.library_version < 90300
|
345
|
-
config.filter_run_excluding( :postgresql_93, :postgresql_94, :postgresql_95 )
|
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
|
351
|
-
end
|
352
373
|
|
374
|
+
config.filter_run_excluding( :postgresql_93 ) if PG.library_version < 90300
|
375
|
+
config.filter_run_excluding( :postgresql_94 ) if PG.library_version < 90400
|
376
|
+
config.filter_run_excluding( :postgresql_95 ) if PG.library_version < 90500
|
377
|
+
config.filter_run_excluding( :postgresql_96 ) if PG.library_version < 90600
|
378
|
+
config.filter_run_excluding( :postgresql_10 ) if PG.library_version < 100000
|
379
|
+
config.filter_run_excluding( :postgresql_12 ) if PG.library_version < 120000
|
380
|
+
end
|