pg 1.1.4 → 1.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.appveyor.yml +42 -0
  4. data/.gems +6 -0
  5. data/.github/workflows/binary-gems.yml +117 -0
  6. data/.github/workflows/source-gem.yml +137 -0
  7. data/.gitignore +22 -0
  8. data/.hgsigs +34 -0
  9. data/.hgtags +41 -0
  10. data/.irbrc +23 -0
  11. data/.pryrc +23 -0
  12. data/.tm_properties +21 -0
  13. data/.travis.yml +49 -0
  14. data/Gemfile +14 -0
  15. data/History.md +876 -0
  16. data/Manifest.txt +3 -3
  17. data/README-Windows.rdoc +4 -4
  18. data/README.ja.md +276 -0
  19. data/README.md +286 -0
  20. data/Rakefile +37 -137
  21. data/Rakefile.cross +62 -62
  22. data/certs/ged.pem +24 -0
  23. data/certs/larskanis-2022.pem +26 -0
  24. data/certs/larskanis-2023.pem +24 -0
  25. data/ext/errorcodes.def +76 -0
  26. data/ext/errorcodes.rb +0 -0
  27. data/ext/errorcodes.txt +21 -2
  28. data/ext/extconf.rb +101 -26
  29. data/ext/gvl_wrappers.c +4 -0
  30. data/ext/gvl_wrappers.h +23 -0
  31. data/ext/pg.c +203 -151
  32. data/ext/pg.h +48 -21
  33. data/ext/pg_binary_decoder.c +89 -10
  34. data/ext/pg_binary_encoder.c +238 -13
  35. data/ext/pg_coder.c +109 -34
  36. data/ext/pg_connection.c +1365 -976
  37. data/ext/pg_copy_coder.c +356 -35
  38. data/ext/pg_errors.c +1 -1
  39. data/ext/pg_record_coder.c +522 -0
  40. data/ext/pg_result.c +436 -171
  41. data/ext/pg_text_decoder.c +42 -18
  42. data/ext/pg_text_encoder.c +201 -56
  43. data/ext/pg_tuple.c +97 -66
  44. data/ext/pg_type_map.c +45 -11
  45. data/ext/pg_type_map_all_strings.c +21 -7
  46. data/ext/pg_type_map_by_class.c +59 -27
  47. data/ext/pg_type_map_by_column.c +80 -37
  48. data/ext/pg_type_map_by_mri_type.c +49 -20
  49. data/ext/pg_type_map_by_oid.c +62 -29
  50. data/ext/pg_type_map_in_ruby.c +56 -22
  51. data/ext/{util.c → pg_util.c} +7 -7
  52. data/lib/pg/basic_type_map_based_on_result.rb +67 -0
  53. data/lib/pg/basic_type_map_for_queries.rb +198 -0
  54. data/lib/pg/basic_type_map_for_results.rb +104 -0
  55. data/lib/pg/basic_type_registry.rb +299 -0
  56. data/lib/pg/binary_decoder/date.rb +9 -0
  57. data/lib/pg/binary_decoder/timestamp.rb +26 -0
  58. data/lib/pg/binary_encoder/timestamp.rb +20 -0
  59. data/lib/pg/coder.rb +35 -12
  60. data/lib/pg/connection.rb +744 -84
  61. data/lib/pg/exceptions.rb +15 -1
  62. data/lib/pg/result.rb +13 -1
  63. data/lib/pg/text_decoder/date.rb +18 -0
  64. data/lib/pg/text_decoder/inet.rb +9 -0
  65. data/lib/pg/text_decoder/json.rb +14 -0
  66. data/lib/pg/text_decoder/numeric.rb +9 -0
  67. data/lib/pg/text_decoder/timestamp.rb +30 -0
  68. data/lib/pg/text_encoder/date.rb +12 -0
  69. data/lib/pg/text_encoder/inet.rb +28 -0
  70. data/lib/pg/text_encoder/json.rb +14 -0
  71. data/lib/pg/text_encoder/numeric.rb +9 -0
  72. data/lib/pg/text_encoder/timestamp.rb +24 -0
  73. data/lib/pg/type_map_by_column.rb +2 -1
  74. data/lib/pg/version.rb +4 -0
  75. data/lib/pg.rb +94 -39
  76. data/misc/openssl-pg-segfault.rb +31 -0
  77. data/misc/postgres/History.txt +9 -0
  78. data/misc/postgres/Manifest.txt +5 -0
  79. data/misc/postgres/README.txt +21 -0
  80. data/misc/postgres/Rakefile +21 -0
  81. data/misc/postgres/lib/postgres.rb +16 -0
  82. data/misc/ruby-pg/History.txt +9 -0
  83. data/misc/ruby-pg/Manifest.txt +5 -0
  84. data/misc/ruby-pg/README.txt +21 -0
  85. data/misc/ruby-pg/Rakefile +21 -0
  86. data/misc/ruby-pg/lib/ruby/pg.rb +16 -0
  87. data/pg.gemspec +34 -0
  88. data/rakelib/task_extension.rb +46 -0
  89. data/sample/array_insert.rb +20 -0
  90. data/sample/async_api.rb +102 -0
  91. data/sample/async_copyto.rb +39 -0
  92. data/sample/async_mixed.rb +56 -0
  93. data/sample/check_conn.rb +21 -0
  94. data/sample/copydata.rb +71 -0
  95. data/sample/copyfrom.rb +81 -0
  96. data/sample/copyto.rb +19 -0
  97. data/sample/cursor.rb +21 -0
  98. data/sample/disk_usage_report.rb +177 -0
  99. data/sample/issue-119.rb +94 -0
  100. data/sample/losample.rb +69 -0
  101. data/sample/minimal-testcase.rb +17 -0
  102. data/sample/notify_wait.rb +72 -0
  103. data/sample/pg_statistics.rb +285 -0
  104. data/sample/replication_monitor.rb +222 -0
  105. data/sample/test_binary_values.rb +33 -0
  106. data/sample/wal_shipper.rb +434 -0
  107. data/sample/warehouse_partitions.rb +311 -0
  108. data/translation/.po4a-version +7 -0
  109. data/translation/po/all.pot +910 -0
  110. data/translation/po/ja.po +1047 -0
  111. data/translation/po4a.cfg +12 -0
  112. data.tar.gz.sig +0 -0
  113. metadata +151 -218
  114. metadata.gz.sig +0 -0
  115. data/ChangeLog +0 -6595
  116. data/History.rdoc +0 -492
  117. data/README.ja.rdoc +0 -14
  118. data/README.rdoc +0 -178
  119. data/lib/pg/basic_type_mapping.rb +0 -459
  120. data/lib/pg/binary_decoder.rb +0 -22
  121. data/lib/pg/constants.rb +0 -11
  122. data/lib/pg/text_decoder.rb +0 -47
  123. data/lib/pg/text_encoder.rb +0 -69
  124. data/spec/data/expected_trace.out +0 -26
  125. data/spec/data/random_binary_data +0 -0
  126. data/spec/helpers.rb +0 -380
  127. data/spec/pg/basic_type_mapping_spec.rb +0 -508
  128. data/spec/pg/connection_spec.rb +0 -1872
  129. data/spec/pg/connection_sync_spec.rb +0 -41
  130. data/spec/pg/result_spec.rb +0 -491
  131. data/spec/pg/tuple_spec.rb +0 -280
  132. data/spec/pg/type_map_by_class_spec.rb +0 -138
  133. data/spec/pg/type_map_by_column_spec.rb +0 -222
  134. data/spec/pg/type_map_by_mri_type_spec.rb +0 -136
  135. data/spec/pg/type_map_by_oid_spec.rb +0 -149
  136. data/spec/pg/type_map_in_ruby_spec.rb +0 -164
  137. data/spec/pg/type_map_spec.rb +0 -22
  138. data/spec/pg/type_spec.rb +0 -949
  139. data/spec/pg_spec.rb +0 -50
  140. /data/ext/{util.h → pg_util.h} +0 -0
@@ -1,41 +0,0 @@
1
- # -*- rspec -*-
2
- #encoding: utf-8
3
-
4
- require_relative '../helpers'
5
-
6
- context "running with sync_* methods" do
7
- before :each do
8
- PG::Connection.async_api = false
9
- end
10
-
11
- after :each do
12
- PG::Connection.async_api = true
13
- end
14
-
15
- fname = File.expand_path("../connection_spec.rb", __FILE__)
16
- eval File.read(fname, encoding: __ENCODING__), binding, fname
17
-
18
-
19
- it "enables/disables async/sync methods by #async_api" do
20
- [true, false].each do |async|
21
- PG::Connection.async_api = async
22
-
23
- start = Time.now
24
- t = Thread.new do
25
- @conn.exec( 'select pg_sleep(1)' )
26
- end
27
- sleep 0.1
28
-
29
- t.kill
30
- t.join
31
- dt = Time.now - start
32
-
33
- if async
34
- expect( dt ).to be < 1.0
35
- else
36
- expect( dt ).to be >= 1.0
37
- end
38
- end
39
- end
40
-
41
- end
@@ -1,491 +0,0 @@
1
- # -*- rspec -*-
2
- # encoding: utf-8
3
-
4
- require_relative '../helpers'
5
-
6
- require 'pg'
7
-
8
-
9
- describe PG::Result do
10
-
11
- it "acts as an array of hashes" do
12
- res = @conn.exec("SELECT 1 AS a, 2 AS b")
13
- expect( res[0]['a'] ).to eq( '1' )
14
- expect( res[0]['b'] ).to eq( '2' )
15
- end
16
-
17
- it "yields a row as an array" do
18
- res = @conn.exec("SELECT 1 AS a, 2 AS b")
19
- list = []
20
- res.each_row { |r| list << r }
21
- expect( list ).to eq [['1', '2']]
22
- end
23
-
24
- it "yields a row as an Enumerator" do
25
- res = @conn.exec("SELECT 1 AS a, 2 AS b")
26
- e = res.each_row
27
- expect( e ).to be_a_kind_of(Enumerator)
28
- pending "Rubinius doesn't define RETURN_SIZED_ENUMERATOR()" if RUBY_ENGINE=='rbx'
29
- expect( e.size ).to eq( 1 )
30
- expect( e.to_a ).to eq [['1', '2']]
31
- end
32
-
33
- it "yields a row as an Enumerator of hashs" do
34
- res = @conn.exec("SELECT 1 AS a, 2 AS b")
35
- e = res.each
36
- expect( e ).to be_a_kind_of(Enumerator)
37
- pending "Rubinius doesn't define RETURN_SIZED_ENUMERATOR()" if RUBY_ENGINE=='rbx'
38
- expect( e.size ).to eq( 1 )
39
- expect( e.to_a ).to eq [{'a'=>'1', 'b'=>'2'}]
40
- end
41
-
42
- context "result streaming in single row mode" do
43
- it "can iterate over all rows as Hash" do
44
- @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
45
- @conn.set_single_row_mode
46
- expect(
47
- @conn.get_result.stream_each.to_a
48
- ).to eq(
49
- [{'a'=>"2"}, {'a'=>"3"}, {'a'=>"4"}]
50
- )
51
- expect(
52
- @conn.get_result.enum_for(:stream_each).to_a
53
- ).to eq(
54
- [{'b'=>"1", 'c'=>"5"}, {'b'=>"1", 'c'=>"6"}]
55
- )
56
- expect( @conn.get_result ).to be_nil
57
- end
58
-
59
- it "can iterate over all rows as Array" do
60
- @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
61
- @conn.set_single_row_mode
62
- expect(
63
- @conn.get_result.enum_for(:stream_each_row).to_a
64
- ).to eq(
65
- [["2"], ["3"], ["4"]]
66
- )
67
- expect(
68
- @conn.get_result.stream_each_row.to_a
69
- ).to eq(
70
- [["1", "5"], ["1", "6"]]
71
- )
72
- expect( @conn.get_result ).to be_nil
73
- end
74
-
75
- it "can iterate over all rows as PG::Tuple" do
76
- @conn.send_query( "SELECT generate_series(2,4) AS a; SELECT 1 AS b, generate_series(5,6) AS c" )
77
- @conn.set_single_row_mode
78
- tuples = @conn.get_result.stream_each_tuple.to_a
79
- expect( tuples[0][0] ).to eq( "2" )
80
- expect( tuples[1]["a"] ).to eq( "3" )
81
- expect( tuples.size ).to eq( 3 )
82
-
83
- tuples = @conn.get_result.enum_for(:stream_each_tuple).to_a
84
- expect( tuples[-1][-1] ).to eq( "6" )
85
- expect( tuples[-2]["b"] ).to eq( "1" )
86
- expect( tuples.size ).to eq( 2 )
87
-
88
- expect( @conn.get_result ).to be_nil
89
- end
90
-
91
- it "complains when not in single row mode" do
92
- @conn.send_query( "SELECT generate_series(2,4)" )
93
- expect{
94
- @conn.get_result.stream_each_row.to_a
95
- }.to raise_error(PG::InvalidResultStatus, /not in single row mode/)
96
- end
97
-
98
- it "complains when intersected with get_result" do
99
- @conn.send_query( "SELECT 1" )
100
- @conn.set_single_row_mode
101
- expect{
102
- @conn.get_result.stream_each_row.each{ @conn.get_result }
103
- }.to raise_error(PG::NoResultError, /no result received/)
104
- end
105
-
106
- it "raises server errors" do
107
- @conn.send_query( "SELECT 0/0" )
108
- expect{
109
- @conn.get_result.stream_each_row.to_a
110
- }.to raise_error(PG::DivisionByZero)
111
- end
112
- end
113
-
114
- it "inserts nil AS NULL and return NULL as nil" do
115
- res = @conn.exec_params("SELECT $1::int AS n", [nil])
116
- expect( res[0]['n'] ).to be_nil()
117
- end
118
-
119
- it "encapsulates errors in a PG::Error object" do
120
- exception = nil
121
- begin
122
- @conn.exec( "SELECT * FROM nonexistant_table" )
123
- rescue PG::Error => err
124
- exception = err
125
- end
126
-
127
- result = exception.result
128
-
129
- expect( result ).to be_a( described_class() )
130
- expect( result.error_field(PG::PG_DIAG_SEVERITY) ).to eq( 'ERROR' )
131
- expect( result.error_field(PG::PG_DIAG_SQLSTATE) ).to eq( '42P01' )
132
- expect(
133
- result.error_field(PG::PG_DIAG_MESSAGE_PRIMARY)
134
- ).to eq( 'relation "nonexistant_table" does not exist' )
135
- expect( result.error_field(PG::PG_DIAG_MESSAGE_DETAIL) ).to be_nil()
136
- expect( result.error_field(PG::PG_DIAG_MESSAGE_HINT) ).to be_nil()
137
- expect( result.error_field(PG::PG_DIAG_STATEMENT_POSITION) ).to eq( '15' )
138
- expect( result.error_field(PG::PG_DIAG_INTERNAL_POSITION) ).to be_nil()
139
- expect( result.error_field(PG::PG_DIAG_INTERNAL_QUERY) ).to be_nil()
140
- expect( result.error_field(PG::PG_DIAG_CONTEXT) ).to be_nil()
141
- expect(
142
- result.error_field(PG::PG_DIAG_SOURCE_FILE)
143
- ).to match( /parse_relation\.c$|namespace\.c$/ )
144
- expect( result.error_field(PG::PG_DIAG_SOURCE_LINE) ).to match( /^\d+$/ )
145
- expect(
146
- result.error_field(PG::PG_DIAG_SOURCE_FUNCTION)
147
- ).to match( /^parserOpenTable$|^RangeVarGetRelid$/ )
148
- end
149
-
150
- it "encapsulates database object names for integrity constraint violations", :postgresql_93 do
151
- @conn.exec( "CREATE TABLE integrity (id SERIAL PRIMARY KEY)" )
152
- exception = nil
153
- begin
154
- @conn.exec( "INSERT INTO integrity VALUES (NULL)" )
155
- rescue PG::Error => err
156
- exception = err
157
- end
158
- result = exception.result
159
-
160
- expect( result.error_field(PG::PG_DIAG_SCHEMA_NAME) ).to eq( 'public' )
161
- expect( result.error_field(PG::PG_DIAG_TABLE_NAME) ).to eq( 'integrity' )
162
- expect( result.error_field(PG::PG_DIAG_COLUMN_NAME) ).to eq( 'id' )
163
- expect( result.error_field(PG::PG_DIAG_DATATYPE_NAME) ).to be_nil
164
- expect( result.error_field(PG::PG_DIAG_CONSTRAINT_NAME) ).to be_nil
165
- end
166
-
167
- it "detects division by zero as SQLSTATE 22012" do
168
- sqlstate = nil
169
- begin
170
- res = @conn.exec("SELECT 1/0")
171
- rescue PG::Error => e
172
- sqlstate = e.result.result_error_field( PG::PG_DIAG_SQLSTATE ).to_i
173
- end
174
- expect( sqlstate ).to eq( 22012 )
175
- end
176
-
177
- it "returns the same bytes in binary format that are sent in binary format" do
178
- binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
179
- bytes = File.open(binary_file, 'rb').read
180
- res = @conn.exec_params('VALUES ($1::bytea)',
181
- [ { :value => bytes, :format => 1 } ], 1)
182
- expect( res[0]['column1'] ).to eq( bytes )
183
- expect( res.getvalue(0,0) ).to eq( bytes )
184
- expect( res.values[0][0] ).to eq( bytes )
185
- expect( res.column_values(0)[0] ).to eq( bytes )
186
- end
187
-
188
- it "returns the same bytes in binary format that are sent as inline text" do
189
- binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
190
- bytes = File.open(binary_file, 'rb').read
191
- @conn.exec("SET standard_conforming_strings=on")
192
- res = @conn.exec_params("VALUES ('#{PG::Connection.escape_bytea(bytes)}'::bytea)", [], 1)
193
- expect( res[0]['column1'] ).to eq( bytes )
194
- expect( res.getvalue(0,0) ).to eq( bytes )
195
- expect( res.values[0][0] ).to eq( bytes )
196
- expect( res.column_values(0)[0] ).to eq( bytes )
197
- end
198
-
199
- it "returns the same bytes in text format that are sent in binary format" do
200
- binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
201
- bytes = File.open(binary_file, 'rb').read
202
- res = @conn.exec_params('VALUES ($1::bytea)',
203
- [ { :value => bytes, :format => 1 } ])
204
- expect( PG::Connection.unescape_bytea(res[0]['column1']) ).to eq( bytes )
205
- end
206
-
207
- it "returns the same bytes in text format that are sent as inline text" do
208
- binary_file = File.join(Dir.pwd, 'spec/data', 'random_binary_data')
209
- in_bytes = File.open(binary_file, 'rb').read
210
-
211
- out_bytes = nil
212
- @conn.exec("SET standard_conforming_strings=on")
213
- res = @conn.exec_params("VALUES ('#{PG::Connection.escape_bytea(in_bytes)}'::bytea)", [], 0)
214
- out_bytes = PG::Connection.unescape_bytea(res[0]['column1'])
215
- expect( out_bytes ).to eq( in_bytes )
216
- end
217
-
218
- it "returns the parameter type of the specified prepared statement parameter" do
219
- query = 'SELECT * FROM pg_stat_activity WHERE user = $1::name AND query = $2::text'
220
- @conn.prepare( 'queryfinder', query )
221
- res = @conn.describe_prepared( 'queryfinder' )
222
-
223
- expect(
224
- @conn.exec_params( 'SELECT format_type($1, -1)', [res.paramtype(0)] ).getvalue( 0, 0 )
225
- ).to eq( 'name' )
226
- expect(
227
- @conn.exec_params( 'SELECT format_type($1, -1)', [res.paramtype(1)] ).getvalue( 0, 0 )
228
- ).to eq( 'text' )
229
- end
230
-
231
- it "raises an exception when a negative index is given to #fformat" do
232
- res = @conn.exec('SELECT * FROM pg_stat_activity')
233
- expect {
234
- res.fformat( -1 )
235
- }.to raise_error( ArgumentError, /column number/i )
236
- end
237
-
238
- it "raises an exception when a negative index is given to #fmod" do
239
- res = @conn.exec('SELECT * FROM pg_stat_activity')
240
- expect {
241
- res.fmod( -1 )
242
- }.to raise_error( ArgumentError, /column number/i )
243
- end
244
-
245
- it "raises an exception when a negative index is given to #[]" do
246
- res = @conn.exec('SELECT * FROM pg_stat_activity')
247
- expect {
248
- res[ -1 ]
249
- }.to raise_error( IndexError, /-1 is out of range/i )
250
- end
251
-
252
- it "raises allow for conversion to an array of arrays" do
253
- @conn.exec( 'CREATE TABLE valuestest ( foo varchar(33) )' )
254
- @conn.exec( 'INSERT INTO valuestest ("foo") values (\'bar\')' )
255
- @conn.exec( 'INSERT INTO valuestest ("foo") values (\'bar2\')' )
256
-
257
- res = @conn.exec( 'SELECT * FROM valuestest' )
258
- expect( res.values ).to eq( [ ["bar"], ["bar2"] ] )
259
- end
260
-
261
- # PQfmod
262
- it "can return the type modifier for a result column" do
263
- @conn.exec( 'CREATE TABLE fmodtest ( foo varchar(33) )' )
264
- res = @conn.exec( 'SELECT * FROM fmodtest' )
265
- expect( res.fmod(0) ).to eq( 33 + 4 ) # Column length + varlena size (4)
266
- end
267
-
268
- it "raises an exception when an invalid index is passed to PG::Result#fmod" do
269
- @conn.exec( 'CREATE TABLE fmodtest ( foo varchar(33) )' )
270
- res = @conn.exec( 'SELECT * FROM fmodtest' )
271
- expect { res.fmod(1) }.to raise_error( ArgumentError )
272
- end
273
-
274
- it "raises an exception when an invalid (negative) index is passed to PG::Result#fmod" do
275
- @conn.exec( 'CREATE TABLE fmodtest ( foo varchar(33) )' )
276
- res = @conn.exec( 'SELECT * FROM fmodtest' )
277
- expect { res.fmod(-11) }.to raise_error( ArgumentError )
278
- end
279
-
280
- it "doesn't raise an exception when a valid index is passed to PG::Result#fmod for a" +
281
- " column with no typemod" do
282
- @conn.exec( 'CREATE TABLE fmodtest ( foo text )' )
283
- res = @conn.exec( 'SELECT * FROM fmodtest' )
284
- expect( res.fmod(0) ).to eq( -1 )
285
- end
286
-
287
- # PQftable
288
- it "can return the oid of the table from which a result column was fetched" do
289
- @conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
290
- res = @conn.exec( 'SELECT * FROM ftabletest' )
291
-
292
- expect( res.ftable(0) ).to be_nonzero()
293
- end
294
-
295
- it "raises an exception when an invalid index is passed to PG::Result#ftable" do
296
- @conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
297
- res = @conn.exec( 'SELECT * FROM ftabletest' )
298
-
299
- expect { res.ftable(18) }.to raise_error( ArgumentError )
300
- end
301
-
302
- it "raises an exception when an invalid (negative) index is passed to PG::Result#ftable" do
303
- @conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
304
- res = @conn.exec( 'SELECT * FROM ftabletest' )
305
-
306
- expect { res.ftable(-2) }.to raise_error( ArgumentError )
307
- end
308
-
309
- it "doesn't raise an exception when a valid index is passed to PG::Result#ftable for a " +
310
- "column with no corresponding table" do
311
- @conn.exec( 'CREATE TABLE ftabletest ( foo text )' )
312
- res = @conn.exec( 'SELECT foo, LENGTH(foo) as length FROM ftabletest' )
313
- expect( res.ftable(1) ).to eq( PG::INVALID_OID )
314
- end
315
-
316
- # PQftablecol
317
- it "can return the column number (within its table) of a column in a result" do
318
- @conn.exec( 'CREATE TABLE ftablecoltest ( foo text, bar numeric )' )
319
- res = @conn.exec( 'SELECT * FROM ftablecoltest' )
320
-
321
- expect( res.ftablecol(0) ).to eq( 1 )
322
- expect( res.ftablecol(1) ).to eq( 2 )
323
- end
324
-
325
- it "raises an exception when an invalid index is passed to PG::Result#ftablecol" do
326
- @conn.exec( 'CREATE TABLE ftablecoltest ( foo text, bar numeric )' )
327
- res = @conn.exec( 'SELECT * FROM ftablecoltest' )
328
-
329
- expect { res.ftablecol(32) }.to raise_error( ArgumentError )
330
- end
331
-
332
- it "raises an exception when an invalid (negative) index is passed to PG::Result#ftablecol" do
333
- @conn.exec( 'CREATE TABLE ftablecoltest ( foo text, bar numeric )' )
334
- res = @conn.exec( 'SELECT * FROM ftablecoltest' )
335
-
336
- expect { res.ftablecol(-1) }.to raise_error( ArgumentError )
337
- end
338
-
339
- it "doesnn't raise an exception when a valid index is passed to PG::Result#ftablecol for a " +
340
- "column with no corresponding table" do
341
- @conn.exec( 'CREATE TABLE ftablecoltest ( foo text )' )
342
- res = @conn.exec( 'SELECT foo, LENGTH(foo) as length FROM ftablecoltest' )
343
- expect( res.ftablecol(1) ).to eq( 0 )
344
- end
345
-
346
- it "can be manually checked for failed result status (async API)" do
347
- @conn.send_query( "SELECT * FROM nonexistant_table" )
348
- res = @conn.get_result
349
- expect {
350
- res.check
351
- }.to raise_error( PG::Error, /relation "nonexistant_table" does not exist/ )
352
- end
353
-
354
- it "can return the values of a single field" do
355
- res = @conn.exec( "SELECT 1 AS x, 'a' AS y UNION ALL SELECT 2, 'b'" )
356
- expect( res.field_values('x') ).to eq( ['1', '2'] )
357
- expect( res.field_values('y') ).to eq( ['a', 'b'] )
358
- expect{ res.field_values('') }.to raise_error(IndexError)
359
- expect{ res.field_values(:x) }.to raise_error(TypeError)
360
- end
361
-
362
- it "can return the values of a single tuple" do
363
- res = @conn.exec( "SELECT 1 AS x, 'a' AS y UNION ALL SELECT 2, 'b'" )
364
- expect( res.tuple_values(0) ).to eq( ['1', 'a'] )
365
- expect( res.tuple_values(1) ).to eq( ['2', 'b'] )
366
- expect{ res.tuple_values(2) }.to raise_error(IndexError)
367
- expect{ res.tuple_values(-1) }.to raise_error(IndexError)
368
- expect{ res.tuple_values("x") }.to raise_error(TypeError)
369
- end
370
-
371
- it "can return the values of a single vary lazy tuple" do
372
- res = @conn.exec( "VALUES(1),(2)" )
373
- expect( res.tuple(0) ).to be_kind_of( PG::Tuple )
374
- expect( res.tuple(1) ).to be_kind_of( PG::Tuple )
375
- expect{ res.tuple(2) }.to raise_error(IndexError)
376
- expect{ res.tuple(-1) }.to raise_error(IndexError)
377
- expect{ res.tuple("x") }.to raise_error(TypeError)
378
- end
379
-
380
- it "raises a proper exception for a nonexistant table" do
381
- expect {
382
- @conn.exec( "SELECT * FROM nonexistant_table" )
383
- }.to raise_error( PG::UndefinedTable, /relation "nonexistant_table" does not exist/ )
384
- end
385
-
386
- it "raises a more generic exception for an unknown SQLSTATE" do
387
- old_error = PG::ERROR_CLASSES.delete('42P01')
388
- begin
389
- expect {
390
- @conn.exec( "SELECT * FROM nonexistant_table" )
391
- }.to raise_error{|error|
392
- expect( error ).to be_an_instance_of(PG::SyntaxErrorOrAccessRuleViolation)
393
- expect( error.to_s ).to match(/relation "nonexistant_table" does not exist/)
394
- }
395
- ensure
396
- PG::ERROR_CLASSES['42P01'] = old_error
397
- end
398
- end
399
-
400
- it "raises a ServerError for an unknown SQLSTATE class" do
401
- old_error1 = PG::ERROR_CLASSES.delete('42P01')
402
- old_error2 = PG::ERROR_CLASSES.delete('42')
403
- begin
404
- expect {
405
- @conn.exec( "SELECT * FROM nonexistant_table" )
406
- }.to raise_error{|error|
407
- expect( error ).to be_an_instance_of(PG::ServerError)
408
- expect( error.to_s ).to match(/relation "nonexistant_table" does not exist/)
409
- }
410
- ensure
411
- PG::ERROR_CLASSES['42P01'] = old_error1
412
- PG::ERROR_CLASSES['42'] = old_error2
413
- end
414
- end
415
-
416
- it "raises a proper exception for a nonexistant schema" do
417
- expect {
418
- @conn.exec( "DROP SCHEMA nonexistant_schema" )
419
- }.to raise_error( PG::InvalidSchemaName, /schema "nonexistant_schema" does not exist/ )
420
- end
421
-
422
- it "the raised result is nil in case of a connection error" do
423
- c = PG::Connection.connect_start( '127.0.0.1', 54320, "", "", "me", "xxxx", "somedb" )
424
- expect {
425
- c.exec "select 1"
426
- }.to raise_error {|error|
427
- expect( error ).to be_an_instance_of(PG::UnableToSend)
428
- expect( error.result ).to eq( nil )
429
- }
430
- end
431
-
432
- it "does not clear the result itself" do
433
- r = @conn.exec "select 1"
434
- expect( r.autoclear? ).to eq(false)
435
- expect( r.cleared? ).to eq(false)
436
- r.clear
437
- expect( r.cleared? ).to eq(true)
438
- end
439
-
440
- it "can be inspected before and after clear" do
441
- r = @conn.exec "select 1"
442
- expect( r.inspect ).to match(/status=PGRES_TUPLES_OK/)
443
- r.clear
444
- expect( r.inspect ).to match(/cleared/)
445
- end
446
-
447
- context 'result value conversions with TypeMapByColumn' do
448
- let!(:textdec_int){ PG::TextDecoder::Integer.new name: 'INT4', oid: 23 }
449
- let!(:textdec_float){ PG::TextDecoder::Float.new name: 'FLOAT4', oid: 700 }
450
-
451
- it "should allow reading, assigning and diabling type conversions" do
452
- res = @conn.exec( "SELECT 123" )
453
- expect( res.type_map ).to be_kind_of(PG::TypeMapAllStrings)
454
- res.type_map = PG::TypeMapByColumn.new [textdec_int]
455
- expect( res.type_map ).to be_an_instance_of(PG::TypeMapByColumn)
456
- expect( res.type_map.coders ).to eq( [textdec_int] )
457
- res.type_map = PG::TypeMapByColumn.new [textdec_float]
458
- expect( res.type_map.coders ).to eq( [textdec_float] )
459
- res.type_map = PG::TypeMapAllStrings.new
460
- expect( res.type_map ).to be_kind_of(PG::TypeMapAllStrings)
461
- end
462
-
463
- it "should be applied to all value retrieving methods" do
464
- res = @conn.exec( "SELECT 123 as f" )
465
- res.type_map = PG::TypeMapByColumn.new [textdec_int]
466
- expect( res.values ).to eq( [[123]] )
467
- expect( res.getvalue(0,0) ).to eq( 123 )
468
- expect( res[0] ).to eq( {'f' => 123 } )
469
- expect( res.enum_for(:each_row).to_a ).to eq( [[123]] )
470
- expect( res.enum_for(:each).to_a ).to eq( [{'f' => 123}] )
471
- expect( res.column_values(0) ).to eq( [123] )
472
- expect( res.field_values('f') ).to eq( [123] )
473
- expect( res.tuple_values(0) ).to eq( [123] )
474
- end
475
-
476
- it "should be usable for several querys" do
477
- colmap = PG::TypeMapByColumn.new [textdec_int]
478
- res = @conn.exec( "SELECT 123" )
479
- res.type_map = colmap
480
- expect( res.values ).to eq( [[123]] )
481
- res = @conn.exec( "SELECT 456" )
482
- res.type_map = colmap
483
- expect( res.values ).to eq( [[456]] )
484
- end
485
-
486
- it "shouldn't allow invalid type maps" do
487
- res = @conn.exec( "SELECT 1" )
488
- expect{ res.type_map = 1 }.to raise_error(TypeError)
489
- end
490
- end
491
- end