Skip to content

Commit 73733ac

Browse files
authored
Merge pull request #499 from larskanis/improve_copy_data
Improve copy_data error handling
2 parents 8c91814 + 1563e73 commit 73733ac

File tree

4 files changed

+54
-35
lines changed

4 files changed

+54
-35
lines changed

lib/pg.rb

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,6 @@ module PG
5050
end
5151
end
5252

53-
54-
class NotAllCopyDataRetrieved < PG::Error
55-
end
56-
class NotInBlockingMode < PG::Error
57-
end
58-
5953
# Get the PG library version.
6054
#
6155
# +include_buildnum+ is no longer used and any value passed will be ignored.

lib/pg/connection.rb

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,19 @@ def copy_data( sql, coder=nil )
196196
yield res
197197
rescue Exception => err
198198
errmsg = "%s while copy data: %s" % [ err.class.name, err.message ]
199-
put_copy_end( errmsg )
200-
get_result
201-
raise
199+
begin
200+
put_copy_end( errmsg )
201+
rescue PG::Error
202+
# Ignore error in cleanup to avoid losing original exception
203+
end
204+
discard_results
205+
raise err
202206
else
203-
put_copy_end
207+
begin
208+
put_copy_end
209+
rescue PG::Error => err
210+
raise PG::LostCopyState.new("#{err} (probably by executing another SQL query while running a COPY command)", connection: self)
211+
end
204212
get_last_result
205213
ensure
206214
self.encoder_for_put_copy_data = old_coder if coder
@@ -213,24 +221,17 @@ def copy_data( sql, coder=nil )
213221
self.decoder_for_get_copy_data = coder
214222
end
215223
yield res
216-
rescue Exception => err
224+
rescue Exception
217225
cancel
218-
begin
219-
while get_copy_data
220-
end
221-
rescue PG::Error
222-
# Ignore error in cleanup to avoid losing original exception
223-
end
224-
while get_result
225-
end
226-
raise err
226+
discard_results
227+
raise
227228
else
228229
res = get_last_result
229-
if !res || res.result_status != PGRES_COMMAND_OK
230-
while get_copy_data
231-
end
232-
while get_result
233-
end
230+
if !res
231+
discard_results
232+
raise PG::LostCopyState.new("Lost COPY state (probably by executing another SQL query while running a COPY command)", connection: self)
233+
elsif res.result_status != PGRES_COMMAND_OK
234+
discard_results
234235
raise PG::NotAllCopyDataRetrieved.new("Not all COPY data retrieved", connection: self)
235236
end
236237
res

lib/pg/exceptions.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,12 @@ def initialize(msg=nil, connection: nil, result: nil)
1414
end
1515
end
1616

17+
class NotAllCopyDataRetrieved < PG::Error
18+
end
19+
class LostCopyState < PG::Error
20+
end
21+
class NotInBlockingMode < PG::Error
22+
end
23+
1724
end # module PG
1825

spec/pg/connection_spec.rb

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,7 @@
11731173
@conn.sync_put_copy_end
11741174
res = @conn.get_last_result
11751175
expect( res.result_status ).to eq( PG::PGRES_COMMAND_OK )
1176+
@conn.exec( "DROP TABLE IF EXISTS copytable2" )
11761177
end
11771178

11781179
describe "#copy_data" do
@@ -1247,6 +1248,7 @@
12471248

12481249
res = @conn.exec( "SELECT * FROM copytable ORDER BY col1" )
12491250
expect( res.values ).to eq( [["1"], ["2"]] )
1251+
@conn.exec( "DROP TABLE IF EXISTS copytable" )
12501252
end
12511253

12521254
it "can process #copy_data input queries with lots of data" do
@@ -1263,6 +1265,7 @@
12631265
expect( res.values ).to eq( [["1000"]] )
12641266
res = @conn.exec( "SELECT * FROM copytable2 LIMIT 1" )
12651267
expect( res.values ).to eq( [[str.chomp]] )
1268+
@conn.exec( "DROP TABLE IF EXISTS copytable2" )
12661269
end
12671270

12681271
it "can handle client errors in #copy_data for input" do
@@ -1277,6 +1280,7 @@
12771280
end
12781281

12791282
expect( @conn ).to still_be_usable
1283+
@conn.exec( "DROP TABLE IF EXISTS copytable" )
12801284
end
12811285

12821286
it "can handle server errors in #copy_data for input" do
@@ -1290,30 +1294,43 @@
12901294
}.to raise_error(PG::Error, /invalid input syntax for .*integer/){|err| expect(err).to have_attributes(connection: @conn) }
12911295
end
12921296
expect( @conn ).to still_be_usable
1297+
@conn.exec( "DROP TABLE IF EXISTS copytable" )
12931298
end
12941299

1295-
it "gracefully handle SQL statements while in #copy_data for input" do
1300+
it "doesn't lose client error when #copy_data can not be finished" do
12961301
@conn.exec "ROLLBACK"
12971302
@conn.transaction do
12981303
@conn.exec( "CREATE TEMP TABLE copytable (col1 INT)" )
12991304
expect {
13001305
@conn.copy_data( "COPY copytable FROM STDOUT" ) do |res|
1301-
@conn.exec "SELECT 1"
1306+
@conn.discard_results # end copy state so that put_copy_end fails in copy_data
1307+
raise "boom"
13021308
end
1303-
}.to raise_error(PG::Error, /no COPY in progress/){|err| expect(err).to have_attributes(connection: @conn) }
1309+
}.to raise_error(RuntimeError, "boom")
13041310
end
13051311
expect( @conn ).to still_be_usable
1312+
@conn.exec( "DROP TABLE IF EXISTS copytable" )
1313+
end
1314+
1315+
it "gracefully handle SQL statements while in #copy_data for input" do
1316+
@conn.exec "ROLLBACK"
1317+
@conn.exec( "CREATE TEMP TABLE copytable (col1 INT)" )
1318+
expect {
1319+
@conn.copy_data( "COPY copytable FROM STDOUT" ) do |res|
1320+
@conn.exec "SELECT 1"
1321+
end
1322+
}.to raise_error(PG::LostCopyState, /another SQL query/){|err| expect(err).to have_attributes(connection: @conn) }
1323+
expect( @conn ).to still_be_usable
1324+
@conn.exec( "DROP TABLE copytable" )
13061325
end
13071326

13081327
it "gracefully handle SQL statements while in #copy_data for output" do
13091328
@conn.exec "ROLLBACK"
1310-
@conn.transaction do
1311-
expect {
1312-
@conn.copy_data( "COPY (VALUES(1), (2)) TO STDOUT" ) do |res|
1313-
@conn.exec "SELECT 3"
1314-
end
1315-
}.to raise_error(PG::Error, /no COPY in progress/){|err| expect(err).to have_attributes(connection: @conn) }
1316-
end
1329+
expect {
1330+
@conn.copy_data( "COPY (VALUES(1), (2)) TO STDOUT" ) do |res|
1331+
@conn.exec "SELECT 3"
1332+
end
1333+
}.to raise_error(PG::LostCopyState, /another SQL query/){|err| expect(err).to have_attributes(connection: @conn) }
13171334
expect( @conn ).to still_be_usable
13181335
end
13191336

0 commit comments

Comments
 (0)