def cleanup_entities(entities): """ Try to revert back the list of entities passed as argument to the state they had just before their setup phase. It will not work entirely for autosetup entities as we need to remove the autosetup triggers. As of now, this function is *not* functional in that it doesn't revert to the exact same state the entities were before setup. For example, the properties do not work yet as those would need to be regenerated (since the columns they are based on are regenerated too -- and as such the corresponding joins are not correct) but this doesn't happen because of the way relationship setup is designed to be called only once (especially the backref stuff in create_properties). """ for entity in entities: desc = entity._descriptor if desc.autosetup: _cleanup_autosetup_triggers(entity) if hasattr(entity, '_setup_done'): del entity._setup_done entity.table = None entity.mapper = None desc._pk_col_done = False desc.has_pk = False desc._columns = ColumnCollection() desc.constraints = [] desc.properties = {}
def __init__(self, entity): self.entity = entity self.parent = None bases = [] for base in entity.__bases__: if isinstance(base, EntityMeta): if is_entity(base) and not is_abstract_entity(base): if self.parent: raise Exception( '%s entity inherits from several entities, ' 'and this is not supported.' % self.entity.__name__) else: self.parent = base bases.extend(base._descriptor.bases) self.parent._descriptor.children.append(entity) else: bases.append(base) self.bases = bases if not is_entity(entity) or is_abstract_entity(entity): return # entity.__module__ is not always reliable (eg in mod_python) self.module = sys.modules.get(entity.__module__) self.builders = [] #XXX: use entity.__subclasses__ ? self.children = [] # used for multi-table inheritance self.join_condition = None self.has_pk = False self._pk_col_done = False # columns and constraints waiting for a table to exist self._columns = ColumnCollection() self.constraints = [] # properties (it is only useful for checking dupe properties at the # moment, and when adding properties before the mapper is created, # which shouldn't happen). self.properties = {} # self.relationships = [] # set default value for options self.table_args = [] # base class(es) options_defaults options_defaults = self.options_defaults() complete_defaults = options.options_defaults.copy() complete_defaults.update({ 'metadata': elixir.metadata, 'session': elixir.session, 'collection': elixir.entities }) # set default value for other options for key in options.valid_options: value = options_defaults.get(key, complete_defaults[key]) if isinstance(value, dict): value = value.copy() setattr(self, key, value) # override options with module-level defaults defined for key in ('metadata', 'session', 'collection'): attr = '__%s__' % key if hasattr(self.module, attr): setattr(self, key, getattr(self.module, attr)) if (tools.config['ecfserver.dbschema.name'] not in (None, '')): self.table_options['schema'] = tools.config['ecfserver.dbschema.name'] else: self.table_options['schema'] = None
def adapt_schema(source: Engine, destination: Engine, tenant="bolivia", echo=None): # noqa: C901 tables = get_all_tables(source, tenant, public=False) destMeta = MetaData(destination, schema="public") # pk = Column('id', Integer, primary_key=True) fks = [] processed = [] if echo: write = echo.write else: write = lambda x: True constraints = [] write(f"Start adapting schema {tenant}\n") for table, deps in tables: if table is None: continue if table.name in processed: continue # if table.name not in ['audit_spotcheck', 'audit_engagement']: # continue if table.schema and table.schema != 'public': write(".") table.schema = "public" new_columns = ColumnCollection() primary_key = { c for c in table.constraints if isinstance(c, PrimaryKeyConstraint) } table.constraints = { c for c in table.constraints if not isinstance(c, PrimaryKeyConstraint) } # remove self relation # to_remove = [] # for constraint in table.constraints: # if isinstance(constraint, ForeignKeyConstraint) and constraint.referred_table == table: # self_relations.append({'columns': constraint.column_keys, # 'table': table.name, # 'constraint': constraint} # ) # to_remove.append(constraint) # table.constraints = table.constraints.difference(to_remove) # if table.name == 'audit_spotcheck': # import pdb; pdb.set_trace() for col in table.columns: if col.primary_key: _seq = Column('pk', Integer, autoincrement=True, unique=True) pk = Column(col.name, Integer, primary_key=True) country_name = Column('country_name', String, default='public', primary_key=True) new_columns.add(_seq) new_columns.add(pk) new_columns.add(country_name) table.primary_key.columns.add(country_name) if col.foreign_keys: fk = [c for c in col.foreign_keys][0] table.constraints.remove(fk.constraint) fks.append({ 'columns': (pk.name, country_name.name), 'refcolumns': fk.constraint.referred_table.primary_key.columns. keys(), 'source': table, 'target': fk.constraint.referred_table }) elif col.foreign_keys: fk = [c for c in col.foreign_keys][0] if fk.constraint.referred_table.schema: _fk = Column(col.name, Integer, nullable=col.nullable) _country_name = Column( get_schema_fieldname( col), # f'{target}_country_name', String, nullable=col.nullable) table.constraints.remove(fk.constraint) new_columns.add(_fk) new_columns.add(_country_name) fks.append({ 'columns': (_fk.name, _country_name.name), 'refcolumns': fk.constraint.referred_table.primary_key.columns. keys(), 'source': table, 'target': fk.constraint.referred_table }) else: new_columns.add(col) else: new_columns.add(col) table.columns = new_columns constraints.extend(table.constraints) table.constraints = primary_key else: pass processed.append(table.name) try: table.tometadata(destMeta) except Exception as e: raise Exception(f"Error processing {table.name}") from e destMeta.create_all() write("\nCreating foreign keys constraints\n") for entry in fks: clause = f"ALTER TABLE {entry['source'].name} \ ADD CONSTRAINT FK_{entry['target'].name} \ FOREIGN KEY({','.join(entry['columns'])}) \ REFERENCES {entry['target'].name}({','.join(entry['refcolumns'])})" try: destination.execute(str(clause)) except Exception as e: print(str(e)) print(CreateTable(entry['source'])) print(clause) raise Exception(clause) from e write("Creating remaining constraints\n") for constraint in constraints: clause = AddConstraint(constraint) # if type(constraint.sqltext) is BinaryExpression: # continue try: destination.execute(str(clause)) except Exception as e: if isinstance(clause.element.columns.values()[0].type, BOOLEAN): pass else: print(e) print(CreateTable(constraint.table)) print(clause) raise Exception(clause) from e # write("\nCreating self-relations constraint\n") # for self_relation in self_relations: # constraint = self_relation['constraint'] # clause = f"ALTER TABLE {constraint.table.name} " \ # f"ADD CONSTRAINT self_relation_{constraint.table.name} " \ # f"FOREIGN KEY({constraint.column_keys[0]}) " \ # f"REFERENCES {constraint.table.name}(pk)" # # TODO: remove me # print(111, "ddl.py:167", clause) # destination.execute(clause) # return tables