Class: PgGraph::Type::Database
- Defined in:
- lib/pg_graph/type/type.rb,
lib/pg_graph.rb,
lib/pg_graph/type/read.rb
Overview
Database is the top-level object and #read is the starting point for loading the type system in PgGraph::Type.new
Instance Attribute Summary collapse
-
#catalog ⇒ Object
readonly
Returns the value of attribute catalog.
-
#reflector ⇒ Object
readonly
The reflector object.
Instance Method Summary collapse
- #dot_lookup(key) ⇒ Object
- #guid ⇒ Object
-
#initialize(name, reflector = PgGraph::Reflector.new) ⇒ Database
constructor
A new instance of Database.
-
#instantiate(arg = nil) ⇒ Object
:call-seq: instantiate() instantiate(hash) instantiate(yaml) instantiate(pg_conn).
-
#is_a?(klass) ⇒ Boolean
Make Database pretend it is a PgGraph::Type object.
- #read(arg, reflector = nil, ignore: []) ⇒ Object
- #read_meta(meta, ignore: []) ⇒ Object
-
#tables ⇒ Object
List of all tables in the database.
Methods inherited from Node
#dump, #identifier, #inspect, #inspect_inner, #schema_identifier
Constructor Details
#initialize(name, reflector = PgGraph::Reflector.new) ⇒ Database
Returns a new instance of Database.
74 75 76 77 78 79 80 |
# File 'lib/pg_graph/type/type.rb', line 74 def initialize(name, reflector = PgGraph::Reflector.new) constrain name, String constrain reflector, PgGraph::Reflector super(nil, name) @catalog = PgCatalogSchema.new(self) @reflector = reflector end |
Instance Attribute Details
#catalog ⇒ Object (readonly)
Returns the value of attribute catalog.
64 65 66 |
# File 'lib/pg_graph/type/type.rb', line 64 def catalog @catalog end |
#reflector ⇒ Object (readonly)
The reflector object. The reflector object takes a name of a link field and produce the field name in this table and the corrsponding field name in the other table
72 73 74 |
# File 'lib/pg_graph/type/type.rb', line 72 def reflector @reflector end |
Instance Method Details
#dot_lookup(key) ⇒ Object
82 83 84 |
# File 'lib/pg_graph/type/type.rb', line 82 def dot_lookup(key) super || catalog[key] end |
#guid ⇒ Object
61 |
# File 'lib/pg_graph/type/type.rb', line 61 def guid() name end |
#instantiate(arg = nil) ⇒ Object
:call-seq:
instantiate()
instantiate(hash)
instantiate(yaml)
instantiate(pg_conn)
82 83 84 85 |
# File 'lib/pg_graph.rb', line 82 def instantiate(arg = nil) constrain arg, NilClass, Hash, PgConn Data.new(self, arg) end |
#is_a?(klass) ⇒ Boolean
Make Database pretend it is a PgGraph::Type object
88 |
# File 'lib/pg_graph.rb', line 88 def is_a?(arg) arg == PgGraph::Type || super end |
#read(arg, reflector = nil, ignore: []) ⇒ Object
93 94 95 96 97 98 99 100 101 |
# File 'lib/pg_graph/type/type.rb', line 93 def read(arg, reflector = nil, ignore: []) constrain arg, PgMeta, PgConn @reflector = reflector || @reflector case arg # #read_meta is a member of Database but defined in read.rb when PgMeta; (arg, ignore: ignore) when PgConn; (PgMeta.new(arg), ignore: ignore) end end |
#read_meta(meta, ignore: []) ⇒ Object
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 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'lib/pg_graph/type/read.rb', line 6 def (, ignore: []) # Array of [record, meta_column] tuples field_columns = [] link_columns = [] kind_columns = [] # The link side id_link_columns = [] # sub tables # Temporary arrays of meta tables tables = [] # Ordinary tables mm_tables = [] # M:M link tables # Create schemas and tables and initialize lists of objects .schemas.values.each { || next if ignore.include?(.name) schema = Schema.new(self, .name) .tables.values.select { |t| t.table? }.each { || # FIXME Ignore views for now (.mm_table? ? mm_tables : tables) << table = Table.new( schema, .name, mm_table: .mm_table?, depending_materialized_views: .depending_views.select(&:materialized?)) record_type = RecordType.new(table) # Process columns array_columns = [] .columns.values.each { || # Create basic types needed by columns type_name = .type if !schema.key?(type_name) && !catalog.key?(type_name) if .array? element_name = .element_type dimensions = .dimensions if !schema.key?(element_name) && !catalog.key?(element_name) element_type = SimpleType.new(catalog, element_name) else element_type = schema[element_name] || catalog[element_name] end ArrayType.new(catalog, type_name, element_type, dimensions) else SimpleType.new(catalog, type_name) end end # Collect columns if .reference? if .name == "id" id_link_columns elsif .name =~ /^(?:.*_)?kind$/ kind_columns else link_columns end else field_columns end << [record_type, ] } } } # Build list of depending tables (tables + mm_tables).each { || table = dot(.path) .depending_tables.each { || depending_table = dot(.path) table.depending_tables << depending_table } } # Create postgres columns except kind_columns (id_link_columns + link_columns + field_columns).each { |record_type, | next if .kind? type = record_type.schema[.type] || catalog[.type] SimpleColumn.new( record_type, .name, .name, type, ordinal: .ordinal, **()) } # link_fields is a list of [uid, record_column] tuples link_fields = [] # Map from referencing table to referenced table to reference. It is # used to detect when the default reverse map doesn't work because of # name collisions reference_count = {} # Create and collect forward-references (link_columns + kind_columns).each { |record_type, | reference_count[record_type] ||= {} .references.each { |constraint| constraint.referencing_columns.size == 1 or raise Error, "Can't handle multi-column keys (for now)" type = dot(constraint.referenced_table.path).type.record_type this_link_column = constraint.referencing_columns.first.name that_link_column = constraint.referenced_columns.first.name # Count references by running the reflector in the link column (this # is expensive because we're doing it again later) (reference_count[record_type] ||= {})[type] ||= 0 reference_count[record_type][type] += 1 if reflector.multi?(this_link_column) field = if .kind? name = reflector.this(.uid) || .name name = .name if record_type[name] # Check for duplicates !record_type.nil? or raise Error, "Duplicate column name: #{name}" # Check again column_type = record_type.schema[.type] || catalog[.type] parent = (name != .name ? record_type : nil) kind_column = SimpleColumn.new( parent, .name, .name, column_type, ordinal: .ordinal, **()) KindRecordColumn.new( record_type, name, .name, type, this_link_column, that_link_column, kind_column, **()) else name = reflector.this(.uid) RecordColumn.new( record_type, name, .name, type, this_link_column, that_link_column, **()) end link_fields << [.uid, field] } } # Create back-reference fields (link_fields).each { |uid, this_column| this_record_type = this_column.record_type this_table = this_record_type.table that_record_type = this_column.type next if this_table.mm_table? # Field name of back-reference if this_column.primary_key? # child record this_column.postgres_column == "id" or raise Error, "Primary keys should be named 'id'" name = this_record_type.name else multi = reference_count[this_record_type][that_record_type] > 1 name = reflector.that(uid, this_column.unique?, multi, table: this_record_type.name) name ||= PgGraph.inflector.pluralize(this_column.table.name) if name.nil? && this_column.kind? end next if !name # Check for name collisions if that_record_type.key?(name) # Check if this is a 1:1 relation with keys on both sides. In that # case, the back-reference is already created that_column = that_record_type[name] if that_column.is_a?(RecordColumn) && that_column.type == this_record_type next # Do nothing # Check if the reference spans across schemes so we can disambiguate # by prefixing the schema name elsif this_column.table.schema != that_column.table.schema name = "#{this_column.table.schema.name}_#{name}" end end # Final check for name collisions !that_record_type.key?(name) or raise Error, "Name collision in reverse map for #{this_column.uid}, trying #{name}" # Create back-references if this_column.unique? RecordColumn.new( that_record_type, name, nil, this_record_type, this_column.that_link_column, this_column.this_link_column) else TableColumn.new(that_record_type, name, this_table.type, this_column.that_link_column, this_column.this_link_column) end } # Setup super/sub_tables id_link_columns.each { |this_record_type, | # Create forward and backward references for id links constraint = .references.first that_record_type = dot(constraint.referenced_table.path).type.record_type SuperRecordColumn.new(this_record_type, that_record_type) SubRecordColumn.new(that_record_type, this_record_type) } # Create N:M or M:M reference fields mm_tables.each { || constraint1 = .referential_constraints.values[0] constraint2 = .referential_constraints.values[1] table1 = dot(constraint1.referenced_table.path) table2 = dot(constraint2.referenced_table.path) mm_table = dot(.path) link_column1 = constraint1.referenced_columns.first.name mm_column1 = constraint1.referencing_columns.first.name mm_column1_uid = constraint1.referencing_columns.first.uid link_column2 = constraint2.referenced_columns.first.name mm_column2 = constraint2.referencing_columns.first.name mm_column2_uid = constraint2.referencing_columns.first.uid column1_name = reflector.that(mm_column1_uid, false, false, table: table2.record_type.name) column2_name = reflector.that(mm_column2_uid, false, false, table: table1.record_type.name) # FIXME: DAGs over an super table creates problems if reflections # doesn't match (eg. role_id/group_id instead of # child_group_id/parent_group_id) # Detect DAGs. This check is performed after column names have been # computed to allow for user defined reflections # if column1_name == column2_name && table1 == table2 # column1_name = reflector.that(mm_column1_uid, false) # column2_name = reflector.that(mm_column2_uid, false) # puts "BINGO" # puts column1_name # puts column2_name # exit 1 # else # end # puts "mm_tables.each" # indent { # puts "mm_column1_uid: #{mm_column1_uid}" # puts "mm_column2_uid: #{mm_column2_uid}" # puts "table1: #{table1}" # puts "column1_name: #{column1_name}" # puts "table2: #{table2}" # puts "column2_name: #{column2_name}" # } if table1.type.record_type.key?(column1_name) raise Error, "Duplicate column name in #{table1.identifier}: #{column1_name}" end if table2.type.record_type.key?(column2_name) raise Error, "Duplicate column name in #{table2.identifier}: #{column2_name}" end klass = .nm_table? ? NmTableColumn : MmTableColumn klass.new( table1.type.record_type, column1_name, table2.type, mm_table.type, link_column1, mm_column1, mm_column2, link_column2) klass.new( table2.type.record_type, column2_name, table1.type, mm_table.type, link_column2, mm_column2, mm_column1, link_column1) } self end |
#tables ⇒ Object
List of all tables in the database
67 |
# File 'lib/pg_graph/type/type.rb', line 67 def tables() schemas.map(&:tables).flatten end |