module PgMeta class Database using Ext::Hash # Create a Database object from the given YAML representation and return # it. The YAML representation can be generated by Database#to_yaml def self.load_yaml(database_yaml) database = Database.new *database_yaml.project(:name, :owner) # Accumulators for second pass views = [] referential_constraints = [] # First pass: Build everything except referential constraints for schema_yaml in database_yaml[:schemas] schema = Schema.new database, *schema_yaml.project(:name, :owner) for table_yaml in schema_yaml[:tables] klass = table_yaml[:table?] ? Table : (table_yaml[:materialized?] ? MaterializedView : View) table = klass.new schema, *table_yaml.project(:name, :insertable?, :typed?) views << [table, table_yaml[:defining_relations]] if klass <= View || klass <= MaterializedView for column_yaml in table_yaml[:columns] column = Column.new(table, *column_yaml.project( :name, :ordinal, :type, :element_type, :dimensions, :default, :identity?, :generated?, :nullable?, :updatable?)) end for constraint_yaml in table_yaml[:constraints] name = constraint_yaml[:name] case kind = constraint_yaml[:kind] when :primary_key columns = constraint_yaml[:columns]&.map { |name| table.columns[name] } PrimaryKeyConstraint.new(table, name, columns) when :unique columns = constraint_yaml[:columns]&.map { |name| table.columns[name] } UniqueConstraint.new(table, name, columns) when :check CheckConstraint.new(table, name, constraint_yaml[:expression]) when :referential # Postpone to second pass columns = constraint_yaml[:referencing_columns]&.map { |name| table.columns[name] } referential_constraints << [table, name, columns, constraint_yaml[:referenced_constraint]] end end for trigger_yaml in table_yaml[:triggers] Trigger.new(table, *trigger_yaml.project(:name, :function, :level, :timing, :events)) end end for function_yaml in schema_yaml[:functions] klass = function_yaml[:function?] ? Function : Procedure klass.new(schema, *function_yaml.project(:name, :owner, :security)) end end # Second pass: Add referential constraints referential_constraints.each { |table, name, columns, referenced_constraint_uid| referenced_constraint = database[referenced_constraint_uid] or raise Error, "Can't find UID '#{referenced_constraint_uid}'" ReferentialConstraint.new(table, name, columns, referenced_constraint) referenced_constraint.table.send(:add_depending_table, table) } # Third pass: Build defining tables views.each { |view, defining_relation_uids| defining_relation_uids.each { |uid| relation = database[uid] relation.send(:add_depending_view, view) view.send(:add_defining_relation, relation) } } database end end end