pg_sql_triggers 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.erb_lint.yml +0 -0
  3. data/.rspec +0 -0
  4. data/.rubocop.yml +6 -16
  5. data/AGENTS.md +8 -0
  6. data/CHANGELOG.md +354 -0
  7. data/COVERAGE.md +39 -41
  8. data/LICENSE +0 -0
  9. data/README.md +44 -26
  10. data/RELEASE.md +0 -0
  11. data/Rakefile +5 -0
  12. data/app/assets/javascripts/pg_sql_triggers/application.js +0 -0
  13. data/app/assets/javascripts/pg_sql_triggers/trigger_actions.js +0 -0
  14. data/app/assets/stylesheets/pg_sql_triggers/application.css +0 -0
  15. data/app/controllers/concerns/pg_sql_triggers/error_handling.rb +0 -0
  16. data/app/controllers/concerns/pg_sql_triggers/kill_switch_protection.rb +0 -0
  17. data/app/controllers/concerns/pg_sql_triggers/permission_checking.rb +6 -5
  18. data/app/controllers/pg_sql_triggers/application_controller.rb +0 -0
  19. data/app/controllers/pg_sql_triggers/audit_logs_controller.rb +81 -64
  20. data/app/controllers/pg_sql_triggers/dashboard_controller.rb +111 -34
  21. data/app/controllers/pg_sql_triggers/migrations_controller.rb +13 -14
  22. data/app/controllers/pg_sql_triggers/tables_controller.rb +8 -0
  23. data/app/controllers/pg_sql_triggers/triggers_controller.rb +1 -0
  24. data/app/helpers/pg_sql_triggers/dashboard_helper.rb +19 -0
  25. data/app/helpers/pg_sql_triggers/permissions_helper.rb +3 -2
  26. data/app/models/pg_sql_triggers/application_record.rb +0 -0
  27. data/app/models/pg_sql_triggers/audit_log.rb +29 -47
  28. data/app/models/pg_sql_triggers/trigger_registry.rb +137 -74
  29. data/app/views/layouts/pg_sql_triggers/application.html.erb +0 -1
  30. data/app/views/pg_sql_triggers/audit_logs/index.html.erb +9 -5
  31. data/app/views/pg_sql_triggers/dashboard/index.html.erb +107 -27
  32. data/app/views/pg_sql_triggers/shared/_confirmation_modal.html.erb +0 -0
  33. data/app/views/pg_sql_triggers/shared/_kill_switch_status.html.erb +0 -0
  34. data/app/views/pg_sql_triggers/tables/index.html.erb +27 -18
  35. data/app/views/pg_sql_triggers/tables/show.html.erb +0 -2
  36. data/app/views/pg_sql_triggers/triggers/_drop_modal.html.erb +0 -0
  37. data/app/views/pg_sql_triggers/triggers/_re_execute_modal.html.erb +0 -0
  38. data/app/views/pg_sql_triggers/triggers/show.html.erb +33 -0
  39. data/config/initializers/pg_sql_triggers.rb +0 -0
  40. data/config/routes.rb +0 -14
  41. data/db/migrate/{20251222000001_create_pg_sql_triggers_tables.rb → 20251222104327_create_pg_sql_triggers_tables.rb} +0 -0
  42. data/db/migrate/20251229071916_add_timing_to_pg_sql_triggers_registry.rb +0 -0
  43. data/db/migrate/{20260103000001_create_pg_sql_triggers_audit_log.rb → 20260103114508_create_pg_sql_triggers_audit_log.rb} +0 -0
  44. data/db/migrate/20260228162233_add_for_each_to_pg_sql_triggers_registry.rb +8 -0
  45. data/db/migrate/20260412185841_add_constraint_deferral_to_pg_sql_triggers_registry.rb +9 -0
  46. data/docs/README.md +3 -0
  47. data/docs/api-reference.md +176 -152
  48. data/docs/audit-trail.md +1 -1
  49. data/docs/configuration.md +196 -3
  50. data/docs/getting-started.md +31 -16
  51. data/docs/kill-switch.md +0 -0
  52. data/docs/permissions.md +6 -9
  53. data/docs/troubleshooting.md +0 -0
  54. data/docs/ui-guide.md +0 -0
  55. data/docs/usage-guide.md +112 -67
  56. data/docs/web-ui.md +3 -103
  57. data/lib/generators/pg_sql_triggers/install_generator.rb +0 -0
  58. data/lib/generators/pg_sql_triggers/templates/README +0 -0
  59. data/lib/generators/pg_sql_triggers/templates/create_pg_sql_triggers_tables.rb +0 -0
  60. data/lib/generators/pg_sql_triggers/templates/initializer.rb +14 -0
  61. data/lib/generators/pg_sql_triggers/templates/trigger_dsl.rb.tt +11 -0
  62. data/lib/generators/pg_sql_triggers/templates/trigger_migration.rb.erb +0 -0
  63. data/lib/generators/pg_sql_triggers/templates/trigger_migration_full.rb.tt +29 -0
  64. data/lib/generators/pg_sql_triggers/trigger_generator.rb +83 -0
  65. data/lib/generators/pg_sql_triggers/trigger_migration_generator.rb +0 -0
  66. data/lib/pg_sql_triggers/alerting.rb +77 -0
  67. data/lib/pg_sql_triggers/database_introspection.rb +0 -0
  68. data/lib/pg_sql_triggers/deferral_checksum.rb +54 -0
  69. data/lib/pg_sql_triggers/drift/db_queries.rb +26 -13
  70. data/lib/pg_sql_triggers/drift/detector.rb +59 -38
  71. data/lib/pg_sql_triggers/drift/reporter.rb +0 -0
  72. data/lib/pg_sql_triggers/drift.rb +5 -0
  73. data/lib/pg_sql_triggers/dsl/trigger_definition.rb +68 -20
  74. data/lib/pg_sql_triggers/dsl.rb +0 -0
  75. data/lib/pg_sql_triggers/engine.rb +49 -0
  76. data/lib/pg_sql_triggers/errors.rb +0 -0
  77. data/lib/pg_sql_triggers/events_checksum.rb +114 -0
  78. data/lib/pg_sql_triggers/migration.rb +5 -6
  79. data/lib/pg_sql_triggers/migrator/pre_apply_comparator.rb +85 -82
  80. data/lib/pg_sql_triggers/migrator/pre_apply_diff_reporter.rb +0 -0
  81. data/lib/pg_sql_triggers/migrator/safety_validator.rb +34 -12
  82. data/lib/pg_sql_triggers/migrator.rb +137 -94
  83. data/lib/pg_sql_triggers/permissions/checker.rb +12 -15
  84. data/lib/pg_sql_triggers/permissions.rb +1 -0
  85. data/lib/pg_sql_triggers/rake_development_boot.rb +65 -0
  86. data/lib/pg_sql_triggers/registry/manager.rb +60 -21
  87. data/lib/pg_sql_triggers/registry/validator.rb +287 -6
  88. data/lib/pg_sql_triggers/registry.rb +0 -0
  89. data/lib/pg_sql_triggers/schema_dumper_extension.rb +32 -0
  90. data/lib/pg_sql_triggers/sql/kill_switch.rb +154 -275
  91. data/lib/pg_sql_triggers/sql.rb +0 -6
  92. data/lib/pg_sql_triggers/testing/dry_run.rb +0 -0
  93. data/lib/pg_sql_triggers/testing/function_tester.rb +97 -107
  94. data/lib/pg_sql_triggers/testing/safe_executor.rb +0 -0
  95. data/lib/pg_sql_triggers/testing/syntax_validator.rb +0 -0
  96. data/lib/pg_sql_triggers/testing.rb +0 -0
  97. data/lib/pg_sql_triggers/trigger_structure_dumper.rb +111 -0
  98. data/lib/pg_sql_triggers/version.rb +1 -1
  99. data/lib/pg_sql_triggers.rb +21 -1
  100. data/lib/tasks/trigger_migrations.rake +235 -152
  101. data/rakelib/pg_sql_triggers_environment.rake +9 -0
  102. data/scripts/generate_coverage_report.rb +4 -1
  103. data/sig/pg_sql_triggers.rbs +0 -0
  104. metadata +68 -22
  105. data/Goal.md +0 -742
  106. data/app/controllers/pg_sql_triggers/generator_controller.rb +0 -213
  107. data/app/controllers/pg_sql_triggers/sql_capsules_controller.rb +0 -161
  108. data/app/views/pg_sql_triggers/generator/new.html.erb +0 -388
  109. data/app/views/pg_sql_triggers/generator/preview.html.erb +0 -305
  110. data/app/views/pg_sql_triggers/sql_capsules/new.html.erb +0 -81
  111. data/app/views/pg_sql_triggers/sql_capsules/show.html.erb +0 -85
  112. data/lib/generators/trigger/migration_generator.rb +0 -60
  113. data/lib/pg_sql_triggers/generator/form.rb +0 -80
  114. data/lib/pg_sql_triggers/generator/service.rb +0 -339
  115. data/lib/pg_sql_triggers/generator.rb +0 -8
  116. data/lib/pg_sql_triggers/sql/capsule.rb +0 -79
  117. data/lib/pg_sql_triggers/sql/executor.rb +0 -200
@@ -7,122 +7,60 @@ module PgSqlTriggers
7
7
  @trigger = trigger_registry
8
8
  end
9
9
 
10
- # Test ONLY the function, not the trigger
11
- # rubocop:disable Lint/UnusedMethodArgument
10
+ FUNCTION_NAME_PATTERN = /CREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/i
11
+
12
+ # Test ONLY the function, not the trigger.
13
+ #
14
+ # +test_context+ is accepted for API compatibility with future invocation logic.
15
+ # It is normalised to an empty hash when +nil+ so callers can pass either.
12
16
  def test_function_only(test_context: {})
17
+ test_context ||= {}
13
18
  results = {
14
19
  function_created: false,
15
20
  function_executed: false,
16
21
  errors: [],
17
- output: []
22
+ output: [],
23
+ context: test_context
18
24
  }
19
25
 
20
- # Check if function_body is present
21
- if @trigger.function_body.blank?
22
- results[:success] = false
23
- results[:errors] << "Function body is missing"
24
- return results
26
+ return fail_result(results, "Function body is missing") if @trigger.function_body.blank?
27
+ unless extract_function_name_from_body
28
+ return fail_result(results, "Function body does not contain a valid CREATE FUNCTION statement")
25
29
  end
26
30
 
27
- # Extract function name to verify it matches
28
- function_name_from_body = nil
29
- if @trigger.function_body.present?
30
- pattern = /CREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/i
31
- match = @trigger.function_body.match(pattern)
32
- function_name_from_body = match[1] if match
33
- end
31
+ run_function_test_transaction(results)
32
+ results[:output] << "\n⚠ Function rolled back (test mode)"
33
+ results
34
+ end
34
35
 
35
- # If function_body doesn't contain a valid function definition, fail early
36
- unless function_name_from_body
37
- results[:success] = false
38
- results[:errors] << "Function body does not contain a valid CREATE FUNCTION statement"
39
- return results
40
- end
36
+ private
37
+
38
+ def fail_result(results, error_message)
39
+ results[:success] = false
40
+ results[:errors] << error_message
41
+ results
42
+ end
43
+
44
+ def extract_function_name_from_body
45
+ return nil if @trigger.function_body.blank?
46
+
47
+ match = @trigger.function_body.match(FUNCTION_NAME_PATTERN)
48
+ match && match[1]
49
+ end
50
+
51
+ def extract_function_name_from_definition
52
+ return nil if @trigger.definition.blank?
53
+
54
+ definition = JSON.parse(@trigger.definition)
55
+ definition["function_name"] || definition["name"]
56
+ rescue StandardError
57
+ nil
58
+ end
41
59
 
42
- # rubocop:disable Metrics/BlockLength
60
+ def run_function_test_transaction(results)
43
61
  ActiveRecord::Base.transaction do
44
- # Create function in transaction
45
- begin
46
- ActiveRecord::Base.connection.execute(@trigger.function_body)
47
- results[:function_created] = true
48
- results[:output] << "✓ Function created in test transaction"
49
- rescue ActiveRecord::StatementInvalid, StandardError => e
50
- results[:success] = false
51
- results[:errors] << "Error during function creation: #{e.message}"
52
- # Don't raise here, let it fall through to ensure block for rollback
53
- end
54
-
55
- # Try to invoke function directly (if test context provided)
56
- # Note: Empty hash {} is not "present" in Rails, so check if it's not nil
57
- if results[:function_created]
58
- # This would require custom invocation logic
59
- # For now, just verify it was created - if function was successfully created,
60
- # we can assume it exists and is executable within the transaction
61
- function_name = nil
62
-
63
- # First, try to extract from function_body (most reliable)
64
- if @trigger.function_body.present?
65
- # Extract function name from CREATE FUNCTION statement
66
- # Match: CREATE [OR REPLACE] FUNCTION function_name(...)
67
- pattern = /CREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/i
68
- match = @trigger.function_body.match(pattern)
69
- function_name = match[1] if match
70
- end
71
-
72
- # Fallback to definition JSON if function_body extraction failed
73
- if function_name.blank? && @trigger.definition.present?
74
- definition = begin
75
- JSON.parse(@trigger.definition)
76
- rescue StandardError
77
- {}
78
- end
79
- function_name = definition["function_name"] || definition[:function_name] ||
80
- definition["name"] || definition[:name]
81
- end
82
-
83
- # Verify function exists in database by checking pg_proc
84
- # Try to verify via query if function_name is available
85
- if function_name.present?
86
- begin
87
- sanitized_name = begin
88
- ActiveRecord::Base.connection.quote_string(function_name)
89
- rescue StandardError => e
90
- # If quote_string fails, use the function name as-is (less safe but allows test to continue)
91
- results[:errors] << "Error during function name sanitization: #{e.message}"
92
- function_name
93
- end
94
- check_sql = <<~SQL.squish
95
- SELECT COUNT(*) as count
96
- FROM pg_proc p
97
- JOIN pg_namespace n ON p.pronamespace = n.oid
98
- WHERE p.proname = '#{sanitized_name}'
99
- AND n.nspname = 'public'
100
- SQL
101
-
102
- result = ActiveRecord::Base.connection.execute(check_sql).first
103
- results[:function_executed] = result && result["count"].to_i.positive?
104
- results[:output] << if results[:function_executed]
105
- "✓ Function exists and is callable"
106
- else
107
- "✓ Function created (verified via successful creation)"
108
- end
109
- rescue ActiveRecord::StatementInvalid, StandardError => e
110
- results[:function_executed] = false
111
- results[:success] = false
112
- results[:errors] << "Error during function verification: #{e.message}"
113
- # Also add the original error message to ensure it's searchable in tests
114
- results[:errors] << e.message unless results[:errors].include?(e.message)
115
- results[:output] << "✓ Function created (verification failed)"
116
- end
117
- else
118
- # If we can't extract function name, assume it was created successfully
119
- # since function_created is true
120
- results[:function_executed] = true
121
- results[:output] << "✓ Function created (execution verified via successful creation)"
122
- end
123
- end
124
-
125
- # Set success to true only if no errors occurred and function was created
62
+ create_function_in_transaction(results)
63
+ verify_function_in_transaction(results) if results[:function_created]
126
64
  results[:success] = results[:errors].empty? && results[:function_created]
127
65
  rescue ActiveRecord::StatementInvalid, StandardError => e
128
66
  results[:success] = false
@@ -130,11 +68,63 @@ module PgSqlTriggers
130
68
  ensure
131
69
  raise ActiveRecord::Rollback
132
70
  end
71
+ end
133
72
 
134
- results[:output] << "\n⚠ Function rolled back (test mode)"
135
- results
73
+ def create_function_in_transaction(results)
74
+ ActiveRecord::Base.connection.execute(@trigger.function_body)
75
+ results[:function_created] = true
76
+ results[:output] << "✓ Function created in test transaction"
77
+ rescue ActiveRecord::StatementInvalid, StandardError => e
78
+ results[:success] = false
79
+ results[:errors] << "Error during function creation: #{e.message}"
80
+ end
81
+
82
+ def verify_function_in_transaction(results)
83
+ function_name = extract_function_name_from_body || extract_function_name_from_definition
84
+
85
+ if function_name.blank?
86
+ results[:function_executed] = true
87
+ results[:output] << "✓ Function created (execution verified via successful creation)"
88
+ return
89
+ end
90
+
91
+ verify_function_in_pg_proc(function_name, results)
136
92
  end
137
- # rubocop:enable Lint/UnusedMethodArgument, Metrics/BlockLength
93
+
94
+ def verify_function_in_pg_proc(function_name, results)
95
+ sanitized_name = safe_quote_function_name(function_name, results)
96
+ check_sql = <<~SQL.squish
97
+ SELECT COUNT(*) as count
98
+ FROM pg_proc p
99
+ JOIN pg_namespace n ON p.pronamespace = n.oid
100
+ WHERE p.proname = '#{sanitized_name}'
101
+ AND n.nspname = 'public'
102
+ SQL
103
+
104
+ result = ActiveRecord::Base.connection.execute(check_sql).first
105
+ results[:function_executed] = result && result["count"].to_i.positive?
106
+ results[:output] << if results[:function_executed]
107
+ "✓ Function exists and is callable"
108
+ else
109
+ "✓ Function created (verified via successful creation)"
110
+ end
111
+ rescue ActiveRecord::StatementInvalid, StandardError => e
112
+ results[:function_executed] = false
113
+ results[:success] = false
114
+ results[:errors] << "Error during function verification: #{e.message}"
115
+ results[:errors] << e.message unless results[:errors].include?(e.message)
116
+ results[:output] << "✓ Function created (verification failed)"
117
+ end
118
+
119
+ def safe_quote_function_name(function_name, results)
120
+ ActiveRecord::Base.connection.quote_string(function_name)
121
+ rescue StandardError => e
122
+ # If quote_string fails, use the function name as-is (less safe but allows test to continue)
123
+ results[:errors] << "Error during function name sanitization: #{e.message}"
124
+ function_name
125
+ end
126
+
127
+ public
138
128
 
139
129
  # Check if function already exists in database
140
130
  def function_exists?
File without changes
File without changes
File without changes
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "time"
5
+
6
+ module PgSqlTriggers
7
+ # Builds a SQL snapshot of PostgreSQL triggers for db/trigger_structure.sql and
8
+ # emits schema.rb annotations so teams know triggers are outside schema.rb.
9
+ class TriggerStructureDumper
10
+ class << self
11
+ def resolve_path(override = nil)
12
+ base = override || PgSqlTriggers.trigger_structure_sql_path
13
+ resolved = base.respond_to?(:call) ? base.call : base
14
+ resolved ||= default_path
15
+ Pathname(resolved.to_s)
16
+ end
17
+
18
+ def default_path
19
+ raise "Rails.root is required" unless defined?(Rails) && Rails.respond_to?(:root) && Rails.root
20
+
21
+ Rails.root.join("db/trigger_structure.sql")
22
+ end
23
+
24
+ def dump_to(path = nil, connection: ActiveRecord::Base.connection)
25
+ target = resolve_path(path)
26
+ target.dirname.mkpath
27
+ target.write(generate_sql(connection: connection))
28
+ target
29
+ end
30
+
31
+ def load_from(path = nil, connection: ActiveRecord::Base.connection)
32
+ target = resolve_path(path)
33
+ raise Errno::ENOENT, target.to_s unless target.file?
34
+
35
+ sql = target.read
36
+ return if sql.strip.empty?
37
+
38
+ connection.raw_connection.exec(sql)
39
+ nil
40
+ end
41
+
42
+ def generate_sql(connection: ActiveRecord::Base.connection)
43
+ header = <<~SQL
44
+ -- pg_sql_triggers trigger_structure.sql
45
+ -- Generated at: #{Time.now.utc.iso8601}
46
+ --
47
+ -- Apply with: bin/rails trigger:load
48
+ -- Prefer checking this file into version control alongside db/triggers migrations.
49
+ SQL
50
+
51
+ rows = trigger_rows(connection)
52
+ parts = [header]
53
+
54
+ rows.each do |row|
55
+ trigger_name = row["trigger_name"] || row[:trigger_name]
56
+ parts << ""
57
+ parts << "-- Trigger: #{trigger_name}"
58
+ append_definition(parts, row["function_definition"] || row[:function_definition])
59
+ append_definition(parts, row["trigger_definition"] || row[:trigger_definition])
60
+ end
61
+
62
+ parts.join("\n").strip.concat("\n")
63
+ end
64
+
65
+ def schema_rb_annotation(connection: ActiveRecord::Base.connection)
66
+ names = managed_trigger_names(connection)
67
+ lines = []
68
+ lines << " # ---------------------------------------------------------------------------"
69
+ lines << " # pg_sql_triggers: PostgreSQL triggers are not captured in schema.rb."
70
+ lines << " # After db:schema:load, run: bin/rails trigger:migrate (or trigger:load)."
71
+ lines << " # SQL snapshot: db/trigger_structure.sql (bin/rails trigger:dump)."
72
+ lines << " # For full fidelity use config.active_record.schema_format = :sql."
73
+ lines << if names.any?
74
+ " # Managed triggers (#{names.length}): #{names.join(', ')}"
75
+ else
76
+ " # Managed triggers: (none registered in pg_sql_triggers_registry)"
77
+ end
78
+ lines << " # ---------------------------------------------------------------------------"
79
+ lines.join("\n")
80
+ end
81
+
82
+ def managed_trigger_names(connection)
83
+ return [] unless connection.table_exists?("pg_sql_triggers_registry")
84
+
85
+ connection.select_values(
86
+ "SELECT trigger_name FROM pg_sql_triggers_registry ORDER BY trigger_name"
87
+ )
88
+ end
89
+
90
+ private
91
+
92
+ def trigger_rows(connection)
93
+ if connection.table_exists?("pg_sql_triggers_registry")
94
+ managed_trigger_names(connection).filter_map do |name|
95
+ PgSqlTriggers::Drift::DbQueries.find_trigger(name)
96
+ end
97
+ else
98
+ PgSqlTriggers::Drift::DbQueries.all_triggers
99
+ end
100
+ end
101
+
102
+ def append_definition(parts, definition)
103
+ stmt = definition.to_s.strip
104
+ return if stmt.empty?
105
+
106
+ stmt += ";" unless stmt.end_with?(";")
107
+ parts << stmt
108
+ end
109
+ end
110
+ end
111
+ end
@@ -11,5 +11,5 @@
11
11
  # 3. Run: bundle exec rake release
12
12
  # See RELEASE.md for detailed release instructions
13
13
  module PgSqlTriggers
14
- VERSION = "1.3.0"
14
+ VERSION = "1.5.0"
15
15
  end
@@ -36,6 +36,24 @@ module PgSqlTriggers
36
36
  mattr_accessor :allow_unsafe_migrations
37
37
  self.allow_unsafe_migrations = false
38
38
 
39
+ # Path or callable returning path for trigger SQL snapshot (default: db/trigger_structure.sql).
40
+ mattr_accessor :trigger_structure_sql_path
41
+
42
+ mattr_accessor :append_trigger_notes_to_schema_dump
43
+ self.append_trigger_notes_to_schema_dump = true
44
+
45
+ mattr_accessor :migrate_triggers_after_schema_load
46
+ self.migrate_triggers_after_schema_load = true
47
+
48
+ # PostgreSQL schema used by DbQueries. Override for non-public schemas.
49
+ mattr_accessor :db_schema
50
+ self.db_schema = "public"
51
+
52
+ # Callable invoked when drift check finds drifted, dropped, or unknown triggers (+nil+ = no-op).
53
+ # See {PgSqlTriggers::Alerting.check_and_notify} and +trigger:check_drift+.
54
+ mattr_accessor :drift_notifier
55
+ self.drift_notifier = nil
56
+
39
57
  # Drift states
40
58
  DRIFT_STATE_IN_SYNC = "in_sync"
41
59
  DRIFT_STATE_DRIFTED = "drifted"
@@ -55,8 +73,10 @@ module PgSqlTriggers
55
73
  autoload :Permissions, "pg_sql_triggers/permissions"
56
74
  autoload :SQL, "pg_sql_triggers/sql"
57
75
  autoload :DatabaseIntrospection, "pg_sql_triggers/database_introspection"
58
- autoload :Generator, "pg_sql_triggers/generator"
59
76
  autoload :Testing, "pg_sql_triggers/testing"
60
77
  autoload :Migration, "pg_sql_triggers/migration"
61
78
  autoload :Migrator, "pg_sql_triggers/migrator"
79
+ autoload :DeferralChecksum, "pg_sql_triggers/deferral_checksum"
80
+ autoload :EventsChecksum, "pg_sql_triggers/events_checksum"
81
+ autoload :Alerting, "pg_sql_triggers/alerting"
62
82
  end