em-pg-client 0.3.0 → 0.3.1

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.
@@ -17,9 +17,7 @@ module PG
17
17
  @timer = ::EM::Timer.new(timeout) do
18
18
  detach
19
19
  @deferrable.protect do
20
- error = ConnectionBad.new("timeout expired (async)")
21
- error.instance_variable_set(:@connection, @client)
22
- raise error
20
+ @client.raise_error ConnectionBad, "timeout expired (async)"
23
21
  end
24
22
  end
25
23
  end
@@ -45,11 +43,7 @@ module PG
45
43
  @timer.cancel if @timer
46
44
  detach
47
45
  @deferrable.protect_and_succeed do
48
- unless polling_ok
49
- error = ConnectionBad.new(@client.error_message)
50
- error.instance_variable_set(:@connection, @client)
51
- raise error
52
- end
46
+ @client.raise_error ConnectionBad unless polling_ok
53
47
  @client.set_default_encoding unless reconnecting?
54
48
  @client
55
49
  end
@@ -17,17 +17,31 @@ module PG
17
17
  @is_connected
18
18
  end
19
19
 
20
- def watch_query(deferrable, send_proc)
21
- self.notify_readable = true
20
+ def one_result_mode?
21
+ @one_result_mode
22
+ end
23
+
24
+ def watch_results(deferrable, send_proc=nil, one_result_mode=false)
25
+ @one_result_mode = one_result_mode
22
26
  @last_result = nil
23
27
  @deferrable = deferrable
24
28
  @send_proc = send_proc
25
- @timer.cancel if @timer
26
- if (timeout = @client.query_timeout) > 0
27
- @notify_timestamp = Time.now
28
- setup_timer timeout
29
+ cancel_timer
30
+ if @client.is_busy
31
+ if @client.status == PG::CONNECTION_OK
32
+ self.notify_readable = true
33
+ if (timeout = @client.query_timeout) > 0
34
+ @notify_timestamp = Time.now
35
+ setup_timer timeout
36
+ end
37
+ else
38
+ @deferrable.protect do
39
+ @client.raise_error ConnectionBad
40
+ end
41
+ end
29
42
  else
30
- @timer = nil
43
+ self.notify_readable = true
44
+ fetch_results
31
45
  end
32
46
  self
33
47
  end
@@ -39,9 +53,7 @@ module PG
39
53
  self.notify_readable = false
40
54
  @client.async_command_aborted = true
41
55
  @deferrable.protect do
42
- error = ConnectionBad.new("query timeout expired (async)")
43
- error.instance_variable_set(:@connection, @client)
44
- raise error
56
+ @client.raise_error ConnectionBad, "query timeout expired (async)"
45
57
  end
46
58
  else
47
59
  setup_timer timeout, last_interval
@@ -56,20 +68,20 @@ module PG
56
68
  end
57
69
  end
58
70
 
59
- # Carefully extract the last result without
71
+ # Carefully extract results without
60
72
  # blocking the EventMachine reactor.
61
- def notify_readable
73
+ def fetch_results
62
74
  result = false
63
75
  @client.consume_input
64
76
  until @client.is_busy
65
- if (single_result = @client.get_result).nil?
66
- if (result = @last_result).nil?
67
- error = Error.new(@client.error_message)
68
- error.instance_variable_set(:@connection, @client)
69
- raise error
77
+ single_result = @client.blocking_get_result
78
+ if one_result_mode?
79
+ result = single_result
80
+ break
81
+ elsif single_result.nil?
82
+ if result = @last_result
83
+ result.check
70
84
  end
71
- result.check
72
- cancel_timer
73
85
  break
74
86
  end
75
87
  @last_result.clear if @last_result
@@ -78,22 +90,34 @@ module PG
78
90
  rescue Exception => e
79
91
  self.notify_readable = false
80
92
  cancel_timer
93
+ send_proc = @send_proc
94
+ @send_proc = nil
95
+ df = @deferrable
96
+ # prevent unbind error on auto re-connect
97
+ @deferrable = false
81
98
  if e.is_a?(PG::Error)
82
- @client.async_autoreconnect!(@deferrable, e, &@send_proc)
99
+ @client.async_autoreconnect!(df, e, &send_proc)
83
100
  else
84
- @deferrable.fail(e)
101
+ df.fail(e)
85
102
  end
86
103
  else
87
104
  if result == false
88
105
  @notify_timestamp = Time.now if @timer
89
106
  else
90
107
  self.notify_readable = false
108
+ cancel_timer
109
+ @send_proc = nil
91
110
  @deferrable.succeed(result)
92
111
  end
93
112
  end
94
113
 
114
+ alias_method :notify_readable, :fetch_results
115
+
95
116
  def unbind
96
117
  @is_connected = false
118
+ @deferrable.protect do
119
+ @client.raise_error ConnectionBad, "connection reset"
120
+ end if @deferrable
97
121
  end
98
122
  end
99
123
 
@@ -5,8 +5,10 @@ require 'date'
5
5
  require 'eventmachine'
6
6
  require 'pg/em'
7
7
 
8
- $pgserver_cmd_stop = %Q[sudo -i -u postgres pg_ctl -D "#{ENV['PGDATA']}" stop -s -m fast]
9
- $pgserver_cmd_start = %Q[sudo -i -u postgres pg_ctl -D "#{ENV['PGDATA']}" start -s -w]
8
+ $pgserver_cmd_stop = ENV['PG_CTL_STOP_CMD'] || %Q[sudo -i -u postgres pg_ctl -D "#{ENV['PGDATA']}" stop -s -m fast]
9
+ $pgserver_cmd_start = ENV['PG_CTL_START_CMD'] || %Q[sudo -i -u postgres pg_ctl -D "#{ENV['PGDATA']}" start -s -w]
10
+
11
+ DISCONNECTED_ERROR = ENV['PGHOST'].include?('/') ? PG::UnableToSend : PG::ConnectionBad
10
12
 
11
13
  shared_context 'pg-em common' do
12
14
  around(:each) do |testcase|
@@ -67,7 +69,7 @@ describe 'pg-em default autoreconnect' do
67
69
  it "should not get database size using query after server shutdown" do
68
70
  system($pgserver_cmd_stop).should be_true
69
71
  @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
70
- ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
72
+ ex.should be_an_instance_of DISCONNECTED_ERROR
71
73
  EM.stop
72
74
  end.should be_a_kind_of ::EM::DefaultDeferrable
73
75
  end
@@ -92,12 +94,47 @@ describe 'pg-em default autoreconnect' do
92
94
  system($pgserver_cmd_stop).should be_true
93
95
  system($pgserver_cmd_start).should be_true
94
96
  @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
95
- ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
97
+ ex.should be_an_instance_of DISCONNECTED_ERROR
96
98
  @tested_proc.call
97
99
  end.should be_a_kind_of ::EM::DefaultDeferrable
98
100
  end
99
101
  end
100
102
 
103
+ it "should fail to get last result asynchronously after server restart" do
104
+ @client.send_query('SELECT pg_sleep(5); SELECT pg_database_size(current_database());')
105
+ system($pgserver_cmd_stop).should be_true
106
+ system($pgserver_cmd_start).should be_true
107
+ @client.get_last_result_defer do |ex|
108
+ ex.should be_an_instance_of PG::ConnectionBad
109
+ @client.status.should be PG::CONNECTION_OK
110
+ @client.get_last_result_defer do |result|
111
+ result.should be_nil
112
+ EM.stop
113
+ end
114
+ end.should be_a_kind_of EM::DefaultDeferrable
115
+ end
116
+
117
+ it "should fail to get each result asynchronously after server restart" do
118
+ @client.send_query('SELECT pg_sleep(5); SELECT pg_database_size(current_database());')
119
+ system($pgserver_cmd_stop).should be_true
120
+ system($pgserver_cmd_start).should be_true
121
+ @client.get_result_defer do |result|
122
+ result.should be_an_instance_of PG::Result
123
+ expect do
124
+ result.check
125
+ end.to raise_error PG::Error
126
+ @client.status.should be PG::CONNECTION_OK
127
+ @client.get_result_defer do |ex|
128
+ ex.should be_an_instance_of PG::ConnectionBad
129
+ @client.status.should be PG::CONNECTION_OK
130
+ @client.get_result_defer do |result|
131
+ result.should be_nil
132
+ EM.stop
133
+ end.should be_a_kind_of ::EM::DefaultDeferrable
134
+ end.should be_a_kind_of EM::DefaultDeferrable
135
+ end
136
+ end
137
+
101
138
  before(:all) do
102
139
  @tested_proc = proc do
103
140
  @client.query_defer('SELECT pg_database_size(current_database());') do |result|
@@ -148,7 +185,7 @@ describe 'pg-em autoreconnect with on_autoreconnect' do
148
185
  system($pgserver_cmd_stop).should be_true
149
186
  system($pgserver_cmd_start).should be_true
150
187
  @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
151
- ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
188
+ ex.should be_an_instance_of DISCONNECTED_ERROR
152
189
  @tested_proc.call
153
190
  end.should be_a_kind_of ::EM::DefaultDeferrable
154
191
  end
@@ -159,7 +196,7 @@ describe 'pg-em autoreconnect with on_autoreconnect' do
159
196
  system($pgserver_cmd_stop).should be_true
160
197
  system($pgserver_cmd_start).should be_true
161
198
  @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
162
- ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
199
+ ex.should be_an_instance_of DISCONNECTED_ERROR
163
200
  EM.stop
164
201
  end.should be_a_kind_of ::EM::DefaultDeferrable
165
202
  end
@@ -209,6 +246,45 @@ describe 'pg-em autoreconnect with on_autoreconnect' do
209
246
  end.should be_a_kind_of ::EM::DefaultDeferrable
210
247
  end
211
248
 
249
+ it "should fail to get last result asynchronously after server restart" do
250
+ @client.on_autoreconnect = proc { true }
251
+ @client.send_query('SELECT pg_sleep(5); SELECT pg_database_size(current_database());')
252
+ system($pgserver_cmd_stop).should be_true
253
+ system($pgserver_cmd_start).should be_true
254
+ @client.get_last_result_defer do |ex|
255
+ ex.should be_an_instance_of PG::ConnectionBad
256
+ @client.status.should be PG::CONNECTION_OK
257
+ @client.get_last_result_defer do |result|
258
+ result.should be_nil
259
+ EM.stop
260
+ end
261
+ end.should be_a_kind_of EM::DefaultDeferrable
262
+ end
263
+
264
+ it "should fail to get each result asynchronously after server restart" do
265
+ @client.on_autoreconnect = proc {
266
+ EM::DefaultDeferrable.new.tap {|df| df.succeed }
267
+ }
268
+ @client.send_query('SELECT pg_sleep(5); SELECT pg_database_size(current_database());')
269
+ system($pgserver_cmd_stop).should be_true
270
+ system($pgserver_cmd_start).should be_true
271
+ @client.get_result_defer do |result|
272
+ result.should be_an_instance_of PG::Result
273
+ expect do
274
+ result.check
275
+ end.to raise_error PG::Error
276
+ @client.status.should be PG::CONNECTION_OK
277
+ @client.get_result_defer do |ex|
278
+ ex.should be_an_instance_of PG::ConnectionBad
279
+ @client.status.should be PG::CONNECTION_OK
280
+ @client.get_result_defer do |result|
281
+ result.should be_nil
282
+ EM.stop
283
+ end.should be_a_kind_of ::EM::DefaultDeferrable
284
+ end.should be_a_kind_of EM::DefaultDeferrable
285
+ end
286
+ end
287
+
212
288
  before(:all) do
213
289
  @tested_proc = proc do
214
290
  @client.exec_prepared_defer('get_db_size') do |result|
@@ -240,20 +316,76 @@ describe 'pg-em with autoreconnect disabled' do
240
316
  system($pgserver_cmd_stop).should be_true
241
317
  system($pgserver_cmd_start).should be_true
242
318
  @client.query_defer('SELECT pg_database_size(current_database());') do |ex|
243
- ex.should be_an_instance_of @client.host.include?('/') ? PG::UnableToSend : PG::ConnectionBad
319
+ ex.should be_an_instance_of DISCONNECTED_ERROR
320
+ @client.status.should be PG::CONNECTION_BAD
244
321
  EM.stop
245
322
  end.should be_a_kind_of ::EM::DefaultDeferrable
246
323
  end
247
324
 
248
325
  it "should get database size using query after async manual connection reset" do
249
326
  @client.status.should be PG::CONNECTION_BAD
250
- @client.async_reset do |conn|
327
+ @client.reset_defer do |conn|
251
328
  conn.should be @client
252
329
  @client.status.should be PG::CONNECTION_OK
253
330
  @tested_proc.call
254
331
  end.should be_a_kind_of ::EM::DefaultDeferrable
255
332
  end
256
333
 
334
+ it "should fail to get last result asynchronously after server restart" do
335
+ system($pgserver_cmd_stop).should be_true
336
+ system($pgserver_cmd_start).should be_true
337
+ begin
338
+ @client.send_query('SELECT pg_sleep(5); SELECT pg_database_size(current_database());')
339
+ rescue PG::UnableToSend
340
+ end
341
+ @client.get_last_result_defer do |ex|
342
+ ex.should be_an_instance_of PG::ConnectionBad
343
+ @client.status.should be PG::CONNECTION_BAD
344
+ @client.get_last_result_defer do |ex|
345
+ ex.should be_an_instance_of PG::ConnectionBad
346
+ @client.reset_defer do |conn|
347
+ conn.should be @client
348
+ @client.status.should be PG::CONNECTION_OK
349
+ EM.stop
350
+ end.should be_a_kind_of ::EM::DefaultDeferrable
351
+ end
352
+ end.should be_a_kind_of EM::DefaultDeferrable
353
+ end
354
+
355
+ it "should fail to get each result asynchronously after server restart" do
356
+ check_get_result = proc do
357
+ @client.get_result_defer do |ex|
358
+ ex.should be_an_instance_of PG::ConnectionBad
359
+ @client.status.should be PG::CONNECTION_BAD
360
+ @client.reset_defer do |conn|
361
+ conn.should be @client
362
+ @client.status.should be PG::CONNECTION_OK
363
+ EM.stop
364
+ end.should be_a_kind_of ::EM::DefaultDeferrable
365
+ end.should be_a_kind_of EM::DefaultDeferrable
366
+ end
367
+ system($pgserver_cmd_stop).should be_true
368
+ system($pgserver_cmd_start).should be_true
369
+ begin
370
+ @client.send_query('SELECT pg_sleep(5); SELECT pg_database_size(current_database());')
371
+ rescue PG::UnableToSend
372
+ @client.get_result_defer do |result|
373
+ result.should be_an_instance_of PG::ConnectionBad
374
+ @client.status.should be PG::CONNECTION_BAD
375
+ check_get_result.call
376
+ end
377
+ else
378
+ @client.get_result_defer do |result|
379
+ result.should be_an_instance_of PG::Result
380
+ expect do
381
+ result.check
382
+ end.to raise_error PG::Error
383
+ @client.status.should be PG::CONNECTION_OK
384
+ check_get_result.call
385
+ end
386
+ end
387
+ end
388
+
257
389
  before(:all) do
258
390
  @tested_proc = proc do
259
391
  @client.query_defer('SELECT pg_database_size(current_database());') do |result|
@@ -106,6 +106,43 @@ end
106
106
 
107
107
  shared_context 'em-pg common after' do
108
108
 
109
+ if described_class.single_row_mode?
110
+
111
+ it "should get each result in single row mode" do
112
+ @client.single_row_mode?.should be_true
113
+ @client.get_result_defer do |result|
114
+ result.should be_nil
115
+ @client.send_query('SELECT data, id FROM foo order by id')
116
+ @client.set_single_row_mode
117
+ EM::Iterator.new(@values, 1).map(proc{ |(data, id), iter|
118
+ @client.get_result_defer do |result|
119
+ result.should be_an_instance_of PG::Result
120
+ result.check
121
+ result.result_status.should eq PG::PGRES_SINGLE_TUPLE
122
+ value = result.to_a
123
+ value.should eq [{'data' => data, 'id' => id.to_s}]
124
+ result.clear
125
+ iter.return value
126
+ end.should be_a_kind_of ::EM::DefaultDeferrable
127
+ }, proc{ |results|
128
+ results.length.should eq @values.length
129
+ @client.get_result_defer do |result|
130
+ result.should be_an_instance_of PG::Result
131
+ result.check
132
+ result.result_status.should eq PG::PGRES_TUPLES_OK
133
+ result.to_a.should eq []
134
+ result.clear
135
+ @client.get_result_defer do |result|
136
+ result.should be_nil
137
+ EM.stop
138
+ end.should be_a_kind_of ::EM::DefaultDeferrable
139
+ end.should be_a_kind_of ::EM::DefaultDeferrable
140
+ })
141
+ end.should be_a_kind_of ::EM::DefaultDeferrable
142
+ end
143
+
144
+ end
145
+
109
146
  it_should_execute("create prepared statement",
110
147
  :prepare_defer, 'get_foo', 'SELECT * FROM foo order by id')
111
148
 
@@ -282,4 +319,44 @@ shared_context 'em-pg common after' do
282
319
  end
283
320
  end
284
321
  end
322
+
323
+ it "should get last result asynchronously" do
324
+ @client.get_last_result_defer do |result|
325
+ result.should be_nil
326
+ @client.send_query('SELECT 1; SELECT 2; SELECT 3')
327
+ @client.get_last_result_defer do |result|
328
+ result.should be_an_instance_of PG::Result
329
+ result.getvalue(0,0).should eq '3'
330
+ result.clear
331
+ @client.get_last_result_defer do |result|
332
+ result.should be_nil
333
+ EM.stop
334
+ end
335
+ end.should be_a_kind_of ::EM::DefaultDeferrable
336
+ end.should be_a_kind_of ::EM::DefaultDeferrable
337
+ end
338
+
339
+ it "should get each result asynchronously" do
340
+ @client.get_result_defer do |result|
341
+ result.should be_nil
342
+ @client.send_query('SELECT 4; SELECT 5; SELECT 6')
343
+ EM::Iterator.new(%w[4 5 6], 1).map(proc{ |value, iter|
344
+ @client.get_result_defer do |result|
345
+ result.should be_an_instance_of PG::Result
346
+ result.check
347
+ result.result_status.should eq PG::PGRES_TUPLES_OK
348
+ result.getvalue(0,0).should eq value
349
+ result.clear
350
+ iter.return value
351
+ end.should be_a_kind_of ::EM::DefaultDeferrable
352
+ }, proc{ |results|
353
+ results.should eq %w[4 5 6]
354
+ @client.get_result_defer do |result|
355
+ result.should be_nil
356
+ EM.stop
357
+ end.should be_a_kind_of ::EM::DefaultDeferrable
358
+ })
359
+ end.should be_a_kind_of ::EM::DefaultDeferrable
360
+ end
361
+
285
362
  end
@@ -113,6 +113,33 @@ describe PG::EM::Client do
113
113
  ).should be_an_instance_of PG::Result
114
114
  end
115
115
 
116
+ if described_class.single_row_mode?
117
+
118
+ it "should get each result in single row mode" do
119
+ @client.single_row_mode?.should be_true
120
+ @client.send_query('SELECT data, id FROM foo order by id')
121
+ @client.set_single_row_mode
122
+ @values.each do |data, id|
123
+ result = @client.get_result
124
+ result.should be_an_instance_of PG::Result
125
+ result.result_status.should eq PG::PGRES_SINGLE_TUPLE
126
+ result.check
127
+ value = result.to_a
128
+ result.clear
129
+ value.should eq [{'data' => data, 'id' => id.to_s}]
130
+ end
131
+ result = @client.get_result
132
+ result.should be_an_instance_of PG::Result
133
+ result.check
134
+ result.result_status.should eq PG::PGRES_TUPLES_OK
135
+ result.to_a.should eq []
136
+ result.clear
137
+ @client.get_result.should be_nil
138
+ EM.stop
139
+ end
140
+
141
+ end
142
+
116
143
  it "should connect to database asynchronously" do
117
144
  this = :first
118
145
  Encoding.default_internal = Encoding::ISO_8859_1
@@ -149,12 +176,12 @@ describe PG::EM::Client do
149
176
  f = Fiber.current
150
177
  Fiber.new do
151
178
  begin
152
- described_class.new do |conn|
153
- this = :second
154
- Encoding.default_internal = nil
155
- conn.should be_an_instance_of described_class
156
- conn.external_encoding.should be conn.internal_encoding
157
- end
179
+ conn = described_class.new
180
+ this = :second
181
+ Encoding.default_internal = nil
182
+ conn.should be_an_instance_of described_class
183
+ conn.external_encoding.should be conn.internal_encoding
184
+ conn.finish
158
185
  ensure
159
186
  f.resume
160
187
  end
@@ -256,18 +283,59 @@ describe PG::EM::Client do
256
283
 
257
284
  it "should not expire after executing erraneous query" do
258
285
  @client.query_timeout.should eq 0
259
- @client.query_timeout = 0.1
260
- @client.query_timeout.should eq 0.1
286
+ @client.query_timeout = 1
287
+ @client.query_timeout.should eq 1
261
288
  expect {
262
289
  @client.query('SELLECT 1')
263
290
  }.to raise_error(PG::SyntaxError, /syntax error/)
264
291
  @client.async_command_aborted.should be_false
265
292
  @client.status.should be PG::CONNECTION_OK
266
- ::EM::Synchrony.sleep 0.11
293
+ ::EM::Synchrony.sleep 1.5
267
294
  @client.async_command_aborted.should be_false
268
295
  @client.status.should be PG::CONNECTION_OK
269
296
  end
270
297
 
298
+ it "should get last result asynchronously" do
299
+ result = @client.get_last_result
300
+ result.should be_nil
301
+ @client.get_last_result.should be_nil
302
+ @client.send_query('SELECT 1; SELECT 2; SELECT 3')
303
+ asynchronous = false
304
+ EM.next_tick { asynchronous = true }
305
+ result = @client.get_last_result
306
+ result.should be_an_instance_of PG::Result
307
+ result.getvalue(0,0).should eq '3'
308
+ result.clear
309
+ @client.get_last_result.should be_nil
310
+ asynchronous.should be true
311
+ end
312
+
313
+ it "should get each result asynchronously" do
314
+ result = @client.get_result
315
+ result.should be_nil
316
+ @client.get_result do |result|
317
+ result.should be_nil
318
+ end.should be_nil
319
+ @client.send_query('SELECT 4,pg_sleep(0.1); SELECT 5; SELECT 6')
320
+ asynchronous = false
321
+ EM.next_tick { asynchronous = true }
322
+ %w[4 5 6].map do |value|
323
+ result = @client.get_result do |result|
324
+ result.should be_an_instance_of PG::Result
325
+ result.check
326
+ result.result_status.should eq PG::PGRES_TUPLES_OK
327
+ result.getvalue(0,0).should eq value
328
+ result
329
+ end
330
+ expect do
331
+ result.clear
332
+ end.to raise_error PG::Error, /cleared/
333
+ value
334
+ end.should eq %w[4 5 6]
335
+ @client.get_result.should be_nil
336
+ asynchronous.should be true
337
+ end
338
+
271
339
  describe 'PG::EM::Client#transaction' do
272
340
 
273
341
  it "should raise ArgumentError when there is no block" do