lib/pg_conn.rb in pg_conn-0.8.0 vs lib/pg_conn.rb in pg_conn-0.9.0
- old
+ new
@@ -507,29 +507,38 @@
# already in progress. The +sql+ argument can be a command (String) or an
# arbitrarily nested array of commands. Note that you can't have commands
# that span multiple lines. The empty array is a NOP but the empty string
# is not.
#
- # #exec pass Postgres exceptions to the caller unless :fail is false. If
- # fail is false #exec instead return nil but note that postgres doesn't
- # ignore it so that if you're inside a transaction, the transaction will be
- # in an error state and if you're also using subtransactions the whole
+ # #exec pass Postgres exceptions to the caller unless :fail is false in which case
+ # it returns nil.
+ #
+ # Note that postgres crashes the whole transaction stack if any error is
+ # met so if you're inside a transaction, the transaction will be in an
+ # error state and if you're also using subtransactions the whole
# transaction stack has collapsed
#
# TODO: Make sure the transaction stack is emptied on postgres errors
def exec(sql, commit: true, fail: true, silent: false)
- transaction(commit: commit) { execute(sql, fail: fail, silent: silent) }
+ begin
+ transaction(commit: commit) { execute(sql, fail: fail, silent: silent) }
+ rescue PG::Error
+ raise if fail
+ cancel_transaction
+ return nil
+ end
end
# Like #exec but returns true/false depending on if the command succeeded.
# There is not corresponding #exeucte? method because any failure rolls
# back the whole transaction stack. TODO: Check which exceptions that
# should be captured
def exec?(sql, commit: true, silent: true)
begin
exec(sql, commit: commit, fail: true, silent: silent)
rescue PG::Error
+ cancel_transaction
return false
end
return true
end
@@ -541,13 +550,18 @@
# unless :fail is false in which case it returns nil
#
# TODO: Handle postgres exceptions wrt transaction state and stack
def execute(sql, fail: true, silent: false)
if @pg_connection
- pg_exec(sql, fail: fail, silent: silent)&.cmd_tuples
+ begin
+ pg_exec(sql, silent: silent)&.cmd_tuples
+ rescue PG::Error
+ raise if fail
+ return nil
+ end
else
- pg_exec(sql, fail: fail, silent: silent)
+ pg_exec(sql, silent: silent)
end
end
# Switch user to the given user and execute the statement before swithcing
# back to the original user
@@ -609,14 +623,20 @@
pg_exec(commit ? "commit" : "rollback")
end
end
# Does a rollback and empties the stack. This should be called in response
- # to PG::Error exceptions because then the whole transaction stack is
- # invalid
+ # to PG::Error exceptions because the whole transaction stack is
+ # invalid and the server is in an invalid state
+ #
+ # It is not an error to call #cancel_transaction when no transaction is in
+ # progress
def cancel_transaction
- pg_exec("rollback")
+ begin
+ pg_exec("rollback")
+ rescue PG::Error
+ end
@savepoints = nil
end
# Execute block within a transaction and return the result of the block.
# The transaction can be rolled back by raising a PgConn::Rollback
@@ -629,20 +649,21 @@
push_transaction
result = yield
rescue PgConn::Rollback
pop_transaction(commit: false)
return nil
- # FIXME: Rescue other postgres errors and wipe-out stack
+ rescue PG::Error
+ @savepoints = nil
+ raise
end
pop_transaction(commit: commit)
result
end
private
# Wrapper around PG::Connection.new that switches to the postgres user
# before connecting if the current user is the root user
- #
def make_connection(*args, **opts)
if Process.euid == 0
begin
postgres_uid = Process::UID.from_name "postgres"
rescue ArgumentError
@@ -671,22 +692,24 @@
# pg_exec(string)
# pg_exec(array)
#
# Execute statement(s) on the server. If the argument is an array of
# commands, the commands are concatenated with ';' before being sent to the
- # server. #pg_exec returns a PG::Result object or nil if +arg+ was empty.
- # #exec pass Postgres exceptions to the caller unless :fail is false
+ # server. #pg_exec returns a PG::Result object or nil if +arg+ was empty
#
+ # Postgres errors are passed through and #error and #err set to the last
+ # statement's SQL errors or nil if it succeeded
+ #
# FIXME: Error message prints the last statement but what if another
# statement failed?
#
# TODO: Connsider executing statements one-by-one so we're able to
# pin-point Postgres errors without a line number. This may be expensive,
# though
#
# TODO: Fix silent by not handling exceptions
- def pg_exec(arg, fail: true, silent: false)
+ def pg_exec(arg, silent: false)
if @pg_connection
@error = @err = nil
begin
last_stmt = nil # To make the current SQL statement visible to the rescue clause. FIXME Not used?
if arg.is_a?(String)
@@ -701,22 +724,19 @@
@pg_connection.exec(stmts.join(";\n"))
end
rescue PG::Error => ex
@error = ex
- if fail
- if !silent # FIXME Why do we handle this?
- $stderr.puts arg
- $stderr.puts
- $stderr.puts ex.message
- $stderr.flush
- end
- raise
- else
- return nil
+ if !silent # FIXME Why do we handle this?
+ $stderr.puts arg
+ $stderr.puts
+ $stderr.puts ex.message
+ $stderr.flush
end
+ raise
end
+ # For dump of SQL statements
else # @pg_commands is defined
if arg.is_a?(String)
@pg_commands << arg if arg != ""
else
@pg_commands.concat(arg)