1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
|
# frozen-string-literal: true
#
# The error_sql extension adds a DatabaseError#sql method
# that you can use to get the sql that caused the error
# to be raised.
#
# begin
# DB.run "Invalid SQL"
# rescue => e
# puts e.sql # "Invalid SQL"
# end
#
# On some databases, the error message contains part or all
# of the SQL used, but on other databases, none of the SQL
# used is displayed in the error message, so it can be
# difficult to track down what is causing the error without
# using a logger. This extension should hopefully make
# debugging easier on databases that have bad error
# messages.
#
# This extension may not work correctly in the following cases:
#
# * log_connection_yield is not used when executing the query.
# * The underlying exception is frozen or reused.
# * The underlying exception doesn't correctly record instance
# variables set on it (seems to happen on JRuby when underlying
# exception objects are Java exceptions).
#
# To load the extension into the database:
#
# DB.extension :error_sql
#
# Related module: Sequel::ErrorSQL
#
module Sequel
class DatabaseError
# Get the SQL code that caused this error to be raised.
def sql
# We store the error SQL in the wrapped exception instead of the
# current exception, since when the error SQL is originally associated
# with the wrapped exception, the current exception doesn't exist. It's
# possible to copy the error SQL into the current exception, but there
# doesn't seem to be a reason to do that.
wrapped_exception.instance_variable_get(:@sequel_error_sql) if wrapped_exception
end
end
module ErrorSQL
# Store the SQL related to the exception with the exception, so it
# is available for DatabaseError#sql later.
def log_exception(exception, message)
exception.instance_variable_set(:@sequel_error_sql, message)
super
end
# If there are no loggers for this database and an exception is raised
# store the SQL related to the exception with the exception, so it
# is available for DatabaseError#sql later.
def log_connection_yield(sql, conn, args=nil)
if @loggers.empty?
begin
yield
rescue => e
sql = "#{connection_info(conn) if conn && log_connection_info}#{sql}#{"; #{args.inspect}" if args}"
e.instance_variable_set(:@sequel_error_sql, sql)
raise
end
else
super
end
end
end
Database.register_extension(:error_sql, ErrorSQL)
end
|