pg 0.12.0 → 0.16.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.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +2 -0
  3. data/BSDL +22 -0
  4. data/ChangeLog +1504 -11
  5. data/Contributors.rdoc +7 -0
  6. data/History.rdoc +181 -3
  7. data/LICENSE +12 -14
  8. data/Manifest.txt +29 -15
  9. data/{BSD → POSTGRES} +0 -0
  10. data/{README.OS_X.rdoc → README-OS_X.rdoc} +0 -0
  11. data/{README.windows.rdoc → README-Windows.rdoc} +0 -0
  12. data/README.ja.rdoc +10 -3
  13. data/README.rdoc +54 -28
  14. data/Rakefile +53 -26
  15. data/Rakefile.cross +235 -196
  16. data/ext/errorcodes.def +931 -0
  17. data/ext/errorcodes.rb +45 -0
  18. data/ext/errorcodes.txt +463 -0
  19. data/ext/extconf.rb +37 -7
  20. data/ext/gvl_wrappers.c +19 -0
  21. data/ext/gvl_wrappers.h +211 -0
  22. data/ext/pg.c +317 -4277
  23. data/ext/pg.h +124 -21
  24. data/ext/pg_connection.c +3642 -0
  25. data/ext/pg_errors.c +89 -0
  26. data/ext/pg_result.c +920 -0
  27. data/lib/pg/connection.rb +86 -0
  28. data/lib/pg/constants.rb +11 -0
  29. data/lib/pg/exceptions.rb +11 -0
  30. data/lib/pg/result.rb +16 -0
  31. data/lib/pg.rb +26 -43
  32. data/sample/array_insert.rb +20 -0
  33. data/sample/async_api.rb +21 -24
  34. data/sample/async_copyto.rb +2 -2
  35. data/sample/async_mixed.rb +56 -0
  36. data/sample/check_conn.rb +21 -0
  37. data/sample/copyfrom.rb +1 -1
  38. data/sample/copyto.rb +1 -1
  39. data/sample/cursor.rb +2 -2
  40. data/sample/disk_usage_report.rb +186 -0
  41. data/sample/issue-119.rb +94 -0
  42. data/sample/losample.rb +6 -6
  43. data/sample/minimal-testcase.rb +17 -0
  44. data/sample/notify_wait.rb +51 -22
  45. data/sample/pg_statistics.rb +294 -0
  46. data/sample/replication_monitor.rb +231 -0
  47. data/sample/test_binary_values.rb +4 -6
  48. data/sample/wal_shipper.rb +434 -0
  49. data/sample/warehouse_partitions.rb +320 -0
  50. data/spec/lib/helpers.rb +70 -23
  51. data/spec/pg/connection_spec.rb +1128 -0
  52. data/spec/{pgresult_spec.rb → pg/result_spec.rb} +142 -47
  53. data/spec/pg_spec.rb +44 -0
  54. data.tar.gz.sig +0 -0
  55. metadata +145 -100
  56. metadata.gz.sig +0 -0
  57. data/GPL +0 -340
  58. data/ext/compat.c +0 -541
  59. data/ext/compat.h +0 -184
  60. data/misc/openssl-pg-segfault.rb +0 -31
  61. data/sample/psql.rb +0 -1181
  62. data/sample/psqlHelp.rb +0 -158
  63. data/sample/test1.rb +0 -60
  64. data/sample/test2.rb +0 -44
  65. data/sample/test4.rb +0 -71
  66. data/spec/m17n_spec.rb +0 -151
  67. data/spec/pgconn_spec.rb +0 -643
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pg'
4
+
5
+ # This is another example of how to use COPY FROM, this time as a
6
+ # minimal test case used to try to figure out what was going on in
7
+ # an issue submitted from a user:
8
+ #
9
+ # https://bitbucket.org/ged/ruby-pg/issue/119
10
+ #
11
+
12
+ conn = PG.connect( dbname: 'test' )
13
+ table_name = 'issue_119'
14
+ field_list = %w[name body_weight brain_weight]
15
+ method = 0
16
+ options = { truncate: true }
17
+ sql_parameters = ''
18
+
19
+ conn.set_error_verbosity( PG::PQERRORS_VERBOSE )
20
+ conn.exec( "DROP TABLE IF EXISTS #{table_name}" )
21
+ conn.exec( "CREATE TABLE #{table_name} ( id SERIAL, name TEXT, body_weight REAL, brain_weight REAL )" )
22
+
23
+ text = <<-END_DATA
24
+ Mountain beaver 1.35 465
25
+ Cow 465 423
26
+ Grey wolf 36.33 119.5
27
+ Goat 27.66 115
28
+ Guinea pig 1.04 5.5
29
+ Dipliodocus 11700 50
30
+ Asian elephant 2547 4603
31
+ Donkey 187.1 419
32
+ Horse 521 655
33
+ Potar monkey 10 115
34
+ Cat 3.3 25.6
35
+ Giraffe 529 680
36
+ Gorilla 207 406
37
+ Human 62 1320
38
+ African elephant 6654 5712
39
+ Triceratops 9400 70
40
+ Rhesus monkey 6.8 179
41
+ Kangaroo 35 56
42
+ Golden hamster 0.12 1
43
+ Mouse 0.023 0.4
44
+ Rabbit 2.5 12.1
45
+ Sheep 55.5 175
46
+ Jaguar 100 157
47
+ Chimpanzee 52.16 440
48
+ Brachiosaurus 87000 154.5
49
+ Mole 0.122 3
50
+ Pig 192 18
51
+ END_DATA
52
+
53
+ #ActiveRecord::Base.connection_pool.with_connection do |conn|
54
+ conn.transaction do
55
+ rc = conn #.raw_connection
56
+ rc.exec "TRUNCATE TABLE #{table_name};" if options[:truncate]
57
+ sql = "COPY #{table_name} (#{field_list.join(',')}) FROM STDIN #{sql_parameters} "
58
+ p sql
59
+ rc.exec(sql)
60
+ errmsg = nil # scope this outside of the rescue below so it's visible later
61
+ begin
62
+ if method == 1
63
+ rc.put_copy_data text + "\\.\n"
64
+ else
65
+ text.each_line { |line| rc.put_copy_data(line) }
66
+ end
67
+ rescue Errno => err
68
+ errmsg = "%s while reading copy data: %s" % [err.class.name, err.message]
69
+ puts "an error occured"
70
+ end
71
+
72
+ if errmsg
73
+ rc.put_copy_end(errmsg)
74
+ puts "ERROR #{errmsg}"
75
+ else
76
+ rc.put_copy_end
77
+ end
78
+
79
+ while res = rc.get_result
80
+ st = res.res_status( res.result_status )
81
+ puts "Result of COPY is: %s" % [ st ]
82
+ if res.result_status != PG::PGRES_COPY_IN
83
+ puts res.error_message
84
+ end
85
+ end
86
+ puts "end"
87
+ end #transaction
88
+ #end #connection
89
+
90
+ conn.exec( "SELECT name, brain_weight FROM #{table_name}" ) do |res|
91
+ p res.values
92
+ end
93
+
94
+
data/sample/losample.rb CHANGED
@@ -5,7 +5,7 @@ require 'pg'
5
5
  SAMPLE_WRITE_DATA = 'some sample data'
6
6
  SAMPLE_EXPORT_NAME = 'lowrite.txt'
7
7
 
8
- conn = PGconn.connect( :dbname => 'test', :host => 'localhost', :port => 5432 )
8
+ conn = PG.connect( :dbname => 'test', :host => 'localhost', :port => 5432 )
9
9
  puts "dbname: " + conn.db + "\thost: " + conn.host + "\tuser: " + conn.user
10
10
 
11
11
  # Start a transaction, as all large object functions require one.
@@ -20,15 +20,15 @@ puts " imported as large object %d" % [ oid ]
20
20
 
21
21
  # Read back 50 bytes of the imported data
22
22
  puts "Read test:"
23
- fd = conn.lo_open( oid, PGconn::INV_READ|PGconn::INV_WRITE )
24
- conn.lo_lseek( fd, 0, PGconn::SEEK_SET )
23
+ fd = conn.lo_open( oid, PG::INV_READ|PG::INV_WRITE )
24
+ conn.lo_lseek( fd, 0, PG::SEEK_SET )
25
25
  buf = conn.lo_read( fd, 50 )
26
26
  puts " read: %p" % [ buf ]
27
27
  puts " read was ok!" if buf =~ /require 'pg'/
28
28
 
29
29
  # Append some test data onto the end of the object
30
30
  puts "Write test:"
31
- conn.lo_lseek( fd, 0, PGconn::SEEK_END )
31
+ conn.lo_lseek( fd, 0, PG::SEEK_END )
32
32
  buf = SAMPLE_WRITE_DATA.dup
33
33
  totalbytes = 0
34
34
  until buf.empty?
@@ -53,9 +53,9 @@ puts 'Testing read and delete from a new transaction:'
53
53
  puts ' starting a new transaction'
54
54
  conn.exec( 'BEGIN' )
55
55
 
56
- fd = conn.lo_open( oid, PGconn::INV_READ )
56
+ fd = conn.lo_open( oid, PG::INV_READ )
57
57
  puts ' reopened okay.'
58
- conn.lo_lseek( fd, 50, PGconn::SEEK_END )
58
+ conn.lo_lseek( fd, 50, PG::SEEK_END )
59
59
  buf = conn.lo_read( fd, 50 )
60
60
  puts ' read okay.' if buf == SAMPLE_WRITE_DATA
61
61
 
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pg'
4
+
5
+ conn = PG.connect( :dbname => 'test' )
6
+ $stderr.puts '---',
7
+ RUBY_DESCRIPTION,
8
+ PG.version_string( true ),
9
+ "Server version: #{conn.server_version}",
10
+ "Client version: #{PG.respond_to?( :library_version ) ? PG.library_version : 'unknown'}",
11
+ '---'
12
+
13
+ result = conn.exec( "SELECT * from pg_stat_activity" )
14
+
15
+ $stderr.puts %Q{Expected this to return: ["select * from pg_stat_activity"]}
16
+ p result.field_values( 'current_query' )
17
+
@@ -2,25 +2,6 @@
2
2
  #
3
3
  # Test script, demonstrating a non-poll notification for a table event.
4
4
  #
5
- # To use, create a table called 'test', and attach a NOTIFY trigger to it
6
- # like so:
7
- #
8
- # CREATE OR REPLACE FUNCTION notify_test()
9
- # RETURNS TRIGGER
10
- # LANGUAGE plpgsql
11
- # AS $$
12
- # BEGIN
13
- # NOTIFY woo;
14
- # RETURN NULL;
15
- # END
16
- # $$
17
- #
18
- # CREATE TRIGGER notify_trigger
19
- # AFTER UPDATE OR INSERT OR DELETE
20
- # ON test
21
- # FOR EACH STATEMENT
22
- # EXECUTE PROCEDURE notify_test()
23
- #
24
5
 
25
6
  BEGIN {
26
7
  require 'pathname'
@@ -31,13 +12,61 @@ BEGIN {
31
12
 
32
13
  require 'pg'
33
14
 
34
- conn = PGconn.connect( :dbname => 'test' )
15
+ TRIGGER_TABLE = %{
16
+ CREATE TABLE IF NOT EXISTS test ( message text );
17
+ }
18
+
19
+ TRIGGER_FUNCTION = %{
20
+ CREATE OR REPLACE FUNCTION notify_test()
21
+ RETURNS TRIGGER
22
+ LANGUAGE plpgsql
23
+ AS $$
24
+ BEGIN
25
+ NOTIFY woo;
26
+ RETURN NULL;
27
+ END
28
+ $$
29
+ }
30
+
31
+ DROP_TRIGGER = %{
32
+ DROP TRIGGER IF EXISTS notify_trigger ON test
33
+ }
34
+
35
+
36
+ TRIGGER = %{
37
+ CREATE TRIGGER notify_trigger
38
+ AFTER UPDATE OR INSERT OR DELETE
39
+ ON test
40
+ FOR EACH STATEMENT
41
+ EXECUTE PROCEDURE notify_test();
42
+ }
43
+
44
+ conn = PG.connect( :dbname => 'test' )
45
+
46
+ conn.exec( TRIGGER_TABLE )
47
+ conn.exec( TRIGGER_FUNCTION )
48
+ conn.exec( DROP_TRIGGER )
49
+ conn.exec( TRIGGER )
50
+
35
51
  conn.exec( 'LISTEN woo' ) # register interest in the 'woo' event
36
52
 
53
+ notifications = []
54
+
55
+ puts "Now switch to a different term and run:",
56
+ '',
57
+ %{ psql test -c "insert into test values ('A message.')"},
58
+ ''
59
+
37
60
  puts "Waiting up to 30 seconds for for an event!"
38
61
  conn.wait_for_notify( 30 ) do |notify, pid|
39
- puts "I got one from pid %d: %s" % [ pid, notify ]
62
+ notifications << [ pid, notify ]
63
+ end
64
+
65
+ if notifications.empty?
66
+ puts "Awww, I didn't see any events."
67
+ else
68
+ puts "I got one from pid %d: %s" % notifications.first
40
69
  end
41
70
 
42
- puts "Awww, I didn't see any events."
71
+
43
72
 
@@ -0,0 +1,294 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+ #
4
+ # PostgreSQL statistic gatherer.
5
+ # Mahlon E. Smith <mahlon@martini.nu>
6
+ #
7
+ # Based on queries by Kenny Gorman.
8
+ # http://www.kennygorman.com/wordpress/?page_id=491
9
+ #
10
+ # An example gnuplot input script is included in the __END__ block
11
+ # of this script. Using it, you can feed the output this script
12
+ # generates to gnuplot (after removing header lines) to generate
13
+ # some nice performance charts.
14
+ #
15
+
16
+ begin
17
+ require 'ostruct'
18
+ require 'optparse'
19
+ require 'etc'
20
+ require 'pg'
21
+
22
+ rescue LoadError # 1.8 support
23
+ unless Object.const_defined?( :Gem )
24
+ require 'rubygems'
25
+ retry
26
+ end
27
+ raise
28
+ end
29
+
30
+
31
+ ### PostgreSQL Stats. Fetch information from pg_stat_* tables.
32
+ ### Optionally run in a continuous loop, displaying deltas.
33
+ ###
34
+ class Stats
35
+ VERSION = %q$Id: pg_statistics.rb,v 36ca5b412583 2012/04/17 23:32:25 mahlon $
36
+
37
+ def initialize( opts )
38
+ @opts = opts
39
+ @db = PG.connect(
40
+ :dbname => opts.database,
41
+ :host => opts.host,
42
+ :port => opts.port,
43
+ :user => opts.user,
44
+ :password => opts.pass,
45
+ :sslmode => 'prefer'
46
+ )
47
+ @last = nil
48
+ end
49
+
50
+ ######
51
+ public
52
+ ######
53
+
54
+ ### Primary loop. Gather statistics and generate deltas.
55
+ ###
56
+ def run
57
+ run_count = 0
58
+
59
+ loop do
60
+ current_stat = self.get_stats
61
+
62
+ # First run, store and continue
63
+ #
64
+ if @last.nil?
65
+ @last = current_stat
66
+ sleep @opts.interval
67
+ next
68
+ end
69
+
70
+ # headers
71
+ #
72
+ if run_count == 0 || run_count % 50 == 0
73
+ puts "%-20s%12s%12s%12s%12s%12s%12s%12s%12s%12s%12s%12s%12s%12s%12s" % %w[
74
+ time commits rollbks blksrd blkshit bkends seqscan
75
+ seqtprd idxscn idxtrd ins upd del locks activeq
76
+ ]
77
+ end
78
+
79
+ # calculate deltas
80
+ #
81
+ delta = current_stat.inject({}) do |h, pair|
82
+ stat, val = *pair
83
+
84
+ if %w[ activeq locks bkends ].include?( stat )
85
+ h[stat] = current_stat[stat].to_i
86
+ else
87
+ h[stat] = current_stat[stat].to_i - @last[stat].to_i
88
+ end
89
+
90
+ h
91
+ end
92
+ delta[ 'time' ] = Time.now.strftime('%F %T')
93
+
94
+ # new values
95
+ #
96
+ puts "%-20s%12s%12s%12s%12s%12s%12s%12s%12s%12s%12s%12s%12s%12s%12s" % [
97
+ delta['time'], delta['commits'], delta['rollbks'], delta['blksrd'],
98
+ delta['blkshit'], delta['bkends'], delta['seqscan'],
99
+ delta['seqtprd'], delta['idxscn'], delta['idxtrd'],
100
+ delta['ins'], delta['upd'], delta['del'], delta['locks'], delta['activeq']
101
+ ]
102
+
103
+ @last = current_stat
104
+ run_count += 1
105
+ sleep @opts.interval
106
+ end
107
+ end
108
+
109
+
110
+ ### Query the database for performance measurements. Returns a hash.
111
+ ###
112
+ def get_stats
113
+ res = @db.exec %Q{
114
+ SELECT
115
+ MAX(stat_db.xact_commit) AS commits,
116
+ MAX(stat_db.xact_rollback) AS rollbks,
117
+ MAX(stat_db.blks_read) AS blksrd,
118
+ MAX(stat_db.blks_hit) AS blkshit,
119
+ MAX(stat_db.numbackends) AS bkends,
120
+ SUM(stat_tables.seq_scan) AS seqscan,
121
+ SUM(stat_tables.seq_tup_read) AS seqtprd,
122
+ SUM(stat_tables.idx_scan) AS idxscn,
123
+ SUM(stat_tables.idx_tup_fetch) AS idxtrd,
124
+ SUM(stat_tables.n_tup_ins) AS ins,
125
+ SUM(stat_tables.n_tup_upd) AS upd,
126
+ SUM(stat_tables.n_tup_del) AS del,
127
+ MAX(stat_locks.locks) AS locks,
128
+ MAX(activity.sess) AS activeq
129
+ FROM
130
+ pg_stat_database AS stat_db,
131
+ pg_stat_user_tables AS stat_tables,
132
+ (SELECT COUNT(*) AS locks FROM pg_locks ) AS stat_locks,
133
+ (SELECT COUNT(*) AS sess FROM pg_stat_activity WHERE current_query <> '<IDLE>') AS activity
134
+ WHERE
135
+ stat_db.datname = '%s';
136
+ } % [ @opts.database ]
137
+
138
+ return res[0]
139
+ end
140
+ end
141
+
142
+
143
+ ### Parse command line arguments. Return a struct of global options.
144
+ ###
145
+ def parse_args( args )
146
+ options = OpenStruct.new
147
+ options.database = Etc.getpwuid( Process.uid ).name
148
+ options.host = '127.0.0.1'
149
+ options.port = 5432
150
+ options.user = Etc.getpwuid( Process.uid ).name
151
+ options.sslmode = 'disable'
152
+ options.interval = 5
153
+
154
+ opts = OptionParser.new do |opts|
155
+ opts.banner = "Usage: #{$0} [options]"
156
+
157
+ opts.separator ''
158
+ opts.separator 'Connection options:'
159
+
160
+ opts.on( '-d', '--database DBNAME',
161
+ "specify the database to connect to (default: \"#{options.database}\")" ) do |db|
162
+ options.database = db
163
+ end
164
+
165
+ opts.on( '-h', '--host HOSTNAME', 'database server host' ) do |host|
166
+ options.host = host
167
+ end
168
+
169
+ opts.on( '-p', '--port PORT', Integer,
170
+ "database server port (default: \"#{options.port}\")" ) do |port|
171
+ options.port = port
172
+ end
173
+
174
+ opts.on( '-U', '--user NAME',
175
+ "database user name (default: \"#{options.user}\")" ) do |user|
176
+ options.user = user
177
+ end
178
+
179
+ opts.on( '-W', 'force password prompt' ) do |pw|
180
+ print 'Password: '
181
+ begin
182
+ system 'stty -echo'
183
+ options.pass = gets.chomp
184
+ ensure
185
+ system 'stty echo'
186
+ puts
187
+ end
188
+ end
189
+
190
+ opts.separator ''
191
+ opts.separator 'Other options:'
192
+
193
+ opts.on( '-i', '--interval SECONDS', Integer,
194
+ "refresh interval in seconds (default: \"#{options.interval}\")") do |seconds|
195
+ options.interval = seconds
196
+ end
197
+
198
+ opts.on_tail( '--help', 'show this help, then exit' ) do
199
+ $stderr.puts opts
200
+ exit
201
+ end
202
+
203
+ opts.on_tail( '--version', 'output version information, then exit' ) do
204
+ puts Stats::VERSION
205
+ exit
206
+ end
207
+ end
208
+
209
+ opts.parse!( args )
210
+ return options
211
+ end
212
+
213
+
214
+ ### Go!
215
+ ###
216
+ if __FILE__ == $0
217
+ $stdout.sync = true
218
+ Stats.new( parse_args( ARGV ) ).run
219
+ end
220
+
221
+
222
+ __END__
223
+ ######################################################################
224
+ ### T E R M I N A L O P T I O N S
225
+ ######################################################################
226
+
227
+ #set terminal png nocrop enhanced font arial 8 size '800x600' x000000 xffffff x444444
228
+ #set output 'graph.png'
229
+
230
+ set terminal pdf linewidth 4 size 11,8
231
+ set output 'graph.pdf'
232
+
233
+ #set terminal aqua
234
+
235
+
236
+ ######################################################################
237
+ ### O P T I O N S F O R A L L G R A P H S
238
+ ######################################################################
239
+
240
+ set multiplot layout 2,1 title "PostgreSQL Statistics\n5 second sample rate (smoothed)"
241
+
242
+ set grid x y
243
+ set key right vertical outside
244
+ set key nobox
245
+ set xdata time
246
+ set timefmt "%Y-%m-%d.%H:%M:%S"
247
+ set format x "%l%p"
248
+ set xtic rotate by -45
249
+ input_file = "database_stats.txt"
250
+
251
+ # edit to taste!
252
+ set xrange ["2012-04-16.00:00:00":"2012-04-17.00:00:00"]
253
+
254
+
255
+ ######################################################################
256
+ ### G R A P H 1
257
+ ######################################################################
258
+
259
+ set title "Database Operations and Connection Totals"
260
+ set yrange [0:200]
261
+
262
+ plot \
263
+ input_file using 1:2 title "Commits" with lines smooth bezier, \
264
+ input_file using 1:3 title "Rollbacks" with lines smooth bezier, \
265
+ input_file using 1:11 title "Inserts" with lines smooth bezier, \
266
+ input_file using 1:12 title "Updates" with lines smooth bezier, \
267
+ input_file using 1:13 title "Deletes" with lines smooth bezier, \
268
+ input_file using 1:6 title "Backends (total)" with lines, \
269
+ input_file using 1:15 title "Active queries (total)" with lines smooth bezier
270
+
271
+
272
+ ######################################################################
273
+ ### G R A P H 2
274
+ ######################################################################
275
+
276
+ set title "Backend Performance"
277
+ set yrange [0:10000]
278
+
279
+ plot \
280
+ input_file using 1:4 title "Block (cache) reads" with lines smooth bezier, \
281
+ input_file using 1:5 title "Block (cache) hits" with lines smooth bezier, \
282
+ input_file using 1:7 title "Sequence scans" with lines smooth bezier, \
283
+ input_file using 1:8 title "Sequence tuple reads" with lines smooth bezier, \
284
+ input_file using 1:9 title "Index scans" with lines smooth bezier, \
285
+ input_file using 1:10 title "Index tuple reads" with lines smooth bezier
286
+
287
+
288
+ ######################################################################
289
+ ### C L E A N U P
290
+ ######################################################################
291
+
292
+ unset multiplot
293
+ reset
294
+