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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +2 -0
- data/BSDL +22 -0
- data/ChangeLog +1504 -11
- data/Contributors.rdoc +7 -0
- data/History.rdoc +181 -3
- data/LICENSE +12 -14
- data/Manifest.txt +29 -15
- data/{BSD → POSTGRES} +0 -0
- data/{README.OS_X.rdoc → README-OS_X.rdoc} +0 -0
- data/{README.windows.rdoc → README-Windows.rdoc} +0 -0
- data/README.ja.rdoc +10 -3
- data/README.rdoc +54 -28
- data/Rakefile +53 -26
- data/Rakefile.cross +235 -196
- data/ext/errorcodes.def +931 -0
- data/ext/errorcodes.rb +45 -0
- data/ext/errorcodes.txt +463 -0
- data/ext/extconf.rb +37 -7
- data/ext/gvl_wrappers.c +19 -0
- data/ext/gvl_wrappers.h +211 -0
- data/ext/pg.c +317 -4277
- data/ext/pg.h +124 -21
- data/ext/pg_connection.c +3642 -0
- data/ext/pg_errors.c +89 -0
- data/ext/pg_result.c +920 -0
- data/lib/pg/connection.rb +86 -0
- data/lib/pg/constants.rb +11 -0
- data/lib/pg/exceptions.rb +11 -0
- data/lib/pg/result.rb +16 -0
- data/lib/pg.rb +26 -43
- data/sample/array_insert.rb +20 -0
- data/sample/async_api.rb +21 -24
- data/sample/async_copyto.rb +2 -2
- data/sample/async_mixed.rb +56 -0
- data/sample/check_conn.rb +21 -0
- data/sample/copyfrom.rb +1 -1
- data/sample/copyto.rb +1 -1
- data/sample/cursor.rb +2 -2
- data/sample/disk_usage_report.rb +186 -0
- data/sample/issue-119.rb +94 -0
- data/sample/losample.rb +6 -6
- data/sample/minimal-testcase.rb +17 -0
- data/sample/notify_wait.rb +51 -22
- data/sample/pg_statistics.rb +294 -0
- data/sample/replication_monitor.rb +231 -0
- data/sample/test_binary_values.rb +4 -6
- data/sample/wal_shipper.rb +434 -0
- data/sample/warehouse_partitions.rb +320 -0
- data/spec/lib/helpers.rb +70 -23
- data/spec/pg/connection_spec.rb +1128 -0
- data/spec/{pgresult_spec.rb → pg/result_spec.rb} +142 -47
- data/spec/pg_spec.rb +44 -0
- data.tar.gz.sig +0 -0
- metadata +145 -100
- metadata.gz.sig +0 -0
- data/GPL +0 -340
- data/ext/compat.c +0 -541
- data/ext/compat.h +0 -184
- data/misc/openssl-pg-segfault.rb +0 -31
- data/sample/psql.rb +0 -1181
- data/sample/psqlHelp.rb +0 -158
- data/sample/test1.rb +0 -60
- data/sample/test2.rb +0 -44
- data/sample/test4.rb +0 -71
- data/spec/m17n_spec.rb +0 -151
- 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
|
+
|
data/lib/pg/constants.rb
ADDED
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
|
-
|
22
|
-
|
18
|
+
# The top-level PG namespace.
|
19
|
+
module PG
|
23
20
|
|
21
|
+
# Library version
|
22
|
+
VERSION = '0.16.0'
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
###
|
32
|
-
|
33
|
-
|
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
|
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 =
|
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 ==
|
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 =
|
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 =
|
42
|
-
until poll_status ==
|
43
|
-
poll_status ==
|
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
|
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
|
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
|
56
|
+
when PG::CONNECTION_STARTED
|
62
57
|
output_progress " waiting for connection to be made."
|
63
|
-
when
|
58
|
+
when PG::CONNECTION_MADE
|
64
59
|
output_progress " connection OK; waiting to send."
|
65
|
-
when
|
60
|
+
when PG::CONNECTION_AWAITING_RESPONSE
|
66
61
|
output_progress " waiting for a response from the server."
|
67
|
-
when
|
62
|
+
when PG::CONNECTION_AUTH_OK
|
68
63
|
output_progress " received authentication; waiting for backend start-up to finish."
|
69
|
-
when
|
64
|
+
when PG::CONNECTION_SSL_STARTUP
|
70
65
|
output_progress " negotiating SSL encryption."
|
71
|
-
when
|
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 ==
|
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
|
data/sample/async_copyto.rb
CHANGED
@@ -6,10 +6,10 @@ require 'stringio'
|
|
6
6
|
# Using COPY asynchronously
|
7
7
|
|
8
8
|
$stderr.puts "Opening database connection ..."
|
9
|
-
conn =
|
9
|
+
conn = PG.connect( :dbname => 'test' )
|
10
10
|
conn.setnonblocking( true )
|
11
11
|
|
12
|
-
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
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 =
|
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 =
|
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
|
+
|