em-pg-client 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|