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
@@ -3,32 +3,40 @@
3
3
 
4
4
  BEGIN {
5
5
  require 'pathname'
6
- require 'rbconfig'
7
6
 
8
- basedir = Pathname( __FILE__ ).dirname.parent
7
+ basedir = Pathname( __FILE__ ).dirname.parent.parent
9
8
  libdir = basedir + 'lib'
10
- archlib = libdir + Config::CONFIG['sitearch']
11
9
 
12
10
  $LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
13
11
  $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
14
- $LOAD_PATH.unshift( archlib.to_s ) unless $LOAD_PATH.include?( archlib.to_s )
15
12
  }
16
13
 
17
14
  require 'rspec'
18
15
  require 'spec/lib/helpers'
19
16
  require 'pg'
20
17
 
21
- describe PGresult do
22
- include PgTestingHelpers
18
+ describe PG::Result do
23
19
 
24
20
  before( :all ) do
25
- @conn = setup_testing_db( "PGresult" )
21
+ @conn = setup_testing_db( "PG_Result" )
26
22
  end
27
23
 
28
24
  before( :each ) do
29
25
  @conn.exec( 'BEGIN' )
30
26
  end
31
27
 
28
+ after( :each ) do
29
+ @conn.exec( 'ROLLBACK' )
30
+ end
31
+
32
+ after( :all ) do
33
+ teardown_testing_db( @conn )
34
+ end
35
+
36
+
37
+ #
38
+ # Examples
39
+ #
32
40
 
33
41
  it "should act as an array of hashes" do
34
42
  res = @conn.exec("SELECT 1 AS a, 2 AS b")
@@ -36,6 +44,13 @@ describe PGresult do
36
44
  res[0]['b'].should== '2'
37
45
  end
38
46
 
47
+ it "should yield a row as an array" do
48
+ res = @conn.exec("SELECT 1 AS a, 2 AS b")
49
+ list = []
50
+ res.each_row { |r| list << r }
51
+ list.should eq [['1', '2']]
52
+ end
53
+
39
54
  it "should insert nil AS NULL and return NULL as nil" do
40
55
  res = @conn.exec("SELECT $1::int AS n", [nil])
41
56
  res[0]['n'].should be_nil()
@@ -51,21 +66,37 @@ describe PGresult do
51
66
 
52
67
  result = exception.result
53
68
 
54
- result.should be_a( PGresult )
55
- result.error_field( PGresult::PG_DIAG_SEVERITY ).should == 'ERROR'
56
- result.error_field( PGresult::PG_DIAG_SQLSTATE ).should == '42P01'
57
- result.error_field( PGresult::PG_DIAG_MESSAGE_PRIMARY ).
69
+ result.should be_a( described_class() )
70
+ result.error_field( PG::PG_DIAG_SEVERITY ).should == 'ERROR'
71
+ result.error_field( PG::PG_DIAG_SQLSTATE ).should == '42P01'
72
+ result.error_field( PG::PG_DIAG_MESSAGE_PRIMARY ).
58
73
  should == 'relation "nonexistant_table" does not exist'
59
- result.error_field( PGresult::PG_DIAG_MESSAGE_DETAIL ).should be_nil()
60
- result.error_field( PGresult::PG_DIAG_MESSAGE_HINT ).should be_nil()
61
- result.error_field( PGresult::PG_DIAG_STATEMENT_POSITION ).should == '15'
62
- result.error_field( PGresult::PG_DIAG_INTERNAL_POSITION ).should be_nil()
63
- result.error_field( PGresult::PG_DIAG_INTERNAL_QUERY ).should be_nil()
64
- result.error_field( PGresult::PG_DIAG_CONTEXT ).should be_nil()
65
- result.error_field( PGresult::PG_DIAG_SOURCE_FILE ).should =~ /parse_relation\.c$/
66
- result.error_field( PGresult::PG_DIAG_SOURCE_LINE ).should == '857'
67
- result.error_field( PGresult::PG_DIAG_SOURCE_FUNCTION ).should == 'parserOpenTable'
74
+ result.error_field( PG::PG_DIAG_MESSAGE_DETAIL ).should be_nil()
75
+ result.error_field( PG::PG_DIAG_MESSAGE_HINT ).should be_nil()
76
+ result.error_field( PG::PG_DIAG_STATEMENT_POSITION ).should == '15'
77
+ result.error_field( PG::PG_DIAG_INTERNAL_POSITION ).should be_nil()
78
+ result.error_field( PG::PG_DIAG_INTERNAL_QUERY ).should be_nil()
79
+ result.error_field( PG::PG_DIAG_CONTEXT ).should be_nil()
80
+ result.error_field( PG::PG_DIAG_SOURCE_FILE ).should =~ /parse_relation\.c$|namespace\.c$/
81
+ result.error_field( PG::PG_DIAG_SOURCE_LINE ).should =~ /^\d+$/
82
+ result.error_field( PG::PG_DIAG_SOURCE_FUNCTION ).should =~ /^parserOpenTable$|^RangeVarGetRelid$/
83
+ end
84
+
85
+ it "encapsulates database object names for integrity constraint violations", :postgresql_93 do
86
+ @conn.exec( "CREATE TABLE integrity (id SERIAL PRIMARY KEY)" )
87
+ exception = nil
88
+ begin
89
+ @conn.exec( "INSERT INTO integrity VALUES (NULL)" )
90
+ rescue PGError => err
91
+ exception = err
92
+ end
93
+ result = exception.result
68
94
 
95
+ result.error_field( PG::PG_DIAG_SCHEMA_NAME ).should == 'public'
96
+ result.error_field( PG::PG_DIAG_TABLE_NAME ).should == 'integrity'
97
+ result.error_field( PG::PG_DIAG_COLUMN_NAME ).should == 'id'
98
+ result.error_field( PG::PG_DIAG_DATATYPE_NAME ).should be_nil
99
+ result.error_field( PG::PG_DIAG_CONSTRAINT_NAME ).should be_nil
69
100
  end
70
101
 
71
102
  it "should detect division by zero as SQLSTATE 22012" do
@@ -73,7 +104,7 @@ describe PGresult do
73
104
  begin
74
105
  res = @conn.exec("SELECT 1/0")
75
106
  rescue PGError => e
76
- sqlstate = e.result.result_error_field( PGresult::PG_DIAG_SQLSTATE ).to_i
107
+ sqlstate = e.result.result_error_field( PG::PG_DIAG_SQLSTATE ).to_i
77
108
  end
78
109
  sqlstate.should == 22012
79
110
  end
@@ -81,27 +112,31 @@ describe PGresult do
81
112
  it "should return the same bytes in binary format that are sent in binary format" do
82
113
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
83
114
  bytes = File.open(binary_file, 'rb').read
84
- res = @conn.exec('VALUES ($1::bytea)',
115
+ res = @conn.exec('VALUES ($1::bytea)',
85
116
  [ { :value => bytes, :format => 1 } ], 1)
86
117
  res[0]['column1'].should== bytes
118
+ res.getvalue(0,0).should == bytes
119
+ res.values[0][0].should == bytes
120
+ res.column_values(0)[0].should == bytes
87
121
  end
88
122
 
89
123
  it "should return the same bytes in binary format that are sent as inline text" do
90
124
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
91
- in_bytes = File.open(binary_file, 'rb').read
92
- out_bytes = nil
125
+ bytes = File.open(binary_file, 'rb').read
93
126
  @conn.exec("SET standard_conforming_strings=on")
94
- res = @conn.exec("VALUES ('#{PGconn.escape_bytea(in_bytes)}'::bytea)", [], 1)
95
- out_bytes = res[0]['column1']
96
- out_bytes.should == in_bytes
127
+ res = @conn.exec("VALUES ('#{PG::Connection.escape_bytea(bytes)}'::bytea)", [], 1)
128
+ res[0]['column1'].should == bytes
129
+ res.getvalue(0,0).should == bytes
130
+ res.values[0][0].should == bytes
131
+ res.column_values(0)[0].should == bytes
97
132
  end
98
133
 
99
134
  it "should return the same bytes in text format that are sent in binary format" do
100
135
  binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
101
136
  bytes = File.open(binary_file, 'rb').read
102
- res = @conn.exec('VALUES ($1::bytea)',
137
+ res = @conn.exec('VALUES ($1::bytea)',
103
138
  [ { :value => bytes, :format => 1 } ])
104
- PGconn.unescape_bytea(res[0]['column1']).should== bytes
139
+ PG::Connection.unescape_bytea(res[0]['column1']).should== bytes
105
140
  end
106
141
 
107
142
  it "should return the same bytes in text format that are sent as inline text" do
@@ -110,13 +145,13 @@ describe PGresult do
110
145
 
111
146
  out_bytes = nil
112
147
  @conn.exec("SET standard_conforming_strings=on")
113
- res = @conn.exec("VALUES ('#{PGconn.escape_bytea(in_bytes)}'::bytea)", [], 0)
114
- out_bytes = PGconn.unescape_bytea(res[0]['column1'])
148
+ res = @conn.exec("VALUES ('#{PG::Connection.escape_bytea(in_bytes)}'::bytea)", [], 0)
149
+ out_bytes = PG::Connection.unescape_bytea(res[0]['column1'])
115
150
  out_bytes.should == in_bytes
116
151
  end
117
152
 
118
- it "should return the parameter type of the specified prepared statment parameter" do
119
- query = 'SELECT * FROM pg_stat_activity WHERE user = $1::name AND current_query = $2::text'
153
+ it "should return the parameter type of the specified prepared statement parameter", :postgresql_92 do
154
+ query = 'SELECT * FROM pg_stat_activity WHERE user = $1::name AND query = $2::text'
120
155
  @conn.prepare( 'queryfinder', query )
121
156
  res = @conn.describe_prepared( 'queryfinder' )
122
157
 
@@ -163,19 +198,19 @@ describe PGresult do
163
198
  res.fmod( 0 ).should == 33 + 4 # Column length + varlena size (4)
164
199
  end
165
200
 
166
- it "should raise an exception when an invalid index is passed to PGresult#fmod" do
201
+ it "should raise an exception when an invalid index is passed to PG::Result#fmod" do
167
202
  @conn.exec( 'CREATE TABLE fmodtest ( foo varchar(33) )' )
168
203
  res = @conn.exec( 'SELECT * FROM fmodtest' )
169
204
  expect { res.fmod(1) }.to raise_error( ArgumentError )
170
205
  end
171
206
 
172
- it "should raise an exception when an invalid (negative) index is passed to PGresult#fmod" do
207
+ it "should raise an exception when an invalid (negative) index is passed to PG::Result#fmod" do
173
208
  @conn.exec( 'CREATE TABLE fmodtest ( foo varchar(33) )' )
174
209
  res = @conn.exec( 'SELECT * FROM fmodtest' )
175
210
  expect { res.fmod(-11) }.to raise_error( ArgumentError )
176
211
  end
177
212
 
178
- it "shouldn't raise an exception when a valid index is passed to PGresult#fmod for a column with no typemod" do
213
+ it "shouldn't raise an exception when a valid index is passed to PG::Result#fmod for a column with no typemod" do
179
214
  @conn.exec( 'CREATE TABLE fmodtest ( foo text )' )
180
215
  res = @conn.exec( 'SELECT * FROM fmodtest' )
181
216
  res.fmod( 0 ).should == -1 # and it shouldn't raise an exception, either
@@ -189,25 +224,25 @@ describe PGresult do
189
224
  res.ftable( 0 ).should == be_nonzero()
190
225
  end
191
226
 
192
- it "should raise an exception when an invalid index is passed to PGresult#ftable" do
227
+ it "should raise an exception when an invalid index is passed to PG::Result#ftable" do
193
228
  @conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
194
229
  res = @conn.exec( 'SELECT * FROM ftabletest' )
195
230
 
196
231
  expect { res.ftable(18) }.to raise_error( ArgumentError )
197
232
  end
198
233
 
199
- it "should raise an exception when an invalid (negative) index is passed to PGresult#ftable" do
234
+ it "should raise an exception when an invalid (negative) index is passed to PG::Result#ftable" do
200
235
  @conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
201
236
  res = @conn.exec( 'SELECT * FROM ftabletest' )
202
237
 
203
238
  expect { res.ftable(-2) }.to raise_error( ArgumentError )
204
239
  end
205
240
 
206
- it "shouldn't raise an exception when a valid index is passed to PGresult#ftable for a " +
241
+ it "shouldn't raise an exception when a valid index is passed to PG::Result#ftable for a " +
207
242
  "column with no corresponding table" do
208
243
  @conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
209
244
  res = @conn.exec( 'SELECT foo, LENGTH(foo) as length FROM ftabletest' )
210
- res.ftable( 1 ).should == PGresult::InvalidOid # and it shouldn't raise an exception, either
245
+ res.ftable( 1 ).should == PG::INVALID_OID # and it shouldn't raise an exception, either
211
246
  end
212
247
 
213
248
  # PQftablecol
@@ -219,32 +254,92 @@ describe PGresult do
219
254
  res.ftablecol( 1 ).should == 2
220
255
  end
221
256
 
222
- it "should raise an exception when an invalid index is passed to PGresult#ftablecol" do
257
+ it "should raise an exception when an invalid index is passed to PG::Result#ftablecol" do
223
258
  @conn.exec( 'CREATE TABLE ftablecoltest ( foo text, bar numeric )' )
224
259
  res = @conn.exec( 'SELECT * FROM ftablecoltest' )
225
260
 
226
261
  expect { res.ftablecol(32) }.to raise_error( ArgumentError )
227
262
  end
228
263
 
229
- it "should raise an exception when an invalid (negative) index is passed to PGresult#ftablecol" do
264
+ it "should raise an exception when an invalid (negative) index is passed to PG::Result#ftablecol" do
230
265
  @conn.exec( 'CREATE TABLE ftablecoltest ( foo text, bar numeric )' )
231
266
  res = @conn.exec( 'SELECT * FROM ftablecoltest' )
232
267
 
233
268
  expect { res.ftablecol(-1) }.to raise_error( ArgumentError )
234
269
  end
235
270
 
236
- it "shouldn't raise an exception when a valid index is passed to PGresult#ftablecol for a " +
271
+ it "shouldn't raise an exception when a valid index is passed to PG::Result#ftablecol for a " +
237
272
  "column with no corresponding table" do
238
273
  @conn.exec( 'CREATE TABLE ftablecoltest ( foo text )' )
239
274
  res = @conn.exec( 'SELECT foo, LENGTH(foo) as length FROM ftablecoltest' )
240
275
  res.ftablecol(1).should == 0 # and it shouldn't raise an exception, either
241
276
  end
242
277
 
243
- after( :each ) do
244
- @conn.exec( 'ROLLBACK' )
278
+ it "can be manually checked for failed result status (async API)" do
279
+ @conn.send_query( "SELECT * FROM nonexistant_table" )
280
+ res = @conn.get_result
281
+ expect {
282
+ res.check
283
+ }.to raise_error( PG::Error, /relation "nonexistant_table" does not exist/ )
245
284
  end
246
285
 
247
- after( :all ) do
248
- teardown_testing_db( @conn )
286
+ it "can return the values of a single field" do
287
+ res = @conn.exec( "SELECT 1 AS x, 'a' AS y UNION ALL SELECT 2, 'b'" )
288
+ res.field_values( 'x' ).should == ['1', '2']
289
+ res.field_values( 'y' ).should == ['a', 'b']
290
+ expect{ res.field_values( '' ) }.to raise_error(IndexError)
291
+ expect{ res.field_values( :x ) }.to raise_error(TypeError)
292
+ end
293
+
294
+ it "should raise a proper exception for a nonexistant table" do
295
+ expect {
296
+ @conn.exec( "SELECT * FROM nonexistant_table" )
297
+ }.to raise_error( PG::UndefinedTable, /relation "nonexistant_table" does not exist/ )
298
+ end
299
+
300
+ it "should raise a more generic exception for an unknown SQLSTATE" do
301
+ old_error = PG::ERROR_CLASSES.delete('42P01')
302
+ begin
303
+ expect {
304
+ @conn.exec( "SELECT * FROM nonexistant_table" )
305
+ }.to raise_error{|error|
306
+ error.should be_an_instance_of(PG::SyntaxErrorOrAccessRuleViolation)
307
+ error.to_s.should match(/relation "nonexistant_table" does not exist/)
308
+ }
309
+ ensure
310
+ PG::ERROR_CLASSES['42P01'] = old_error
311
+ end
312
+ end
313
+
314
+ it "should raise a ServerError for an unknown SQLSTATE class" do
315
+ old_error1 = PG::ERROR_CLASSES.delete('42P01')
316
+ old_error2 = PG::ERROR_CLASSES.delete('42')
317
+ begin
318
+ expect {
319
+ @conn.exec( "SELECT * FROM nonexistant_table" )
320
+ }.to raise_error{|error|
321
+ error.should be_an_instance_of(PG::ServerError)
322
+ error.to_s.should match(/relation "nonexistant_table" does not exist/)
323
+ }
324
+ ensure
325
+ PG::ERROR_CLASSES['42P01'] = old_error1
326
+ PG::ERROR_CLASSES['42'] = old_error2
327
+ end
328
+ end
329
+
330
+ it "should raise a proper exception for a nonexistant schema" do
331
+ expect {
332
+ @conn.exec( "DROP SCHEMA nonexistant_schema" )
333
+ }.to raise_error( PG::InvalidSchemaName, /schema "nonexistant_schema" does not exist/ )
334
+ end
335
+
336
+ it "the raised result should be nil in case of a connection error" do
337
+ c = PGconn.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
338
+ expect {
339
+ c.exec "select 1"
340
+ }.to raise_error{|error|
341
+ error.should be_an_instance_of(PG::UnableToSend)
342
+ error.result.should == nil
343
+ }
249
344
  end
250
345
  end
data/spec/pg_spec.rb ADDED
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env rspec
2
+ # encoding: utf-8
3
+
4
+ BEGIN {
5
+ require 'pathname'
6
+
7
+ basedir = Pathname( __FILE__ ).dirname.parent
8
+ libdir = basedir + 'lib'
9
+
10
+ $LOAD_PATH.unshift( basedir.to_s ) unless $LOAD_PATH.include?( basedir.to_s )
11
+ $LOAD_PATH.unshift( libdir.to_s ) unless $LOAD_PATH.include?( libdir.to_s )
12
+ }
13
+
14
+ require 'rspec'
15
+ require 'spec/lib/helpers'
16
+ require 'pg'
17
+
18
+ describe PG do
19
+
20
+ it "knows what version of the libpq library is loaded", :postgresql_91 do
21
+ PG.library_version.should be_an( Integer )
22
+ PG.library_version.should >= 90100
23
+ end
24
+
25
+
26
+ it "knows whether or not the library is threadsafe" do
27
+ PG.should be_threadsafe()
28
+ end
29
+
30
+ it "does have hierarchical error classes" do
31
+ PG::UndefinedTable.ancestors[0,4].should == [
32
+ PG::UndefinedTable,
33
+ PG::SyntaxErrorOrAccessRuleViolation,
34
+ PG::ServerError,
35
+ PG::Error]
36
+
37
+ PG::InvalidSchemaName.ancestors[0,3].should == [
38
+ PG::InvalidSchemaName,
39
+ PG::ServerError,
40
+ PG::Error]
41
+ end
42
+
43
+ end
44
+
data.tar.gz.sig CHANGED
Binary file