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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
|
# frozen-string-literal: true
#
# The duplicate_columns_handler extension allows you to customize handling of
# duplicate column names in your queries on a per-database or per-dataset level.
#
# For example, you may want to raise an exception if you join 2 tables together
# which contains a column that will override another columns.
#
# To use the extension, you need to load the extension into the database:
#
# DB.extension :duplicate_columns_handler
#
# or into individual datasets:
#
# ds = DB[:items].extension(:duplicate_columns_handler)
#
# A database option is introduced: :on_duplicate_columns. It accepts a Symbol
# or any object that responds to :call.
#
# on_duplicate_columns: :raise
# on_duplicate_columns: :warn
# on_duplicate_columns: :ignore
# on_duplicate_columns: lambda{|columns| arbitrary_condition? ? :raise : :warn}
#
# You may also configure duplicate columns handling for a specific dataset:
#
# ds.on_duplicate_columns(:warn)
# ds.on_duplicate_columns(:raise)
# ds.on_duplicate_columns(:ignore)
# ds.on_duplicate_columns{|columns| arbitrary_condition? ? :raise : :warn}
# ds.on_duplicate_columns(lambda{|columns| arbitrary_condition? ? :raise : :warn})
#
# If :raise is specified, a Sequel::DuplicateColumnError is raised.
# If :warn is specified, you will receive a warning via +warn+.
# If a callable is specified, it will be called.
# If no on_duplicate_columns is specified, the default is :warn.
#
# Related module: Sequel::DuplicateColumnsHandler
module Sequel
module DuplicateColumnsHandler
# :nocov:
CALLER_ARGS = (RUBY_VERSION >= '2.0' ? [0,1] : [0]).freeze
# :nocov:
# Customize handling of duplicate columns for this dataset.
def on_duplicate_columns(handler = (raise Error, "Must provide either an argument or a block to on_duplicate_columns" unless defined?(yield); nil), &block)
raise Error, "Cannot provide both an argument and a block to on_duplicate_columns" if handler && block
clone(:on_duplicate_columns=>handler||block)
end
private
# Call handle_duplicate_columns if there are duplicate columns.
def columns=(cols)
if cols && cols.uniq.size != cols.size
handle_duplicate_columns(cols)
end
super
end
# Invoke the appropriate behavior when duplicate columns are present.
def handle_duplicate_columns(cols)
message = "#{caller(*CALLER_ARGS).first}: One or more duplicate columns present in #{cols.inspect}"
case duplicate_columns_handler_type(cols)
when :raise
raise DuplicateColumnError, message
when :warn
warn message
end
end
# Try to find dataset option for on_duplicate_columns. If not present on the dataset,
# use the on_duplicate_columns option on the database. If not present on the database,
# default to :warn.
def duplicate_columns_handler_type(cols)
handler = opts.fetch(:on_duplicate_columns){db.opts.fetch(:on_duplicate_columns, :warn)}
if handler.respond_to?(:call)
handler.call(cols)
else
handler
end
end
end
# Error which is raised when duplicate columns are present in a dataset which is configured
# to :raise on_duplicate_columns.
class DuplicateColumnError < Error
end
Dataset.register_extension(:duplicate_columns_handler, Sequel::DuplicateColumnsHandler)
end
|