pg 0.12.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
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
+