Skip to content

Commit

Permalink
Refactor adapter checks/logic (#127)
Browse files Browse the repository at this point in the history
Centralize the adapter types so that we don't need to keep tweaking case
statements
  • Loading branch information
jenseng authored Sep 4, 2024
1 parent 6a33b5a commit bac1cac
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 40 deletions.
12 changes: 4 additions & 8 deletions lib/hair_trigger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
require 'hair_trigger/railtie' if defined?(Rails::Railtie)

module HairTrigger
POSTGRESQL_ADAPTERS = %i[postgresql postgis]
MYSQL_ADAPTERS = %i[mysql mysql2rgeo trilogy]
SQLITE_ADAPTERS = %i[sqlite litedb]

autoload :Builder, 'hair_trigger/builder'
autoload :MigrationReader, 'hair_trigger/migration_reader'
Expand Down Expand Up @@ -233,14 +236,7 @@ def pg_schema
end

def adapter_name_for(adapter)
adapter_name = adapter.adapter_name.downcase
adapter_name.sub!(/\d$/, '')
case adapter_name
when "litedb"
:sqlite
else
adapter_name.to_sym
end
adapter.adapter_name.downcase.sub(/\d$/, '').to_sym
end
end
end
Expand Down
6 changes: 3 additions & 3 deletions lib/hair_trigger/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ def triggers(options = {})
name_clause = options[:only] ? "IN ('" + options[:only].join("', '") + "')" : nil
adapter_name = HairTrigger.adapter_name_for(self)
case adapter_name
when :sqlite
when *HairTrigger::SQLITE_ADAPTERS
select_rows("SELECT name, sql FROM sqlite_master WHERE type = 'trigger' #{name_clause ? " AND name " + name_clause : ""}").each do |(name, definition)|
triggers[name] = quote_table_name_in_trigger(definition) + ";\n"
end
when :mysql, :trilogy, :mysql2rgeo
when *HairTrigger::MYSQL_ADAPTERS
select_rows("SHOW TRIGGERS").each do |(name, event, table, actions, timing, created, sql_mode, definer)|
definer = normalize_mysql_definer(definer)
next if options[:only] && !options[:only].include?(name)
Expand All @@ -41,7 +41,7 @@ def triggers(options = {})
#{actions}
SQL
end
when :postgresql, :postgis
when *POSTGRESQL_ADAPTERS
function_conditions = "(SELECT typname FROM pg_type WHERE oid = prorettype) = 'trigger'"
function_conditions << <<-SQL unless options[:simple_check]
AND oid IN (
Expand Down
46 changes: 23 additions & 23 deletions lib/hair_trigger/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def drop_triggers
end

def name(name)
@errors << ["trigger name cannot exceed 63 for postgres", :postgresql] if name.to_s.size > 63
@errors << ["trigger name cannot exceed 63 for postgres", *HairTrigger::POSTGRESQL_ADAPTERS] if name.to_s.size > 63
options[:name] = name.to_s
end

Expand All @@ -54,7 +54,7 @@ def on(table)
end

def for_each(for_each)
@errors << ["sqlite and mysql don't support FOR EACH STATEMENT triggers", :sqlite, :mysql] if for_each == :statement
@errors << ["sqlite and mysql don't support FOR EACH STATEMENT triggers", *HairTrigger::SQLITE_ADAPTERS, *HairTrigger::MYSQL_ADAPTERS] if for_each == :statement
raise DeclarationError, "invalid for_each" unless [:row, :statement].include?(for_each)
options[:for_each] = for_each.to_s.upcase
end
Expand Down Expand Up @@ -107,9 +107,9 @@ def security(user)
raise DeclarationError, "trigger security should be :invoker, :definer, CURRENT_USER, or a valid user (e.g. 'user'@'host')"
end
# sqlite default is n/a, mysql default is :definer, postgres default is :invoker
@errors << ["sqlite doesn't support trigger security", :sqlite]
@errors << ["postgresql doesn't support arbitrary users for trigger security", :postgresql] unless [:definer, :invoker].include?(user)
@errors << ["mysql doesn't support invoker trigger security", :mysql] if user == :invoker
@errors << ["sqlite doesn't support trigger security", *HairTrigger::SQLITE_ADAPTERS]
@errors << ["postgresql doesn't support arbitrary users for trigger security", *HairTrigger::POSTGRESQL_ADAPTERS] unless [:definer, :invoker].include?(user)
@errors << ["mysql doesn't support invoker trigger security", *HairTrigger::MYSQL_ADAPTERS] if user == :invoker
options[:security] = user
end

Expand All @@ -122,8 +122,8 @@ def events(*events)
events << :insert if events.delete(:create)
events << :delete if events.delete(:destroy)
raise DeclarationError, "invalid events" unless events & [:insert, :update, :delete, :truncate] == events
@errors << ["sqlite and mysql triggers may not be shared by multiple actions", :mysql, :sqlite] if events.size > 1
@errors << ["sqlite and mysql do not support truncate triggers", :mysql, :sqlite] if events.include?(:truncate)
@errors << ["sqlite and mysql triggers may not be shared by multiple actions", *HairTrigger::MYSQL_ADAPTERS, *HairTrigger::SQLITE_ADAPTERS] if events.size > 1
@errors << ["sqlite and mysql do not support truncate triggers", *HairTrigger::MYSQL_ADAPTERS, *HairTrigger::SQLITE_ADAPTERS] if events.include?(:truncate)
options[:events] = events.map{ |e| e.to_s.upcase }
end

Expand Down Expand Up @@ -152,7 +152,7 @@ def self.chainable_methods(*methods)
def #{method}(*args, &block)
@chained_calls << :#{method}
if @triggers || @trigger_group
@errors << ["mysql doesn't support #{method} within a trigger group", :mysql] unless [:name, :where, :all, :of].include?(:#{method})
@errors << ["mysql doesn't support #{method} within a trigger group", *HairTrigger::MYSQL_ADAPTERS] unless [:name, :where, :all, :of].include?(:#{method})
end
set_#{method}(*args, &(block_given? ? block : nil))
end
Expand All @@ -174,7 +174,7 @@ def set_#{method}(*args, &block)
chainable_methods :name, :on, :for_each, :before, :after, :where, :security, :timing, :events, :all, :nowrap, :of, :declare, :old_as, :new_as

def create_grouped_trigger?
adapter_name == :mysql || adapter_name == :trilogy || adapter_name == :mysql2rgeo
HairTrigger::MYSQL_ADAPTERS.include?(adapter_name)
end

def prepare!
Expand Down Expand Up @@ -234,11 +234,11 @@ def generate(validate = true)

[generate_drop_trigger] +
[case adapter_name
when :sqlite
when *HairTrigger::SQLITE_ADAPTERS
generate_trigger_sqlite
when :mysql, :trilogy, :mysql2rgeo
when *HairTrigger::MYSQL_ADAPTERS
generate_trigger_mysql
when :postgresql, :postgis
when *HairTrigger::POSTGRESQL_ADAPTERS
generate_trigger_postgresql
else
raise GenerationError, "don't know how to build #{adapter_name} triggers yet"
Expand Down Expand Up @@ -345,8 +345,8 @@ def actions_to_ruby(indent = '')
def maybe_execute(&block)
raise DeclarationError, "of may only be specified on update triggers" if options[:of] && options[:events] != ["UPDATE"]
if block.arity > 0 # we're creating a trigger group, so set up some stuff and pass the buck
@errors << ["trigger group must specify timing and event(s) for mysql", :mysql] unless options[:timing] && options[:events]
@errors << ["nested trigger groups are not supported for mysql", :mysql] if @trigger_group
@errors << ["trigger group must specify timing and event(s) for mysql", *HairTrigger::MYSQL_ADAPTERS] unless options[:timing] && options[:events]
@errors << ["nested trigger groups are not supported for mysql", *HairTrigger::MYSQL_ADAPTERS] if @trigger_group
@triggers = []
block.call(self)
raise DeclarationError, "trigger group did not define any triggers" if @triggers.empty?
Expand Down Expand Up @@ -375,9 +375,9 @@ def validate_names!
subtriggers = all_triggers(false)
named_subtriggers = subtriggers.select{ |t| t.options[:name] }
if named_subtriggers.present? && !options[:name]
@warnings << ["nested triggers have explicit names, but trigger group does not. trigger name will be inferred", :mysql]
@warnings << ["nested triggers have explicit names, but trigger group does not. trigger name will be inferred", *HairTrigger::MYSQL_ADAPTERS]
elsif subtriggers.present? && !named_subtriggers.present? && options[:name]
@warnings << ["trigger group has an explicit name, but nested triggers do not. trigger names will be inferred", :postgresql, :sqlite]
@warnings << ["trigger group has an explicit name, but nested triggers do not. trigger names will be inferred", *HairTrigger::POSTGRESQL_ADAPTERS, *HairTrigger::SQLITE_ADAPTERS]
end
end

Expand Down Expand Up @@ -412,9 +412,9 @@ def declarations

def supports_of?
case adapter_name
when :sqlite
when *HairTrigger::SQLITE_ADAPTERS
true
when :postgresql, :postgis
when *HairTrigger::POSTGRESQL_ADAPTERS
db_version >= 90000
else
false
Expand All @@ -429,9 +429,9 @@ def referencing_clause(check_support = true)

def supports_referencing?
case adapter_name
when :sqlite, :mysql
when *HairTrigger::SQLITE_ADAPTERS, *HairTrigger::MYSQL_ADAPTERS
false
when :postgresql, :postgis
when *HairTrigger::POSTGRESQL_ADAPTERS
db_version >= 100000
else
false
Expand All @@ -440,9 +440,9 @@ def supports_referencing?

def generate_drop_trigger
case adapter_name
when :sqlite, :mysql, :trilogy, :mysql2rgeo
when *HairTrigger::SQLITE_ADAPTERS, *HairTrigger::MYSQL_ADAPTERS
"DROP TRIGGER IF EXISTS #{prepared_name};\n"
when :postgresql, :postgis
when *HairTrigger::POSTGRESQL_ADAPTERS
"DROP TRIGGER IF EXISTS #{prepared_name} ON #{adapter.quote_table_name(options[:table])};\nDROP FUNCTION IF EXISTS #{adapter.quote_table_name(prepared_name)}();\n"
else
raise GenerationError, "don't know how to drop #{adapter_name} triggers yet"
Expand Down Expand Up @@ -532,7 +532,7 @@ def generate_trigger_mysql

def db_version
@db_version ||= case adapter_name
when :postgresql, :postgis
when *HairTrigger::POSTGRESQL_ADAPTERS
adapter.send(:postgresql_version)
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/hair_trigger/schema_dumper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def triggers(stream)
def normalize_trigger(name, definition, type)
@adapter_name = @connection.adapter_name.downcase.to_sym

return definition unless @adapter_name == :postgresql || @adapter_name == :postgis
return definition unless HairTrigger::POSTGRESQL_ADAPTERS.include?(@adapter_name)
# because postgres does not preserve the original CREATE TRIGGER/
# FUNCTION statements, its decompiled reconstruction will not match
# ours. we work around it by creating our generated trigger/function,
Expand Down
5 changes: 0 additions & 5 deletions spec/adapter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@
end
end

context "mysql" do
let(:adapter) { :mysql }
it_behaves_like "mysql"
end if ADAPTERS.include? :mysql

context "mysql2" do
let(:adapter) { :mysql2 }
it_behaves_like "mysql"
Expand Down

0 comments on commit bac1cac

Please sign in to comment.