em-pg-client 0.2.1 → 0.3.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.
- data/.yardopts +1 -0
- data/{BENCHMARKS.rdoc → BENCHMARKS.md} +15 -10
- data/{HISTORY.rdoc → HISTORY.md} +32 -5
- data/LICENSE +21 -0
- data/README.md +392 -0
- data/Rakefile +30 -14
- data/em-pg-client.gemspec +8 -7
- data/lib/em-pg-client.rb +1 -0
- data/lib/em-synchrony/pg.rb +2 -107
- data/lib/pg/em-version.rb +5 -0
- data/lib/pg/em.rb +638 -344
- data/lib/pg/em/client/connect_watcher.rb +65 -0
- data/lib/pg/em/client/watcher.rb +102 -0
- data/lib/pg/em/connection_pool.rb +448 -0
- data/lib/pg/em/featured_deferrable.rb +43 -0
- data/spec/connection_pool_helpers.rb +89 -0
- data/spec/{em_devel_client.rb → em_client.rb} +3 -2
- data/spec/em_client_autoreconnect.rb +268 -144
- data/spec/em_client_common.rb +55 -54
- data/spec/em_synchrony_client.rb +254 -5
- data/spec/em_synchrony_client_autoreconnect.rb +154 -130
- data/spec/pg_em_client_connect_finish.rb +54 -0
- data/spec/pg_em_client_connect_timeout.rb +91 -0
- data/spec/pg_em_client_options.rb +85 -0
- data/spec/pg_em_connection_pool.rb +655 -0
- data/spec/pg_em_featured_deferrable.rb +125 -0
- metadata +64 -34
- data/README.rdoc +0 -431
- data/spec/em_release_client.rb +0 -39
data/spec/em_client_common.rb
CHANGED
@@ -11,17 +11,18 @@ module PGSpecMacros
|
|
11
11
|
end.should be_a_kind_of ::EM::DefaultDeferrable
|
12
12
|
end
|
13
13
|
|
14
|
-
def pg_exec_and_check_with_error(client, err_message, method, *args, &additional_checks)
|
14
|
+
def pg_exec_and_check_with_error(client, stop, err_class, err_message, method, *args, &additional_checks)
|
15
15
|
client.__send__(method, *args) do |exception|
|
16
|
-
exception.should be_an_instance_of
|
16
|
+
exception.should be_an_instance_of err_class
|
17
17
|
exception.to_s.should include err_message if err_message
|
18
18
|
additional_checks.call(exception) if additional_checks
|
19
|
-
EM.stop
|
19
|
+
EM.next_tick { EM.stop } if stop
|
20
20
|
end.should be_a_kind_of ::EM::DefaultDeferrable
|
21
21
|
end
|
22
22
|
|
23
23
|
def ensure_em_stop
|
24
24
|
yield
|
25
|
+
ensure
|
25
26
|
EM.stop
|
26
27
|
end
|
27
28
|
|
@@ -32,18 +33,18 @@ module PGSpecMacros
|
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
35
|
-
def it_should_execute_with_error(text, err_message, method, *args)
|
36
|
+
def it_should_execute_with_error(text, err_class, err_message, method, *args)
|
36
37
|
it "should #{text}" do
|
37
|
-
pg_exec_and_check_with_error(@client, err_message, method, *args)
|
38
|
+
pg_exec_and_check_with_error(@client, true, err_class, err_message, method, *args)
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
42
|
def it_should_rollback
|
42
|
-
it_should_execute("rollback transaction", :
|
43
|
+
it_should_execute("rollback transaction", :query_defer, 'ROLLBACK')
|
43
44
|
end
|
44
45
|
|
45
46
|
def it_should_begin
|
46
|
-
it_should_execute("begin transaction", :
|
47
|
+
it_should_execute("begin transaction", :query_defer, 'BEGIN TRANSACTION')
|
47
48
|
end
|
48
49
|
end
|
49
50
|
end
|
@@ -87,36 +88,6 @@ shared_context 'em-pg common before' do
|
|
87
88
|
end
|
88
89
|
end
|
89
90
|
|
90
|
-
it "should set async_autoreconnect according to on_autoreconnect" do
|
91
|
-
ensure_em_stop do
|
92
|
-
on_autoreconnect = proc {|c, e| false }
|
93
|
-
async_args = described_class.parse_async_args([])
|
94
|
-
async_args.should be_an_instance_of Hash
|
95
|
-
async_args[:@on_autoreconnect].should be_nil
|
96
|
-
async_args[:@async_autoreconnect].should be_false
|
97
|
-
args = [on_autoreconnect: on_autoreconnect]
|
98
|
-
async_args = described_class.parse_async_args(args)
|
99
|
-
args.should eq [{}]
|
100
|
-
async_args.should be_an_instance_of Hash
|
101
|
-
async_args[:@on_autoreconnect].should be on_autoreconnect
|
102
|
-
async_args[:@async_autoreconnect].should be_true
|
103
|
-
args = [async_autoreconnect: false,
|
104
|
-
on_autoreconnect: on_autoreconnect]
|
105
|
-
async_args = described_class.parse_async_args(args)
|
106
|
-
args.should eq [{}]
|
107
|
-
async_args.should be_an_instance_of Hash
|
108
|
-
async_args[:@on_autoreconnect].should be on_autoreconnect
|
109
|
-
async_args[:@async_autoreconnect].should be_false
|
110
|
-
args = [on_autoreconnect: on_autoreconnect,
|
111
|
-
async_autoreconnect: false]
|
112
|
-
async_args = described_class.parse_async_args(args)
|
113
|
-
args.should eq [{}]
|
114
|
-
async_args.should be_an_instance_of Hash
|
115
|
-
async_args[:@on_autoreconnect].should be on_autoreconnect
|
116
|
-
async_args[:@async_autoreconnect].should be_false
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
91
|
it "should have same internal and external encoding" do
|
121
92
|
ensure_em_stop do
|
122
93
|
@client.external_encoding.should be @client.internal_encoding
|
@@ -126,20 +97,20 @@ shared_context 'em-pg common before' do
|
|
126
97
|
it_should_begin
|
127
98
|
|
128
99
|
it_should_execute("drop table `foo` if exists",
|
129
|
-
:
|
100
|
+
:query_defer, 'DROP TABLE IF EXISTS foo')
|
130
101
|
|
131
102
|
it_should_execute("create simple table `foo`",
|
132
|
-
:
|
103
|
+
:query_defer, 'CREATE TABLE foo (id integer,cdate timestamp with time zone,data varchar)')
|
133
104
|
|
134
105
|
end
|
135
106
|
|
136
107
|
shared_context 'em-pg common after' do
|
137
108
|
|
138
109
|
it_should_execute("create prepared statement",
|
139
|
-
:
|
110
|
+
:prepare_defer, 'get_foo', 'SELECT * FROM foo order by id')
|
140
111
|
|
141
112
|
it "should describe prepared statement" do
|
142
|
-
pg_exec_and_check(@client, :
|
113
|
+
pg_exec_and_check(@client, :describe_prepared_defer, 'get_foo') do |result|
|
143
114
|
result.nfields.should eq 3
|
144
115
|
result.fname(0).should eq 'id'
|
145
116
|
result.values.should be_empty
|
@@ -147,7 +118,7 @@ shared_context 'em-pg common after' do
|
|
147
118
|
end
|
148
119
|
|
149
120
|
it "should read foo table with prepared statement" do
|
150
|
-
pg_exec_and_check(@client, :
|
121
|
+
pg_exec_and_check(@client, :exec_prepared_defer, 'get_foo') do |result|
|
151
122
|
result.each_with_index do |row, i|
|
152
123
|
row['id'].to_i.should == i
|
153
124
|
DateTime.parse(row['cdate']).should == @cdates[i]
|
@@ -157,10 +128,10 @@ shared_context 'em-pg common after' do
|
|
157
128
|
end
|
158
129
|
|
159
130
|
it_should_execute("declare cursor",
|
160
|
-
:
|
131
|
+
:query_defer, 'DECLARE foobar SCROLL CURSOR FOR SELECT * FROM foo')
|
161
132
|
|
162
133
|
it "should fetch two rows from table" do
|
163
|
-
pg_exec_and_check(@client, :
|
134
|
+
pg_exec_and_check(@client, :query_defer, 'FETCH FORWARD 2 FROM foobar') do |result|
|
164
135
|
result.nfields.should eq 3
|
165
136
|
result.fname(0).should eq 'id'
|
166
137
|
result.values.length.should eq 2
|
@@ -168,25 +139,25 @@ shared_context 'em-pg common after' do
|
|
168
139
|
end
|
169
140
|
|
170
141
|
it "should describe cursor with describe_portal" do
|
171
|
-
pg_exec_and_check(@client, :
|
142
|
+
pg_exec_and_check(@client, :describe_portal_defer, 'foobar') do |result|
|
172
143
|
result.nfields.should eq 3
|
173
144
|
result.fname(0).should eq 'id'
|
174
145
|
end
|
175
146
|
end
|
176
147
|
|
177
|
-
it_should_execute("close cursor", :
|
148
|
+
it_should_execute("close cursor", :query_defer, 'CLOSE foobar')
|
178
149
|
|
179
150
|
it "should connect to database asynchronously" do
|
180
151
|
this = :first
|
181
152
|
Encoding.default_internal = Encoding::ISO_8859_1
|
182
|
-
described_class.
|
153
|
+
described_class.connect_defer do |conn|
|
183
154
|
this = :second
|
184
155
|
Encoding.default_internal = nil
|
185
156
|
conn.should be_an_instance_of described_class
|
186
157
|
conn.external_encoding.should_not eq(conn.internal_encoding)
|
187
158
|
conn.internal_encoding.should be Encoding::ISO_8859_1
|
188
159
|
conn.get_client_encoding.should eq "LATIN1"
|
189
|
-
pg_exec_and_check(conn, :
|
160
|
+
pg_exec_and_check(conn, :query_defer, 'SELECT pg_database_size(current_database());') do |result|
|
190
161
|
result[0]['pg_database_size'].to_i.should be > 0
|
191
162
|
end
|
192
163
|
end.should be_a_kind_of ::EM::DefaultDeferrable
|
@@ -196,7 +167,7 @@ shared_context 'em-pg common after' do
|
|
196
167
|
it "should connect without setting incompatible encoding" do
|
197
168
|
this = :first
|
198
169
|
Encoding.default_internal = Encoding::Emacs_Mule
|
199
|
-
described_class.
|
170
|
+
described_class.connect_defer do |conn|
|
200
171
|
this = :second
|
201
172
|
Encoding.default_internal = nil
|
202
173
|
conn.should be_an_instance_of described_class
|
@@ -207,13 +178,14 @@ shared_context 'em-pg common after' do
|
|
207
178
|
end
|
208
179
|
|
209
180
|
it_should_execute_with_error("raise syntax error in misspelled multiple statement",
|
181
|
+
PG::SyntaxError,
|
210
182
|
"syntax error",
|
211
|
-
:
|
183
|
+
:query_defer, 'SELECT * from pg_class; SRELECT CURRENT_TIMESTAMP; SELECT 42 number')
|
212
184
|
|
213
185
|
it_should_rollback
|
214
186
|
|
215
187
|
it "should return only last statement" do
|
216
|
-
pg_exec_and_check(@client, :
|
188
|
+
pg_exec_and_check(@client, :query_defer,
|
217
189
|
'SELECT * from pg_class; SELECT CURRENT_TIMESTAMP; SELECT 42 number') do |result|
|
218
190
|
result[0]['number'].should eq "42"
|
219
191
|
end
|
@@ -224,12 +196,20 @@ shared_context 'em-pg common after' do
|
|
224
196
|
@client.query_timeout = 1.5
|
225
197
|
@client.query_timeout.should eq 1.5
|
226
198
|
start_time = Time.now
|
227
|
-
pg_exec_and_check_with_error(@client,
|
199
|
+
pg_exec_and_check_with_error(@client, false,
|
200
|
+
PG::ConnectionBad, "query timeout expired",
|
201
|
+
:query_defer, 'SELECT pg_sleep(2)') do
|
228
202
|
(Time.now - start_time).should be < 2
|
229
203
|
@client.async_command_aborted.should be_true
|
230
204
|
@client.status.should be PG::CONNECTION_BAD
|
231
205
|
@client.query_timeout = 0
|
232
206
|
@client.query_timeout.should eq 0
|
207
|
+
@client.async_autoreconnect = false
|
208
|
+
pg_exec_and_check_with_error(@client, true,
|
209
|
+
PG::ConnectionBad, "previous query expired",
|
210
|
+
:query_defer, 'SELECT 1') do
|
211
|
+
@client.async_autoreconnect = true
|
212
|
+
end
|
233
213
|
end
|
234
214
|
end
|
235
215
|
|
@@ -238,7 +218,7 @@ shared_context 'em-pg common after' do
|
|
238
218
|
@client.query_timeout = 1.1
|
239
219
|
@client.query_timeout.should eq 1.1
|
240
220
|
start_time = Time.now
|
241
|
-
pg_exec_and_check(@client, :
|
221
|
+
pg_exec_and_check(@client, :query_defer,
|
242
222
|
'SELECT * from pg_class;' +
|
243
223
|
'SELECT pg_sleep(1);' +
|
244
224
|
'SELECT * from pg_class;' +
|
@@ -258,7 +238,9 @@ shared_context 'em-pg common after' do
|
|
258
238
|
@client.query_timeout = 1.1
|
259
239
|
@client.query_timeout.should eq 1.1
|
260
240
|
start_time = Time.now
|
261
|
-
pg_exec_and_check_with_error(@client,
|
241
|
+
pg_exec_and_check_with_error(@client, true,
|
242
|
+
PG::ConnectionBad, "query timeout expired",
|
243
|
+
:query_defer,
|
262
244
|
'SELECT * from pg_class;' +
|
263
245
|
'SELECT pg_sleep(1);' +
|
264
246
|
'SELECT * from pg_class;' +
|
@@ -281,4 +263,23 @@ shared_context 'em-pg common after' do
|
|
281
263
|
@client.status.should be PG::CONNECTION_OK
|
282
264
|
end
|
283
265
|
end
|
266
|
+
|
267
|
+
it "should not expire after executing erraneous query" do
|
268
|
+
@client.query_timeout.should eq 0
|
269
|
+
@client.query_timeout = 0.1
|
270
|
+
@client.query_timeout.should eq 0.1
|
271
|
+
start_time = Time.now
|
272
|
+
pg_exec_and_check_with_error(@client, false,
|
273
|
+
PG::SyntaxError, "syntax error",
|
274
|
+
:query_defer, 'SELLECT 1') do
|
275
|
+
@client.async_command_aborted.should be_false
|
276
|
+
::EM.add_timer(0.11) do
|
277
|
+
@client.async_command_aborted.should be_false
|
278
|
+
@client.status.should be PG::CONNECTION_OK
|
279
|
+
@client.query_timeout = 0
|
280
|
+
@client.query_timeout.should eq 0
|
281
|
+
EM.stop
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
284
285
|
end
|
data/spec/em_synchrony_client.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
$:.unshift "lib"
|
2
|
+
gem 'eventmachine', '~> 1.0.0'
|
2
3
|
gem 'pg', ENV['EM_PG_CLIENT_TEST_PG_VERSION']
|
3
4
|
require 'date'
|
4
5
|
require 'em-synchrony'
|
5
|
-
require 'em
|
6
|
+
require 'pg/em'
|
6
7
|
|
7
8
|
describe PG::EM::Client do
|
8
9
|
|
@@ -41,7 +42,7 @@ describe PG::EM::Client do
|
|
41
42
|
|
42
43
|
it "should populate foo with some data " do
|
43
44
|
results = @values.map do |(data, id)|
|
44
|
-
@client.
|
45
|
+
@client.exec_params('INSERT INTO foo (id,cdate,data) VALUES($1,$2,$3) returning cdate', [id, DateTime.now, data]) do |result|
|
45
46
|
result.should be_an_instance_of PG::Result
|
46
47
|
DateTime.parse(result[0]['cdate'])
|
47
48
|
end
|
@@ -166,7 +167,7 @@ describe PG::EM::Client do
|
|
166
167
|
it "should raise syntax error in misspelled multiple statement" do
|
167
168
|
expect {
|
168
169
|
@client.query('SELECT * from pg_class; SRELECT CURRENT_TIMESTAMP; SELECT 42 number')
|
169
|
-
}.to raise_error(PG::
|
170
|
+
}.to raise_error(PG::SyntaxError, /syntax error/)
|
170
171
|
end
|
171
172
|
|
172
173
|
it "should rollback transaction" do
|
@@ -189,12 +190,17 @@ describe PG::EM::Client do
|
|
189
190
|
start_time = Time.now
|
190
191
|
expect {
|
191
192
|
@client.query('SELECT pg_sleep(2)')
|
192
|
-
}.to raise_error(PG::
|
193
|
+
}.to raise_error(PG::ConnectionBad, /query timeout expired/)
|
193
194
|
(Time.now - start_time).should be < 2
|
194
195
|
@client.query_timeout = 0
|
195
196
|
@client.query_timeout.should eq 0
|
196
197
|
@client.async_command_aborted.should be_true
|
197
198
|
@client.status.should be PG::CONNECTION_BAD
|
199
|
+
@client.async_autoreconnect = false
|
200
|
+
expect {
|
201
|
+
@client.query('SELECT 1')
|
202
|
+
}.to raise_error(PG::ConnectionBad, /previous query expired/)
|
203
|
+
@client.async_autoreconnect = true
|
198
204
|
end
|
199
205
|
|
200
206
|
it "should timeout not expire while executing query with partial results" do
|
@@ -230,7 +236,7 @@ describe PG::EM::Client do
|
|
230
236
|
'SELECT * from pg_class;' +
|
231
237
|
'SELECT pg_sleep(2);' +
|
232
238
|
'SELECT 42 number')
|
233
|
-
}.to raise_error(PG::
|
239
|
+
}.to raise_error(PG::ConnectionBad, /query timeout expired/)
|
234
240
|
(Time.now - start_time).should be > 2
|
235
241
|
@client.async_command_aborted.should be_true
|
236
242
|
@client.status.should be PG::CONNECTION_BAD
|
@@ -248,6 +254,249 @@ describe PG::EM::Client do
|
|
248
254
|
end
|
249
255
|
end
|
250
256
|
|
257
|
+
it "should not expire after executing erraneous query" do
|
258
|
+
@client.query_timeout.should eq 0
|
259
|
+
@client.query_timeout = 0.1
|
260
|
+
@client.query_timeout.should eq 0.1
|
261
|
+
expect {
|
262
|
+
@client.query('SELLECT 1')
|
263
|
+
}.to raise_error(PG::SyntaxError, /syntax error/)
|
264
|
+
@client.async_command_aborted.should be_false
|
265
|
+
@client.status.should be PG::CONNECTION_OK
|
266
|
+
::EM::Synchrony.sleep 0.11
|
267
|
+
@client.async_command_aborted.should be_false
|
268
|
+
@client.status.should be PG::CONNECTION_OK
|
269
|
+
end
|
270
|
+
|
271
|
+
describe 'PG::EM::Client#transaction' do
|
272
|
+
|
273
|
+
it "should raise ArgumentError when there is no block" do
|
274
|
+
expect do
|
275
|
+
@client.transaction
|
276
|
+
end.to raise_error(ArgumentError, /Must supply block for PG::EM::Client#transaction/)
|
277
|
+
end
|
278
|
+
|
279
|
+
it "should commit transaction and return whatever block yields" do
|
280
|
+
@client.transaction_status.should be PG::PQTRANS_IDLE
|
281
|
+
@client.transaction do |pg|
|
282
|
+
pg.should be @client
|
283
|
+
@client.transaction_status.should be PG::PQTRANS_INTRANS
|
284
|
+
@client.instance_variable_get(:@client_tran_count).should eq 1
|
285
|
+
@client.query(
|
286
|
+
'DROP TABLE IF EXISTS bar'
|
287
|
+
).should be_an_instance_of PG::Result
|
288
|
+
@client.query(
|
289
|
+
'CREATE TABLE bar (key integer, value varchar)'
|
290
|
+
).should be_an_instance_of PG::Result
|
291
|
+
@client.query("INSERT INTO bar (key,value) VALUES(42,'xyz') returning value") do |result|
|
292
|
+
result.should be_an_instance_of PG::Result
|
293
|
+
result[0]['value']
|
294
|
+
end
|
295
|
+
end.should eq 'xyz'
|
296
|
+
@client.query('SELECT * FROM bar') do |result|
|
297
|
+
result.should be_an_instance_of PG::Result
|
298
|
+
result[0]['key'].should eq '42'
|
299
|
+
end
|
300
|
+
@client.transaction_status.should be PG::PQTRANS_IDLE
|
301
|
+
@client.instance_variable_get(:@client_tran_count).should eq 0
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should rollback transaction on error and raise that error" do
|
305
|
+
@client.transaction_status.should be PG::PQTRANS_IDLE
|
306
|
+
expect do
|
307
|
+
@client.transaction do |pg|
|
308
|
+
pg.should be @client
|
309
|
+
@client.transaction_status.should be PG::PQTRANS_INTRANS
|
310
|
+
@client.instance_variable_get(:@client_tran_count).should eq 1
|
311
|
+
@client.query(
|
312
|
+
"INSERT INTO bar (key,value) VALUES(11,'abc')"
|
313
|
+
).should be_an_instance_of PG::Result
|
314
|
+
@client.query('SELECT * FROM bar ORDER BY key') do |result|
|
315
|
+
result.should be_an_instance_of PG::Result
|
316
|
+
result[0]['key'].should eq '11'
|
317
|
+
end
|
318
|
+
@client.query('SELECT count(*) AS count FROM bar') do |result|
|
319
|
+
result.should be_an_instance_of PG::Result
|
320
|
+
result[0]['count'].should eq '2'
|
321
|
+
end
|
322
|
+
raise "rollback"
|
323
|
+
end
|
324
|
+
end.to raise_error(RuntimeError, /rollback/)
|
325
|
+
@client.query('SELECT count(*) AS count FROM bar') do |result|
|
326
|
+
result.should be_an_instance_of PG::Result
|
327
|
+
result[0]['count'].should eq '1'
|
328
|
+
end
|
329
|
+
@client.transaction_status.should be PG::PQTRANS_IDLE
|
330
|
+
@client.instance_variable_get(:@client_tran_count).should eq 0
|
331
|
+
end
|
332
|
+
|
333
|
+
it "should allow nesting transaction and return whatever innermost block yields" do
|
334
|
+
@client.transaction_status.should be PG::PQTRANS_IDLE
|
335
|
+
@client.transaction do |pg|
|
336
|
+
pg.should be @client
|
337
|
+
@client.transaction_status.should be PG::PQTRANS_INTRANS
|
338
|
+
@client.instance_variable_get(:@client_tran_count).should eq 1
|
339
|
+
@client.query(
|
340
|
+
"INSERT INTO bar (key,value) VALUES(100,'hundred') returning value"
|
341
|
+
).should be_an_instance_of PG::Result
|
342
|
+
@client.transaction do |pg|
|
343
|
+
pg.should be @client
|
344
|
+
@client.transaction_status.should be PG::PQTRANS_INTRANS
|
345
|
+
@client.instance_variable_get(:@client_tran_count).should eq 2
|
346
|
+
@client.query(
|
347
|
+
"INSERT INTO bar (key,value) VALUES(1000,'thousand') returning value"
|
348
|
+
).should be_an_instance_of PG::Result
|
349
|
+
@client.transaction do |pg|
|
350
|
+
pg.should be @client
|
351
|
+
@client.transaction_status.should be PG::PQTRANS_INTRANS
|
352
|
+
@client.instance_variable_get(:@client_tran_count).should eq 3
|
353
|
+
@client.query("INSERT INTO bar (key,value) VALUES(1000000,'million') returning value")
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end.tap do |result|
|
357
|
+
result.should be_an_instance_of PG::Result
|
358
|
+
result[0]['value'].should eq 'million'
|
359
|
+
end
|
360
|
+
@client.query('SELECT key,value FROM bar ORDER BY key') do |result|
|
361
|
+
result.should be_an_instance_of PG::Result
|
362
|
+
result.column_values(0).should eq ['42','100','1000','1000000']
|
363
|
+
result.column_values(1).should eq ['xyz','hundred','thousand','million']
|
364
|
+
end
|
365
|
+
@client.transaction_status.should be PG::PQTRANS_IDLE
|
366
|
+
@client.instance_variable_get(:@client_tran_count).should eq 0
|
367
|
+
end
|
368
|
+
|
369
|
+
it "should allow nesting transaction and rollback on error" do
|
370
|
+
@client.transaction_status.should be PG::PQTRANS_IDLE
|
371
|
+
expect do
|
372
|
+
@client.transaction do |pg|
|
373
|
+
pg.should be @client
|
374
|
+
@client.transaction_status.should be PG::PQTRANS_INTRANS
|
375
|
+
@client.instance_variable_get(:@client_tran_count).should eq 1
|
376
|
+
@client.query(
|
377
|
+
"INSERT INTO bar (key,value) VALUES(200,'two hundred') returning value"
|
378
|
+
).should be_an_instance_of PG::Result
|
379
|
+
@client.transaction do |pg|
|
380
|
+
pg.should be @client
|
381
|
+
@client.transaction_status.should be PG::PQTRANS_INTRANS
|
382
|
+
@client.instance_variable_get(:@client_tran_count).should eq 2
|
383
|
+
@client.query(
|
384
|
+
"INSERT INTO bar (key,value) VALUES(2000,'two thousands') returning value"
|
385
|
+
).should be_an_instance_of PG::Result
|
386
|
+
@client.transaction do |pg|
|
387
|
+
pg.should be @client
|
388
|
+
@client.transaction_status.should be PG::PQTRANS_INTRANS
|
389
|
+
@client.instance_variable_get(:@client_tran_count).should eq 3
|
390
|
+
@client.query(
|
391
|
+
"INSERT INTO bar (key,value) VALUES(2000000,'two millions') returning value"
|
392
|
+
).should be_an_instance_of PG::Result
|
393
|
+
raise "rollback from here"
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end.to raise_error(RuntimeError, /rollback from here/)
|
398
|
+
@client.query('SELECT key,value FROM bar ORDER BY key') do |result|
|
399
|
+
result.should be_an_instance_of PG::Result
|
400
|
+
result.column_values(0).should eq ['42','100','1000','1000000']
|
401
|
+
result.column_values(1).should eq ['xyz','hundred','thousand','million']
|
402
|
+
end
|
403
|
+
@client.transaction_status.should be PG::PQTRANS_IDLE
|
404
|
+
@client.instance_variable_get(:@client_tran_count).should eq 0
|
405
|
+
end
|
406
|
+
|
407
|
+
it "should allow rollback on rescued sql error from nested transaction" do
|
408
|
+
flag = false
|
409
|
+
@client.transaction_status.should be PG::PQTRANS_IDLE
|
410
|
+
@client.transaction do |pg|
|
411
|
+
pg.should be @client
|
412
|
+
@client.transaction_status.should be PG::PQTRANS_INTRANS
|
413
|
+
@client.instance_variable_get(:@client_tran_count).should eq 1
|
414
|
+
@client.query(
|
415
|
+
"INSERT INTO bar (key,value) VALUES(300,'three hundred') returning value"
|
416
|
+
).should be_an_instance_of PG::Result
|
417
|
+
@client.transaction do |pg|
|
418
|
+
pg.should be @client
|
419
|
+
@client.transaction_status.should be PG::PQTRANS_INTRANS
|
420
|
+
@client.instance_variable_get(:@client_tran_count).should eq 2
|
421
|
+
@client.query(
|
422
|
+
"INSERT INTO bar (key,value) VALUES(3000,'three thousands') returning value"
|
423
|
+
).should be_an_instance_of PG::Result
|
424
|
+
@client.transaction do |pg|
|
425
|
+
pg.should be @client
|
426
|
+
@client.transaction_status.should be PG::PQTRANS_INTRANS
|
427
|
+
@client.instance_variable_get(:@client_tran_count).should eq 3
|
428
|
+
expect {
|
429
|
+
@client.query('SRELECT CURRENT_TIMESTAMP')
|
430
|
+
}.to raise_error(PG::SyntaxError, /syntax error/)
|
431
|
+
@client.transaction_status.should be PG::PQTRANS_INERROR
|
432
|
+
@client.instance_variable_get(:@client_tran_count).should eq 3
|
433
|
+
expect {
|
434
|
+
@client.query('SELECT CURRENT_TIMESTAMP')
|
435
|
+
}.to raise_error(PG::InFailedSqlTransaction, /transaction is aborted/)
|
436
|
+
@client.transaction_status.should be PG::PQTRANS_INERROR
|
437
|
+
@client.instance_variable_get(:@client_tran_count).should eq 3
|
438
|
+
expect do
|
439
|
+
@client.transaction { 'foo' }
|
440
|
+
end.to raise_error(PG::InFailedSqlTransaction, /transaction is aborted/)
|
441
|
+
@client.transaction_status.should be PG::PQTRANS_INERROR
|
442
|
+
@client.instance_variable_get(:@client_tran_count).should eq 3
|
443
|
+
flag = :was_here
|
444
|
+
end
|
445
|
+
@client.transaction_status.should be PG::PQTRANS_INERROR
|
446
|
+
@client.instance_variable_get(:@client_tran_count).should eq 2
|
447
|
+
expect {
|
448
|
+
@client.query('SELECT CURRENT_TIMESTAMP')
|
449
|
+
}.to raise_error(PG::InFailedSqlTransaction, /transaction is aborted/)
|
450
|
+
expect do
|
451
|
+
@client.transaction { 'foo' }
|
452
|
+
end.to raise_error(PG::InFailedSqlTransaction, /transaction is aborted/)
|
453
|
+
@client.transaction_status.should be PG::PQTRANS_INERROR
|
454
|
+
@client.instance_variable_get(:@client_tran_count).should eq 2
|
455
|
+
end
|
456
|
+
@client.transaction_status.should be PG::PQTRANS_INERROR
|
457
|
+
@client.instance_variable_get(:@client_tran_count).should eq 1
|
458
|
+
expect {
|
459
|
+
@client.query('SELECT CURRENT_TIMESTAMP')
|
460
|
+
}.to raise_error(PG::InFailedSqlTransaction, /transaction is aborted/)
|
461
|
+
expect do
|
462
|
+
@client.transaction { 'foo' }
|
463
|
+
end.to raise_error(PG::InFailedSqlTransaction, /transaction is aborted/)
|
464
|
+
@client.transaction_status.should be PG::PQTRANS_INERROR
|
465
|
+
@client.instance_variable_get(:@client_tran_count).should eq 1
|
466
|
+
end
|
467
|
+
@client.transaction_status.should be PG::PQTRANS_IDLE
|
468
|
+
@client.instance_variable_get(:@client_tran_count).should eq 0
|
469
|
+
@client.transaction { 'foo' }.should eq 'foo'
|
470
|
+
@client.query('SELECT key,value FROM bar ORDER BY key') do |result|
|
471
|
+
result.should be_an_instance_of PG::Result
|
472
|
+
result.column_values(0).should eq ['42','100','1000','1000000']
|
473
|
+
result.column_values(1).should eq ['xyz','hundred','thousand','million']
|
474
|
+
end
|
475
|
+
flag.should be :was_here
|
476
|
+
end
|
477
|
+
|
478
|
+
it "should detect premature transaction state change" do
|
479
|
+
flag = false
|
480
|
+
@client.transaction_status.should be PG::PQTRANS_IDLE
|
481
|
+
@client.instance_variable_get(:@client_tran_count).should eq 0
|
482
|
+
@client.transaction do |pg|
|
483
|
+
pg.should be @client
|
484
|
+
@client.transaction_status.should be PG::PQTRANS_INTRANS
|
485
|
+
@client.query('ROLLBACK')
|
486
|
+
@client.instance_variable_get(:@client_tran_count).should eq 1
|
487
|
+
@client.transaction_status.should be PG::PQTRANS_IDLE
|
488
|
+
@client.transaction do
|
489
|
+
@client.transaction_status.should be PG::PQTRANS_INTRANS
|
490
|
+
@client.instance_variable_get(:@client_tran_count).should eq 1
|
491
|
+
'foo'
|
492
|
+
end.should eq 'foo'
|
493
|
+
@client.transaction_status.should be PG::PQTRANS_IDLE
|
494
|
+
@client.instance_variable_get(:@client_tran_count).should eq 0
|
495
|
+
end
|
496
|
+
@client.instance_variable_get(:@client_tran_count).should eq 0
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
251
500
|
around(:each) do |testcase|
|
252
501
|
@after_em_stop = nil
|
253
502
|
EM.synchrony do
|