hairtrigger 0.2.8 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -83,6 +83,11 @@ Required (but may be satisified by `before`/`after`). Possible values are `:befo
83
83
  #### events(*events)
84
84
  Required (but may be satisified by `before`/`after`). Possible values are `:insert`/`:update`/`:delete`/`:truncate`. MySQL/SQLite only support one action per trigger, and don't support `:truncate`.
85
85
 
86
+ #### nowrap(flag = true)
87
+ PostgreSQL specific option to prevent the trigger action from being wrapped in a `CREATE FUNCTION`. This is useful for executing existing triggers/functions directly, but is not compatible with the `security` setting nor can it be used with pre-9.0 PostgreSQL when supplying a `where` condition.
88
+
89
+ Example: `trigger.after(:update).nowrap { "tsvector_update_trigger(...)" }`
90
+
86
91
  #### all
87
92
  Noop, useful for trigger groups (see below).
88
93
 
@@ -4,7 +4,7 @@ module HairTrigger
4
4
  class Builder
5
5
  class DeclarationError < StandardError; end
6
6
  class GenerationError < StandardError; end
7
-
7
+
8
8
  attr_accessor :options
9
9
  attr_reader :triggers # nil unless this is a trigger group
10
10
  attr_reader :prepared_actions, :prepared_where # after delayed interpolation
@@ -73,6 +73,10 @@ module HairTrigger
73
73
  options[:where] = where
74
74
  end
75
75
 
76
+ def nowrap(flag = true)
77
+ options[:nowrap] = flag
78
+ end
79
+
76
80
  # noop, just a way you can pass a block within a trigger group
77
81
  def all
78
82
  end
@@ -146,7 +150,7 @@ module HairTrigger
146
150
  METHOD
147
151
  end
148
152
  end
149
- chainable_methods :name, :on, :for_each, :before, :after, :where, :security, :timing, :events, :all
153
+ chainable_methods :name, :on, :for_each, :before, :after, :where, :security, :timing, :events, :all, :nowrap
150
154
 
151
155
  def create_grouped_trigger?
152
156
  adapter_name == :mysql
@@ -366,36 +370,48 @@ END;
366
370
  def generate_trigger_postgresql
367
371
  raise GenerationError, "truncate triggers are only supported on postgres 8.4 and greater" if db_version < 80400 && options[:events].include?('TRUNCATE')
368
372
  raise GenerationError, "FOR EACH ROW triggers may not be triggered by truncate events" if options[:for_each] == 'ROW' && options[:events].include?('TRUNCATE')
369
- security = options[:security] if options[:security] && options[:security] != :invoker
370
- sql = <<-SQL
373
+ raise GenerationError, "security cannot be used in conjunction with nowrap" if options[:nowrap] && options[:security]
374
+ raise GenerationError, "where can only be used in conjunction with nowrap on postgres 9.0 and greater" if options[:nowrap] && prepared_where && db_version < 90000
375
+
376
+ sql = ''
377
+
378
+ if options[:nowrap]
379
+ trigger_action = raw_actions
380
+ else
381
+ security = options[:security] if options[:security] && options[:security] != :invoker
382
+ sql << <<-SQL
371
383
  CREATE FUNCTION #{prepared_name}()
372
384
  RETURNS TRIGGER AS $$
373
385
  BEGIN
374
- SQL
375
- if prepared_where && db_version < 90000
376
- sql << normalize("IF #{prepared_where} THEN", 1)
377
- sql << normalize(raw_actions, 2)
378
- sql << normalize("END IF;", 1)
379
- else
380
- sql << normalize(raw_actions, 1)
381
- end
382
- # if no return is specified at the end, be sure we set a sane one
383
- unless raw_actions =~ /return [^;]+;\s*\z/i
384
- if options[:timing] == "AFTER" || options[:for_each] == 'STATEMENT'
385
- sql << normalize("RETURN NULL;", 1)
386
- elsif options[:events].include?('DELETE')
387
- sql << normalize("RETURN OLD;", 1)
386
+ SQL
387
+ if prepared_where && db_version < 90000
388
+ sql << normalize("IF #{prepared_where} THEN", 1)
389
+ sql << normalize(raw_actions, 2)
390
+ sql << normalize("END IF;", 1)
388
391
  else
389
- sql << normalize("RETURN NEW;", 1)
392
+ sql << normalize(raw_actions, 1)
390
393
  end
391
- end
392
- sql << <<-SQL
394
+ # if no return is specified at the end, be sure we set a sane one
395
+ unless raw_actions =~ /return [^;]+;\s*\z/i
396
+ if options[:timing] == "AFTER" || options[:for_each] == 'STATEMENT'
397
+ sql << normalize("RETURN NULL;", 1)
398
+ elsif options[:events].include?('DELETE')
399
+ sql << normalize("RETURN OLD;", 1)
400
+ else
401
+ sql << normalize("RETURN NEW;", 1)
402
+ end
403
+ end
404
+ sql << <<-SQL
393
405
  END;
394
406
  $$ LANGUAGE plpgsql#{security ? " SECURITY #{security.to_s.upcase}" : ""};
395
- SQL
407
+ SQL
408
+
409
+ trigger_action = "#{prepared_name}()"
410
+ end
411
+
396
412
  [sql, <<-SQL]
397
413
  CREATE TRIGGER #{prepared_name} #{options[:timing]} #{options[:events].join(" OR ")} ON #{options[:table]}
398
- FOR EACH #{options[:for_each]}#{prepared_where && db_version >= 90000 ? " WHEN (" + prepared_where + ')': ''} EXECUTE PROCEDURE #{prepared_name}();
414
+ FOR EACH #{options[:for_each]}#{prepared_where && db_version >= 90000 ? " WHEN (" + prepared_where + ')': ''} EXECUTE PROCEDURE #{trigger_action};
399
415
  SQL
400
416
  end
401
417
 
@@ -1,5 +1,5 @@
1
1
  module HairTrigger
2
- VERSION = "0.2.8"
2
+ VERSION = "0.2.9"
3
3
 
4
4
  def VERSION.<=>(other)
5
5
  split(/\./).map(&:to_i) <=> other.split(/\./).map(&:to_i)
data/spec/builder_spec.rb CHANGED
@@ -100,7 +100,7 @@ describe "builder" do
100
100
  }.should raise_error
101
101
  end
102
102
  end
103
-
103
+
104
104
  context "mysql" do
105
105
  before(:each) do
106
106
  @adapter = MockAdapter.new("mysql")
@@ -242,6 +242,17 @@ describe "builder" do
242
242
  grep(/RETURN NULL;/).size.should eql(1)
243
243
  end
244
244
 
245
+ it "should not wrap the action in a function" do
246
+ builder.on(:foos).after(:update).nowrap{ 'existing_procedure()' }.generate.
247
+ grep(/CREATE FUNCTION/).size.should eql(0)
248
+ end
249
+
250
+ it "should reject combined use of security and nowrap" do
251
+ lambda {
252
+ builder.on(:foos).after(:update).security("'user'@'host'").nowrap{ "FOO" }.generate
253
+ }.should raise_error
254
+ end
255
+
245
256
  context "legacy" do
246
257
  it "should reject truncate pre-8.4" do
247
258
  @adapter = MockAdapter.new("postgresql", :postgresql_version => 80300)
@@ -255,6 +266,13 @@ describe "builder" do
255
266
  builder.on(:foos).after(:insert).where("BAR"){ "FOO" }.generate.
256
267
  grep(/IF BAR/).size.should eql(1)
257
268
  end
269
+
270
+ it "should reject combined use of where and nowrap pre-9.0" do
271
+ @adapter = MockAdapter.new("postgresql", :postgresql_version => 80400)
272
+ lambda {
273
+ builder.on(:foos).after(:insert).where("BAR").nowrap{ "FOO" }.generate
274
+ }.should raise_error
275
+ end
258
276
  end
259
277
  end
260
278
 
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.8
4
+ version: 0.2.9
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-04-27 00:00:00.000000000 Z
12
+ date: 2014-05-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord