class ProductPlan(Plan): """ The Plan class corresponding to the <i>product</i> relational algebra operator. """ def __init__(self, p1: Plan, p2: Plan): """ Creates a new product node in the query tree, having the two specified subqueries. :param p1: the left-hand subquery :param p2: the right-hand subquery """ self._p1 = p1 self._p2 = p2 self._schema = Schema() self._schema.add_all(p1.schema()) self._schema.add_all(p2.schema()) def open(self): """ Creates a product scan for this query. """ s1 = self._p1.open() s2 = self._p2.open() return ProductScan(s1, s2) def blocks_accessed(self): """ Estimates the number of block accesses in the product. The formula is: B(product(p1,p2)) = B(p1) + R(p1)*B(p2) """ return self._p1.blocks_accessed() + self._p1.records_output() * self._p2.blocks_accessed() def records_output(self): """ Estimates the number of output records in the product. The formula is: R(product(p1,p2)) = R(p1)*R(p2) """ return self._p1.records_output() * self._p2.records_output() def distinct_values(self, fldname): """ Estimates the distinct number of field values in the product. Since the product does not increase or decrease field values, the estimate is the same as in the appropriate underlying query. """ if self._p1.schema().has_field(fldname): return self._p1.distinct_values(fldname) else: return self._p2.distinct_values(fldname) def schema(self): """ Returns the schema of the product, which is the union of the schemas of the underlying queries. """ return self._schema
class ProductPlan(Plan): """ The Plan class corresponding to the <i>product</i> relational algebra operator. """ def __init__(self, p1: Plan, p2: Plan): """ Creates a new product node in the query tree, having the two specified subqueries. :param p1: the left-hand subquery :param p2: the right-hand subquery """ self._p1 = p1 self._p2 = p2 self._schema = Schema() self._schema.add_all(p1.schema()) self._schema.add_all(p2.schema()) def open(self): """ Creates a product scan for this query. """ s1 = self._p1.open() s2 = self._p2.open() return ProductScan(s1, s2) def blocks_accessed(self): """ Estimates the number of block accesses in the product. The formula is: B(product(p1,p2)) = B(p1) + R(p1)*B(p2) """ return self._p1.blocks_accessed( ) + self._p1.records_output() * self._p2.blocks_accessed() def records_output(self): """ Estimates the number of output records in the product. The formula is: R(product(p1,p2)) = R(p1)*R(p2) """ return self._p1.records_output() * self._p2.records_output() def distinct_values(self, fldname): """ Estimates the distinct number of field values in the product. Since the product does not increase or decrease field values, the estimate is the same as in the appropriate underlying query. """ if self._p1.schema().has_field(fldname): return self._p1.distinct_values(fldname) else: return self._p2.distinct_values(fldname) def schema(self): """ Returns the schema of the product, which is the union of the schemas of the underlying queries. """ return self._schema
def join_pred(self, sch1, sch2): """ Returns the subpredicate consisting of terms that apply to the union of the two specified schemas, but not to either schema separately. :param sch1: the first schema :param sch2: the second schema :return: the subpredicate whose terms apply to the union of the two schemas but not either schema separately. """ assert isinstance(sch1, Schema) assert isinstance(sch2, Schema) result = Predicate() newsch = Schema() newsch.add_all(sch1) newsch.add_all(sch2) for t in self._terms: if not t.applies_to(sch1) and not t.applies_to(sch2) and t.applies_to(newsch): result._terms.append(t) if len(result._terms) == 0: return None else: return result
def join_pred(self, sch1, sch2): """ Returns the subpredicate consisting of terms that apply to the union of the two specified schemas, but not to either schema separately. :param sch1: the first schema :param sch2: the second schema :return: the subpredicate whose terms apply to the union of the two schemas but not either schema separately. """ assert isinstance(sch1, Schema) assert isinstance(sch2, Schema) result = Predicate() newsch = Schema() newsch.add_all(sch1) newsch.add_all(sch2) for t in self._terms: if not t.applies_to(sch1) and not t.applies_to( sch2) and t.applies_to(newsch): result._terms.append(t) if len(result._terms) == 0: return None else: return result
class IndexJoinPlan(Plan): """ The Plan class corresponding to the <i>indexjoin</i> relational algebra operator. """ def __init__(self, p1: Plan, p2: Plan, ii: IndexInfo, joinfield: str, tx: Transaction): """ Implements the join operator, using the specified LHS and RHS plans. :param p1: the left-hand plan :param p2: the right-hand plan :param ii: information about the right-hand index :param joinfield: the left-hand field used for joining :param tx: the calling transaction """ self._sch = Schema() self._p1 = p1 self._p2 = p2 self._ii = ii self._joinfield = joinfield self._sch.add_all(p1.schema()) self._sch.add_all(p2.schema()) def open(self): """ Opens an indexjoin scan for this query """ s = self._p1.open() # throws an exception if p2 is not a tableplan assert isinstance(self._p2, TablePlan) ts = self._p2.open() idx = self._ii.open() assert isinstance(idx, Index) return IndexJoinScan(s, idx, self._joinfield, ts) def blocks_accessed(self): """ Estimates the number of block accesses to compute the join. The formula is: B(indexjoin(p1,p2,idx)) = B(p1) + R(p1)*B(idx) + R(indexjoin(p1,p2,idx) """ return self._p1.blocks_accessed() + self._p1.records_output() * self._ii.blocks_accessed() + \ self.records_output() def records_output(self): """ Estimates the number of output records in the join. The formula is: R(indexjoin(p1,p2,idx)) = R(p1)*R(idx) """ return self._p1.records_output() * self._ii.records_output() def distinct_values(self, fldname): """ Estimates the number of distinct values for the specified field. """ if self._p1.schema().has_field(fldname): return self._p1.distinct_values(fldname) else: return self._p2.distinct_values(fldname) def schema(self): """ Returns the schema of the index join. """ return self._sch