hairtrigger 0.2.12 → 0.2.13

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011 Jon Jensen
1
+ Copyright (c) 2011-2015 Jon Jensen
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -325,4 +325,4 @@ existing trigger if you wish to redefine it.
325
325
 
326
326
  ## Copyright
327
327
 
328
- Copyright (c) 2011-2014 Jon Jensen. See LICENSE.txt for further details.
328
+ Copyright (c) 2011-2015 Jon Jensen. See LICENSE.txt for further details.
data/lib/hair_trigger.rb CHANGED
@@ -157,21 +157,35 @@ end
157
157
  end
158
158
 
159
159
  def infer_migration_name(migration_names, create_triggers, drop_triggers)
160
- migration_base_name = if create_triggers.size > 0
161
- ("create trigger#{create_triggers.size > 1 ? 's' : ''} " +
162
- create_triggers.map{ |t| [t.options[:table], t.options[:events].join(" ")].join(" ") }.join(" and ")
163
- ).downcase.gsub(/[^a-z0-9_]/, '_').gsub(/_+/, '_').camelize
160
+ if create_triggers.size > 0
161
+ migration_base_name = "create trigger#{create_triggers.size > 1 ? 's' : ''} "
162
+ name_parts = create_triggers.map { |t| [t.options[:table], t.options[:events].join(" ")].join(" ") }.uniq
163
+ part_limit = 4
164
164
  else
165
- ("drop trigger#{drop_triggers.size > 1 ? 's' : ''} " +
166
- drop_triggers.map{ |t| t.options[:table] }.join(" and ")
167
- ).downcase.gsub(/[^a-z0-9_]/, '_').gsub(/_+/, '_').camelize
165
+ migration_base_name = "drop trigger#{drop_triggers.size > 1 ? 's' : ''} "
166
+ name_parts = drop_triggers.map { |t| t.options[:table] }
167
+ part_limit = 6
168
168
  end
169
169
 
170
+ # don't let migration names get too ridiculous
171
+ if name_parts.size > part_limit
172
+ migration_base_name << " multiple tables"
173
+ else
174
+ migration_base_name << name_parts.join(" OR ")
175
+ end
176
+
177
+ migration_base_name = migration_base_name.
178
+ downcase.
179
+ gsub(/[^a-z0-9_]/, '_').
180
+ gsub(/_+/, '_').
181
+ camelize
182
+
170
183
  name_version = nil
171
184
  while migration_names.include?("#{migration_base_name}#{name_version}")
172
185
  name_version = name_version.to_i + 1
173
186
  end
174
- migration_name = "#{migration_base_name}#{name_version}"
187
+
188
+ "#{migration_base_name}#{name_version}"
175
189
  end
176
190
 
177
191
  def infer_migration_version(migration_name)
@@ -29,14 +29,14 @@ module HairTrigger
29
29
  case adapter_name
30
30
  when :sqlite
31
31
  select_rows("SELECT name, sql FROM sqlite_master WHERE type = 'trigger' #{name_clause ? " AND name " + name_clause : ""}").each do |(name, definition)|
32
- triggers[name] = definition + ";\n"
32
+ triggers[name] = quote_table_name_in_trigger(definition) + ";\n"
33
33
  end
34
34
  when :mysql
35
35
  select_rows("SHOW TRIGGERS").each do |(name, event, table, actions, timing, created, sql_mode, definer)|
36
36
  definer = normalize_mysql_definer(definer)
37
37
  next if options[:only] && !options[:only].include?(name)
38
38
  triggers[name.strip] = <<-SQL
39
- CREATE #{definer != implicit_mysql_definer ? "DEFINER = #{definer} " : ""}TRIGGER #{name} #{timing} #{event} ON #{table}
39
+ CREATE #{definer != implicit_mysql_definer ? "DEFINER = #{definer} " : ""}TRIGGER #{name} #{timing} #{event} ON `#{table}`
40
40
  FOR EACH ROW
41
41
  #{actions}
42
42
  SQL
@@ -52,7 +52,7 @@ FOR EACH ROW
52
52
  )
53
53
  )
54
54
  SQL
55
- function_conditions =
55
+
56
56
  sql = <<-SQL
57
57
  SELECT tgname::varchar, pg_get_triggerdef(oid, true)
58
58
  FROM pg_trigger
@@ -68,12 +68,31 @@ FOR EACH ROW
68
68
  #{name_clause ? " AND (proname || '()')::varchar " + name_clause : ""}
69
69
  SQL
70
70
  select_rows(sql).each do |(name, definition)|
71
- triggers[name] = definition
71
+ triggers[name] = quote_table_name_in_trigger(definition)
72
72
  end
73
73
  else
74
74
  raise "don't know how to retrieve #{adapter_name} triggers yet"
75
75
  end
76
76
  triggers
77
77
  end
78
+
79
+ # a bit hacky, but we need to ensure the table name is always quoted
80
+ # on the way out, not just for reserved words. this is because we
81
+ # always quote on the way in, so we need them to match exactly
82
+ # when diffing
83
+ def quote_table_name_in_trigger(definition)
84
+ pattern = /
85
+ (
86
+ CREATE\sTRIGGER\s+
87
+ \S+\s+
88
+ (BEFORE|AFTER|INSTEAD\s+OF)\s+
89
+ (INSERT|UPDATE|DELETE|TRUNCATE)\s+
90
+ (OR\s+(INSERT|UPDATE|DELETE|TRUNCATE)\s+)*
91
+ (ON\s+)
92
+ )
93
+ (\w+) # quote if not already quoted
94
+ /ixm
95
+ definition.sub(pattern, '\\1"\\7"')
96
+ end
78
97
  end
79
98
  end
@@ -411,7 +411,7 @@ module HairTrigger
411
411
 
412
412
  def generate_trigger_sqlite
413
413
  <<-SQL
414
- CREATE TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].first} #{of_clause}ON #{options[:table]}
414
+ CREATE TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].first} #{of_clause}ON "#{options[:table]}"
415
415
  FOR EACH #{options[:for_each]}#{prepared_where ? " WHEN " + prepared_where : ''}
416
416
  BEGIN
417
417
  #{normalize(raw_actions, 1).rstrip}
@@ -464,7 +464,7 @@ $$ LANGUAGE plpgsql#{security ? " SECURITY #{security.to_s.upcase}" : ""};
464
464
  end
465
465
 
466
466
  [sql, <<-SQL]
467
- CREATE TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].join(" OR ")} #{of_clause}ON #{options[:table]}
467
+ CREATE TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].join(" OR ")} #{of_clause}ON "#{options[:table]}"
468
468
  FOR EACH #{options[:for_each]}#{prepared_where && db_version >= 90000 ? " WHEN (" + prepared_where + ')': ''} EXECUTE PROCEDURE #{trigger_action};
469
469
  SQL
470
470
  end
@@ -472,7 +472,7 @@ FOR EACH #{options[:for_each]}#{prepared_where && db_version >= 90000 ? " WHEN (
472
472
  def generate_trigger_mysql
473
473
  security = options[:security] if options[:security] && options[:security] != :definer
474
474
  sql = <<-SQL
475
- CREATE #{security ? "DEFINER = #{security} " : ""}TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].first} ON #{options[:table]}
475
+ CREATE #{security ? "DEFINER = #{security} " : ""}TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].first} ON `#{options[:table]}`
476
476
  FOR EACH #{options[:for_each]}
477
477
  BEGIN
478
478
  SQL
@@ -1,5 +1,5 @@
1
1
  module HairTrigger
2
- VERSION = "0.2.12"
2
+ VERSION = "0.2.13"
3
3
 
4
4
  def VERSION.<=>(other)
5
5
  split(/\./).map(&:to_i) <=> other.split(/\./).map(&:to_i)
data/spec/adapter_spec.rb CHANGED
@@ -31,6 +31,18 @@ describe "adapter" do
31
31
 
32
32
  expect(db_triggers).to eq(triggers)
33
33
  end
34
+
35
+ it "quotes table names" do
36
+ conn.execute <<-SQL
37
+ CREATE TRIGGER foos_tr AFTER DELETE ON users
38
+ FOR EACH ROW
39
+ BEGIN
40
+ UPDATE groups SET bob_count = bob_count - 1;
41
+ END
42
+ SQL
43
+
44
+ expect(conn.triggers["foos_tr"]).to match(/CREATE TRIGGER foos_tr AFTER DELETE ON `users`/)
45
+ end
34
46
  end
35
47
 
36
48
  context "mysql" do
@@ -42,6 +54,42 @@ describe "adapter" do
42
54
  let(:adapter) { :mysql2 }
43
55
  it_behaves_like "mysql"
44
56
  end
57
+
58
+ context "postgresql" do
59
+ let(:adapter) { :postgresql }
60
+
61
+ it "quotes table names" do
62
+ conn.execute <<-SQL
63
+ CREATE FUNCTION foos_tr()
64
+ RETURNS TRIGGER AS $$
65
+ BEGIN
66
+ UPDATE groups SET bob_count = bob_count - 1;
67
+ END;
68
+ $$ LANGUAGE plpgsql;
69
+
70
+ CREATE TRIGGER foos_tr AFTER DELETE ON users
71
+ FOR EACH ROW EXECUTE PROCEDURE foos_tr();
72
+ SQL
73
+
74
+ expect(conn.triggers["foos_tr"]).to match(/CREATE TRIGGER foos_tr AFTER DELETE ON "users"/)
75
+ end
76
+ end
77
+
78
+ context "sqlite3" do
79
+ let(:adapter) { :sqlite3 }
80
+
81
+ it "quotes table names" do
82
+ conn.execute <<-SQL
83
+ CREATE TRIGGER foos_tr AFTER DELETE ON users
84
+ FOR EACH ROW
85
+ BEGIN
86
+ UPDATE groups SET bob_count = bob_count - 1;
87
+ END;
88
+ SQL
89
+
90
+ expect(conn.triggers["foos_tr"]).to match(/CREATE TRIGGER foos_tr AFTER DELETE ON "users"/)
91
+ end
92
+ end
45
93
  end
46
94
  end
47
95
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hairtrigger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.12
4
+ version: 0.2.13
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-10-14 00:00:00.000000000 Z
12
+ date: 2015-04-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -114,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
114
114
  version: 1.3.5
115
115
  requirements: []
116
116
  rubyforge_project:
117
- rubygems_version: 1.8.23
117
+ rubygems_version: 1.8.23.2
118
118
  signing_key:
119
119
  specification_version: 3
120
120
  summary: easy database triggers for active record