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)