pg 0.12.2 → 0.13.0.pre298

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.hoerc +2 -0
  3. data/.tm_properties +12 -0
  4. data/ChangeLog +86 -6
  5. data/Contributors.rdoc +7 -0
  6. data/History.rdoc +29 -0
  7. data/LICENSE +12 -14
  8. data/Manifest.txt +15 -14
  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 +1 -1
  13. data/README.rdoc +39 -27
  14. data/Rakefile +1 -2
  15. data/ext/extconf.rb +9 -2
  16. data/ext/pg.c +232 -4297
  17. data/ext/pg.h +87 -23
  18. data/ext/pg_connection.c +3301 -0
  19. data/ext/pg_result.c +905 -0
  20. data/lib/pg.rb +26 -43
  21. data/lib/pg/connection.rb +58 -0
  22. data/lib/pg/constants.rb +11 -0
  23. data/lib/pg/exceptions.rb +11 -0
  24. data/lib/pg/result.rb +11 -0
  25. data/misc/openssl-pg-segfault.rb +1 -1
  26. data/sample/async_api.rb +16 -21
  27. data/sample/async_copyto.rb +1 -1
  28. data/sample/async_mixed.rb +56 -0
  29. data/sample/copyfrom.rb +1 -1
  30. data/sample/copyto.rb +1 -1
  31. data/sample/cursor.rb +1 -1
  32. data/sample/losample.rb +6 -6
  33. data/sample/notify_wait.rb +51 -22
  34. data/sample/test_binary_values.rb +4 -6
  35. data/spec/lib/helpers.rb +14 -10
  36. data/spec/{pgconn_spec.rb → pg/connection_spec.rb} +227 -60
  37. data/spec/{pgresult_spec.rb → pg/result_spec.rb} +31 -35
  38. data/spec/pg_spec.rb +22 -0
  39. metadata +44 -42
  40. metadata.gz.sig +0 -0
  41. data/GPL +0 -340
  42. data/ext/compat.c +0 -541
  43. data/ext/compat.h +0 -184
  44. data/sample/psql.rb +0 -1181
  45. data/sample/psqlHelp.rb +0 -158
  46. data/sample/test1.rb +0 -60
  47. data/sample/test2.rb +0 -44
  48. data/sample/test4.rb +0 -71
  49. data/spec/m17n_spec.rb +0 -170
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.13.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: b2cd37832d02 $
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,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pg' unless defined?( PG )
4
+
5
+ # The PG connection class.
6
+ class PG::Connection
7
+
8
+ # The order the options are passed to the ::connect method.
9
+ CONNECT_ARGUMENT_ORDER = %w[host port options tty dbname user password]
10
+
11
+
12
+ ### Quote the given +value+ for use in a connection-parameter string.
13
+ def self::quote_connstr( value )
14
+ return "'" + value.to_s.gsub( /[\\']/ ) {|m| '\\' + m } + "'"
15
+ end
16
+
17
+
18
+ ### Parse the connection +args+ into a connection-parameter string. See PG::Connection.new
19
+ ### for valid arguments.
20
+ def self::parse_connect_args( *args )
21
+ return '' if args.empty?
22
+
23
+ # This will be swapped soon for code that makes options like those required for
24
+ # PQconnectdbParams()/PQconnectStartParams(). For now, stick to an options string for
25
+ # PQconnectdb()/PQconnectStart().
26
+ connopts = []
27
+
28
+ # Handle an options hash first
29
+ if args.last.is_a?( Hash )
30
+ opthash = args.pop
31
+ opthash.each do |key, val|
32
+ connopts.push( "%s=%s" % [key, PG::Connection.quote_connstr(val)] )
33
+ end
34
+ end
35
+
36
+ # Option string style
37
+ if args.length == 1 && args.first.to_s.index( '=' )
38
+ connopts.unshift( args.first )
39
+
40
+ # Append positional parameters
41
+ else
42
+ args.each_with_index do |val, i|
43
+ next unless val # Skip nil placeholders
44
+
45
+ key = CONNECT_ARGUMENT_ORDER[ i ] or
46
+ raise ArgumentError, "Extra positional parameter %d: %p" % [ i+1, val ]
47
+ connopts.push( "%s=%s" % [key, PG::Connection.quote_connstr(val.to_s)] )
48
+ end
49
+ end
50
+
51
+ return connopts.join(' ')
52
+ end
53
+
54
+ end # class PG::Connection
55
+
56
+ # Backward-compatible alias
57
+ PGconn = PG::Connection
58
+
@@ -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
+
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pg' unless defined?( PG )
4
+
5
+
6
+ class PG::Result
7
+
8
+ end # class PG::Result
9
+
10
+ # Backward-compatible alias
11
+ PGresult = PG::Result
@@ -8,7 +8,7 @@ SOCKHOST = 'it-trac.laika.com'
8
8
  # Load pg first, so the libssl.so that libpq is linked against is loaded.
9
9
  require 'pg'
10
10
  $stderr.puts "connecting to postgres://#{PGHOST}/#{PGDB}"
11
- conn = PGconn.connect( PGHOST, :dbname => PGDB )
11
+ conn = PG.connect( PGHOST, :dbname => PGDB )
12
12
 
13
13
  # Now load OpenSSL, which might be linked against a different libssl.
14
14
  require 'socket'
@@ -4,16 +4,10 @@ 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,
@@ -28,9 +22,10 @@ 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
@@ -38,19 +33,19 @@ socket = IO.for_fd( conn.socket )
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,17 +53,17 @@ 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."
73
68
  end
74
69
 
@@ -76,7 +71,7 @@ until poll_status == PGconn::PGRES_POLLING_OK ||
76
71
  poll_status = conn.connect_poll
77
72
  end
78
73
 
79
- abort "Connect failed: %s" % [ conn.error_message ] unless conn.status == PGconn::CONNECTION_OK
74
+ abort "Connect failed: %s" % [ conn.error_message ] unless conn.status == PG::CONNECTION_OK
80
75
 
81
76
  output_progress "Sending query"
82
77
  conn.send_query( "SELECT * FROM pg_stat_activity" )
@@ -6,7 +6,7 @@ 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
12
  socket = IO.for_fd( conn.socket )
@@ -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 = IO.for_fd( conn.socket )
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
+
@@ -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;
@@ -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 = ''
@@ -6,7 +6,7 @@ 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
@@ -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
 
@@ -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