Module: PgSqlTriggers::EventsChecksum

Defined in:
lib/pg_sql_triggers/events_checksum.rb

Overview

Normalizes trigger event lists (including PostgreSQL UPDATE OF col1, col2) for checksums so TriggerRegistry#calculate_checksum, Registry::Manager.calculate_checksum, and Drift::Detector.calculate_db_checksum stay aligned.

Class Method Summary collapse

Class Method Details

.build_tokens(events, sorted_lowercase_columns) ⇒ Object



65
66
67
68
69
70
71
72
73
74
# File 'lib/pg_sql_triggers/events_checksum.rb', line 65

def build_tokens(events, sorted_lowercase_columns)
  cols_join = sorted_lowercase_columns.join(",")
  events.map do |ev|
    if ev == "update" && cols_join.present?
      "update:#{cols_join}"
    else
      ev
    end
  end
end

.canonical_from_definition(defn) ⇒ String

Returns canonical token string, or empty when events is missing/empty.

Parameters:

  • defn (Hash)

    parsed registry definition JSON (string or symbol keys)

Returns:

  • (String)

    canonical token string, or empty when events is missing/empty



12
13
14
15
16
17
18
19
# File 'lib/pg_sql_triggers/events_checksum.rb', line 12

def canonical_from_definition(defn)
  hash = stringify(defn)
  events = Array(hash["events"]).map(&:to_s).map(&:downcase)
  return "" if events.empty?

  columns = normalized_columns(hash["columns"])
  build_tokens(events, columns).sort.join("|")
end

.canonical_from_pg_triggerdef(trigger_def) ⇒ Object

Parameters:

  • trigger_def (String)

    pg_get_triggerdef output



31
32
33
34
35
36
37
38
# File 'lib/pg_sql_triggers/events_checksum.rb', line 31

def canonical_from_pg_triggerdef(trigger_def)
  return "" if trigger_def.blank?

  clause = extract_events_clause(trigger_def)
  return "" if clause.blank?

  parse_events_clause(clause).sort.join("|")
end

.events_sql_fragment(defn, quote_column:) ⇒ Object

SQL fragment for the event list (e.g. INSERT OR UPDATE OF “email”), preserving DSL event order.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/pg_sql_triggers/events_checksum.rb', line 41

def events_sql_fragment(defn, quote_column:)
  hash = stringify(defn)
  events = Array(hash["events"]).map(&:to_s).map(&:downcase)
  columns = ordered_columns(hash["columns"])
  return "INSERT" if events.empty?

  events.map do |ev|
    if ev == "update" && columns.any?
      quoted = columns.map { |c| quote_column.call(c) }.join(", ")
      "UPDATE OF #{quoted}"
    else
      ev.upcase
    end
  end.join(" OR ")
end

.extract_events_clause(trigger_def) ⇒ Object



76
77
78
79
80
81
82
# File 'lib/pg_sql_triggers/events_checksum.rb', line 76

def extract_events_clause(trigger_def)
  s = trigger_def.to_s.squish
  m = s.match(/(?:BEFORE|AFTER|INSTEAD\s+OF)\s+(.+?)\s+ON\s+/i)
  return "" unless m

  m.captures.first.to_s.strip
end

.normalize_pg_identifier(fragment) ⇒ Object



99
100
101
102
103
104
105
106
# File 'lib/pg_sql_triggers/events_checksum.rb', line 99

def normalize_pg_identifier(fragment)
  f = fragment.to_s.strip
  if f.start_with?('"') && f.end_with?('"') && f.length >= 2
    f[1...-1].gsub('""', '"').downcase
  else
    f.downcase
  end
end

.normalized_columns(raw) ⇒ Object



57
58
59
# File 'lib/pg_sql_triggers/events_checksum.rb', line 57

def normalized_columns(raw)
  ordered_columns(raw).map(&:downcase).sort
end

.ordered_columns(raw) ⇒ Object



61
62
63
# File 'lib/pg_sql_triggers/events_checksum.rb', line 61

def ordered_columns(raw)
  Array(raw).flatten.compact.map { |c| c.to_s.strip }.reject(&:empty?)
end

.parse_events_clause(clause) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/pg_sql_triggers/events_checksum.rb', line 84

def parse_events_clause(clause)
  clause.split(/\s+OR\s+/i).filter_map do |part|
    part = part.strip
    next if part.empty?

    um = part.match(/\AUPDATE\s+OF\s+(.+)\z/i)
    if um
      cols = um[1].split(",").map { |c| normalize_pg_identifier(c) }.sort
      "update:#{cols.join(',')}"
    else
      part.downcase
    end
  end
end

.segment_from_definition_json(definition_json) ⇒ Object

Parameters:

  • definition_json (String, nil)


22
23
24
25
26
27
28
# File 'lib/pg_sql_triggers/events_checksum.rb', line 22

def segment_from_definition_json(definition_json)
  return "" if definition_json.blank?

  canonical_from_definition(JSON.parse(definition_json))
rescue JSON::ParserError
  ""
end

.stringify(defn) ⇒ Object



108
109
110
111
112
# File 'lib/pg_sql_triggers/events_checksum.rb', line 108

def stringify(defn)
  return {} if defn.nil?

  defn.transform_keys(&:to_s)
end