Welcome to mirror list, hosted at ThFree Co, Russian Federation.

structure_sql.rb « schema_validation « database « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 4d6fa17f0fca47c7c60c4c904cdbe57245ecefa2 (plain)
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
# frozen_string_literal: true

module Gitlab
  module Database
    module SchemaValidation
      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)
          indexes.find { |index| index.name == index_name }.present?
        end

        def trigger_exists?(trigger_name)
          triggers.find { |trigger| trigger.name == trigger_name }.present?
        end

        def foreign_key_exists?(foreign_key_name)
          foreign_keys.find { |fk| fk.name == foreign_key_name }.present?
        end

        def fetch_table_by_name(table_name)
          tables.find { |table| table.name == table_name }
        end

        def table_exists?(table_name)
          fetch_table_by_name(table_name).present?
        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