예제 #1
0
def merge_tables(source: DbData, target: DbData, table_name: str) -> RowsDict:
    source_table = db_helpers.get_table(source, table_name)
    target_table = db_helpers.get_table(target, table_name)

    # source_table.columns.user_id.foreign_keys
    # TODO: pass strategy from user input
    merged_rows = RowsDict(table_name=table_name, strategy=SourceMergeStrategy())

    if db_helpers.table_structures_equal(source_table, target_table):
        for row in source.session.query(source_table).all():
            merged_rows.put(
                hash_row(source_table, row),
                row,
                "source"
            )
        for row in target.session.query(target_table).all():
            merged_rows.put(
                hash_row(target_table, row),
                row,
                "target"
            )
        print("merged:", merged_rows)
        # rows = adjust_relationships(merged_rows)
        # # truncating does not work...
        # db_helpers.truncate_table(target, table_name)
        # db_helpers.insert_rows(target, target_table, rows)
    else:
        print(
            f"WARNING: not merging tables with name '{table_name}' "
            "because their structures are not equal."
        )
    return merged_rows
예제 #2
0
    def test_get_table(self):
        user_table = db_helpers.get_table(self.db, "users")
        order_table = db_helpers.get_table(self.db, "orders")

        self.assertIsInstance(user_table, Table)
        self.assertEqual(user_table.name, "users")
        self.assertIsInstance(order_table, Table)
        self.assertEqual(order_table.name, "orders")
예제 #3
0
    def setUp(self):
        Base = declarative_base()
        class User(Base): # type: ignore
            __tablename__ = "users"
            id = Column(Integer, primary_key=True)
            name = Column(String)
            password = Column(String)
            orders = relationship("Order")
        self.User = User

        class Order(Base): # type: ignore
            __tablename__ = "orders"
            id = Column(Integer, primary_key=True)
            # Using string instead of numeric to avoid sqlalchemy's warning.
            total = Column(String)
            user_id = Column(Integer, ForeignKey("users.id"))
            user = relationship("User", back_populates="orders")
        self.Order = Order

        engine = create_engine(self.DB_URL)
        engine2 = create_engine(self.DB2_URL)
        Base.metadata.create_all(bind=engine)
        Base.metadata.create_all(bind=engine2)

        self.db = db_helpers.get_reflected_db(self.DB_URL)
        self.db2 = db_helpers.get_reflected_db(self.DB2_URL)

        # DATA SETUP
        db_helpers.insert_rows(self.db, db_helpers.get_table(self.db, "users"), [
            (1, "testuser1", "pw1"),
            (2, "testuser2 with more data", "pw2"),
            (3, "testuser3", "pw3"),
            (4, "testuser4", "pw4"),
        ])
        db_helpers.insert_rows(self.db, db_helpers.get_table(self.db, "orders"), [
            (1, "30.12", 1),
            (2, "12.39", 2),
            (3, "42.00", 3),
            (4, "43.00", 4),
        ])
        db_helpers.insert_rows(self.db2, db_helpers.get_table(self.db2, "users"), [
            (2, "testuser2", "pw21"),
            (3, "testuser3 with more data", "pw3"),
            (7, "testuser4", "pw4"),
        ])
        db_helpers.insert_rows(self.db2, db_helpers.get_table(self.db2, "orders"), [
            (2, "13.37", 2),
            (5, "51.10", 3),
            (6, "1.18", 7),
        ])
예제 #4
0
def merge_dbs(source: DbData, target: DbData) -> None:
    common_tables = (
        set(table_name for table_name in source.inspector.get_table_names())
        &
        set(table_name for table_name in target.inspector.get_table_names())
    )
    logging.debug(
        f"common_tables of {source.engine.url} and {target.engine.url}:"
        + str(sorted(common_tables))
    )
    # TODO: analyze foreign keys:
    # Each database is expected to have valid foreign keys.
    # That means all FKs of a database reference the databas'es tables only.
    # Thus there are 2 dependency graphs (1 for each database).
    # They most likely have tables (in the dep. graph) in common.
    # Since we assume the database's tables to have the same structure we know
    # that tables with the same name also have the same dependencies.
    # EXAMPLE:
    # Graph 1:
    # A -> B -> B ...
    # In the case of cycles (B, e.g. tree structures) assigning primary and
    # foreign keys MUST BE 2 STEPS.

    merged_tables = []

    # The sorting will place Table objects that have dependencies first,
    # before the dependencies themselves, representing the order
    # in which they can be created.
    source_tables = reversed(source.base.metadata.sorted_tables)

    for source_table in source_tables:
        table_name = source_table.name
        # source_table = source_tables[table_name]
        if table_name in common_tables:
            print("merging table", table_name)
            merged_tables.append(merge_tables(source, target, table_name))
        else:
            print("copying table", table_name)
            db_helpers.copy_table(source, target, table_name)

    merged_and_adjusted_relations_tables = adjust_relationships(target, merged_tables)
    for table_name, rows_to_insert in merged_and_adjusted_relations_tables.items():
        db_helpers.truncate_table(target, table_name)
        db_helpers.insert_rows(
            target,
            db_helpers.get_table(target, table_name),
            rows_to_insert
        )
예제 #5
0
 def test_insert_rows(self):
     db_helpers.insert_rows(self.db, db_helpers.get_table(self.db, "users"),
                            DbHelpersTest.get_test_data())
     queried_rows = self.db.session.query(self.tables["users"]).all()
     self.assertEqual(queried_rows, DbHelpersTest.get_test_data())