def get_trigger_tables(): """Determines which tables need to have triggers set on them. Returns a dictionary of table names (key) with a dictionary (value) that provides additional information about a table: * list of primary keys for each table. * whether it's an entity table """ tables = collections.OrderedDict() # mapping of table names to their models and their "kind" (direct or not) for _, entity in SCHEMA.items(): # Entity table itself mapped_class = class_mapper(entity.model) tables[mapped_class.mapped_table.name] = { "model": entity.model, "is_direct": True, "has_gid": mapped_class.has_property('gid'), } # Tables that contain the referenced column # TODO(roman): maybe come up with a better description above for path in unique_split_paths([path for field in entity.fields for path in field.paths if field.trigger]): model = last_model_in_path(entity.model, path) if model is not None: table_name = class_mapper(model).mapped_table.name if table_name not in tables: tables[table_name] = { "model": model, "is_direct": False, } return tables
def generate_update_map(): """ Generates mapping from tables to Solr cores (entities) that depend on these tables and the columns of those tables. In addition provides a path along which data of an entity can be retrieved by performing a set of JOINs and a map of table names to SQLAlchemy ORM models Uses paths to determine the dependency. :rtype (dict, dict, dict) """ from sir.trigger_generation.paths import (unique_split_paths, last_model_in_path, second_last_model_in_path) paths = defaultdict(set) models = {} column_map = defaultdict(set) for core_name, entity in SCHEMA.items(): # Entity itself: # TODO(roman): See if the line below is necessary, if there is a better way to implement this. mapped_table = class_mapper(entity.model).mapped_table.name paths[mapped_table].add((core_name, None)) models[mapped_table] = entity.model # Related tables: for path in unique_split_paths([path for field in entity.fields for path in field.paths] + [path for path in entity.extrapaths or []]): model = last_model_in_path(entity.model, path) if model is not None: name = class_mapper(model).mapped_table.name paths[name].add((core_name, path)) if name not in models: models[name] = model # For generating column map model, _ = second_last_model_in_path(entity.model, path) prop_name = path.split(".")[-1] actual_columns = [col.name for col in class_mapper(model).columns] try: prop = getattr(model, prop_name).prop # We only care about columns, not relations if isinstance(prop, (ColumnProperty, CompositeProperty)): # In case of Composite properties, there might be more # than 1 columns involved column_names = [col.name for col in prop.columns if col.name in actual_columns] column_map[model.__table__.name].update(column_names) elif isinstance(prop, RelationshipProperty): if prop.direction.name == 'MANYTOONE': if prop.key in actual_columns: column_map[model.__table__.name].add(prop.key) # This happens in case of annotation and url paths # which have path to figure out the table name via transform funcs except AttributeError: pass return dict(paths), dict(column_map), models
def generate_update_map(): """ Generates mapping from tables to Solr cores (entities) that depend on these tables and the columns of those tables. In addition provides a path along which data of an entity can be retrieved by performing a set of JOINs and a map of table names to SQLAlchemy ORM models and other useful mappings. Uses paths to determine the dependency. :rtype (dict, dict, dict, dict) """ from sir.trigger_generation.paths import (unique_split_paths, last_model_in_path, second_last_model_in_path) paths = defaultdict(set) column_map = defaultdict(set) # Used to map table names to mbdata.models for indexing. models = {} # Used to map table names to core names while handling entity deletion. core_map = {} for core_name, entity in SCHEMA.items(): # Entity itself: # TODO(roman): See if the line below is necessary, if there is a better way to implement this. mapped_table = class_mapper(entity.model).mapped_table.name core_map[mapped_table] = core_name paths[mapped_table].add((core_name, None)) models[mapped_table] = entity.model # Related tables: for path in unique_split_paths([path for field in entity.fields for path in field.paths if field.trigger] + [path for path in entity.extrapaths or []]): model = last_model_in_path(entity.model, path) if model is not None: name = class_mapper(model).mapped_table.name paths[name].add((core_name, path)) if name not in models: models[name] = model # For generating column map model, _ = second_last_model_in_path(entity.model, path) prop_name = path.split(".")[-1] try: prop = getattr(model, prop_name).prop # We only care about columns, not relations if isinstance(prop, (ColumnProperty, CompositeProperty)): # In case of Composite properties, there might be more # than 1 columns involved column_names = [col.name for col in prop.columns] column_map[model.__table__.name].update(column_names) elif isinstance(prop, RelationshipProperty): if prop.direction.name == 'MANYTOONE': # We are assuming MB-DB uses only non-composite FKs. # In case this changes in the future, `test.DBTest.test_non_composite_fk` # will fail. column_map[model.__table__.name].add(list(prop.local_columns)[0].name) # This happens in case of annotation and url paths # which have path to figure out the table name via transform funcs except AttributeError: pass return dict(paths), dict(column_map), models, core_map