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.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ChangeLog +0 -6595
  5. data/History.rdoc +184 -0
  6. data/Manifest.txt +8 -3
  7. data/README-Windows.rdoc +4 -4
  8. data/README.ja.rdoc +1 -2
  9. data/README.rdoc +58 -13
  10. data/Rakefile +10 -9
  11. data/Rakefile.cross +68 -71
  12. data/ext/errorcodes.def +76 -0
  13. data/ext/errorcodes.rb +1 -1
  14. data/ext/errorcodes.txt +21 -2
  15. data/ext/extconf.rb +18 -36
  16. data/ext/gvl_wrappers.c +4 -0
  17. data/ext/gvl_wrappers.h +23 -39
  18. data/ext/pg.c +154 -144
  19. data/ext/pg.h +68 -95
  20. data/ext/pg_binary_decoder.c +82 -15
  21. data/ext/pg_binary_encoder.c +13 -12
  22. data/ext/pg_coder.c +73 -12
  23. data/ext/pg_connection.c +699 -459
  24. data/ext/pg_copy_coder.c +16 -8
  25. data/ext/pg_record_coder.c +491 -0
  26. data/ext/pg_result.c +571 -195
  27. data/ext/pg_text_decoder.c +606 -40
  28. data/ext/pg_text_encoder.c +185 -54
  29. data/ext/pg_tuple.c +549 -0
  30. data/ext/pg_type_map.c +1 -1
  31. data/ext/pg_type_map_all_strings.c +4 -4
  32. data/ext/pg_type_map_by_class.c +9 -4
  33. data/ext/pg_type_map_by_column.c +7 -6
  34. data/ext/pg_type_map_by_mri_type.c +1 -1
  35. data/ext/pg_type_map_by_oid.c +3 -2
  36. data/ext/pg_type_map_in_ruby.c +1 -1
  37. data/ext/{util.c → pg_util.c} +10 -10
  38. data/ext/{util.h → pg_util.h} +2 -2
  39. data/lib/pg.rb +8 -10
  40. data/lib/pg/basic_type_mapping.rb +121 -25
  41. data/lib/pg/binary_decoder.rb +23 -0
  42. data/lib/pg/coder.rb +23 -2
  43. data/lib/pg/connection.rb +28 -4
  44. data/lib/pg/constants.rb +2 -1
  45. data/lib/pg/exceptions.rb +2 -1
  46. data/lib/pg/result.rb +14 -2
  47. data/lib/pg/text_decoder.rb +21 -26
  48. data/lib/pg/text_encoder.rb +32 -8
  49. data/lib/pg/tuple.rb +30 -0
  50. data/lib/pg/type_map_by_column.rb +3 -2
  51. data/spec/helpers.rb +61 -33
  52. data/spec/pg/basic_type_mapping_spec.rb +362 -37
  53. data/spec/pg/connection_spec.rb +602 -329
  54. data/spec/pg/connection_sync_spec.rb +41 -0
  55. data/spec/pg/result_spec.rb +242 -17
  56. data/spec/pg/tuple_spec.rb +333 -0
  57. data/spec/pg/type_map_by_class_spec.rb +2 -2
  58. data/spec/pg/type_map_by_column_spec.rb +6 -2
  59. data/spec/pg/type_map_by_mri_type_spec.rb +1 -1
  60. data/spec/pg/type_map_by_oid_spec.rb +3 -3
  61. data/spec/pg/type_map_in_ruby_spec.rb +1 -1
  62. data/spec/pg/type_map_spec.rb +1 -1
  63. data/spec/pg/type_spec.rb +364 -18
  64. data/spec/pg_spec.rb +2 -2
  65. metadata +48 -43
  66. metadata.gz.sig +0 -0
  67. 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
- #!/usr/bin/env ruby
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
- #!/usr/bin/env ruby
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.regexp
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
- end # class PG::Connection
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
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pg' unless defined?( PG )
4
5
 
data/lib/pg/exceptions.rb CHANGED
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'pg' unless defined?( PG )
4
5
 
data/lib/pg/result.rb CHANGED
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env ruby
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
- # See PG::BasicTypeMapForResults
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
@@ -1,4 +1,5 @@
1
- #!/usr/bin/env ruby
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 =~ ISO_DATE
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 TimestampWithoutTimeZone < SimpleDecoder
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
- if string =~ ISO_DATETIME_WITHOUT_TIMEZONE
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
- class TimestampWithTimeZone < SimpleDecoder
33
- ISO_DATETIME_WITH_TIMEZONE = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?([-\+]\d\d):?(\d\d)?:?(\d\d)?\z/
34
-
35
- def decode(string, tuple=nil, field=nil)
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
- class JSON < SimpleDecoder
45
- def decode(string, tuple=nil, field=nil)
46
- ::JSON.parse(string, quirks_mode: true)
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
-
@@ -1,27 +1,32 @@
1
- #!/usr/bin/env ruby
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(STRFTIME_ISO_DATE) : value
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(STRFTIME_ISO_DATETIME_WITHOUT_TIMEZONE) : value
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(STRFTIME_ISO_DATETIME_WITH_TIMEZONE) : value
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
- #!/usr/bin/env ruby
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 ? "#{c.name}:#{c.format}" : 'nil' }
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
- #!/usr/bin/env ruby
1
+ # -*- ruby -*-
2
2
 
3
3
  require 'pathname'
4
4
  require 'rspec'
5
5
  require 'shellwords'
6
6
  require 'pg'
7
7
 
8
- TEST_DIRECTORY = Pathname.getwd + "tmp_test_specs"
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
- if PG.respond_to?( :library_version )
25
- desc = example.source_location.join(':')
26
- @conn.exec_params %Q{SET application_name TO '%s'} %
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
- $stderr.puts "pidfile (%p) exists: %d" % [ pidfile, pid ]
175
+ trace "pidfile (%p) exists: %d" % [ pidfile, pid ]
175
176
  begin
176
177
  Process.kill( 0, pid )
177
178
  rescue Errno::ESRCH
178
- $stderr.puts "No postmaster running for %s" % [ datadir ]
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
- $stderr.puts "Stopping lingering database at PID %d" % [ pid ]
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
- $stderr.puts "No pidfile (%p)" % [ pidfile ]
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
- puts "Setting up test database for #{description}"
197
+ trace "Setting up test database for #{description}"
197
198
  @test_pgdata = TEST_DIRECTORY + 'data'
198
199
  @test_pgdata.mkpath
199
200
 
200
- @port = 54321
201
- ENV['PGPORT'] = @port.to_s
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
- $stderr.puts "Running initdb"
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
- $stderr.puts "Creating the test DB"
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 *err.backtrace if $DEBUG
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
- puts "Tearing down test database"
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.exec( "SELECT " + res.nfields.times.map{|i| "format_type($#{i*2+1},$#{i*2+2})"}.join(","),
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