/*{ ** Name: opj_ctree - compare tree for purposes of "union equality" ** ** Description: ** This routine will return TRUE if the qualifications are ** structurally equivalent, and reference the same var nodes ** ** Inputs: ** subquery ptr to subquery containing qual ** qual qual to compare ** usubquery ptr to union subquery ** uqual ptr to qual in union subquery ** ** Outputs: ** Returns: ** TRUE if qualifications are equivalent ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 27-jun-89 (seputis) ** initial creation [@history_template@]... */ static bool opj_ctree( OPS_SUBQUERY *subquery, PST_QNODE *qual, OPV_IVARS varno, OPS_SUBQUERY *usubquery, PST_QNODE *uqual) { for (;qual; qual = qual->pst_right, uqual = uqual->pst_right) { if (!uqual) break; if (qual->pst_sym.pst_type == PST_VAR) { DB_ATT_ID attid; if (uqual->pst_sym.pst_type != PST_VAR) return(FALSE); opj_uvar(subquery, qual, varno, &attid); /* get the ** union view attribute */ return (attid.db_att_id == uqual->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id); /* make sure the same attribute number ** is referenced */ } else { /* use regular comparision routine for all other nodes */ PST_QNODE *lqual; PST_QNODE *rqual; PST_QNODE *luqual; PST_QNODE *ruqual; bool ret_val; lqual = qual->pst_left; rqual = qual->pst_right; qual->pst_left = (PST_QNODE *)NULL; qual->pst_right = (PST_QNODE *)NULL; luqual = uqual->pst_left; ruqual = uqual->pst_right; uqual->pst_left = (PST_QNODE *)NULL; uqual->pst_right = (PST_QNODE *)NULL; ret_val = opv_ctrees(subquery->ops_global, qual, uqual); qual->pst_left= lqual; qual->pst_right = rqual; uqual->pst_left= luqual; uqual->pst_right = ruqual; if (!ret_val) return(FALSE); } if (qual->pst_left) { if (!opj_ctree(subquery, qual->pst_left, varno, usubquery, uqual->pst_left)) return(FALSE); } } return(qual == uqual); }
/*{ ** Name: opa_rqual - remove unnecessary clauses in the qualification ** ** Description: ** This routine will traverse the qualification of the outer aggregate ** which has just been optimized and remove any unnecessary ** qualifications, which may occur due to the optimization ** ** Inputs: ** subquery ptr to subquery containing qualification ** previous ptr to ptr to qualification list ** of outer aggregate ** ** Outputs: ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 16-apr-86 (seputis) ** initial creation [@history_line@]... */ static VOID opa_rqual( OPS_SUBQUERY *subquery, PST_QNODE **previous) { register PST_QNODE *bop; /* ptr to binary operator node associated ** with the qualification */ register PST_QNODE *qual; /* ptr to qualification being analyzed */ for (qual = *previous; /* get initial qualification */ qual && (qual->pst_sym.pst_type == PST_AND); /* check for end of list */ qual = *previous) /* get the next qualification ** in list */ { /* if this is an EQ node then check for an unnecessary compare */ bop = qual->pst_left; if (bop->pst_sym.pst_type == PST_BOP && bop->pst_sym.pst_value.pst_s_op.pst_opno == subquery->ops_global->ops_cb->ops_server->opg_eq && opv_ctrees(subquery->ops_global, bop->pst_left, bop->pst_right) ) { /* eliminate the equality clause if the left and right side ** are equal */ subquery->ops_vmflag = FALSE; /* map invalid since qual is removed*/ *previous = qual->pst_right; /* throw away link */ continue; /* previous ptr is still valid so go to top of ** loop without updating previous */ } previous = &(*previous)->pst_right; } }
/*{ ** Name: opa_commit - commit the subsitutions found in opa_checkopt ** ** Description: ** This routine will traverse the outer aggregate in the same way ** as opa_checkopt but in this traversal, all the substitutions will ** be made, since it has been determined that they were useful. ** ** Inputs: ** global global state variable ** bylist bylist used for substitutions ** varno var number of inner aggregate ** ** Outputs: ** root ptr to subtree which will have ** all possible substitutions made ** Returns: ** E_DB_OK ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 16-apr-86 (seputis) ** initial creation ** 5-dec-90 (seputis) ** fix b34217 - define eqc for update when agg substitution occurs. ** 6-may-91 (seputis) ** fix b37347 - do not substitute constant nodes, since simple aggregates have not been fully processed at this point [@history_line@]... */ static VOID opa_commit( OPS_STATE *global, PST_QNODE *bylist, PST_QNODE **root, OPV_IGVARS varno) { PST_QNODE *node; /* ptr to current by list element being ** substituted */ if ( *root ) { for(node = bylist; /* get first element of by list */ node && node->pst_sym.pst_type != PST_TREE; /* at the end of ** the bylist ? */ node = node->pst_left) /* get next bydom attribute */ { if ( opv_ctrees( global, *root, node->pst_right ) ) { /* query tree matched so perform substitution */ DB_ATT_ID attribute; /* attribute number of inner aggregate ** bylist element */ attribute.db_att_id = node->pst_sym.pst_value.pst_s_rsdm. pst_rsno; /* FIXME - do we need to do a PNODERSLV here??? */ if ((*root)->pst_sym.pst_type != PST_VAR) { /* if node is not a var node then create a new var ** node for the substitution */ if ((*root)->pst_sym.pst_type != PST_CONST) (*root) = opv_varnode(global, &node->pst_sym.pst_dataval, varno, &attribute); /* do not substitute constant ** nodes since it is not required and ** made cause problems if simple aggregates ** are referenced - fix bug 37347 */ } else { if ((global->ops_gmask & OPS_TCHECK) && ((*root)->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id == DB_IMTID) && ( (*root)->pst_sym.pst_value.pst_s_var.pst_vno == global->ops_qheader->pst_restab.pst_resvno )) { /* if this is the TID eqc which will be used for the ** update then keep track of it in ** the global structure, since update by TID will need ** to be via this temporary by list attribute */ global->ops_tvar = varno; STRUCT_ASSIGN_MACRO( attribute, global->ops_tattr); } /* if it is already a var then just change it */ (*root)->pst_sym.pst_value.pst_s_var.pst_vno = varno; STRUCT_ASSIGN_MACRO( attribute, (*root)->pst_sym.pst_value.pst_s_var.pst_atno); } return; } } /* try the subtrees if none of the bylist elements matched */ { if ((*root)->pst_sym.pst_type == PST_VAR) { return; } else { opa_commit(global, bylist, &((*root)->pst_left), varno); opa_commit(global, bylist, &((*root)->pst_right), varno); } } } return; }
/*{ ** Name: opa_checkopt - check for possibility of optimization ** ** Description: ** This routine will create variable map of what the outer aggregate ** would appear like if all possible substitutions of the inner aggregate ** bylist attributes were made. The map of the all the inner aggregate ** bylist elements used in the substitution is also created. Thus, if ** no substitutions are made then this map would be empty. ** ** Inputs: ** global global state variable ** root root of query tree which will ** be analyzed for possible substitutions ** - this root is a subtree of the outer ** aggregate. ** bylist base of bylist which will be ** used for substitutions ** ** Outputs: ** usedmap ptr to map of all variables found ** in the "hit list" i.e. map of variables ** which will be replaced if the ** substitution were actually made ** newmap ptr to varmap of root, filled in if ** the substitutions were actually made ** Returns: ** varmap of root if the substitutions were actually made ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 15-apr-86 (seputis) ** initial creation ** 4-dec-02 (inkdo01) ** Return variable changed to a call-by-ref parm to support ** range table expansion. [@history_line@]... */ static VOID opa_checkopt( OPS_STATE *global, PST_QNODE *bylist, PST_QNODE *root, OPV_GBMVARS *usedmap, OPV_GBMVARS *newmap) { PST_QNODE *node; /* ptr to current by list element being ** substituted */ MEfill(sizeof(*newmap), 0, (char *)newmap); /* init the bit map */ if ( root ) { for(node = bylist; /* get first element of by list */ node && node->pst_sym.pst_type != PST_TREE; /* at the end of ** the bylist ? */ node = node->pst_left) /* get next bydom attribute */ { if ( opv_ctrees( global, root, node->pst_right ) ) { opv_mapvar( root, usedmap); /* update map of global range ** variables used in subtree */ return; /* substitution is made so no vars will ** be contributed to the map of the tree ** after substitution i.e. return 0 */ } } /* try the subtrees if none of the bylist elements matched */ { OPV_GBMVARS tempmap; /* used to create bit map for var node*/ if (root->pst_sym.pst_type == PST_VAR) { OPV_GRV *gvarp; /* global range var associated ** with this node */ BTset( (i4)root->pst_sym.pst_value.pst_s_var.pst_vno, (char *)newmap); gvarp = global->ops_rangetab.opv_base-> opv_grv[root->pst_sym.pst_value.pst_s_var.pst_vno]; if (gvarp->opv_gsubselect && gvarp->opv_gsubselect->opv_subquery) { MEcopy((char *)&gvarp->opv_gsubselect->opv_subquery-> ops_correlated, sizeof(tempmap), (char *)&tempmap); BTor(OPV_MAXVAR, (char *)&gvarp->opv_gsubselect->opv_subquery-> ops_fcorelated, (char *)&tempmap); if (BTcount((char *)&tempmap, OPV_MAXVAR)) { /* if a correlated subquery is referenced then the ** correlated variables cannot be substituted ** - FIXME need to find all correlated vars, and see if ** enough attributes exist to supply correlated values ** this can be done by running opa_subselect in OPAFINAL.C ** prior to this optimization ** ... also need to make sure the correlated subquery ** is not used in another context as determined by ** opa_compat ** - for now do not substitute correlated variables */ BTor( (i4)BITS_IN(OPV_GBMVARS), (char *) &gvarp->opv_gsubselect->opv_subquery->ops_correlated, (char *)newmap); BTor( (i4)BITS_IN(OPV_GBMVARS), (char *) &gvarp->opv_gsubselect->opv_subquery->ops_fcorelated, (char *)newmap); } } return; /* return map with bit set for this var ** since a substition will not be made, so ** the var will appear in the optimized tree ** if it is created */ } else { opa_checkopt( global, bylist, root->pst_left, usedmap, newmap); MEcopy((char *)newmap, sizeof(tempmap), (char *)&tempmap); BTand(OPV_MAXVAR, (char *)usedmap, (char *)&tempmap); if (BTcount((char *)&tempmap, OPV_MAXVAR)) /* if the maps have an intersection then abort the search ** since there will not be a commit made */ return; /* traverse the right side of the tree */ opa_checkopt( global, bylist, root->pst_right,usedmap, &tempmap); BTor(OPV_MAXVAR, (char *)&tempmap, (char *)newmap); return; } } } MEfill(sizeof(*newmap), 0, (char *)newmap); /* return empty map */ return; }
/*{ ** Name: opa_bydom_project - project the "by domains" if necessary ** ** Description: ** set the opa_projection flag if we need to project the by domain values ** ** We don't need to project the by_doms if the aggregate qualification ** is a subset of the qualification in the parent query ** In the future, we can implement the code that would turn off projections ** if the parent restriction was at least as restrictive as the BOOL_EXPR. ** This would cover the following types of examples: ** ret (x=sum(r.a by r.b where r.c < C)) where r.c < C - 1. ** or ** ret (x=sum(r.a by r.b where r.c = A or r.c = B)) where r.c = A ** ** The idea is that if the BOOL_EXPR created by-vals with no agg result ** values then the more restrictive parent qual will throw at ** least those rows out (and possibly more). ** However, determining restrictiveness levels is more complicated and I am ** not sure that it will be needed or will benefit that many people. The ** Intellegent user can always add a qualification to the outer ** that mirrors the inner to remove duplicates. Unfortunately this ** requires knowledge of an implementation. ** ** Inputs: ** subquery ptr to subquery being analzyed ** ** Outputs: ** Returns: ** TRUE - if the "by domain values" require projection ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 13-apr-86 (seputis) ** initial creation from bydom_project [@history_line@]... */ static bool opa_bydomproject( OPS_SUBQUERY *subquery) { PST_QNODE *inner; /* ptr to qualification list for inner ** aggregate */ PST_QNODE *outer; /* ptr to qualification list for outer ** aggregate */ if (subquery->ops_root->pst_right && (subquery->ops_root->pst_right->pst_sym.pst_type == PST_QLEND) ) return(FALSE); /* there is no qualification so do not ** project */ outer = subquery->ops_agg.opa_father->ops_root->pst_right;/* this ** node is the right branch of the parent ** subquery root node, which should be the ** qualification in both the aghead case ** and root node case */ /* Since there is a list of subqueries, the multi_parent failure can ** be eliminated if code was written to test each of the subqueries ** which will be executed together (FIXME) */ if ((outer && (outer->pst_sym.pst_type == PST_QLEND)) /* if no ** parent qualification */ || subquery->ops_agg.opa_multi_parent ) /* OR more than one parent for this ** aggregate */ return (TRUE); /* project since aggregate qualification ** is not a subset of the parent's */ opj_normalize(subquery, &subquery->ops_root->pst_right); /* normalize inner subquery if necessary*/ opj_normalize(subquery->ops_agg.opa_father, &subquery->ops_agg.opa_father->ops_root->pst_right); /* normalize ** outer subquery if necessary*/ outer = subquery->ops_agg.opa_father->ops_root->pst_right;/* this ** node is the right branch of the parent ** subquery root node, which should be the ** qualification in both the aghead case ** and root node case */ /*find a match for each qualification in the aggregate qualification list */ for (inner = subquery->ops_root->pst_right; /* qualification is on the ** right branch of the root node */ inner->pst_sym.pst_type != PST_QLEND; inner = inner->pst_right) /*get next inner conjunct of qualification*/ { bool status; /* TRUE if projection is required */ PST_QNODE *tmp; /* used to traverse parent qualification ** list */ status = TRUE; /* assume projection is required until ** a match is found in the parent's list */ for (tmp = outer; tmp->pst_sym.pst_type != PST_QLEND; tmp = tmp->pst_right) { if (opv_ctrees(subquery->ops_global, inner->pst_left, tmp->pst_left) ) { /* compare the inner aggregate conjunct tree with the ** each conjunct of the outer aggregate qualification, ** - if a match is found then try the next inner conjunct ** - if a match is not found then the qualification ** may not be a subset so a projection is required */ status = FALSE; /* do not project */ break; } } if (status) return(TRUE); /* quit if no matching qual in outer qual ** - project is required since qualification ** is not a subset */ } return (FALSE); /* do not project since the aggregate qualification ** is a subset of the parent's qualification */ }