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.
@@ -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 PG::Error
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", :query, 'ROLLBACK')
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", :query, '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
- :query, 'DROP TABLE IF EXISTS foo')
100
+ :query_defer, 'DROP TABLE IF EXISTS foo')
130
101
 
131
102
  it_should_execute("create simple table `foo`",
132
- :query, 'CREATE TABLE foo (id integer,cdate timestamp with time zone,data varchar)')
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
- :prepare, 'get_foo', 'SELECT * FROM foo order by id')
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, :describe_prepared, 'get_foo') do |result|
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, :exec_prepared, 'get_foo') do |result|
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
- :query, 'DECLARE foobar SCROLL CURSOR FOR SELECT * FROM foo')
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, :query, 'FETCH FORWARD 2 FROM foobar') do |result|
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, :describe_portal, 'foobar') do |result|
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", :query, 'CLOSE foobar')
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.async_connect do |conn|
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, :query, 'SELECT pg_database_size(current_database());') do |result|
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.async_connect do |conn|
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
- :query, 'SELECT * from pg_class; SRELECT CURRENT_TIMESTAMP; SELECT 42 number')
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, :query,
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, "query timeout expired", :query, 'SELECT pg_sleep(2)') do
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, :query,
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, "query timeout expired", :query,
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
@@ -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-synchrony/pg'
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.query('INSERT INTO foo (id,cdate,data) VALUES($1,$2,$3) returning cdate', [id, DateTime.now, data]) do |result|
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::Error, /syntax error/)
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::Error, /query timeout expired/)
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::Error, /query timeout expired/)
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