pg_sql_triggers 1.4.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.
- checksums.yaml +4 -4
- data/.erb_lint.yml +0 -0
- data/.rspec +0 -0
- data/.rubocop.yml +6 -16
- data/AGENTS.md +8 -0
- data/CHANGELOG.md +104 -2
- data/COVERAGE.md +39 -41
- data/LICENSE +0 -0
- data/README.md +24 -3
- data/RELEASE.md +0 -0
- data/Rakefile +5 -0
- data/app/assets/javascripts/pg_sql_triggers/application.js +0 -0
- data/app/assets/javascripts/pg_sql_triggers/trigger_actions.js +0 -0
- data/app/assets/stylesheets/pg_sql_triggers/application.css +0 -0
- data/app/controllers/concerns/pg_sql_triggers/error_handling.rb +0 -0
- data/app/controllers/concerns/pg_sql_triggers/kill_switch_protection.rb +0 -0
- data/app/controllers/concerns/pg_sql_triggers/permission_checking.rb +6 -5
- data/app/controllers/pg_sql_triggers/application_controller.rb +0 -0
- data/app/controllers/pg_sql_triggers/audit_logs_controller.rb +81 -64
- data/app/controllers/pg_sql_triggers/dashboard_controller.rb +111 -34
- data/app/controllers/pg_sql_triggers/migrations_controller.rb +13 -14
- data/app/controllers/pg_sql_triggers/tables_controller.rb +8 -0
- data/app/controllers/pg_sql_triggers/triggers_controller.rb +1 -0
- data/app/helpers/pg_sql_triggers/dashboard_helper.rb +19 -0
- data/app/helpers/pg_sql_triggers/permissions_helper.rb +3 -2
- data/app/models/pg_sql_triggers/application_record.rb +0 -0
- data/app/models/pg_sql_triggers/audit_log.rb +29 -47
- data/app/models/pg_sql_triggers/trigger_registry.rb +105 -78
- data/app/views/layouts/pg_sql_triggers/application.html.erb +0 -0
- data/app/views/pg_sql_triggers/audit_logs/index.html.erb +9 -5
- data/app/views/pg_sql_triggers/dashboard/index.html.erb +107 -24
- data/app/views/pg_sql_triggers/shared/_confirmation_modal.html.erb +0 -0
- data/app/views/pg_sql_triggers/shared/_kill_switch_status.html.erb +0 -0
- data/app/views/pg_sql_triggers/tables/index.html.erb +26 -14
- data/app/views/pg_sql_triggers/tables/show.html.erb +0 -0
- data/app/views/pg_sql_triggers/triggers/_drop_modal.html.erb +0 -0
- data/app/views/pg_sql_triggers/triggers/_re_execute_modal.html.erb +0 -0
- data/app/views/pg_sql_triggers/triggers/show.html.erb +33 -0
- data/config/initializers/pg_sql_triggers.rb +0 -0
- data/config/routes.rb +0 -0
- data/db/migrate/{20251222000001_create_pg_sql_triggers_tables.rb → 20251222104327_create_pg_sql_triggers_tables.rb} +0 -0
- data/db/migrate/20251229071916_add_timing_to_pg_sql_triggers_registry.rb +0 -0
- data/db/migrate/{20260103000001_create_pg_sql_triggers_audit_log.rb → 20260103114508_create_pg_sql_triggers_audit_log.rb} +0 -0
- data/db/migrate/{20260228000001_add_for_each_to_pg_sql_triggers_registry.rb → 20260228162233_add_for_each_to_pg_sql_triggers_registry.rb} +0 -0
- data/db/migrate/20260412185841_add_constraint_deferral_to_pg_sql_triggers_registry.rb +9 -0
- data/docs/README.md +3 -0
- data/docs/api-reference.md +133 -0
- data/docs/audit-trail.md +1 -1
- data/docs/configuration.md +172 -0
- data/docs/getting-started.md +14 -0
- data/docs/kill-switch.md +0 -0
- data/docs/permissions.md +6 -9
- data/docs/troubleshooting.md +0 -0
- data/docs/ui-guide.md +0 -0
- data/docs/usage-guide.md +74 -0
- data/docs/web-ui.md +0 -0
- data/lib/generators/pg_sql_triggers/install_generator.rb +0 -0
- data/lib/generators/pg_sql_triggers/templates/README +0 -0
- data/lib/generators/pg_sql_triggers/templates/create_pg_sql_triggers_tables.rb +0 -0
- data/lib/generators/pg_sql_triggers/templates/initializer.rb +14 -0
- data/lib/generators/pg_sql_triggers/templates/trigger_dsl.rb.tt +0 -0
- data/lib/generators/pg_sql_triggers/templates/trigger_migration.rb.erb +0 -0
- data/lib/generators/pg_sql_triggers/templates/trigger_migration_full.rb.tt +0 -0
- data/lib/generators/pg_sql_triggers/trigger_generator.rb +0 -0
- data/lib/generators/pg_sql_triggers/trigger_migration_generator.rb +0 -0
- data/lib/pg_sql_triggers/alerting.rb +77 -0
- data/lib/pg_sql_triggers/database_introspection.rb +0 -0
- data/lib/pg_sql_triggers/deferral_checksum.rb +54 -0
- data/lib/pg_sql_triggers/drift/db_queries.rb +14 -5
- data/lib/pg_sql_triggers/drift/detector.rb +9 -1
- data/lib/pg_sql_triggers/drift/reporter.rb +0 -0
- data/lib/pg_sql_triggers/drift.rb +5 -0
- data/lib/pg_sql_triggers/dsl/trigger_definition.rb +56 -2
- data/lib/pg_sql_triggers/dsl.rb +0 -0
- data/lib/pg_sql_triggers/engine.rb +35 -0
- data/lib/pg_sql_triggers/errors.rb +0 -0
- data/lib/pg_sql_triggers/events_checksum.rb +114 -0
- data/lib/pg_sql_triggers/migration.rb +5 -6
- data/lib/pg_sql_triggers/migrator/pre_apply_comparator.rb +77 -73
- data/lib/pg_sql_triggers/migrator/pre_apply_diff_reporter.rb +0 -0
- data/lib/pg_sql_triggers/migrator/safety_validator.rb +3 -1
- data/lib/pg_sql_triggers/migrator.rb +90 -94
- data/lib/pg_sql_triggers/permissions/checker.rb +12 -15
- data/lib/pg_sql_triggers/permissions.rb +1 -0
- data/lib/pg_sql_triggers/rake_development_boot.rb +65 -0
- data/lib/pg_sql_triggers/registry/manager.rb +27 -13
- data/lib/pg_sql_triggers/registry/validator.rb +226 -2
- data/lib/pg_sql_triggers/registry.rb +0 -0
- data/lib/pg_sql_triggers/schema_dumper_extension.rb +32 -0
- data/lib/pg_sql_triggers/sql/kill_switch.rb +2 -1
- data/lib/pg_sql_triggers/sql.rb +0 -0
- data/lib/pg_sql_triggers/testing/dry_run.rb +0 -0
- data/lib/pg_sql_triggers/testing/function_tester.rb +97 -107
- data/lib/pg_sql_triggers/testing/safe_executor.rb +0 -0
- data/lib/pg_sql_triggers/testing/syntax_validator.rb +0 -0
- data/lib/pg_sql_triggers/testing.rb +0 -0
- data/lib/pg_sql_triggers/trigger_structure_dumper.rb +111 -0
- data/lib/pg_sql_triggers/version.rb +1 -1
- data/lib/pg_sql_triggers.rb +17 -0
- data/lib/tasks/trigger_migrations.rake +235 -152
- data/rakelib/pg_sql_triggers_environment.rake +9 -0
- data/scripts/generate_coverage_report.rb +4 -1
- data/sig/pg_sql_triggers.rbs +0 -0
- metadata +65 -13
- data/GEM_ANALYSIS.md +0 -368
- data/Goal.md +0 -742
- data/pg_sql_triggers.gemspec +0 -53
data/lib/pg_sql_triggers.rb
CHANGED
|
@@ -36,10 +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
|
+
|
|
39
48
|
# PostgreSQL schema used by DbQueries. Override for non-public schemas.
|
|
40
49
|
mattr_accessor :db_schema
|
|
41
50
|
self.db_schema = "public"
|
|
42
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
|
+
|
|
43
57
|
# Drift states
|
|
44
58
|
DRIFT_STATE_IN_SYNC = "in_sync"
|
|
45
59
|
DRIFT_STATE_DRIFTED = "drifted"
|
|
@@ -62,4 +76,7 @@ module PgSqlTriggers
|
|
|
62
76
|
autoload :Testing, "pg_sql_triggers/testing"
|
|
63
77
|
autoload :Migration, "pg_sql_triggers/migration"
|
|
64
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"
|
|
65
82
|
end
|
|
@@ -1,179 +1,201 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
operation: operation,
|
|
7
|
-
environment: Rails.env,
|
|
8
|
-
confirmation: ENV.fetch("CONFIRMATION_TEXT", nil),
|
|
9
|
-
actor: { type: "CLI", id: ENV.fetch("USER", "unknown") }
|
|
10
|
-
)
|
|
11
|
-
rescue PgSqlTriggers::KillSwitchError => e
|
|
12
|
-
puts "\n#{e.message}\n"
|
|
13
|
-
exit 1
|
|
14
|
-
end
|
|
3
|
+
module PgSqlTriggersRakeHelpers
|
|
4
|
+
module MigrateTasks
|
|
5
|
+
module_function
|
|
15
6
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
check_kill_switch!(:trigger_migrate)
|
|
7
|
+
def run_migrate
|
|
8
|
+
PgSqlTriggersRakeHelpers.check_kill_switch!(:trigger_migrate)
|
|
9
|
+
PgSqlTriggers::Migrator.ensure_migrations_table!
|
|
20
10
|
|
|
21
|
-
|
|
11
|
+
target_version = ENV["VERSION"]&.to_i
|
|
12
|
+
verbose = ENV["VERBOSE"] != "false"
|
|
22
13
|
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
if verbose
|
|
15
|
+
puts "Running trigger migrations..."
|
|
16
|
+
puts "Current version: #{PgSqlTriggers::Migrator.current_version}"
|
|
17
|
+
end
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
puts "Running trigger migrations..."
|
|
28
|
-
puts "Current version: #{PgSqlTriggers::Migrator.current_version}"
|
|
29
|
-
end
|
|
19
|
+
PgSqlTriggers::Migrator.run_up(target_version)
|
|
30
20
|
|
|
31
|
-
|
|
21
|
+
puts "Trigger migrations complete. Current version: #{PgSqlTriggers::Migrator.current_version}" if verbose
|
|
22
|
+
end
|
|
32
23
|
|
|
33
|
-
|
|
34
|
-
|
|
24
|
+
def run_rollback
|
|
25
|
+
PgSqlTriggersRakeHelpers.check_kill_switch!(:trigger_rollback)
|
|
26
|
+
PgSqlTriggers::Migrator.ensure_migrations_table!
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
steps = ENV["STEP"] ? ENV["STEP"].to_i : 1
|
|
29
|
+
current_version = PgSqlTriggers::Migrator.current_version
|
|
30
|
+
target_version = [0, current_version - steps].max
|
|
39
31
|
|
|
40
|
-
|
|
32
|
+
puts "Rolling back trigger migrations..."
|
|
33
|
+
puts "Current version: #{current_version}"
|
|
34
|
+
puts "Target version: #{target_version}"
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
current_version = PgSqlTriggers::Migrator.current_version
|
|
44
|
-
target_version = [0, current_version - steps].max
|
|
36
|
+
PgSqlTriggers::Migrator.run_down(target_version)
|
|
45
37
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
puts "Target version: #{target_version}"
|
|
38
|
+
puts "Rollback complete. Current version: #{PgSqlTriggers::Migrator.current_version}"
|
|
39
|
+
end
|
|
49
40
|
|
|
50
|
-
|
|
41
|
+
def print_migrate_status
|
|
42
|
+
PgSqlTriggers::Migrator.ensure_migrations_table!
|
|
43
|
+
statuses = PgSqlTriggers::Migrator.status
|
|
44
|
+
return puts "No trigger migrations found" if statuses.empty?
|
|
51
45
|
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
print_status_table(statuses)
|
|
47
|
+
end
|
|
54
48
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
def print_status_table(statuses)
|
|
50
|
+
puts "\nTrigger Migration Status"
|
|
51
|
+
puts "=" * 80
|
|
52
|
+
printf "%<version>-20s %<name>-40s %<status>-10s\n", version: "Version", name: "Name", status: "Status"
|
|
53
|
+
puts "-" * 80
|
|
54
|
+
statuses.each do |status|
|
|
55
|
+
printf "%<version>-20s %<name>-40s %<status>-10s\n",
|
|
56
|
+
version: status[:version], name: status[:name], status: status[:status]
|
|
57
|
+
end
|
|
58
|
+
puts "=" * 80
|
|
59
|
+
puts "Current version: #{PgSqlTriggers::Migrator.current_version}"
|
|
60
|
+
end
|
|
58
61
|
|
|
59
|
-
|
|
62
|
+
def run_migrate_up
|
|
63
|
+
PgSqlTriggersRakeHelpers.check_kill_switch!(:trigger_migrate_up)
|
|
64
|
+
version = ENV.fetch("VERSION", nil)
|
|
65
|
+
raise "VERSION is required" unless version
|
|
60
66
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
PgSqlTriggers::Migrator.ensure_migrations_table!
|
|
68
|
+
PgSqlTriggers::Migrator.run_up(version.to_i)
|
|
69
|
+
puts "Trigger migration #{version} up complete"
|
|
64
70
|
end
|
|
65
71
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
name: status[:name],
|
|
75
|
-
status: status[:status]
|
|
72
|
+
def run_migrate_down
|
|
73
|
+
PgSqlTriggersRakeHelpers.check_kill_switch!(:trigger_migrate_down)
|
|
74
|
+
version = ENV.fetch("VERSION", nil)
|
|
75
|
+
raise "VERSION is required" unless version
|
|
76
|
+
|
|
77
|
+
PgSqlTriggers::Migrator.ensure_migrations_table!
|
|
78
|
+
PgSqlTriggers::Migrator.run_down(version.to_i)
|
|
79
|
+
puts "Trigger migration #{version} down complete"
|
|
76
80
|
end
|
|
77
81
|
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
def run_migrate_redo
|
|
83
|
+
PgSqlTriggersRakeHelpers.check_kill_switch!(:trigger_migrate_redo)
|
|
84
|
+
PgSqlTriggers::Migrator.ensure_migrations_table!
|
|
85
|
+
|
|
86
|
+
if ENV["VERSION"]
|
|
87
|
+
version = ENV["VERSION"].to_i
|
|
88
|
+
PgSqlTriggers::Migrator.run_down(version)
|
|
89
|
+
PgSqlTriggers::Migrator.run_up(version)
|
|
90
|
+
else
|
|
91
|
+
steps = ENV["STEP"] ? ENV["STEP"].to_i : 1
|
|
92
|
+
current_version = PgSqlTriggers::Migrator.current_version
|
|
93
|
+
target_version = [0, current_version - steps].max
|
|
94
|
+
PgSqlTriggers::Migrator.run_down(target_version)
|
|
95
|
+
PgSqlTriggers::Migrator.run_up
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
puts "Trigger migration redo complete"
|
|
99
|
+
end
|
|
80
100
|
end
|
|
81
101
|
|
|
82
|
-
|
|
83
|
-
task "migrate:up" => :environment do
|
|
84
|
-
check_kill_switch!(:trigger_migrate_up)
|
|
102
|
+
module_function
|
|
85
103
|
|
|
86
|
-
|
|
87
|
-
raise "VERSION is required" unless version
|
|
104
|
+
extend MigrateTasks
|
|
88
105
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
106
|
+
# @param result [Hash] single drift result from {PgSqlTriggers::Drift::Detector}
|
|
107
|
+
def drift_check_trigger_label(result)
|
|
108
|
+
result[:registry_entry]&.trigger_name ||
|
|
109
|
+
result[:db_trigger]&.fetch("trigger_name", nil) ||
|
|
110
|
+
"(unknown)"
|
|
92
111
|
end
|
|
93
112
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
113
|
+
def check_kill_switch!(operation)
|
|
114
|
+
PgSqlTriggers::SQL::KillSwitch.check!(
|
|
115
|
+
operation: operation,
|
|
116
|
+
environment: Rails.env,
|
|
117
|
+
confirmation: ENV.fetch("CONFIRMATION_TEXT", nil),
|
|
118
|
+
actor: { type: "CLI", id: ENV.fetch("USER", "unknown") }
|
|
119
|
+
)
|
|
120
|
+
rescue PgSqlTriggers::KillSwitchError => e
|
|
121
|
+
puts "\n#{e.message}\n"
|
|
122
|
+
exit 1
|
|
104
123
|
end
|
|
105
124
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
125
|
+
def dump_trigger_structure
|
|
126
|
+
path = ENV["FILE"].presence || ENV["TRIGGER_STRUCTURE_SQL"].presence
|
|
127
|
+
written = PgSqlTriggers::TriggerStructureDumper.dump_to(path)
|
|
128
|
+
puts "Wrote #{written}"
|
|
129
|
+
end
|
|
109
130
|
|
|
110
|
-
|
|
131
|
+
def load_trigger_structure
|
|
132
|
+
check_kill_switch!(:trigger_load)
|
|
133
|
+
path = ENV["FILE"].presence || ENV["TRIGGER_STRUCTURE_SQL"].presence
|
|
134
|
+
PgSqlTriggers::TriggerStructureDumper.load_from(path)
|
|
135
|
+
puts "Loaded #{PgSqlTriggers::TriggerStructureDumper.resolve_path(path)}"
|
|
136
|
+
end
|
|
111
137
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
else
|
|
117
|
-
steps = ENV["STEP"] ? ENV["STEP"].to_i : 1
|
|
118
|
-
current_version = PgSqlTriggers::Migrator.current_version
|
|
119
|
-
target_version = [0, current_version - steps].max
|
|
138
|
+
def run_check_drift
|
|
139
|
+
outcome = PgSqlTriggers::Alerting.check_and_notify
|
|
140
|
+
results = outcome[:results]
|
|
141
|
+
alertable = outcome[:alertable]
|
|
120
142
|
|
|
121
|
-
|
|
122
|
-
|
|
143
|
+
puts "PgSqlTriggers drift check: #{results.size} trigger(s), #{alertable.size} problem(s)."
|
|
144
|
+
alertable.each do |r|
|
|
145
|
+
puts " - #{drift_check_trigger_label(r)}: #{r[:state]} — #{r[:details]}"
|
|
146
|
+
end
|
|
147
|
+
puts "Notifier invoked." if outcome[:notified]
|
|
148
|
+
if alertable.any? && !outcome[:notified]
|
|
149
|
+
puts "No drift notifier configured; set PgSqlTriggers.drift_notifier to receive alerts."
|
|
123
150
|
end
|
|
124
151
|
|
|
125
|
-
|
|
152
|
+
exit 1 if ENV["FAIL_ON_DRIFT"].present? && alertable.any?
|
|
126
153
|
end
|
|
127
154
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
155
|
+
def run_validate_order
|
|
156
|
+
errors = PgSqlTriggers::Registry::Validator.trigger_order_validation_errors
|
|
157
|
+
if errors.empty?
|
|
158
|
+
puts "PgSqlTriggers: trigger depends_on / name order OK."
|
|
159
|
+
else
|
|
160
|
+
puts "PgSqlTriggers: trigger order validation failed:"
|
|
161
|
+
errors.each { |msg| puts " - #{msg}" }
|
|
162
|
+
exit 1
|
|
163
|
+
end
|
|
132
164
|
end
|
|
133
165
|
|
|
134
|
-
|
|
135
|
-
task "abort_if_pending_migrations" => :environment do
|
|
166
|
+
def abort_if_pending_trigger_migrations
|
|
136
167
|
PgSqlTriggers::Migrator.ensure_migrations_table!
|
|
137
|
-
|
|
138
168
|
pending = PgSqlTriggers::Migrator.pending_migrations
|
|
139
|
-
if pending.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
raise "Pending trigger migrations found"
|
|
145
|
-
end
|
|
169
|
+
return if pending.empty?
|
|
170
|
+
|
|
171
|
+
puts "You have #{pending.length} pending trigger migration(s):"
|
|
172
|
+
pending.each { |migration| puts " #{migration.version}_#{migration.name}" }
|
|
173
|
+
raise "Pending trigger migrations found"
|
|
146
174
|
end
|
|
147
175
|
end
|
|
148
176
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
desc "Migrate the database schema and triggers (options: VERSION=x, VERBOSE=false)"
|
|
152
|
-
task "migrate:with_triggers" => :environment do
|
|
153
|
-
check_kill_switch!(:db_migrate_with_triggers)
|
|
177
|
+
module PgSqlTriggersDbRakeHelpers
|
|
178
|
+
module_function
|
|
154
179
|
|
|
155
|
-
|
|
180
|
+
def check_kill_switch!(operation)
|
|
181
|
+
PgSqlTriggersRakeHelpers.check_kill_switch!(operation)
|
|
182
|
+
end
|
|
156
183
|
|
|
184
|
+
def run_db_migrate_with_triggers
|
|
185
|
+
check_kill_switch!(:db_migrate_with_triggers)
|
|
186
|
+
verbose = ENV["VERBOSE"] != "false"
|
|
157
187
|
puts "Running schema and trigger migrations..." if verbose
|
|
158
|
-
|
|
159
|
-
# Run schema migrations first
|
|
160
188
|
Rake::Task["db:migrate"].invoke
|
|
161
|
-
|
|
162
|
-
# Then run trigger migrations
|
|
163
189
|
Rake::Task["trigger:migrate"].invoke
|
|
164
190
|
end
|
|
165
191
|
|
|
166
|
-
|
|
167
|
-
task "rollback:with_triggers" => :environment do
|
|
192
|
+
def run_db_rollback_with_triggers
|
|
168
193
|
check_kill_switch!(:db_rollback_with_triggers)
|
|
169
|
-
|
|
170
194
|
ENV["STEP"] ? ENV["STEP"].to_i : 1
|
|
171
195
|
|
|
172
|
-
# Determine which type of migration was last run
|
|
173
196
|
schema_version = ActiveRecord::Base.connection.schema_migration_context.current_version || 0
|
|
174
197
|
trigger_version = PgSqlTriggers::Migrator.current_version
|
|
175
198
|
|
|
176
|
-
# Rollback the most recent migration (schema or trigger)
|
|
177
199
|
if schema_version > trigger_version
|
|
178
200
|
Rake::Task["db:rollback"].invoke
|
|
179
201
|
else
|
|
@@ -181,8 +203,7 @@ namespace :db do
|
|
|
181
203
|
end
|
|
182
204
|
end
|
|
183
205
|
|
|
184
|
-
|
|
185
|
-
task "migrate:status:with_triggers" => :environment do
|
|
206
|
+
def print_migrate_status_with_triggers
|
|
186
207
|
puts "\nSchema Migrations:"
|
|
187
208
|
puts "=" * 80
|
|
188
209
|
begin
|
|
@@ -196,24 +217,21 @@ namespace :db do
|
|
|
196
217
|
Rake::Task["trigger:migrate:status"].invoke
|
|
197
218
|
end
|
|
198
219
|
|
|
199
|
-
|
|
200
|
-
task "migrate:up:with_triggers" => :environment do
|
|
220
|
+
def run_db_migrate_up_with_triggers
|
|
201
221
|
check_kill_switch!(:db_migrate_up_with_triggers)
|
|
202
|
-
|
|
203
222
|
version = ENV.fetch("VERSION", nil)
|
|
204
223
|
raise "VERSION is required" unless version
|
|
205
224
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
225
|
+
invoke_up_for_version(version.to_i, version)
|
|
226
|
+
rescue StandardError => e
|
|
227
|
+
puts "Error: #{e.message}"
|
|
228
|
+
raise
|
|
229
|
+
end
|
|
211
230
|
|
|
212
|
-
|
|
213
|
-
trigger_migration =
|
|
231
|
+
def invoke_up_for_version(version_int, version)
|
|
232
|
+
schema_migration, trigger_migration = find_migrations(version_int)
|
|
214
233
|
|
|
215
234
|
if schema_migration && trigger_migration
|
|
216
|
-
# Both exist - run schema first
|
|
217
235
|
Rake::Task["db:migrate:up"].invoke
|
|
218
236
|
Rake::Task["trigger:migrate:up"].invoke
|
|
219
237
|
elsif schema_migration
|
|
@@ -223,29 +241,20 @@ namespace :db do
|
|
|
223
241
|
else
|
|
224
242
|
raise "No migration found with version #{version}"
|
|
225
243
|
end
|
|
226
|
-
rescue StandardError => e
|
|
227
|
-
puts "Error: #{e.message}"
|
|
228
|
-
raise
|
|
229
244
|
end
|
|
230
245
|
|
|
231
|
-
|
|
232
|
-
task "migrate:down:with_triggers" => :environment do
|
|
246
|
+
def run_db_migrate_down_with_triggers
|
|
233
247
|
check_kill_switch!(:db_migrate_down_with_triggers)
|
|
234
|
-
|
|
235
248
|
version = ENV.fetch("VERSION", nil)
|
|
236
249
|
raise "VERSION is required" unless version
|
|
237
250
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
# Check if it's a schema or trigger migration
|
|
241
|
-
schema_migrations = ActiveRecord::Base.connection.migration_context.migrations
|
|
242
|
-
trigger_migrations = PgSqlTriggers::Migrator.migrations
|
|
251
|
+
invoke_down_for_version(version.to_i, version)
|
|
252
|
+
end
|
|
243
253
|
|
|
244
|
-
|
|
245
|
-
trigger_migration =
|
|
254
|
+
def invoke_down_for_version(version_int, version)
|
|
255
|
+
schema_migration, trigger_migration = find_migrations(version_int)
|
|
246
256
|
|
|
247
257
|
if schema_migration && trigger_migration
|
|
248
|
-
# Both exist - run trigger down first
|
|
249
258
|
Rake::Task["trigger:migrate:down"].invoke
|
|
250
259
|
Rake::Task["db:migrate:down"].invoke
|
|
251
260
|
elsif schema_migration
|
|
@@ -257,8 +266,16 @@ namespace :db do
|
|
|
257
266
|
end
|
|
258
267
|
end
|
|
259
268
|
|
|
260
|
-
|
|
261
|
-
|
|
269
|
+
def find_migrations(version_int)
|
|
270
|
+
schema_migrations = ActiveRecord::Base.connection.migration_context.migrations
|
|
271
|
+
trigger_migrations = PgSqlTriggers::Migrator.migrations
|
|
272
|
+
[
|
|
273
|
+
schema_migrations.find { |m| m.version == version_int },
|
|
274
|
+
trigger_migrations.find { |m| m.version == version_int }
|
|
275
|
+
]
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def run_db_migrate_redo_with_triggers
|
|
262
279
|
check_kill_switch!(:db_migrate_redo_with_triggers)
|
|
263
280
|
|
|
264
281
|
if ENV["VERSION"]
|
|
@@ -270,17 +287,83 @@ namespace :db do
|
|
|
270
287
|
end
|
|
271
288
|
end
|
|
272
289
|
|
|
273
|
-
|
|
274
|
-
task "version:with_triggers" => :environment do
|
|
290
|
+
def print_version_with_triggers
|
|
275
291
|
schema_version = ActiveRecord::Base.connection.schema_migration_context.current_version
|
|
276
292
|
trigger_version = PgSqlTriggers::Migrator.current_version
|
|
277
293
|
|
|
278
294
|
puts "Schema migration version: #{schema_version || 0}"
|
|
279
295
|
puts "Trigger migration version: #{trigger_version}"
|
|
280
296
|
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
namespace :trigger do
|
|
300
|
+
desc "Dump managed PostgreSQL triggers to db/trigger_structure.sql (FILE=path to override)"
|
|
301
|
+
task(dump: :environment) { PgSqlTriggersRakeHelpers.dump_trigger_structure }
|
|
302
|
+
|
|
303
|
+
desc "Execute SQL from db/trigger_structure.sql (FILE=path to override)"
|
|
304
|
+
task(load: :environment) { PgSqlTriggersRakeHelpers.load_trigger_structure }
|
|
305
|
+
|
|
306
|
+
desc "Migrate trigger migrations (options: VERSION=x, VERBOSE=false)"
|
|
307
|
+
task(migrate: :environment) { PgSqlTriggersRakeHelpers.run_migrate }
|
|
308
|
+
|
|
309
|
+
desc "Rollback trigger migrations (specify steps w/ STEP=n)"
|
|
310
|
+
task(rollback: :environment) { PgSqlTriggersRakeHelpers.run_rollback }
|
|
311
|
+
|
|
312
|
+
desc "Display status of trigger migrations"
|
|
313
|
+
task("migrate:status" => :environment) { PgSqlTriggersRakeHelpers.print_migrate_status }
|
|
314
|
+
|
|
315
|
+
desc "Runs the 'up' for a given migration VERSION"
|
|
316
|
+
task("migrate:up" => :environment) { PgSqlTriggersRakeHelpers.run_migrate_up }
|
|
317
|
+
|
|
318
|
+
desc "Runs the 'down' for a given migration VERSION"
|
|
319
|
+
task("migrate:down" => :environment) { PgSqlTriggersRakeHelpers.run_migrate_down }
|
|
320
|
+
|
|
321
|
+
desc "Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x)"
|
|
322
|
+
task("migrate:redo" => :environment) { PgSqlTriggersRakeHelpers.run_migrate_redo }
|
|
323
|
+
|
|
324
|
+
desc "Retrieves the current schema version number for trigger migrations"
|
|
325
|
+
task version: :environment do
|
|
326
|
+
PgSqlTriggers::Migrator.ensure_migrations_table!
|
|
327
|
+
puts "Current trigger migration version: #{PgSqlTriggers::Migrator.current_version}"
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
desc "Detect trigger drift; calls drift_notifier when drifted/dropped/unknown (FAIL_ON_DRIFT=1 exits non-zero)"
|
|
331
|
+
task(check_drift: :environment) { PgSqlTriggersRakeHelpers.run_check_drift }
|
|
332
|
+
|
|
333
|
+
desc "Validate trigger depends_on metadata (refs, cycles, compatibility, PostgreSQL name order)"
|
|
334
|
+
task(validate_order: :environment) { PgSqlTriggersRakeHelpers.run_validate_order }
|
|
335
|
+
|
|
336
|
+
desc "Raises an error if there are pending trigger migrations"
|
|
337
|
+
task("abort_if_pending_migrations" => :environment) do
|
|
338
|
+
PgSqlTriggersRakeHelpers.abort_if_pending_trigger_migrations
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Combined tasks for running both schema and trigger migrations
|
|
343
|
+
namespace :db do
|
|
344
|
+
desc "Migrate the database schema and triggers (options: VERSION=x, VERBOSE=false)"
|
|
345
|
+
task("migrate:with_triggers" => :environment) { PgSqlTriggersDbRakeHelpers.run_db_migrate_with_triggers }
|
|
346
|
+
|
|
347
|
+
desc "Rollback schema and trigger migrations (specify steps w/ STEP=n)"
|
|
348
|
+
task("rollback:with_triggers" => :environment) { PgSqlTriggersDbRakeHelpers.run_db_rollback_with_triggers }
|
|
349
|
+
|
|
350
|
+
desc "Display status of schema and trigger migrations"
|
|
351
|
+
task("migrate:status:with_triggers" => :environment) { PgSqlTriggersDbRakeHelpers.print_migrate_status_with_triggers }
|
|
352
|
+
|
|
353
|
+
desc "Runs the 'up' for a given migration VERSION (schema or trigger)"
|
|
354
|
+
task("migrate:up:with_triggers" => :environment) { PgSqlTriggersDbRakeHelpers.run_db_migrate_up_with_triggers }
|
|
355
|
+
|
|
356
|
+
desc "Runs the 'down' for a given migration VERSION (schema or trigger)"
|
|
357
|
+
task("migrate:down:with_triggers" => :environment) { PgSqlTriggersDbRakeHelpers.run_db_migrate_down_with_triggers }
|
|
358
|
+
|
|
359
|
+
desc "Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x)"
|
|
360
|
+
task("migrate:redo:with_triggers" => :environment) { PgSqlTriggersDbRakeHelpers.run_db_migrate_redo_with_triggers }
|
|
361
|
+
|
|
362
|
+
desc "Retrieves the current schema version numbers for schema and trigger migrations"
|
|
363
|
+
task("version:with_triggers" => :environment) { PgSqlTriggersDbRakeHelpers.print_version_with_triggers }
|
|
281
364
|
|
|
282
365
|
desc "Raises an error if there are pending migrations or trigger migrations"
|
|
283
|
-
task
|
|
366
|
+
task("abort_if_pending_migrations:with_triggers" => :environment) do
|
|
284
367
|
Rake::Task["db:abort_if_pending_migrations"].invoke
|
|
285
368
|
Rake::Task["trigger:abort_if_pending_migrations"].invoke
|
|
286
369
|
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Provides :environment for engine tasks when developing the gem (see Rakefile).
|
|
4
|
+
# rubocop:disable Rails/RakeEnvironment -- this task *is* the Rails environment for gem dev
|
|
5
|
+
task :environment do
|
|
6
|
+
require_relative "../lib/pg_sql_triggers/rake_development_boot"
|
|
7
|
+
PgSqlTriggers::RakeDevelopmentBoot.boot!
|
|
8
|
+
end
|
|
9
|
+
# rubocop:enable Rails/RakeEnvironment
|
|
@@ -31,7 +31,8 @@ def extract_file_path(full_path)
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def parse_coverage_data
|
|
34
|
-
|
|
34
|
+
workspace_root = Pathname.new(__dir__).parent.realpath
|
|
35
|
+
coverage_dir = workspace_root.join("coverage")
|
|
35
36
|
resultset_file = coverage_dir.join(".resultset.json")
|
|
36
37
|
last_run_file = coverage_dir.join(".last_run.json")
|
|
37
38
|
|
|
@@ -57,6 +58,8 @@ def parse_coverage_data
|
|
|
57
58
|
relative_path = extract_file_path(full_path)
|
|
58
59
|
# Skip files in spec, vendor, etc.
|
|
59
60
|
next if relative_path.include?("/spec/") || relative_path.include?("/vendor/")
|
|
61
|
+
# Omit deleted or moved sources so stale .resultset.json cannot inflate the report
|
|
62
|
+
next unless workspace_root.join(relative_path).file?
|
|
60
63
|
|
|
61
64
|
lines = file_data["lines"]
|
|
62
65
|
coverage_percentage = calculate_file_coverage(lines)
|
data/sig/pg_sql_triggers.rbs
CHANGED
|
File without changes
|