Class: PgMeta::Database

Inherits:
Node
  • Object
show all
Defined in:
lib/pg_meta/meta.rb,
lib/pg_meta/load_conn.rb,
lib/pg_meta/load_yaml.rb

Instance Attribute Summary collapse

Attributes inherited from Node

#name, #root

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Node

#dump, #dump_value, #inspect, #sid, #to_s, #to_yaml

Constructor Details

#initialize(name, owner = nil) ⇒ Database

Returns a new instance of Database.



91
92
93
94
95
96
97
# File 'lib/pg_meta/meta.rb', line 91

def initialize(name, owner = nil)
  super(nil, name)
  @owner = owner
  @schemas = {}
  @hidden_schemas = {}
  @nodes = {}
end

Instance Attribute Details

#hidden_schemasObject (readonly)

Hash of hidden schemas



86
87
88
# File 'lib/pg_meta/meta.rb', line 86

def hidden_schemas
  @hidden_schemas
end

#ownerObject (readonly)

Owner of the database. Defined by #load_conn



80
81
82
# File 'lib/pg_meta/meta.rb', line 80

def owner
  @owner
end

#schemasObject (readonly)

Hash of schemas



83
84
85
# File 'lib/pg_meta/meta.rb', line 83

def schemas
  @schemas
end

Class Method Details

.load_conn(pg_conn, exclude_schemas: []) ⇒ Object

Load the database from the given PgConn::Connection object. This method is a transaction wrapper around #do_load_conn



8
9
10
11
12
13
# File 'lib/pg_meta/load_conn.rb', line 8

def self.load_conn(pg_conn, exclude_schemas: [])
  pg_conn.pg_connection.transaction { |conn|
    conn.exec "set transaction isolation level serializable read only deferrable"
    do_load_from_connection(conn, exclude_schemas: exclude_schemas)
  }
end

.load_yaml(database_yaml) ⇒ Object

Create a Database object from the given YAML representation and return it. The YAML representation can be generated by Database#to_yaml



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
# File 'lib/pg_meta/load_yaml.rb', line 8

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

Instance Method Details

#[](uid) ⇒ Object

Lookup database object by UID



100
# File 'lib/pg_meta/meta.rb', line 100

def [](uid) @nodes[uid] end

#exist?(uid) ⇒ Boolean

Return true if the given UID exists

Returns:

  • (Boolean)


103
# File 'lib/pg_meta/meta.rb', line 103

def exist?(uid) @nodes.key?(uid) end

#guidObject

GUID is the name of the database



77
# File 'lib/pg_meta/meta.rb', line 77

def guid() name end

#is_a?(klass) ⇒ Boolean

Make Database pretend to be an instance of the PgMeta module

Returns:

  • (Boolean)


106
# File 'lib/pg_meta/meta.rb', line 106

def is_a?(klass) klass == PgMeta or super end

#parentObject

Redefine Node#parent



89
# File 'lib/pg_meta/meta.rb', line 89

def parent() nil end

#to_hObject



108
# File 'lib/pg_meta/meta.rb', line 108

def to_h() attrs_to_h(:name, :owner, :schemas) end

#to_marshalObject



109
# File 'lib/pg_meta/meta.rb', line 109

def to_marshal() Marshal.dump(self) end

#uidObject

A database has no UID



74
# File 'lib/pg_meta/meta.rb', line 74

def uid() nil end