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,86 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pg' unless defined?( PG )
4
+
5
+ # The PostgreSQL connection class. The interface for this class is based on
6
+ # {libpq}[http://www.postgresql.org/docs/9.2/interactive/libpq.html], the C
7
+ # application programmer's interface to PostgreSQL. Some familiarity with libpq
8
+ # is recommended, but not necessary.
9
+ #
10
+ # For example, to send query to the database on the localhost:
11
+ #
12
+ # require 'pg'
13
+ # conn = PG::Connection.open(:dbname => 'test')
14
+ # res = conn.exec_params('SELECT $1 AS a, $2 AS b, $3 AS c', [1, 2, nil])
15
+ # # Equivalent to:
16
+ # # res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
17
+ #
18
+ # See the PG::Result class for information on working with the results of a query.
19
+ #
20
+ class PG::Connection
21
+
22
+ # The order the options are passed to the ::connect method.
23
+ CONNECT_ARGUMENT_ORDER = %w[host port options tty dbname user password]
24
+
25
+
26
+ ### Quote the given +value+ for use in a connection-parameter string.
27
+ def self::quote_connstr( value )
28
+ return "'" + value.to_s.gsub( /[\\']/ ) {|m| '\\' + m } + "'"
29
+ end
30
+
31
+
32
+ ### Parse the connection +args+ into a connection-parameter string. See PG::Connection.new
33
+ ### for valid arguments.
34
+ def self::parse_connect_args( *args )
35
+ return '' if args.empty?
36
+
37
+ # This will be swapped soon for code that makes options like those required for
38
+ # PQconnectdbParams()/PQconnectStartParams(). For now, stick to an options string for
39
+ # PQconnectdb()/PQconnectStart().
40
+
41
+ # Parameter 'fallback_application_name' was introduced in PostgreSQL 9.0
42
+ # together with PQescapeLiteral().
43
+ if PG::Connection.instance_methods.find{|m| m.to_sym == :escape_literal }
44
+ appname = $0.sub(/^(.{30}).{4,}(.{30})$/){ $1+"..."+$2 }
45
+ appname = PG::Connection.quote_connstr( appname )
46
+ connopts = ["fallback_application_name=#{appname}"]
47
+ else
48
+ connopts = []
49
+ end
50
+
51
+ # Handle an options hash first
52
+ if args.last.is_a?( Hash )
53
+ opthash = args.pop
54
+ opthash.each do |key, val|
55
+ connopts.push( "%s=%s" % [key, PG::Connection.quote_connstr(val)] )
56
+ end
57
+ end
58
+
59
+ # Option string style
60
+ if args.length == 1 && args.first.to_s.index( '=' )
61
+ connopts.unshift( args.first )
62
+
63
+ # Append positional parameters
64
+ else
65
+ args.each_with_index do |val, i|
66
+ next unless val # Skip nil placeholders
67
+
68
+ key = CONNECT_ARGUMENT_ORDER[ i ] or
69
+ raise ArgumentError, "Extra positional parameter %d: %p" % [ i+1, val ]
70
+ connopts.push( "%s=%s" % [key, PG::Connection.quote_connstr(val.to_s)] )
71
+ end
72
+ end
73
+
74
+ return connopts.join(' ')
75
+ end
76
+
77
+
78
+ # Backward-compatibility aliases for stuff that's moved into PG.
79
+ class << self
80
+ define_method( :isthreadsafe, &PG.method(:isthreadsafe) )
81
+ end
82
+ end # class PG::Connection
83
+
84
+ # Backward-compatible alias
85
+ PGconn = PG::Connection
86
+
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pg' unless defined?( PG )
4
+
5
+
6
+ module PG::Constants
7
+
8
+ # Most of these are defined in the extension.
9
+
10
+ end # module PG::Constants
11
+
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pg' unless defined?( PG )
4
+
5
+
6
+ module PG
7
+
8
+ class Error < StandardError; end
9
+
10
+ end # module PG
11
+
data/lib/pg/result.rb ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pg' unless defined?( PG )
4
+
5
+
6
+ class PG::Result
7
+
8
+ ### Returns all tuples as an array of arrays
9
+ def values
10
+ return enum_for(:each_row).to_a
11
+ end
12
+
13
+ end # class PG::Result
14
+
15
+ # Backward-compatible alias
16
+ PGresult = PG::Result
data/lib/pg.rb CHANGED
@@ -14,56 +14,39 @@ rescue LoadError
14
14
 
15
15
  end
16
16
 
17
- #--
18
- # The PG connection class.
19
- class PGconn
20
17
 
21
- # The order the options are passed to the ::connect method.
22
- CONNECT_ARGUMENT_ORDER = %w[host port options tty dbname user password]
18
+ # The top-level PG namespace.
19
+ module PG
23
20
 
21
+ # Library version
22
+ VERSION = '0.16.0'
24
23
 
25
- ### Quote the given +value+ for use in a connection-parameter string.
26
- def self::quote_connstr( value )
27
- return "'" + value.to_s.gsub( /[\\']/ ) {|m| '\\' + m } + "'"
24
+ # VCS revision
25
+ REVISION = %q$Revision: 4e0606f5f5aa $
26
+
27
+
28
+ ### Get the PG library version. If +include_buildnum+ is +true+, include the build ID.
29
+ def self::version_string( include_buildnum=false )
30
+ vstring = "%s %s" % [ self.name, VERSION ]
31
+ vstring << " (build %s)" % [ REVISION[/: ([[:xdigit:]]+)/, 1] || '0' ] if include_buildnum
32
+ return vstring
28
33
  end
29
34
 
30
35
 
31
- ### Parse the connection +args+ into a connection-parameter string. See PGconn.new
32
- ### for valid arguments.
33
- def self::parse_connect_args( *args )
34
- return '' if args.empty?
35
-
36
- # This will be swapped soon for code that makes options like those required for
37
- # PQconnectdbParams()/PQconnectStartParams(). For now, stick to an options string for
38
- # PQconnectdb()/PQconnectStart().
39
- connopts = []
40
-
41
- # Handle an options hash first
42
- if args.last.is_a?( Hash )
43
- opthash = args.pop
44
- opthash.each do |key, val|
45
- connopts.push( "%s=%s" % [key, PGconn.quote_connstr(val)] )
46
- end
47
- end
48
-
49
- # Option string style
50
- if args.length == 1 && args.first.to_s.index( '=' )
51
- connopts.unshift( args.first )
52
-
53
- # Append positional parameters
54
- else
55
- args.each_with_index do |val, i|
56
- next unless val # Skip nil placeholders
57
-
58
- key = CONNECT_ARGUMENT_ORDER[ i ] or
59
- raise ArgumentError, "Extra positional parameter %d: %p" % [ i+1, val ]
60
- connopts.push( "%s=%s" % [key, PGconn.quote_connstr(val.to_s)] )
61
- end
62
- end
63
-
64
- return connopts.join(' ')
36
+ ### Convenience alias for PG::Connection.new.
37
+ def self::connect( *args )
38
+ return PG::Connection.new( *args )
65
39
  end
66
40
 
67
- end # class PGconn
68
41
 
42
+ require 'pg/exceptions'
43
+ require 'pg/constants'
44
+ require 'pg/connection'
45
+ require 'pg/result'
46
+
47
+ end # module PG
48
+
49
+
50
+ # Backward-compatible aliase
51
+ PGError = PG::Error
69
52
 
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pg'
4
+
5
+ c = PG.connect( dbname: 'test' )
6
+
7
+ # this one works:
8
+ c.exec( "DROP TABLE IF EXISTS foo" )
9
+ c.exec( "CREATE TABLE foo (strings character varying[]);" )
10
+
11
+ # But using a prepared statement works:
12
+ c.set_error_verbosity( PG::PQERRORS_VERBOSE )
13
+ c.prepare( 'stmt', "INSERT INTO foo VALUES ($1);" )
14
+
15
+ # This won't work
16
+ #c.exec_prepared( 'stmt', ["ARRAY['this','that']"] )
17
+
18
+ # but this will:
19
+ c.exec_prepared( 'stmt', ["{'this','that'}"] )
20
+
data/sample/async_api.rb CHANGED
@@ -4,20 +4,14 @@ require 'pg'
4
4
 
5
5
  # This is a example of how to use the asynchronous API to query the
6
6
  # server without blocking other threads. It's intentionally low-level;
7
- # if you hooked up the PGconn#socket to some kind of reactor, you
7
+ # if you hooked up the PG::Connection#socket to some kind of reactor, you
8
8
  # could make this much nicer.
9
9
 
10
10
  TIMEOUT = 5.0 # seconds to wait for an async operation to complete
11
- CONN_OPTS = {
12
- :host => 'localhost',
13
- :dbname => 'test',
14
- :user => 'jrandom',
15
- :password => 'banks!stealUR$',
16
- }
17
11
 
18
12
  # Print 'x' continuously to demonstrate that other threads aren't
19
13
  # blocked while waiting for the connection, for the query to be sent,
20
- # for results, etc. You might want to sleep inside the loop or
14
+ # for results, etc. You might want to sleep inside the loop or
21
15
  # comment this out entirely for cleaner output.
22
16
  progress_thread = Thread.new { loop { print 'x' } }
23
17
 
@@ -28,29 +22,30 @@ end
28
22
 
29
23
  # Start the connection
30
24
  output_progress "Starting connection..."
31
- conn = PGconn.connect_start( CONN_OPTS ) or abort "Unable to create a new connection!"
25
+ conn = PG::Connection.connect_start( :dbname => 'test' ) or
26
+ abort "Unable to create a new connection!"
32
27
  abort "Connection failed: %s" % [ conn.error_message ] if
33
- conn.status == PGconn::CONNECTION_BAD
28
+ conn.status == PG::CONNECTION_BAD
34
29
 
35
30
  # Now grab a reference to the underlying socket so we know when the
36
31
  # connection is established
37
- socket = IO.for_fd( conn.socket )
32
+ socket = conn.socket_io
38
33
 
39
34
  # Track the progress of the connection, waiting for the socket to become readable/writable
40
35
  # before polling it
41
- poll_status = PGconn::PGRES_POLLING_WRITING
42
- until poll_status == PGconn::PGRES_POLLING_OK ||
43
- poll_status == PGconn::PGRES_POLLING_FAILED
36
+ poll_status = PG::PGRES_POLLING_WRITING
37
+ until poll_status == PG::PGRES_POLLING_OK ||
38
+ poll_status == PG::PGRES_POLLING_FAILED
44
39
 
45
40
  # If the socket needs to read, wait 'til it becomes readable to poll again
46
41
  case poll_status
47
- when PGconn::PGRES_POLLING_READING
42
+ when PG::PGRES_POLLING_READING
48
43
  output_progress " waiting for socket to become readable"
49
44
  select( [socket], nil, nil, TIMEOUT ) or
50
45
  raise "Asynchronous connection timed out!"
51
46
 
52
47
  # ...and the same for when the socket needs to write
53
- when PGconn::PGRES_POLLING_WRITING
48
+ when PG::PGRES_POLLING_WRITING
54
49
  output_progress " waiting for socket to become writable"
55
50
  select( nil, [socket], nil, TIMEOUT ) or
56
51
  raise "Asynchronous connection timed out!"
@@ -58,25 +53,27 @@ until poll_status == PGconn::PGRES_POLLING_OK ||
58
53
 
59
54
  # Output a status message about the progress
60
55
  case conn.status
61
- when PGconn::CONNECTION_STARTED
56
+ when PG::CONNECTION_STARTED
62
57
  output_progress " waiting for connection to be made."
63
- when PGconn::CONNECTION_MADE
58
+ when PG::CONNECTION_MADE
64
59
  output_progress " connection OK; waiting to send."
65
- when PGconn::CONNECTION_AWAITING_RESPONSE
60
+ when PG::CONNECTION_AWAITING_RESPONSE
66
61
  output_progress " waiting for a response from the server."
67
- when PGconn::CONNECTION_AUTH_OK
62
+ when PG::CONNECTION_AUTH_OK
68
63
  output_progress " received authentication; waiting for backend start-up to finish."
69
- when PGconn::CONNECTION_SSL_STARTUP
64
+ when PG::CONNECTION_SSL_STARTUP
70
65
  output_progress " negotiating SSL encryption."
71
- when PGconn::CONNECTION_SETENV
66
+ when PG::CONNECTION_SETENV
72
67
  output_progress " negotiating environment-driven parameter settings."
68
+ when PG::CONNECTION_NEEDED
69
+ output_progress " internal state: connect() needed."
73
70
  end
74
71
 
75
72
  # Check to see if it's finished or failed yet
76
73
  poll_status = conn.connect_poll
77
74
  end
78
75
 
79
- abort "Connect failed: %s" % [ conn.error_message ] unless conn.status == PGconn::CONNECTION_OK
76
+ abort "Connect failed: %s" % [ conn.error_message ] unless conn.status == PG::CONNECTION_OK
80
77
 
81
78
  output_progress "Sending query"
82
79
  conn.send_query( "SELECT * FROM pg_stat_activity" )
@@ -85,7 +82,7 @@ conn.send_query( "SELECT * FROM pg_stat_activity" )
85
82
  loop do
86
83
  output_progress " waiting for a response"
87
84
 
88
- # Buffer any incoming data on the socket until a full result is ready.
85
+ # Buffer any incoming data on the socket until a full result is ready.
89
86
  conn.consume_input
90
87
  while conn.is_busy
91
88
  select( [socket], nil, nil, TIMEOUT ) or
@@ -6,10 +6,10 @@ require 'stringio'
6
6
  # Using COPY asynchronously
7
7
 
8
8
  $stderr.puts "Opening database connection ..."
9
- conn = PGconn.connect( :dbname => 'test' )
9
+ conn = PG.connect( :dbname => 'test' )
10
10
  conn.setnonblocking( true )
11
11
 
12
- socket = IO.for_fd( conn.socket )
12
+ socket = conn.socket_io
13
13
 
14
14
  $stderr.puts "Running COPY command ..."
15
15
  buf = ''
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pg'
4
+
5
+ $stdout.sync = true
6
+
7
+ # This is a example of how to mix and match synchronous and async APIs. In this case,
8
+ # the connection to the server is made syncrhonously, and then queries are
9
+ # asynchronous.
10
+
11
+ TIMEOUT = 5.0 # seconds to wait for an async operation to complete
12
+ CONN_OPTS = {
13
+ :host => 'localhost',
14
+ :dbname => 'test',
15
+ }
16
+
17
+ # Output progress messages
18
+ def output_progress( msg )
19
+ puts ">>> #{msg}\n"
20
+ end
21
+
22
+ # Start the (synchronous) connection
23
+ output_progress "Starting connection..."
24
+ conn = PG.connect( CONN_OPTS ) or abort "Unable to create a new connection!"
25
+
26
+ abort "Connect failed: %s" % [ conn.error_message ] unless conn.status == PG::CONNECTION_OK
27
+
28
+ # Now grab a reference to the underlying socket to select() on while the query is running
29
+ socket = conn.socket_io
30
+
31
+ # Send the (asynchronous) query
32
+ output_progress "Sending query"
33
+ conn.send_query( "SELECT * FROM pg_stat_activity" )
34
+
35
+ # Fetch results until there aren't any more
36
+ loop do
37
+ output_progress " waiting for a response"
38
+
39
+ # Buffer any incoming data on the socket until a full result is ready.
40
+ conn.consume_input
41
+ while conn.is_busy
42
+ output_progress " waiting for data to be available on %p..." % [ socket ]
43
+ select( [socket], nil, nil, TIMEOUT ) or
44
+ raise "Timeout waiting for query response."
45
+ conn.consume_input
46
+ end
47
+
48
+ # Fetch the next result. If there isn't one, the query is finished
49
+ result = conn.get_result or break
50
+
51
+ output_progress "Query result:\n%p\n" % [ result.values ]
52
+ end
53
+
54
+ output_progress "Done."
55
+ conn.finish
56
+
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set nosta noet ts=4 sw=4:
3
+ # encoding: utf-8
4
+
5
+ require 'pg'
6
+
7
+ # This is a minimal example of a function that can test an existing PG::Connection and
8
+ # reset it if necessary.
9
+
10
+ def check_connection( conn )
11
+ begin
12
+ conn.exec( "SELECT 1" )
13
+ rescue PG::Error => err
14
+ $stderr.puts "%p while testing connection: %s" % [ err.class, err.message ]
15
+ conn.reset
16
+ end
17
+ end
18
+
19
+ conn = PG.connect( dbname: 'test' )
20
+ check_connection( conn )
21
+
data/sample/copyfrom.rb CHANGED
@@ -4,7 +4,7 @@ require 'pg'
4
4
  require 'stringio'
5
5
 
6
6
  $stderr.puts "Opening database connection ..."
7
- conn = PGconn.connect( :dbname => 'test' )
7
+ conn = PG.connect( :dbname => 'test' )
8
8
 
9
9
  conn.exec( <<END_SQL )
10
10
  DROP TABLE IF EXISTS logs;
data/sample/copyto.rb CHANGED
@@ -6,7 +6,7 @@ require 'stringio'
6
6
  # An example of how to stream data to your local host from the database as CSV.
7
7
 
8
8
  $stderr.puts "Opening database connection ..."
9
- conn = PGconn.connect( :dbname => 'test' )
9
+ conn = PG.connect( :dbname => 'test' )
10
10
 
11
11
  $stderr.puts "Running COPY command ..."
12
12
  buf = ''
data/sample/cursor.rb CHANGED
@@ -6,9 +6,9 @@ require 'pg'
6
6
  # the cursor portion of testlibpq.c from src/test/examples.
7
7
 
8
8
  $stderr.puts "Opening database connection ..."
9
- conn = PGconn.connect( :dbname => 'test' )
9
+ conn = PG.connect( :dbname => 'test' )
10
10
 
11
- #
11
+ #
12
12
  conn.transaction do
13
13
  conn.exec( "DECLARE myportal CURSOR FOR select * from pg_database" )
14
14
  res = conn.exec( "FETCH ALL IN myportal" )
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+ #
4
+ # Quickly dump size information for a given database.
5
+ # Top twenty objects, and size per schema.
6
+ #
7
+ # Mahlon E. Smith <mahlon@martini.nu>
8
+ #
9
+ # Based on work by Jeff Davis <ruby@j-davis.com>.
10
+ #
11
+
12
+
13
+ begin
14
+ require 'ostruct'
15
+ require 'optparse'
16
+ require 'etc'
17
+ require 'pg'
18
+
19
+ rescue LoadError # 1.8 support
20
+ unless Object.const_defined?( :Gem )
21
+ require 'rubygems'
22
+ retry
23
+ end
24
+ raise
25
+ end
26
+
27
+ SCRIPT_VERSION = %q$Id: disk_usage_report.rb,v 76ebae01c937 2013/03/26 17:50:02 ged $
28
+
29
+
30
+ ### Gather data and output it to $stdout.
31
+ ###
32
+ def report( opts )
33
+ db = PG.connect(
34
+ :dbname => opts.database,
35
+ :host => opts.host,
36
+ :port => opts.port,
37
+ :user => opts.user,
38
+ :password => opts.pass,
39
+ :sslmode => 'prefer'
40
+ )
41
+
42
+ # -----------------------------------------
43
+
44
+ db_info = db.exec %Q{
45
+ SELECT
46
+ count(oid) AS num_relations,
47
+ pg_size_pretty(pg_database_size('#{opts.database}')) AS dbsize
48
+ FROM
49
+ pg_class
50
+ }
51
+
52
+ puts '=' * 70
53
+ puts "Disk usage information for %s: (%d relations, %s total)" % [
54
+ opts.database,
55
+ db_info[0]['num_relations'],
56
+ db_info[0]['dbsize']
57
+ ]
58
+ puts '=' * 70
59
+
60
+ # -----------------------------------------
61
+
62
+ top_twenty = db.exec %q{
63
+ SELECT
64
+ relname AS name,
65
+ relkind AS kind,
66
+ pg_size_pretty(pg_relation_size(pg_class.oid)) AS size
67
+ FROM
68
+ pg_class
69
+ ORDER BY
70
+ pg_relation_size(pg_class.oid) DESC
71
+ LIMIT 20
72
+ }
73
+
74
+ puts 'Top twenty objects by size:'
75
+ puts '-' * 70
76
+ top_twenty.each do |row|
77
+ type = case row['kind']
78
+ when 'i'; 'index'
79
+ when 't'; 'toast'
80
+ when 'r'; 'table'
81
+ when 'S'; 'sequence'
82
+ else; '???'
83
+ end
84
+
85
+ puts "%40s %10s (%s)" % [ row['name'], row['size'], type ]
86
+ end
87
+ puts '-' * 70
88
+
89
+ # -----------------------------------------
90
+
91
+ schema_sizes = db.exec %q{
92
+ SELECT
93
+ table_schema,
94
+ pg_size_pretty( CAST( SUM(pg_total_relation_size(table_schema || '.' || table_name)) AS bigint)) AS size
95
+ FROM
96
+ information_schema.tables
97
+ GROUP BY
98
+ table_schema
99
+ ORDER BY
100
+ CAST( SUM(pg_total_relation_size(table_schema || '.' || table_name)) AS bigint ) DESC
101
+ }
102
+
103
+
104
+ puts 'Size per schema:'
105
+ puts '-' * 70
106
+ schema_sizes.each do |row|
107
+ puts "%20s %10s" % [ row['table_schema'], row['size'] ]
108
+ end
109
+ puts '-' * 70
110
+ puts
111
+
112
+ db.finish
113
+ end
114
+
115
+
116
+ ### Parse command line arguments. Return a struct of global options.
117
+ ###
118
+ def parse_args( args )
119
+ options = OpenStruct.new
120
+ options.database = Etc.getpwuid( Process.uid ).name
121
+ options.host = '127.0.0.1'
122
+ options.port = 5432
123
+ options.user = Etc.getpwuid( Process.uid ).name
124
+ options.sslmode = 'prefer'
125
+ options.interval = 5
126
+
127
+ opts = OptionParser.new do |opts|
128
+ opts.banner = "Usage: #{$0} [options]"
129
+
130
+ opts.separator ''
131
+ opts.separator 'Connection options:'
132
+
133
+ opts.on( '-d', '--database DBNAME',
134
+ "specify the database to connect to (default: \"#{options.database}\")" ) do |db|
135
+ options.database = db
136
+ end
137
+
138
+ opts.on( '-h', '--host HOSTNAME', 'database server host' ) do |host|
139
+ options.host = host
140
+ end
141
+
142
+ opts.on( '-p', '--port PORT', Integer,
143
+ "database server port (default: \"#{options.port}\")" ) do |port|
144
+ options.port = port
145
+ end
146
+
147
+ opts.on( '-U', '--user NAME',
148
+ "database user name (default: \"#{options.user}\")" ) do |user|
149
+ options.user = user
150
+ end
151
+
152
+ opts.on( '-W', 'force password prompt' ) do |pw|
153
+ print 'Password: '
154
+ begin
155
+ system 'stty -echo'
156
+ options.pass = gets.chomp
157
+ ensure
158
+ system 'stty echo'
159
+ puts
160
+ end
161
+ end
162
+
163
+ opts.separator ''
164
+ opts.separator 'Other options:'
165
+
166
+ opts.on_tail( '--help', 'show this help, then exit' ) do
167
+ $stderr.puts opts
168
+ exit
169
+ end
170
+
171
+ opts.on_tail( '--version', 'output version information, then exit' ) do
172
+ puts SCRIPT_VERSION
173
+ exit
174
+ end
175
+ end
176
+
177
+ opts.parse!( args )
178
+ return options
179
+ end
180
+
181
+
182
+ if __FILE__ == $0
183
+ opts = parse_args( ARGV )
184
+ report( opts )
185
+ end
186
+