module ETL #:nodoc:
module Processor #:nodoc:
# A row-level processor that checks if the row already exists in the
# target table
class CheckExistProcessor < ETL::Processor::RowProcessor
# A symbol or array of symbols representing keys that should be skipped
attr_accessor :skip
# The target database
attr_accessor :target
# The name of the table to check against
attr_accessor :table
# An array of columns representing the natural key
attr_accessor :columns
# Is set to true if the processor should execute the check. If there are
# no rows in the target table then this should return false.
attr_accessor :should_check
# Initialize the processor
# Configuration options:
# * :skip: A symbol or array of column names that should not
# be checked
# * :table: The table name
# * :columns: An array of columns which represent the natural
# key
def initialize(control, configuration)
super
@skip = configuration[:skip] || []
@target = configuration[:target] || raise(ETL::ControlError, "target must be specified")
@table = configuration[:table] || raise(ETL::ControlError, "table must be specified")
@columns = configuration[:columns]
q = "SELECT COUNT(*) FROM #{table_name}"
@should_check = ETL::Engine.connection(target).select_value(q).to_i > 0
end
# Return true if the given key should be skipped
def skip?(key)
case skip
when Array
skip.include?(key)
else
skip.to_sym == key.to_sym
end
end
# Return true if the row should be checked
def should_check?
@should_check ? true : false
end
# Process the row
def process(row)
return row unless should_check?
conn = ETL::Engine.connection(target)
q = "SELECT * FROM #{table_name} WHERE "
conditions = []
row.each do |k,v|
if columns.nil? || columns.include?(k.to_sym)
conditions << "#{k} = #{conn.quote(v)}" unless skip?(k.to_sym)
end
end
q << conditions.join(" AND ")
#puts "query: #{q}"
result = conn.select_one(q)
return row if result.nil?
end
private
def table_name
ETL::Engine.table(table, ETL::Engine.connection(target))
end
end
end
end