1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
# frozen_string_literal: true
module Gitlab
module Schema
module Validation
module Sources
class StructureSql
DEFAULT_SCHEMA = 'public'
def initialize(structure_file_path, schema_name = DEFAULT_SCHEMA)
@structure_file_path = structure_file_path
@schema_name = schema_name
end
def index_exists?(index_name)
index = indexes.find { |index| index.name == index_name }
return false if index.nil?
true
end
def trigger_exists?(trigger_name)
trigger = triggers.find { |trigger| trigger.name == trigger_name }
return false if trigger.nil?
true
end
def foreign_key_exists?(foreign_key_name)
foreign_key = foreign_keys.find { |fk| fk.name == foreign_key_name }
return false if foreign_key.nil?
true
end
def table_exists?(table_name)
table = fetch_table_by_name(table_name)
return false if table.nil?
true
end
def fetch_table_by_name(table_name)
tables.find { |table| table.name == table_name }
end
def indexes
@indexes ||= map_with_default_schema(index_statements, SchemaObjects::Index)
end
def triggers
@triggers ||= map_with_default_schema(trigger_statements, SchemaObjects::Trigger)
end
def foreign_keys
@foreign_keys ||= foreign_key_statements.map do |stmt|
stmt.relation.schemaname = schema_name if stmt.relation.schemaname == ''
SchemaObjects::ForeignKey.new(Adapters::ForeignKeyStructureSqlAdapter.new(stmt))
end
end
def tables
@tables ||= table_statements.map do |stmt|
table_name = stmt.relation.relname
partition_stmt = stmt.partspec
columns = stmt.table_elts.select { |n| n.node == :column_def }.map do |column|
adapter = Adapters::ColumnStructureSqlAdapter.new(table_name, column.column_def, partition_stmt)
SchemaObjects::Column.new(adapter)
end
SchemaObjects::Table.new(table_name, columns)
end
end
private
attr_reader :structure_file_path, :schema_name
def index_statements
statements.filter_map { |s| s.stmt.index_stmt }
end
def trigger_statements
statements.filter_map { |s| s.stmt.create_trig_stmt }
end
def table_statements
statements.filter_map { |s| s.stmt.create_stmt }
end
def foreign_key_statements
constraint_statements(:CONSTR_FOREIGN)
end
# Filter constraint statement nodes
#
# @param constraint_type [Symbol] node type. One of CONSTR_PRIMARY, CONSTR_CHECK, CONSTR_EXCLUSION,
# CONSTR_UNIQUE or CONSTR_FOREIGN.
def constraint_statements(constraint_type)
alter_table_statements(:AT_AddConstraint).filter do |stmt|
stmt.cmds.first.alter_table_cmd.def.constraint.contype == constraint_type
end
end
# Filter alter table statement nodes
#
# @param subtype [Symbol] node subtype +AT_AttachPartition+, +AT_ColumnDefault+ or +AT_AddConstraint+
def alter_table_statements(subtype)
statements.filter_map do |statement|
node = statement.stmt.alter_table_stmt
next unless node
node if node.cmds.first.alter_table_cmd.subtype == subtype
end
end
def statements
@statements ||= parsed_structure_file.tree.stmts
end
def parsed_structure_file
PgQuery.parse(File.read(structure_file_path))
end
def map_with_default_schema(statements, validation_class)
statements.map do |statement|
statement.relation.schemaname = schema_name if statement.relation.schemaname == ''
validation_class.new(statement)
end
end
end
end
end
end
end
|