jruby-pg 0.1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/BSDL +22 -0
  4. data/ChangeLog +0 -0
  5. data/Contributors.rdoc +45 -0
  6. data/History.rdoc +270 -0
  7. data/LICENSE +56 -0
  8. data/Manifest.txt +44 -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 +102 -0
  14. data/Rakefile +211 -0
  15. data/Rakefile.cross +273 -0
  16. data/ext/gvl_wrappers.c +13 -0
  17. data/ext/pg.c +545 -0
  18. data/ext/pg_connection.c +3643 -0
  19. data/ext/pg_errors.c +89 -0
  20. data/ext/pg_result.c +920 -0
  21. data/lib/pg.rb +52 -0
  22. data/lib/pg/connection.rb +179 -0
  23. data/lib/pg/constants.rb +11 -0
  24. data/lib/pg/exceptions.rb +11 -0
  25. data/lib/pg/result.rb +16 -0
  26. data/lib/pg_ext.jar +0 -0
  27. data/sample/array_insert.rb +20 -0
  28. data/sample/async_api.rb +106 -0
  29. data/sample/async_copyto.rb +39 -0
  30. data/sample/async_mixed.rb +56 -0
  31. data/sample/check_conn.rb +21 -0
  32. data/sample/copyfrom.rb +81 -0
  33. data/sample/copyto.rb +19 -0
  34. data/sample/cursor.rb +21 -0
  35. data/sample/disk_usage_report.rb +186 -0
  36. data/sample/issue-119.rb +94 -0
  37. data/sample/losample.rb +69 -0
  38. data/sample/minimal-testcase.rb +17 -0
  39. data/sample/notify_wait.rb +72 -0
  40. data/sample/pg_statistics.rb +294 -0
  41. data/sample/replication_monitor.rb +231 -0
  42. data/sample/test_binary_values.rb +33 -0
  43. data/sample/wal_shipper.rb +434 -0
  44. data/sample/warehouse_partitions.rb +320 -0
  45. data/spec/data/expected_trace.out +26 -0
  46. data/spec/data/random_binary_data +0 -0
  47. data/spec/lib/helpers.rb +350 -0
  48. data/spec/pg/connection_spec.rb +1276 -0
  49. data/spec/pg/result_spec.rb +345 -0
  50. data/spec/pg_spec.rb +44 -0
  51. metadata +190 -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,350 @@
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
+ class Object
11
+ def jruby?
12
+ RUBY_PLATFORM =~ /java/
13
+ end
14
+ end
15
+
16
+ module PG::TestingHelpers
17
+
18
+
19
+ # Set some ANSI escape code constants (Shamelessly stolen from Perl's
20
+ # Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
21
+ ANSI_ATTRIBUTES = {
22
+ 'clear' => 0,
23
+ 'reset' => 0,
24
+ 'bold' => 1,
25
+ 'dark' => 2,
26
+ 'underline' => 4,
27
+ 'underscore' => 4,
28
+ 'blink' => 5,
29
+ 'reverse' => 7,
30
+ 'concealed' => 8,
31
+
32
+ 'black' => 30, 'on_black' => 40,
33
+ 'red' => 31, 'on_red' => 41,
34
+ 'green' => 32, 'on_green' => 42,
35
+ 'yellow' => 33, 'on_yellow' => 43,
36
+ 'blue' => 34, 'on_blue' => 44,
37
+ 'magenta' => 35, 'on_magenta' => 45,
38
+ 'cyan' => 36, 'on_cyan' => 46,
39
+ 'white' => 37, 'on_white' => 47
40
+ }
41
+
42
+
43
+ ###############
44
+ module_function
45
+ ###############
46
+
47
+ ### Create a string that contains the ANSI codes specified and return it
48
+ def ansi_code( *attributes )
49
+ attributes.flatten!
50
+ attributes.collect! {|at| at.to_s }
51
+
52
+ return '' unless /(?:vt10[03]|xterm(?:-color)?|linux|screen)/i =~ ENV['TERM']
53
+ attributes = ANSI_ATTRIBUTES.values_at( *attributes ).compact.join(';')
54
+
55
+ # $stderr.puts " attr is: %p" % [attributes]
56
+ if attributes.empty?
57
+ return ''
58
+ else
59
+ return "\e[%sm" % attributes
60
+ end
61
+ end
62
+
63
+
64
+ ### Colorize the given +string+ with the specified +attributes+ and return it, handling
65
+ ### line-endings, color reset, etc.
66
+ def colorize( *args )
67
+ string = ''
68
+
69
+ if block_given?
70
+ string = yield
71
+ else
72
+ string = args.shift
73
+ end
74
+
75
+ ending = string[/(\s)$/] || ''
76
+ string = string.rstrip
77
+
78
+ return ansi_code( args.flatten ) + string + ansi_code( 'reset' ) + ending
79
+ end
80
+
81
+
82
+ ### Output a message with highlighting.
83
+ def message( *msg )
84
+ $stderr.puts( colorize(:bold) { msg.flatten.join(' ') } )
85
+ end
86
+
87
+
88
+ ### Output a logging message if $VERBOSE is true
89
+ def trace( *msg )
90
+ return unless $VERBOSE
91
+ output = colorize( msg.flatten.join(' '), 'yellow' )
92
+ $stderr.puts( output )
93
+ end
94
+
95
+
96
+ ### Return the specified args as a string, quoting any that have a space.
97
+ def quotelist( *args )
98
+ return args.flatten.collect {|part| part.to_s =~ /\s/ ? part.to_s.inspect : part.to_s }
99
+ end
100
+
101
+
102
+ ### Run the specified command +cmd+ with system(), failing if the execution
103
+ ### fails.
104
+ def run( *cmd )
105
+ cmd.flatten!
106
+
107
+ if cmd.length > 1
108
+ trace( quotelist(*cmd) )
109
+ else
110
+ trace( cmd )
111
+ end
112
+
113
+ system( *cmd )
114
+ raise "Command failed: [%s]" % [cmd.join(' ')] unless $?.success?
115
+ end
116
+
117
+
118
+ ### Run the specified command +cmd+ after redirecting stdout and stderr to the specified
119
+ ### +logpath+, failing if the execution fails.
120
+ def log_and_run( logpath, *cmd )
121
+ cmd.flatten!
122
+
123
+ if cmd.length > 1
124
+ trace( quotelist(*cmd) )
125
+ else
126
+ trace( cmd )
127
+ end
128
+
129
+ # Eliminate the noise of creating/tearing down the database by
130
+ # redirecting STDERR/STDOUT to a logfile if the Ruby interpreter
131
+ # supports fork()
132
+ logfh = File.open( logpath, File::WRONLY|File::CREAT|File::APPEND )
133
+
134
+ if jruby?
135
+ # FIXME: for some reason redirection in the system method don't
136
+ # work, so I ended up with this lengthy hack
137
+ logpath ||= "/dev/null"
138
+ f = File.open logpath, 'a'
139
+ old_stdout = $stdout
140
+ old_stderr = $stderr
141
+ $stdout = $stderr = f
142
+ system( *cmd )
143
+ $stdout = old_stdout
144
+ $stderr = old_stderr
145
+ else
146
+ begin
147
+ pid = fork
148
+ rescue NotImplementedError
149
+ logfh.close
150
+ end
151
+ if pid
152
+ logfh.close
153
+ else
154
+ $stdout.reopen( logfh )
155
+ $stderr.reopen( $stdout )
156
+ $stderr.puts( ">>> " + cmd.shelljoin )
157
+ exec( *cmd )
158
+ $stderr.puts "After the exec()?!??!"
159
+ exit!
160
+ end
161
+
162
+ Process.wait( pid )
163
+ end
164
+
165
+ unless $?.success?
166
+ system("cat #{logpath}")
167
+ raise "Command failed: [%s]" % [cmd.join(' ')]
168
+ end
169
+ end
170
+
171
+ def certs_directory
172
+ File.expand_path "#{__FILE__}/../../../certs"
173
+ end
174
+
175
+ alias :old_log_and_run :log_and_run
176
+ def log_and_run( logpath, *cmd )
177
+ return old_log_and_run logpath, cmd unless cmd.first =~ /initdb/
178
+
179
+ $stderr.puts "Setting up pg_hba.conf with some dummy usernames and passwords"
180
+ tmp = old_log_and_run logpath, cmd
181
+ File.open("#{@test_pgdata}/pg_hba.conf", "w") do |f|
182
+ f.puts "# TYPE DATABASE USER ADDRESS METHOD"
183
+ f.puts " hostssl all ssl samehost password"
184
+ f.puts " host all ssl samehost reject"
185
+ f.puts " host all password samehost password"
186
+ f.puts " host all encrypt samehost md5"
187
+ f.puts " host all all samehost trust"
188
+ f.puts " local all all trust"
189
+ end
190
+ File.open("#{@test_pgdata}/postgresql.conf", "a") do |f|
191
+ f.puts "ssl = yes"
192
+ end
193
+ FileUtils.cp "#{certs_directory}/server.key", @test_pgdata
194
+ File.chmod 0400, "#{@test_pgdata}/server.key"
195
+ FileUtils.cp "#{certs_directory}/server.crt", @test_pgdata
196
+ tmp
197
+ end
198
+
199
+ ### Check the current directory for directories that look like they're
200
+ ### testing directories from previous tests, and tell any postgres instances
201
+ ### running in them to shut down.
202
+ def stop_existing_postmasters
203
+ # tmp_test_0.22329534700318
204
+ pat = Pathname.getwd + 'tmp_test_*'
205
+ Pathname.glob( pat.to_s ).each do |testdir|
206
+ datadir = testdir + 'data'
207
+ pidfile = datadir + 'postmaster.pid'
208
+ if pidfile.exist? && pid = pidfile.read.chomp.to_i
209
+ $stderr.puts "pidfile (%p) exists: %d" % [ pidfile, pid ]
210
+ begin
211
+ Process.kill( 0, pid )
212
+ rescue Errno::ESRCH
213
+ $stderr.puts "No postmaster running for %s" % [ datadir ]
214
+ # Process isn't alive, so don't try to stop it
215
+ else
216
+ $stderr.puts "Stopping lingering database at PID %d" % [ pid ]
217
+ run pg_bin_path('pg_ctl'), '-D', datadir.to_s, '-m', 'fast', 'stop'
218
+ end
219
+ else
220
+ $stderr.puts "No pidfile (%p)" % [ pidfile ]
221
+ end
222
+ end
223
+ end
224
+
225
+
226
+ def pg_bin_path cmd
227
+ begin
228
+ bin_dir = `pg_config --bindir`.strip
229
+ "#{bin_dir}/#{cmd}"
230
+ rescue
231
+ cmd
232
+ end
233
+ end
234
+
235
+ ### Set up a PostgreSQL database instance for testing.
236
+ def setup_testing_db( description )
237
+ require 'pg'
238
+ stop_existing_postmasters()
239
+
240
+ puts "Setting up test database for #{description}"
241
+ @test_pgdata = TEST_DIRECTORY + 'data'
242
+ @test_pgdata.mkpath
243
+
244
+ @port = 54321
245
+ ENV['PGPORT'] = @port.to_s
246
+ ENV['PGHOST'] = 'localhost'
247
+ @conninfo = "host=127.0.0.1 port=#{@port} dbname=test"
248
+
249
+ @logfile = TEST_DIRECTORY + 'setup.log'
250
+ trace "Command output logged to #{@logfile}"
251
+
252
+ begin
253
+ unless (@test_pgdata+"postgresql.conf").exist?
254
+ FileUtils.rm_rf( @test_pgdata, :verbose => $DEBUG )
255
+ $stderr.puts "Running initdb"
256
+ log_and_run @logfile, pg_bin_path('initdb'), '-E', 'UTF8', '--no-locale', '-D', @test_pgdata.to_s
257
+ end
258
+
259
+ $stderr.puts "Starting postgres"
260
+ log_and_run @logfile, pg_bin_path('pg_ctl'), 'start', '-l', @logfile.to_s, '-w', '-o', "-k #{TEST_DIRECTORY.to_s.dump}",
261
+ '-D', @test_pgdata.to_s
262
+ sleep 2
263
+
264
+ $stderr.puts "Creating the test DB"
265
+ log_and_run @logfile, pg_bin_path('psql'), '-e', '-c', 'DROP DATABASE IF EXISTS test', 'postgres'
266
+ log_and_run @logfile, pg_bin_path('createdb'), '-e', 'test'
267
+
268
+ rescue => err
269
+ $stderr.puts "%p during test setup: %s" % [ err.class, err.message ]
270
+ $stderr.puts "See #{@logfile} for details."
271
+ $stderr.puts *err.backtrace if $DEBUG
272
+ fail
273
+ end
274
+
275
+ conn = PG.connect( @conninfo )
276
+ conn.set_notice_processor do |message|
277
+ $stderr.puts( description + ':' + message ) if $DEBUG
278
+ end
279
+
280
+ return conn
281
+ end
282
+
283
+
284
+ def teardown_testing_db( conn )
285
+ puts "Tearing down test database"
286
+
287
+ if conn
288
+ check_for_lingering_connections( conn )
289
+ conn.finish
290
+ end
291
+
292
+ log_and_run @logfile, pg_bin_path('pg_ctl'), 'stop', '-m', 'fast', '-D', @test_pgdata.to_s
293
+ end
294
+
295
+
296
+ def check_for_lingering_connections( conn )
297
+ conn.exec( "SELECT * FROM pg_stat_activity" ) do |res|
298
+ conns = res.find_all {|row| row['pid'].to_i != conn.backend_pid }
299
+ unless conns.empty?
300
+ puts "Lingering connections remain:"
301
+ conns.each do |row|
302
+ puts " [%d] {%s} %s -- %s" % row.values_at( 'pid', 'state', 'application_name', 'query' )
303
+ end
304
+ end
305
+ end
306
+ end
307
+
308
+ def connection_string_should_contain_application_name(conn_args, app_name)
309
+ conn_name = conn_args.match(/application_name='(.*)'/)[1]
310
+ conn_name.should include(app_name[0..10])
311
+ conn_name.should include(app_name[-10..-1])
312
+ conn_name.length.should <= 64
313
+ end
314
+
315
+ # Ensure the connection is in a clean execution status.
316
+ def verify_clean_exec_status
317
+ @conn.send_query( "VALUES (1)" )
318
+ @conn.get_last_result.values.should == [["1"]]
319
+ end
320
+ end
321
+
322
+
323
+ RSpec.configure do |config|
324
+ ruby_version_vec = RUBY_VERSION.split('.').map {|c| c.to_i }.pack( "N*" )
325
+
326
+ config.include( PG::TestingHelpers )
327
+ config.treat_symbols_as_metadata_keys_with_true_values = true
328
+
329
+ config.mock_with :rspec
330
+ config.filter_run_excluding :ruby_19 if ruby_version_vec <= [1,9,1].pack( "N*" )
331
+ if RUBY_PLATFORM =~ /mingw|mswin/
332
+ config.filter_run_excluding :unix
333
+ else
334
+ config.filter_run_excluding :windows
335
+ end
336
+ config.filter_run_excluding :socket_io unless
337
+ PG::Connection.instance_methods.map( &:to_sym ).include?( :socket_io )
338
+
339
+ config.filter_run_excluding :postgresql_90 unless
340
+ PG::Connection.instance_methods.map( &:to_sym ).include?( :escape_literal )
341
+
342
+ if !PG.respond_to?( :library_version )
343
+ config.filter_run_excluding( :postgresql_91, :postgresql_92, :postgresql_93 )
344
+ elsif PG.library_version < 90200
345
+ config.filter_run_excluding( :postgresql_92, :postgresql_93 )
346
+ elsif PG.library_version < 90300
347
+ config.filter_run_excluding( :postgresql_93 )
348
+ end
349
+ end
350
+