def contexts(all_relations, base, dependencies): """ Build Contexts - sets of relations that satisfy Lossless join property based on the `base` relations provided. :param all_relations: list of all relations to add to the context :param base: list of strings - names of the relations that are initially in the context :param dependencies: list of dependencies held in the DB :return yield list of relations representing context """ base_relations = [r for r in all_relations if r['name'] in base] relations_to_check = [r for r in all_relations if r['name'] not in base] relations_to_check, priorities = zip(*prioritized_relations( relations_to_check, base_relations, dependencies)) n = len(relations_to_check) for k in range(n + 1): relation_packs = itertools.combinations(relations_to_check, k) for relations in relation_packs: context = base_relations + list(relations) satisfied_deps = filter( lambda d: set(d['left'] | d['right']).issubset( all_attributes(context)), dependencies) if is_lossless(context, satisfied_deps): yield context
def test_simplest_case(self): """ Test chase algorithm in the simplest succeeding case with single dependency held on same attribute value """ r1 = {'name': 'R1', 'attributes': {'A': 'INT', 'B': 'INT', 'C': 'INT'}} r2 = {'name': 'R2', 'attributes': {'C': 'INT', 'D': 'INT'}} deps = [{'left': ('C', ), 'right': ('D', )}] self.assertTrue(is_lossless([r1, r2], deps))
def test_simplest_case(self): """ Test chase algorithm in the simplest succeeding case with single dependency held on same attribute value """ r1 = {'name': 'R1', 'attributes': {'A': 'INT', 'B': 'INT', 'C': 'INT'}} r2 = {'name': 'R2', 'attributes': {'C': 'INT', 'D': 'INT'}} deps = [{'left': ('C',), 'right': ('D',)}] self.assertTrue(is_lossless([r1, r2], deps))
def test_advanced_lossless_not_held(self): """ This example was also taken from the Ullman's "Database Systems - The Complete book" """ r1 = {'name': 'R1', 'attributes': {'A': 'INT', 'B': 'INT'}} r2 = {'name': 'R2', 'attributes': {'B': 'INT', 'C': 'INT'}} r3 = {'name': 'R3', 'attributes': {'C': 'INT', 'D': 'INT'}} deps = [{'left': ('B', ), 'right': ('A', 'D')}] self.assertFalse(is_lossless([r1, r2, r3], deps))
def test_cartesian_product_separate_deps(self): r1 = {'name': 'R1', 'attributes': {'A': 'INT', 'B': 'INT'}} r2 = {'name': 'R2', 'attributes': {'C': 'INT', 'D': 'INT'}} deps = [{ 'left': ('A', ), 'right': ('B', ) }, { 'left': ('C', ), 'right': ('D', ) }] self.assertFalse(is_lossless([r1, r2], deps))
def test_single_relation(self): r1 = { 'name': 'R1', 'attributes': { 'pk': 'INT', 'A_1': 'INT', 'A_2': 'INT' } } deps = [{'left': {'pk'}, 'right': {'A_1', 'A_2'}}] self.assertTrue(is_lossless([r1], deps))
def test_separate_deps_common_attributes(self): """ Check the case when relations intersect over some attributes but don't have any dependencies uniting them """ r1 = {'name': 'R1', 'attributes': {'A': 'INT', 'B': 'INT', 'C': 'INT'}} r2 = {'name': 'R2', 'attributes': {'B': 'INT', 'D': 'INT', 'E': 'INT'}} deps = [ {'left': ('A', 'B'), 'right': ('C',)}, # R1 primary key {'left': ('B', 'D'), 'right': ('E',)}, # R2 primary key ] self.assertFalse(is_lossless([r1, r2], deps))
def test_advanced_lossless_not_held(self): """ This example was also taken from the Ullman's "Database Systems - The Complete book" """ r1 = {'name': 'R1', 'attributes': {'A': 'INT', 'B': 'INT'}} r2 = {'name': 'R2', 'attributes': {'B': 'INT', 'C': 'INT'}} r3 = {'name': 'R3', 'attributes': {'C': 'INT', 'D': 'INT'}} deps = [ {'left': ('B',), 'right': ('A', 'D')} ] self.assertFalse(is_lossless([r1, r2, r3], deps))
def lossless_combinations(relations, dependencies): n = len(relations) for k in range(n + 1): combinations = itertools.combinations(relations, k) for combination in combinations: def is_dependency_held(dep): attributes = set(dep['left'] | dep['right']) return attributes.issubset(all_attributes(combination)) satisfied_deps = filter(is_dependency_held, dependencies) if is_lossless(combination, satisfied_deps): yield list(combination)
def test_separate_deps_common_attributes(self): """ Check the case when relations intersect over some attributes but don't have any dependencies uniting them """ r1 = {'name': 'R1', 'attributes': {'A': 'INT', 'B': 'INT', 'C': 'INT'}} r2 = {'name': 'R2', 'attributes': {'B': 'INT', 'D': 'INT', 'E': 'INT'}} deps = [ { 'left': ('A', 'B'), 'right': ('C', ) }, # R1 primary key { 'left': ('B', 'D'), 'right': ('E', ) }, # R2 primary key ] self.assertFalse(is_lossless([r1, r2], deps))
def test_cartesian_product_no_deps(self): r1 = {'name': 'R1', 'attributes': {'A': 'INT', 'B': 'INT'}} r2 = {'name': 'R2', 'attributes': {'C': 'INT', 'D': 'INT'}} deps = [] self.assertFalse(is_lossless([r1, r2], deps))
def test_single_relation_no_deps(self): r1 = {'name': 'R1', 'attributes': {'A': 'INT', 'B': 'INT'}} deps = [] self.assertTrue(is_lossless([r1], deps))
def test_cartesian_product_separate_deps(self): r1 = {'name': 'R1', 'attributes': {'A': 'INT', 'B': 'INT'}} r2 = {'name': 'R2', 'attributes': {'C': 'INT', 'D': 'INT'}} deps = [{'left': ('A',), 'right': ('B',)}, {'left': ('C',), 'right': ('D',)}] self.assertFalse(is_lossless([r1, r2], deps))
def test_single_relation(self): r1 = {'name': 'R1', 'attributes': {'pk': 'INT', 'A_1': 'INT', 'A_2': 'INT'}} deps = [{'left': {'pk'}, 'right': {'A_1', 'A_2'}}] self.assertTrue(is_lossless([r1], deps))