Skip to content

Commit

Permalink
fix definer comparisons, closes jenseng#29
Browse files Browse the repository at this point in the history
* correctly handle '%' in SHOW TRIGGERS result
* quote user and host so as to match generated triggers
  • Loading branch information
jenseng committed Apr 27, 2014
1 parent 17dacfa commit 5174e6e
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 4 deletions.
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ gemspec

group :test do
gem 'rake'
end
gem 'debugger'
end
2 changes: 1 addition & 1 deletion hairtrigger.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Gem::Specification.new do |s|
s.add_dependency 'ruby_parser', '>= 3.5'
s.add_dependency 'ruby2ruby', '~> 2.0.6'
s.add_development_dependency 'rake'
s.add_development_dependency 'rspec', '~> 2.12.0'
s.add_development_dependency 'rspec', '~> 2.14.0'
s.add_development_dependency 'mysql', '~> 2.9.1'
s.add_development_dependency 'mysql2', '>= 0.3.11'
s.add_development_dependency 'pg', '>= 0.15.1'
Expand Down
13 changes: 12 additions & 1 deletion lib/hair_trigger/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ def drop_trigger(name, table, options = {})
::HairTrigger::Builder.new(name, options.merge(:execute => true, :drop => true, :table => table, :adapter => self)).all{}
end

def normalize_mysql_definer(definer)
user, host = definer.split('@')
host = @config[:host] || 'localhost' if host == '%'
"'#{user}'@'#{host}'" # SHOW TRIGGERS doesn't quote them, but we need quotes for creating a trigger
end

def implicit_mysql_definer
"'#{@config[:username] || 'root'}'@'#{@config[:host] || 'localhost'}'"
end

def triggers(options = {})
triggers = {}
name_clause = options[:only] ? "IN ('" + options[:only].join("', '") + "')" : nil
Expand All @@ -23,9 +33,10 @@ def triggers(options = {})
end
when :mysql
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)
triggers[name.strip] = <<-SQL
CREATE #{definer != "#{@config[:username] || 'root'}@#{@config[:host] || 'localhost'}" ? "DEFINER = #{definer} " : ""}TRIGGER #{name} #{timing} #{event} ON #{table}
CREATE #{definer != implicit_mysql_definer ? "DEFINER = #{definer} " : ""}TRIGGER #{name} #{timing} #{event} ON #{table}
FOR EACH ROW
#{actions}
SQL
Expand Down
47 changes: 47 additions & 0 deletions spec/adapter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
require 'spec_helper'

# for this spec to work, you need to have postgres and mysql installed (in
# addition to the gems), and you should make sure that you have set up
# appropriate users and permissions. see database.yml for more info

describe "adapter" do
include_context "hairtrigger utils"

describe ".triggers" do
before do
reset_tmp(:migration_glob => "*initial_tables*")
initialize_db
migrate_db
end

shared_examples_for "mysql" do
# have to stub SHOW TRIGGERS to get back a '%' host, since GRANTs
# and such get a little dicey for testing (local vs travis, etc.)
it "matches the generated trigger with a '%' grant" do
conn.instance_variable_get(:@config)[:host] = "somehost" # wheeeee!
implicit_definer = "'root'@'somehost'"
show_triggers_definer = "root@%"

builder = trigger.on(:users).before(:insert){ "UPDATE foos SET bar = 1" }
triggers = builder.generate.select{|t|t !~ /\ADROP/}
expect(conn).to receive(:implicit_mysql_definer).and_return(implicit_definer)
expect(conn).to receive(:select_rows).with("SHOW TRIGGERS").and_return([
['users_before_insert_row_tr', 'INSERT', 'users', "BEGIN\n UPDATE foos SET bar = 1;\nEND", 'BEFORE', 'NULL', 'STRICT_ALL_TABLES', show_triggers_definer]
])

expect(db_triggers).to eq(triggers)
end
end

context "mysql" do
let(:adapter) { :mysql }
it_behaves_like "mysql"
end

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

10 changes: 9 additions & 1 deletion spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,16 @@ def dump_schema
io.read
end

def trigger(*args)
HairTrigger::Builder.new(*args)
end

def conn
ActiveRecord::Base.connection
end

def db_triggers
ActiveRecord::Base.connection.triggers.values
conn.triggers.values
end

def replace_file_contents(path, source, replacement)
Expand Down

0 comments on commit 5174e6e

Please sign in to comment.