pg 0.15.0.pre.454-x64-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.gemtest +0 -0
  3. data/BSDL +22 -0
  4. data/ChangeLog +2945 -0
  5. data/Contributors.rdoc +46 -0
  6. data/History.rdoc +205 -0
  7. data/LICENSE +56 -0
  8. data/Manifest.txt +53 -0
  9. data/POSTGRES +23 -0
  10. data/README-OS_X.rdoc +68 -0
  11. data/README-Windows.rdoc +67 -0
  12. data/README.ja.rdoc +14 -0
  13. data/README.rdoc +111 -0
  14. data/Rakefile +156 -0
  15. data/Rakefile.cross +271 -0
  16. data/ext/extconf.rb +91 -0
  17. data/ext/gvl_wrappers.c +13 -0
  18. data/ext/gvl_wrappers.h +185 -0
  19. data/ext/pg.c +525 -0
  20. data/ext/pg.h +126 -0
  21. data/ext/pg_connection.c +3600 -0
  22. data/ext/pg_result.c +939 -0
  23. data/ext/vc/pg.sln +26 -0
  24. data/ext/vc/pg_18/pg.vcproj +216 -0
  25. data/ext/vc/pg_19/pg_19.vcproj +209 -0
  26. data/lib/2.0/pg_ext.so +0 -0
  27. data/lib/pg.rb +52 -0
  28. data/lib/pg/connection.rb +71 -0
  29. data/lib/pg/constants.rb +11 -0
  30. data/lib/pg/exceptions.rb +11 -0
  31. data/lib/pg/result.rb +16 -0
  32. data/sample/array_insert.rb +20 -0
  33. data/sample/async_api.rb +106 -0
  34. data/sample/async_copyto.rb +39 -0
  35. data/sample/async_mixed.rb +56 -0
  36. data/sample/check_conn.rb +21 -0
  37. data/sample/copyfrom.rb +81 -0
  38. data/sample/copyto.rb +19 -0
  39. data/sample/cursor.rb +21 -0
  40. data/sample/disk_usage_report.rb +186 -0
  41. data/sample/issue-119.rb +94 -0
  42. data/sample/losample.rb +69 -0
  43. data/sample/minimal-testcase.rb +17 -0
  44. data/sample/notify_wait.rb +72 -0
  45. data/sample/pg_statistics.rb +294 -0
  46. data/sample/replication_monitor.rb +231 -0
  47. data/sample/test_binary_values.rb +33 -0
  48. data/sample/wal_shipper.rb +434 -0
  49. data/sample/warehouse_partitions.rb +320 -0
  50. data/spec/data/expected_trace.out +26 -0
  51. data/spec/data/random_binary_data +0 -0
  52. data/spec/lib/helpers.rb +279 -0
  53. data/spec/pg/connection_spec.rb +1013 -0
  54. data/spec/pg/result_spec.rb +278 -0
  55. data/spec/pg_spec.rb +31 -0
  56. metadata +275 -0
  57. metadata.gz.sig +0 -0
@@ -0,0 +1,320 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ #
4
+ # Script to automatically move partitioned tables and their indexes
5
+ # to a separate area on disk.
6
+ #
7
+ # Mahlon E. Smith <mahlon@martini.nu>
8
+ #
9
+ # Example use case:
10
+ #
11
+ # - You've got a heavy insert table, such as syslog data.
12
+ # - This table has a partitioning trigger (or is manually partitioned)
13
+ # by date, to separate incoming stuff from archival/report stuff.
14
+ # - You have a tablespace on cheap or slower disk (maybe even
15
+ # ZFS compressed, or some such!)
16
+ #
17
+ # The only assumption this script makes is that your tables are dated, and
18
+ # the tablespace they're moving into already exists.
19
+ #
20
+ # A full example, using the syslog idea from above, where each child
21
+ # table is date partitioned by a convention of "syslog_YEAR-WEEKOFYEAR":
22
+ #
23
+ # syslog # <--- parent
24
+ # syslog_2012_06 # <--- inherited
25
+ # syslog_2012_07 # <--- inherited
26
+ # syslog_2012_08 # <--- inherited
27
+ # ...
28
+ #
29
+ # You'd run this script like so:
30
+ #
31
+ # ./warehouse_partitions.rb -F syslog_%Y_%U
32
+ #
33
+ # Assuming this was week 12 of the year, tables syslog_2012_06 through
34
+ # syslog_2012_11 would start sequentially migrating into the tablespace
35
+ # called 'warehouse'.
36
+ #
37
+
38
+
39
+ begin
40
+ require 'date'
41
+ require 'ostruct'
42
+ require 'optparse'
43
+ require 'pathname'
44
+ require 'etc'
45
+ require 'pg'
46
+
47
+ rescue LoadError # 1.8 support
48
+ unless Object.const_defined?( :Gem )
49
+ require 'rubygems'
50
+ retry
51
+ end
52
+ raise
53
+ end
54
+
55
+
56
+ ### A tablespace migration class.
57
+ ###
58
+ class PGWarehouse
59
+
60
+ def initialize( opts )
61
+ @opts = opts
62
+ @db = PG.connect(
63
+ :dbname => opts.database,
64
+ :host => opts.host,
65
+ :port => opts.port,
66
+ :user => opts.user,
67
+ :password => opts.pass,
68
+ :sslmode => 'prefer'
69
+ )
70
+ @db.exec "SET search_path TO %s" % [ opts.schema ] if opts.schema
71
+
72
+ @relations = self.relations
73
+ end
74
+
75
+ attr_reader :db
76
+
77
+ ######
78
+ public
79
+ ######
80
+
81
+ ### Perform the tablespace moves.
82
+ ###
83
+ def migrate
84
+ if @relations.empty?
85
+ $stderr.puts 'No tables were found for warehousing.'
86
+ return
87
+ end
88
+
89
+ $stderr.puts "Found %d relation%s to move." % [ relations.length, relations.length == 1 ? '' : 's' ]
90
+ @relations.sort_by{|_,v| v[:name] }.each do |_, val|
91
+ $stderr.print " - Moving table '%s' to '%s'... " % [
92
+ val[:name], @opts.tablespace
93
+ ]
94
+
95
+ if @opts.dryrun
96
+ $stderr.puts '(not really)'
97
+
98
+ else
99
+ age = self.timer do
100
+ db.exec "ALTER TABLE %s SET TABLESPACE %s;" % [
101
+ val[:name], @opts.tablespace
102
+ ]
103
+ end
104
+ puts age
105
+ end
106
+
107
+ val[ :indexes ].each do |idx|
108
+ $stderr.print " - Moving index '%s' to '%s'... " % [
109
+ idx, @opts.tablespace
110
+ ]
111
+ if @opts.dryrun
112
+ $stderr.puts '(not really)'
113
+
114
+ else
115
+ age = self.timer do
116
+ db.exec "ALTER INDEX %s SET TABLESPACE %s;" % [
117
+ idx, @opts.tablespace
118
+ ]
119
+ end
120
+ puts age
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+
127
+ #########
128
+ protected
129
+ #########
130
+
131
+ ### Get OIDs and current tablespaces for everything under the
132
+ ### specified schema.
133
+ ###
134
+ def relations
135
+ return @relations if @relations
136
+ relations = {}
137
+
138
+ query = %q{
139
+ SELECT c.oid AS oid,
140
+ c.relname AS name,
141
+ c.relkind AS kind,
142
+ t.spcname AS tspace
143
+ FROM pg_class AS c
144
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
145
+ LEFT JOIN pg_tablespace t ON t.oid = c.reltablespace
146
+ WHERE c.relkind = 'r' }
147
+ query << "AND n.nspname='#{@opts.schema}'" if @opts.schema
148
+
149
+ # Get the relations list, along with each element's current tablespace.
150
+ #
151
+ self.db.exec( query ) do |res|
152
+ res.each do |row|
153
+ relations[ row['oid'] ] = {
154
+ :name => row['name'],
155
+ :tablespace => row['tspace'],
156
+ :indexes => [],
157
+ :parent => nil
158
+ }
159
+ end
160
+ end
161
+
162
+ # Add table inheritence information.
163
+ #
164
+ db.exec 'SELECT inhrelid AS oid, inhparent AS parent FROM pg_inherits' do |res|
165
+ res.each do |row|
166
+ relations[ row['oid'] ][ :parent ] = row['parent']
167
+ end
168
+ end
169
+
170
+ # Remove tables that don't qualify for warehousing.
171
+ #
172
+ # - Tables that are not children of a parent
173
+ # - Tables that are already in the warehouse tablespace
174
+ # - The currently active child (it's likely being written to!)
175
+ # - Any table that can't be parsed into the specified format
176
+ #
177
+ relations.reject! do |oid, val|
178
+ begin
179
+ val[:parent].nil? ||
180
+ val[:tablespace] == @opts.tablespace ||
181
+ val[:name] == Time.now.strftime( @opts.format ) ||
182
+ ! DateTime.strptime( val[:name], @opts.format )
183
+ rescue ArgumentError
184
+ true
185
+ end
186
+ end
187
+
188
+ query = %q{
189
+ SELECT c.oid AS oid,
190
+ i.indexname AS name
191
+ FROM pg_class AS c
192
+ INNER JOIN pg_indexes AS i
193
+ ON i.tablename = c.relname }
194
+ query << "AND i.schemaname='#{@opts.schema}'" if @opts.schema
195
+
196
+ # Attach index names to tables.
197
+ #
198
+ db.exec( query ) do |res|
199
+ res.each do |row|
200
+ relations[ row['oid'] ][ :indexes ] << row['name'] if relations[ row['oid'] ]
201
+ end
202
+ end
203
+
204
+ return relations
205
+ end
206
+
207
+
208
+ ### Wrap arbitrary commands in a human readable timer.
209
+ ###
210
+ def timer
211
+ start = Time.now
212
+ yield
213
+ age = Time.now - start
214
+
215
+ diff = age
216
+ secs = diff % 60
217
+ diff = ( diff - secs ) / 60
218
+ mins = diff % 60
219
+ diff = ( diff - mins ) / 60
220
+ hour = diff % 24
221
+
222
+ return "%02d:%02d:%02d" % [ hour, mins, secs ]
223
+ end
224
+ end
225
+
226
+
227
+ ### Parse command line arguments. Return a struct of global options.
228
+ ###
229
+ def parse_args( args )
230
+ options = OpenStruct.new
231
+ options.database = Etc.getpwuid( Process.uid ).name
232
+ options.host = '127.0.0.1'
233
+ options.port = 5432
234
+ options.user = Etc.getpwuid( Process.uid ).name
235
+ options.sslmode = 'prefer'
236
+ options.tablespace = 'warehouse'
237
+
238
+ opts = OptionParser.new do |opts|
239
+ opts.banner = "Usage: #{$0} [options]"
240
+
241
+ opts.separator ''
242
+ opts.separator 'Connection options:'
243
+
244
+ opts.on( '-d', '--database DBNAME',
245
+ "specify the database to connect to (default: \"#{options.database}\")" ) do |db|
246
+ options.database = db
247
+ end
248
+
249
+ opts.on( '-h', '--host HOSTNAME', 'database server host' ) do |host|
250
+ options.host = host
251
+ end
252
+
253
+ opts.on( '-p', '--port PORT', Integer,
254
+ "database server port (default: \"#{options.port}\")" ) do |port|
255
+ options.port = port
256
+ end
257
+
258
+ opts.on( '-n', '--schema SCHEMA', String,
259
+ "operate on the named schema only (default: none)" ) do |schema|
260
+ options.schema = schema
261
+ end
262
+
263
+ opts.on( '-T', '--tablespace SPACE', String,
264
+ "move old tables to this tablespace (default: \"#{options.tablespace}\")" ) do |tb|
265
+ options.tablespace = tb
266
+ end
267
+
268
+ opts.on( '-F', '--tableformat FORMAT', String,
269
+ "The naming format (strftime) for the inherited tables (default: none)" ) do |format|
270
+ options.format = format
271
+ end
272
+
273
+ opts.on( '-U', '--user NAME',
274
+ "database user name (default: \"#{options.user}\")" ) do |user|
275
+ options.user = user
276
+ end
277
+
278
+ opts.on( '-W', 'force password prompt' ) do |pw|
279
+ print 'Password: '
280
+ begin
281
+ system 'stty -echo'
282
+ options.pass = gets.chomp
283
+ ensure
284
+ system 'stty echo'
285
+ puts
286
+ end
287
+ end
288
+
289
+ opts.separator ''
290
+ opts.separator 'Other options:'
291
+
292
+ opts.on_tail( '--dry-run', "don't actually do anything" ) do
293
+ options.dryrun = true
294
+ end
295
+
296
+ opts.on_tail( '--help', 'show this help, then exit' ) do
297
+ $stderr.puts opts
298
+ exit
299
+ end
300
+
301
+ opts.on_tail( '--version', 'output version information, then exit' ) do
302
+ puts Stats::VERSION
303
+ exit
304
+ end
305
+ end
306
+
307
+ opts.parse!( args )
308
+ return options
309
+ end
310
+
311
+
312
+ if __FILE__ == $0
313
+ opts = parse_args( ARGV )
314
+ raise ArgumentError, "A naming format (-F) is required." unless opts.format
315
+
316
+ $stdout.sync = true
317
+ PGWarehouse.new( opts ).migrate
318
+ end
319
+
320
+
@@ -0,0 +1,26 @@
1
+ To backend> Msg Q
2
+ To backend> "SELECT 1 AS one"
3
+ To backend> Msg complete, length 21
4
+ From backend> T
5
+ From backend (#4)> 28
6
+ From backend (#2)> 1
7
+ From backend> "one"
8
+ From backend (#4)> 0
9
+ From backend (#2)> 0
10
+ From backend (#4)> 23
11
+ From backend (#2)> 4
12
+ From backend (#4)> -1
13
+ From backend (#2)> 0
14
+ From backend> D
15
+ From backend (#4)> 11
16
+ From backend (#2)> 1
17
+ From backend (#4)> 1
18
+ From backend (1)> 1
19
+ From backend> C
20
+ From backend (#4)> 11
21
+ From backend> "SELECT"
22
+ From backend> Z
23
+ From backend (#4)> 5
24
+ From backend> Z
25
+ From backend (#4)> 5
26
+ From backend> T
@@ -0,0 +1,279 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pathname'
4
+ require 'rspec'
5
+ require 'shellwords'
6
+ require 'pg'
7
+
8
+ TEST_DIRECTORY = Pathname.getwd + "tmp_test_specs"
9
+
10
+ module PG::TestingHelpers
11
+
12
+
13
+ # Set some ANSI escape code constants (Shamelessly stolen from Perl's
14
+ # Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
15
+ ANSI_ATTRIBUTES = {
16
+ 'clear' => 0,
17
+ 'reset' => 0,
18
+ 'bold' => 1,
19
+ 'dark' => 2,
20
+ 'underline' => 4,
21
+ 'underscore' => 4,
22
+ 'blink' => 5,
23
+ 'reverse' => 7,
24
+ 'concealed' => 8,
25
+
26
+ 'black' => 30, 'on_black' => 40,
27
+ 'red' => 31, 'on_red' => 41,
28
+ 'green' => 32, 'on_green' => 42,
29
+ 'yellow' => 33, 'on_yellow' => 43,
30
+ 'blue' => 34, 'on_blue' => 44,
31
+ 'magenta' => 35, 'on_magenta' => 45,
32
+ 'cyan' => 36, 'on_cyan' => 46,
33
+ 'white' => 37, 'on_white' => 47
34
+ }
35
+
36
+
37
+ ###############
38
+ module_function
39
+ ###############
40
+
41
+ ### Create a string that contains the ANSI codes specified and return it
42
+ def ansi_code( *attributes )
43
+ attributes.flatten!
44
+ attributes.collect! {|at| at.to_s }
45
+ # $stderr.puts "Returning ansicode for TERM = %p: %p" %
46
+ # [ ENV['TERM'], attributes ]
47
+ return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
48
+ attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
49
+
50
+ # $stderr.puts " attr is: %p" % [attributes]
51
+ if attributes.empty?
52
+ return ''
53
+ else
54
+ return "\e[%sm" % attributes
55
+ end
56
+ end
57
+
58
+
59
+ ### Colorize the given +string+ with the specified +attributes+ and return it, handling
60
+ ### line-endings, color reset, etc.
61
+ def colorize( *args )
62
+ string = ''
63
+
64
+ if block_given?
65
+ string = yield
66
+ else
67
+ string = args.shift
68
+ end
69
+
70
+ ending = string[/(\s)$/] || ''
71
+ string = string.rstrip
72
+
73
+ return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
74
+ end
75
+
76
+
77
+ ### Output a message with highlighting.
78
+ def message( *msg )
79
+ $stderr.puts( colorize(:bold) { msg.flatten.join(' ') } )
80
+ end
81
+
82
+
83
+ ### Output a logging message if $VERBOSE is true
84
+ def trace( *msg )
85
+ return unless $VERBOSE
86
+ output = colorize( msg.flatten.join(' '), 'yellow' )
87
+ $stderr.puts( output )
88
+ end
89
+
90
+
91
+ ### Return the specified args as a string, quoting any that have a space.
92
+ def quotelist( *args )
93
+ return args.flatten.collect {|part| part.to_s =~ /\s/ ? part.to_s.inspect : part.to_s }
94
+ end
95
+
96
+
97
+ ### Run the specified command +cmd+ with system(), failing if the execution
98
+ ### fails.
99
+ def run( *cmd )
100
+ cmd.flatten!
101
+
102
+ if cmd.length > 1
103
+ trace( quotelist(*cmd) )
104
+ else
105
+ trace( cmd )
106
+ end
107
+
108
+ system( *cmd )
109
+ raise "Command failed: [%s]" % [cmd.join(' ')] unless $?.success?
110
+ end
111
+
112
+
113
+ ### Run the specified command +cmd+ after redirecting stdout and stderr to the specified
114
+ ### +logpath+, failing if the execution fails.
115
+ def log_and_run( logpath, *cmd )
116
+ cmd.flatten!
117
+
118
+ if cmd.length > 1
119
+ trace( quotelist(*cmd) )
120
+ else
121
+ trace( cmd )
122
+ end
123
+
124
+ # Eliminate the noise of creating/tearing down the database by
125
+ # redirecting STDERR/STDOUT to a logfile if the Ruby interpreter
126
+ # supports fork()
127
+ logfh = File.open( logpath, File::WRONLY|File::CREAT|File::APPEND )
128
+ begin
129
+ pid = fork
130
+ rescue NotImplementedError
131
+ logfh.close
132
+ system( *cmd )
133
+ else
134
+ if pid
135
+ logfh.close
136
+ else
137
+ $stdout.reopen( logfh )
138
+ $stderr.reopen( $stdout )
139
+ $stderr.puts( ">>> " + cmd.shelljoin )
140
+ exec( *cmd )
141
+ $stderr.puts "After the exec()?!??!"
142
+ exit!
143
+ end
144
+
145
+ Process.wait( pid )
146
+ end
147
+
148
+ raise "Command failed: [%s]" % [cmd.join(' ')] unless $?.success?
149
+ end
150
+
151
+
152
+ ### Check the current directory for directories that look like they're
153
+ ### testing directories from previous tests, and tell any postgres instances
154
+ ### running in them to shut down.
155
+ def stop_existing_postmasters
156
+ # tmp_test_0.22329534700318
157
+ pat = Pathname.getwd + 'tmp_test_*'
158
+ Pathname.glob( pat.to_s ).each do |testdir|
159
+ datadir = testdir + 'data'
160
+ pidfile = datadir + 'postmaster.pid'
161
+ if pidfile.exist? && pid = pidfile.read.chomp.to_i
162
+ $stderr.puts "pidfile (%p) exists: %d" % [ pidfile, pid ]
163
+ begin
164
+ Process.kill( 0, pid )
165
+ rescue Errno::ESRCH
166
+ $stderr.puts "No postmaster running for %s" % [ datadir ]
167
+ # Process isn't alive, so don't try to stop it
168
+ else
169
+ $stderr.puts "Stopping lingering database at PID %d" % [ pid ]
170
+ run 'pg_ctl', '-D', datadir.to_s, '-m', 'fast', 'stop'
171
+ end
172
+ else
173
+ $stderr.puts "No pidfile (%p)" % [ pidfile ]
174
+ end
175
+ end
176
+ end
177
+
178
+
179
+ ### Set up a PostgreSQL database instance for testing.
180
+ def setup_testing_db( description )
181
+ require 'pg'
182
+ stop_existing_postmasters()
183
+
184
+ puts "Setting up test database for #{description}"
185
+ @test_pgdata = TEST_DIRECTORY + 'data'
186
+ @test_pgdata.mkpath
187
+
188
+ @port = 54321
189
+ ENV['PGPORT'] = @port.to_s
190
+ ENV['PGHOST'] = 'localhost'
191
+ @conninfo = "host=localhost port=#{@port} dbname=test"
192
+
193
+ @logfile = TEST_DIRECTORY + 'setup.log'
194
+ trace "Command output logged to #{@logfile}"
195
+
196
+ begin
197
+ unless (@test_pgdata+"postgresql.conf").exist?
198
+ FileUtils.rm_rf( @test_pgdata, :verbose => $DEBUG )
199
+ $stderr.puts "Running initdb"
200
+ log_and_run @logfile, 'initdb', '-E', 'UTF8', '--no-locale', '-D', @test_pgdata.to_s
201
+ end
202
+
203
+ trace "Starting postgres"
204
+ log_and_run @logfile, 'pg_ctl', '-w', '-o', "-k #{TEST_DIRECTORY.to_s.dump}",
205
+ '-D', @test_pgdata.to_s, 'start'
206
+ sleep 2
207
+
208
+ $stderr.puts "Creating the test DB"
209
+ log_and_run @logfile, 'psql', '-e', '-c', 'DROP DATABASE IF EXISTS test', 'postgres'
210
+ log_and_run @logfile, 'createdb', '-e', 'test'
211
+
212
+ rescue => err
213
+ $stderr.puts "%p during test setup: %s" % [ err.class, err.message ]
214
+ $stderr.puts "See #{@logfile} for details."
215
+ $stderr.puts *err.backtrace if $DEBUG
216
+ fail
217
+ end
218
+
219
+ conn = PG.connect( @conninfo )
220
+ conn.set_notice_processor do |message|
221
+ $stderr.puts( description + ':' + message ) if $DEBUG
222
+ end
223
+
224
+ return conn
225
+ end
226
+
227
+
228
+ def teardown_testing_db( conn )
229
+ puts "Tearing down test database"
230
+
231
+ if conn
232
+ check_for_lingering_connections( conn )
233
+ conn.finish
234
+ end
235
+
236
+ log_and_run @logfile, 'pg_ctl', '-D', @test_pgdata.to_s, 'stop'
237
+ end
238
+
239
+
240
+ def check_for_lingering_connections( conn )
241
+ conn.exec( "SELECT * FROM pg_stat_activity" ) do |res|
242
+ conns = res.find_all {|row| row['pid'].to_i != conn.backend_pid }
243
+ unless conns.empty?
244
+ puts "Lingering connections remain:"
245
+ conns.each do |row|
246
+ puts " [%d] {%s} %s -- %s" % row.values_at( 'pid', 'state', 'application_name', 'query' )
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
252
+
253
+
254
+ RSpec.configure do |config|
255
+ ruby_version_vec = RUBY_VERSION.split('.').map {|c| c.to_i }.pack( "N*" )
256
+
257
+ config.include( PG::TestingHelpers )
258
+ config.treat_symbols_as_metadata_keys_with_true_values = true
259
+
260
+ config.mock_with :rspec
261
+ config.filter_run_excluding :ruby_19 if ruby_version_vec <= [1,9,1].pack( "N*" )
262
+ if RUBY_PLATFORM =~ /mingw|mswin/
263
+ config.filter_run_excluding :unix
264
+ else
265
+ config.filter_run_excluding :windows
266
+ end
267
+ config.filter_run_excluding :socket_io unless
268
+ PG::Connection.instance_methods.map( &:to_sym ).include?( :socket_io )
269
+
270
+ config.filter_run_excluding :postgresql_90 unless
271
+ PG::Connection.instance_methods.map( &:to_sym ).include?( :escape_literal )
272
+
273
+ if !PG.respond_to?( :library_version )
274
+ config.filter_run_excluding( :postgresql_91, :postgresql_92 )
275
+ elsif PG.library_version < 90200
276
+ config.filter_run_excluding( :postgresql_92 )
277
+ end
278
+ end
279
+