/*{ ** Name: opo_fordering - find or create multi-attribute ordering ** ** Description: {@comment_line@}... ** ** Inputs: [@PARAM_DESCR@]... ** ** Outputs: [@PARAM_DESCR@]... ** Returns: ** {@return_description@} ** Exceptions: ** [@description_or_none@] ** ** Side Effects: ** [@description_or_none@] ** ** History: [@history_template@]... */ OPO_ISORT opo_fordering( OPS_SUBQUERY *subquery, OPE_BMEQCLS *eqcmap) { OPO_ISORT ordering; /* number used to represent ** multi-attribute ordering */ OPO_SORT *orderp; /* ptr to multi-attribute ** ordering descriptor */ OPE_IEQCLS maxeqcls; maxeqcls = subquery->ops_eclass.ope_ev; ordering = BTnext( (i4)-1, (char *)eqcmap, (i4)maxeqcls); if (ordering < 0) return(OPE_NOEQCLS); if ( BTnext( (i4)ordering, (char *)eqcmap, (i4)maxeqcls) < 0) return(ordering); /* only one equivalence class ** so use it as the ordering */ if (!subquery->ops_msort.opo_base) opo_iobase(subquery); { /* search existing list for the multi-attribute ordering */ i4 maxorder; maxorder = subquery->ops_msort.opo_sv; for( ordering = 0; ordering < maxorder; ordering++) { orderp = subquery->ops_msort.opo_base->opo_stable[ordering]; if ( orderp->opo_stype == OPO_SINEXACT) { if (BTsubset((char *)eqcmap, (char *)orderp->opo_bmeqcls, (i4)maxeqcls) && BTsubset((char *)orderp->opo_bmeqcls, (char *)eqcmap, (i4)maxeqcls)) return((OPO_ISORT)(ordering+maxeqcls)); /* correct ordering found ** - FIXME need a bitwise ** equality */ } } } /* create new ordering since current one was not found */ ordering = opo_gnumber(subquery); /* get the next multi-attribute ** ordering number */ orderp = (OPO_SORT *)opn_memory(subquery->ops_global, (i4)sizeof(*orderp)); orderp->opo_stype = OPO_SINEXACT; orderp->opo_bmeqcls = (OPE_BMEQCLS *)opn_memory(subquery->ops_global, (i4)sizeof(OPE_BMEQCLS)); MEcopy((PTR)eqcmap, sizeof(OPE_BMEQCLS), (PTR)orderp->opo_bmeqcls); orderp->opo_eqlist = NULL; subquery->ops_msort.opo_base->opo_stable[ordering] = orderp; return((OPO_ISORT)(ordering+maxeqcls)); }
/*{ ** Name: opj_union - optimize unions ** ** Description: ** The routine determines which qualifications of a parent query ** can be copied to the union, so that the union can be more restrictive ** or partitions of the union can be eliminated entirely in some cases. ** ** Inputs: ** subquery ptr to subquery be analyzed ** ** Outputs: ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 21-jun-89 (seputis) ** initial creation ** 01-Oct-2001 (hanal04) Bug 105464 INGSRV 1517 ** After calling opv_copytree() mark the target subquery ** as non-CNF if the source suquery was non-CNF. ** 11-may-05 (inkdo01) ** Don't copy ON clause BFs to union subqueries - fixes 114493. ** 20-apr-07 (hayke02) ** Limit the fix for bug 114493 (test for a OPL_NOOUTER pst_joinid) ** to those subqueries containing outer joins, otherwise pst_joinid ** will be PST_NOJOIN (0). This change fixes bug 118088. ** 27-mar-2009 (huazh01) ** don't copy OPZ_FANUM, OPZ_SNUM, OPZ_CORRELATED attribute into ** 'unionbm'. Doing so will cause opj_utree() to convert the ** union_subquery->ops_sunion.opu_qual qualification into a ** PST_QNODE node containing negative attribute id, and causes ** E_OP0384 error. (bug 121602) [@history_template@]... */ VOID opj_union( OPS_SUBQUERY *subquery) { OPV_IVARS lvar; for (lvar = subquery->ops_vars.opv_prv; --lvar>=0;) { /* look at the union views referenced in this subquery */ OPV_VARS *unionp; unionp = subquery->ops_vars.opv_base->opv_rt[lvar]; if (unionp->opv_grv && (unionp->opv_grv->opv_created == OPS_VIEW) && unionp->opv_grv->opv_gquery && unionp->opv_grv->opv_gquery->ops_union ) { /* a union view has been found, so search the list of boolean ** factors to determine which terms can be placed into the qualifications ** of the union */ OPE_BMEQCLS unionbm; /* bitmap of the equivalence classes which ** are available in the union view */ OPS_SUBQUERY *union_subquery; OPB_BMBF bfbm; OPB_IBF maxbf; /* current boolean factor number */ maxbf = BITS_IN(bfbm); union_subquery = unionp->opv_grv->opv_gquery; if (union_subquery->ops_sunion.opu_mask & OPU_QUAL) { if (union_subquery->ops_sunion.opu_qual == (PST_QNODE *)NULL) continue; /* since entire union is required by ** another subquery, no need to proceed */ MEfill(sizeof(bfbm), (u_char)0, (PTR)&bfbm); BTnot((i4)BITS_IN(bfbm), (char *)&bfbm); /* bits will get ** reset as identical qualifications are ** found */ } MEfill(sizeof(unionbm), (u_char)0, (PTR)&unionbm); { /* search thru list of attributes and mark ** appropriate equivalence classes*/ /* opv_eqcmp not initialized yet */ OPZ_IATTS attr; OPZ_AT *abase; abase = subquery->ops_attrs.opz_base; /* ptr to base of array of ptrs ** to joinop attributes */ for (attr = subquery->ops_attrs.opz_av; --attr>=0;) { /* opv_eqcmp not initialized yet */ OPZ_ATTS *attrp; attrp = abase->opz_attnums[attr]; if ((attrp->opz_varnm == lvar) && (attrp->opz_equcls >= 0) && attrp->opz_attnm.db_att_id >= 0 ) BTset ((i4)attrp->opz_equcls, (char *)&unionbm); } } { /* search list of conjuncts, boolean factor structure has ** not been created at this point */ OPB_IBF total_bfs; /* number of qualifications placed into ** union view list */ PST_QNODE *qual; total_bfs = 0; for (qual = subquery->ops_root->pst_right; qual->pst_sym.pst_type != PST_QLEND; qual = qual->pst_right) { OPE_BMEQCLS qual_eqcmp; MEfill(sizeof(qual_eqcmp), (u_char)0, (PTR)&qual_eqcmp); ope_aseqcls(subquery, &qual_eqcmp, qual->pst_left); /* assign ** equivalence classes for vars in ** the qualification which have not ** been assigned yet */ if (BTsubset((char *)&qual_eqcmp, (char *)&unionbm, (i4)BITS_IN(unionbm)) && !(subquery->ops_oj.opl_base && (qual->pst_left->pst_sym.pst_value.pst_s_op.pst_joinid != OPL_NOOUTER))) { /* this boolean factor contains equivalence classes which ** are all in the union view, so it can be evaluated inside ** the union view */ if (union_subquery->ops_sunion.opu_mask & OPU_QUAL) { /* qualification must already be in the list ** so compare to existing list of entries */ PST_QNODE *old_qual; OPB_IBF bfcount; bfcount = 0; for (old_qual = union_subquery->ops_sunion.opu_qual; old_qual; old_qual = old_qual->pst_right) { if (BTtest((i4)bfcount, (char *)&bfbm) /* if this ** boolean factor has not been ** matched */ && opj_ctree(subquery, qual->pst_left, lvar, union_subquery, old_qual->pst_left) /* ** and if the qualification ** matches the current one */ ) { /* found a match so mark the boolean ** factor as being in common */ BTclear((i4)bfcount, (char *)&bfbm); break; } bfcount++; } } else { /* if this is the first time then just copy the ** qualification and place into the list */ PST_QNODE *and_node; total_bfs++; if (total_bfs >= BITS_IN(unionbm)) break; /* cannot add anymore qualifications ** since the max boolean factor ** count has been reached */ and_node = opv_opnode(subquery->ops_global, PST_AND, (ADI_OP_ID)0, (OPL_IOUTER)OPL_NOOUTER); and_node->pst_left = qual->pst_left; opv_copytree(subquery->ops_global, &and_node->pst_left); and_node->pst_right = union_subquery->ops_sunion.opu_qual; union_subquery->ops_sunion.opu_qual = and_node; if(subquery->ops_mask & OPS_NONCNF) { union_subquery->ops_mask |= OPS_NONCNF; } } } } } if (union_subquery->ops_sunion.opu_mask & OPU_QUAL) { /* previous qualification exists so need to get common qualifications, ** by using boolean factor bitmap of those qualifications which are ** to be eliminated */ OPB_IBF bfno; PST_QNODE **qualpp; OPB_IBF current; current = 0; qualpp = &union_subquery->ops_sunion.opu_qual; for (bfno = -1; (bfno = BTnext((i4)bfno, (char *)&bfbm, (i4)maxbf)) >= 0;) { while ((current < bfno) && *qualpp) { /* find next qualification to eliminate */ current++; qualpp = &(*qualpp)->pst_right; } if (*qualpp) *qualpp = (*qualpp)->pst_right; /* remove this qual since ** no match was found for this subquery */ else break; /* end of qual list reached */ } } else { /* this is the first qualification in the list, so it can be applied */ union_subquery->ops_sunion.opu_mask |= OPU_QUAL; if (union_subquery->ops_sunion.opu_qual) opj_utree(subquery, union_subquery->ops_sunion.opu_qual, lvar); /* ** convert the ** qualification to reference var nodes ** of the union view, so that comparisons ** can be made later */ } } } }
/*{ ** Name: opn_gnperm - get next valid permutation of relations ** ** Description: ** A valid permutation of relations is a group of relations ** that provide the necessary data (eqc classes) to process ** the query. The equivalence classes provided by the base ** relations must be provided by this resulting permutiation ** either through the use of the base relations themselves ** or through their own secondary indexes. ** ** Relations move back and forth between the 2 partitions. ** There must be exactly 2 partitions where partsz[0] is the number of ** relations to be considered for enumeration and partsz[1] contain the ** remainder. Each call will produce ** a "new" set (where order does not matter) of relations ** in the first partition which satisfy the constraints mentioned ** earlier. ** ** FIXME - can make this routine go much faster by calculating which ** relations can be replaced by indexes and iterating through all ** combinations of those only i.e. there will be 3 partitions, 1)the ** primaries, 2)the set of indexes which cannot ever be ** used to replace the base ** relation, and 3)the set of indexes which can. Iterate through ** all combinations of (1&3), for each subset of 2 ** ** Also, can decide that if an index was included to replace the ** base relation, then any indexes on that relation would not be ** useful if the relation was not included, and eliminate that case here ** since a scan is required anyways. An improvement would be to detect ** that a keyed lookup would be useful on these indexes ** ** Inputs: ** subquery ptr to current subquery being analyzed ** permutation current permutation of relations ** numleaves number of relations in permutation ** partsz array of partition sizes where partsz[0] ** is the size of the first partition ** "partsz[0]+partsz[1] == numleaves" ** pr_n_included map of base relations not included in ** valid partition ** firstcomb TRUE - combination was set by opn_process ** so we're just checking heuristics ** ** Outputs: ** Returns: ** TRUE if another permutation exists ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 11-jun-86 (seputis) ** initial creation ** 29-mar-89 (seputis) ** gateway additions ** 29-mar-90 (seputis) ** fix byte alignment problems ** 1-apr-94 (ed) ** b60125 - cart prod variable incorrectly eliminated ** 27-aug-02 (inkdo01) ** Apply heuristics to initial combination generated by opn_process ** so some index heuristics aren't applied to all permutations of a ** bad combination in opn_jintersect. ** 21-mar-06 (dougi) ** Verify that current combination satisfies index hints (if any). [@history_line@]... */ bool opn_gnperm( OPS_SUBQUERY *subquery, OPN_STLEAVES permutation, OPN_LEAVES numleaves, OPN_PARTSZ partsz, OPV_BMVARS *pr_n_included, bool firstcomb) { OPN_CHILD numpartitions; /* number of partitions */ OPV_IVARS maxprimary; /* number of primary joinop ** range variables in the ** query */ OPV_RT *vbase; /* ptr to base of array of ** ptrs to joinop range ** variables */ OPN_LEAVES firstpartsz; /* number of elements in the ** first partition */ OPE_IEQCLS maxeqcls; /* number of equivalence classes ** defined */ bool indexes_gateway; /* TRUE if subquery contains secondaries ** on restricted gateway tables */ maxeqcls = subquery->ops_eclass.ope_ev; /* get number of equivalence ** classes in subquery */ maxprimary = subquery->ops_vars.opv_prv; /* number of primary relations ** defined */ numpartitions = 2; /* number of partitions of the ** set of relations is always 2 ** - first partition contains ** the set of relations which ** will be considered for ** the next enumeration ** - second partition contains ** relations which are not being ** considered for this ** enumeration */ vbase = subquery->ops_vars.opv_base; /* ptr to base of array of ptrs ** to joinop range variables */ firstpartsz = partsz[0]; /* number of elements in first ** partition */ indexes_gateway = (subquery->ops_gateway.opg_smask & OPG_INDEX) != 0; /* gateway mask indicates whether ** indexes need to be checked */ /* Return when a valid partition has been found */ for (;;) { if (!firstcomb && numleaves == firstpartsz) return(FALSE); /* no combinations and it failed ** heuristics */ /* Get next partition */ if (!firstcomb && !opn_partition(subquery, permutation, numleaves, partsz, numpartitions, FALSE)) return(FALSE); /* no more partitions */ firstcomb = FALSE; /* reset for loop */ MEfill(sizeof(*pr_n_included), (u_char)0, (PTR)pr_n_included); BTnot((i4)maxprimary, (char *)pr_n_included); /* all base relations are ** not included */ { /* For each element in the partition, find out if it is a ** base relation */ OPV_IVARS partvarno; /* joinop range variable in the ** first partition */ OPV_BMVARS replaced; /* bitmap of vars that should ** be replaced */ OPV_BMVARS table_gateway; /* for gateways, there are tables ** which cannot accessed by TID joins ** but secondaries can be used to ** replace the base relation ** entirely */ bool skip_gateway; /* set TRUE if this particular set of ** relations references both a base table ** and a secondary index , which is ** not supported for gateway tables */ if (indexes_gateway) MEfill(sizeof(table_gateway), (u_char)0, (PTR)&table_gateway); skip_gateway = FALSE; MEfill(sizeof(replaced), (u_char)0, (PTR)&replaced); for (partvarno = 0; partvarno < firstpartsz; partvarno++) { OPV_IVARS vno; /* variable being analzyed */ OPV_VARS *ivarp; /* ptr to index range variable*/ vno = permutation[partvarno]; ivarp = vbase->opv_rt[vno]; /* if its included, don't worry about it */ if (vno < maxprimary) BTclear((i4) vno, (char *)pr_n_included); else { if ( ivarp->opv_index.opv_eqclass == OPE_NOEQCLS || ivarp->opv_mask & OPV_CINDEX ) /* set bitmap if index was added only to replace ** base relation */ BTset ( (i4) ivarp->opv_index.opv_poffset, (char *) &replaced ); } if (indexes_gateway && ivarp->opv_grv->opv_relation /* if this is an RDF relation */ && (ivarp->opv_grv->opv_relation->rdr_rel->tbl_status_mask & DMT_GATEWAY) /* is this ** a gateway table */ && (subquery->ops_global->ops_cb->ops_server->opg_smask & OPF_INDEXSUB) /* and the gateway relation has index ** constraints on it */ && (vno != ivarp->opv_index.opv_poffset) /* do not consider the primary ** relation for this test */ ) { /* check constraint on secondary index access for gateway */ if (BTtest((i4)ivarp->opv_index.opv_poffset, (char *)&table_gateway)) { skip_gateway = TRUE; /* this base relation was referenced ** previously by this set, so this set ** of relations needs to be skipped */ break; } BTset((i4)ivarp->opv_index.opv_poffset, (char *)&table_gateway); /* ** if only one index for this table in this ** partition then it is legal, so mark ** base table bit so that 2+ index search ** space is eliminated */ } } if ((!BTsubset((char *)&replaced, (char *)pr_n_included, (i4)maxprimary)) || skip_gateway) continue; /* this is a useless partition ** since an index and the ** respective base relation it ** was intended to replace are ** both included ** OR a restricted gateway table ** was accessed */ if ((subquery->ops_mask2 & OPS_IXHINTS) && !opn_index_hint(subquery, permutation, partsz[0])) continue; /* if there are index hints in ** this subquery and the ** current combination doesn't ** satisfy them, get the next */ } { OPV_IVARS varno; /* varno of primary which was ** not included */ bool noprimary; /* TRUE if no primaries were ** replaced */ noprimary = TRUE; for (varno = -1; (varno = BTnext((i4)varno, (char *)pr_n_included, (i4)maxprimary)) >= 0;) { /* initialize temp equivalence class map associated with ** primary relations - the map will be used to gather ** all equivalence classes which the indexes provide */ noprimary = FALSE; MEfill( sizeof(vbase->opv_rt[varno]->opv_primary.opv_teqcmp), (u_char)0, (PTR)&vbase->opv_rt[varno]->opv_primary.opv_teqcmp);/* ** init temporary work ** area of joinop range ** variable element */ } if (noprimary) return(TRUE); /* no need to check indexes ** since no primaries were ** replaced */ } { /* For each index being included in the partition, add the available ** equivalence classes to the primary's map */ OPV_IVARS indexvarno; /* joinop range var number of ** index being analyzed */ for (indexvarno = 0; indexvarno < firstpartsz; indexvarno++) { OPV_VARS *indexp; /* ptr to joinop range var ** element of current index ** being analyzed */ if(permutation[indexvarno] < maxprimary) continue; /* not an index so continue*/ indexp = vbase->opv_rt[permutation[indexvarno]]; /* get ptr ** to index element */ BTor( (i4)maxeqcls, (char *)&indexp->opv_maps.opo_eqcmap, (char *)&vbase->opv_rt[indexp->opv_index.opv_poffset]-> opv_primary.opv_teqcmp); /* accumulate all ** all equivalence classes ** available from indexes in ** the temp associated with ** the primary */ } } { /* check if each primary which is replaced has all the necessary ** equivalence classes available from the indexes */ OPV_IVARS primvarno; /* joinop range variable ** number of primary which ** is not included */ for (primvarno = -1; (primvarno = BTnext((i4)primvarno, (char *)pr_n_included, (i4) maxprimary)) >= 0;) { OPV_VARS *primvarp; /* ptr to primary joinop range ** var element to be ** replaced */ primvarp = vbase->opv_rt[primvarno]; /* get ptr to primary to ** be replaced */ if (!BTsubset( (char *)&primvarp->opv_maps.opo_eqcmap, (char *)&primvarp->opv_primary.opv_teqcmp, (i4)maxeqcls) || (primvarp->opv_mask & OPV_NOATTRIBUTES)) /* check if this ** is a no attribute cart prod ** in which case the base relation ** is not removed */ break; /* if there are equivalence ** classes in the primary which ** are not in the set provided ** by the indexes then exit ** with primvarno >= 0 */ } if (primvarno < 0) return (TRUE); /* all the primaries have been ** successfully replaced */ } } }
/*{ ** Name: opn_jmaps - set various join operator tree maps ** ** Description: {@comment_line@}... ** ** Inputs: ** subquery ptr to subquery being analyzed ** nodep ptr to current operator node which ** will have maps initialized ** ojmap NULL if no outer joins exist ** - map of outer joins which are ** evaluated in parent nodes. ** ** Outputs: ** nodep->opn_eqm set of equivalence class available ** from subtree ** nodep->opn_rlmap bitmap of relations in the subtree ** nodep->opn_rlasg order in which the relations in ** opn_rlmap are assigned to the leaves ** Returns: ** TRUE if there is a valid placement of subselect nodes with ** all required equivalence classes available for execution of the ** boolean factor used for the subselect ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 11-jun-86 (seputis) ** initial creation from setjmaps ** 29-mar-90 (seputis) ** fix byte alignment problems ** 2-apr-91 (seputis) ** only partially copied relation assignment causing run_all diffs ** fix for b35461, this would cause the OPF cache of query plans ** to not be effectively used, and could cause performance problems ** on non-VMS systems. This fix should be used whenever a poor ** query plan bug is reported which cannot be reproduced on VMS. ** 15-feb-94 (ed) ** - bug 59598 - correct mechanism in which boolean factors are ** evaluated at several join nodes ** 11-apr-94 (ed) ** - bug 59937 - E_OP0489 consistency check due to inner join ojid ** not being visible in boolean factor placement maps ** 31-jul-97 (inkdo01) ** Fix to force SVAR func atts ref'ed in ON clauses to materialize ** just before needed (to incorporate proper null semantics). ** 9-jul-99 (hayke02 for inkdo01) ** Set opn_eqm bits in lower level nodes (e.g. leaf nodes) when ** func attrs are found in higher level OJ nodes. This change ** fixes bug 92749. ** 20-Mar-02 (wanfr01) ** Bug 106678, INGSRV 1633 ** Confirm function attribute as an ojid before associating it ** with an outer join node. ** 16-sep-03 (hayke02) ** Check for OPN_OJINNERIDX in nodep->opn_jmask to indicate that ** placement of a coverqual inner index outer join needs to be ** checked. ** 28-apr-04 (hayke02) ** Modify previous change so that we return FALSE and reject the QEP ** if the index outer join node (node->LEFT) has a non-zero opn_nchild ** for node->LEFT->RIGHT. This will allow only QEPs where the OJ ** inner is the index only and not the index joined to another ** relation. This change fixes problem INGSRV 2808, bug 112211. ** 05-Oct-2004 (huazh01) ** Remove the above fix. The fix for 111627 handles b106678 ** as well. This fixes b113160, INGSRV2984. ** 14-mar-05 (hayke02) ** Modify the fix for problem INGSRV 2808, bug 112211 so that we now ** check for more than 1 var in the opl_ivmap. This now allows the ** correct rejection of plans that have had their opn_child's set up ** with left joins 'reversed' into right joins. This change fixes ** problems INGSRV 3049 and 3094, bugs 113457 and 113990. ** 23-sep-05 (hayke02) ** Check for a WHERE clause (OPL_NOOOUTER opz_ojid) OJSVAR func att, ** and make sure that all OJs that this func att is inner to are ** executed before the join involving this func att. This change fixes ** bug 114912. ** 30-jan-07 (hayke02) ** Disable the fix for bug 114912 for cart prod (OPL_BOJCARTPROD) OJs. ** This change fixes bug 117513. ** 27-Oct-2009 (kiria01) SIR 121883 ** Scalar sub-selects - protect subp->opv_eqcrequired from bad de-ref. [@history_line@]... */ bool opn_jmaps( OPS_SUBQUERY *subquery, OPN_JTREE *nodep, OPL_BMOJ *ojmap) { OPL_OUTER *outerp; if (nodep->opn_nleaves == 1) { /* leaf node */ OPV_IVARS varno; /* joinop range variable number ** of leaf */ varno = nodep->opn_prb[0]; /* by definition of leaf - only ** one variable in partition */ nodep->opn_rlasg[0] = varno; /* trivial ordering for leaf */ MEfill( sizeof(nodep->opn_rlmap), (u_char)0, (PTR)&nodep->opn_rlmap ); if (subquery->ops_oj.opl_lv > 0) { MEfill( sizeof(nodep->opn_ojinnermap), (u_char)0, (PTR)&nodep->opn_ojinnermap); MEfill( sizeof(nodep->opn_ojevalmap), (u_char)0, (PTR)&nodep->opn_ojevalmap); opl_sjij(subquery, varno, &nodep->opn_ojinnermap, ojmap); } BTset((i4)varno, (char *)&nodep->opn_rlmap); /* only one bit set for leaf*/ MEcopy ((PTR)&subquery->ops_vars.opv_base->opv_rt[varno]->opv_maps.opo_eqcmap, sizeof(nodep->opn_eqm), (PTR)&nodep->opn_eqm ); /* copy map of ** equivalence classes ** associated with varno of this ** leaf */ return(TRUE); } else { /* non-leaf node */ OPN_JTREE *leftchildp; /* ptr to left child node */ OPN_JTREE *rightchildp; /* ptr to right child node */ leftchildp = nodep->opn_child[OPN_LEFT]; rightchildp = nodep->opn_child[OPN_RIGHT]; if (ojmap) { MEfill(sizeof(nodep->opn_ojevalmap), (u_char)0, (PTR)&nodep->opn_ojevalmap); if( (nodep->opn_ojid >= 0) && !BTtest((i4)nodep->opn_ojid, (char *)ojmap)) { /* setup the outer join map which contains all outer joins ** which are completely evaluated within this subtree */ BTset((i4)nodep->opn_ojid, (char *)&nodep->opn_ojevalmap); BTset((i4)nodep->opn_ojid, (char *)ojmap); } } if (!opn_jmaps (subquery, leftchildp, ojmap)) /* get info on left child */ return(FALSE); if (!opn_jmaps (subquery, rightchildp, ojmap)) /* get info on right child */ return(FALSE); MEcopy ((PTR)&leftchildp->opn_rlmap, sizeof(nodep->opn_rlmap), (PTR)&nodep->opn_rlmap ); /* get var bitmap from left ** child */ BTor( (i4)BITS_IN(nodep->opn_rlmap), (char *)&rightchildp->opn_rlmap, (char *)&nodep->opn_rlmap ); /* "OR" rightchildp->opn_rlmap ** into nodep->opn_rlmap */ if (ojmap) { /* setup ojinnermap which contains all outer joins which are ** partially or totally evaluated within this subtree, but ** not at this node unless it is in the subtree */ MEcopy((PTR)&leftchildp->opn_ojinnermap, sizeof(leftchildp->opn_ojinnermap), (PTR)&nodep->opn_ojinnermap); BTor((i4)BITS_IN(rightchildp->opn_ojinnermap), (char *)&rightchildp->opn_ojinnermap, (char *)&nodep->opn_ojinnermap); if (leftchildp->opn_ojid >= 0) BTset((i4)leftchildp->opn_ojid, (char *)&nodep->opn_ojinnermap); if (rightchildp->opn_ojid >= 0) BTset((i4)rightchildp->opn_ojid, (char *)&nodep->opn_ojinnermap); if (subquery->ops_mask & OPS_IJCHECK) opl_ijcheck(subquery, &leftchildp->opn_ojinnermap, &rightchildp->opn_ojinnermap, &nodep->opn_ojinnermap, &leftchildp->opn_ojevalmap, &rightchildp->opn_ojevalmap, &nodep->opn_rlmap); /* map of outerjoins which are entirely evaluated within ** this subtree */ BTor((i4)BITS_IN(leftchildp->opn_ojevalmap), (char *)&leftchildp->opn_ojevalmap, (char *)&nodep->opn_ojevalmap); BTor((i4)BITS_IN(rightchildp->opn_ojevalmap), (char *)&rightchildp->opn_ojevalmap, (char *)&nodep->opn_ojevalmap); if ((nodep->opn_jmask & OPN_OJINNERIDX) && ((nodep->opn_ojid != nodep->opn_child[OPN_LEFT]->opn_ojid) || ((nodep->opn_ojid == nodep->opn_child[OPN_LEFT]->opn_ojid) && (nodep->opn_ojid >= 0) && (BTcount((char *)subquery->ops_oj.opl_base->opl_ojt [nodep->opn_ojid]->opl_ivmap, subquery->ops_vars.opv_rv) > 1)))) return(FALSE); } MEcopy ((PTR)leftchildp->opn_rlasg, leftchildp->opn_nleaves * sizeof(nodep->opn_rlasg[0]), (PTR)nodep->opn_rlasg); /* get relations from left ** child */ MEcopy ((PTR)rightchildp->opn_rlasg, rightchildp->opn_nleaves * sizeof(nodep->opn_rlasg[0]), (PTR)(nodep->opn_rlasg + leftchildp->opn_nleaves)); /* get ** relations from right child ** and place them beside the ** ones from the left child */ MEcopy ((PTR)&leftchildp->opn_eqm, sizeof(nodep->opn_eqm), (PTR)&nodep->opn_eqm ); /* copy equivalence class map ** from left child to this node */ BTor( (i4)BITS_IN(nodep->opn_eqm), (char *)&rightchildp->opn_eqm, (char *)&nodep->opn_eqm ); /* "OR" right child equivalence ** map into this to produce ** map for this node */ if (subquery->ops_joinop.opj_virtual) { /* there is a subselect in this query so check for availability ** of equivalence classes used for evaluation of boolean factors ** containing the subselect */ OPV_SUBSELECT *subp; /* ptr to subselect descriptor ** for range variable */ if ( (rightchildp->opn_nleaves == 1) && ( subp = subquery->ops_vars.opv_base-> opv_rt[rightchildp->opn_prb[0]]->opv_subselect ) ) { /* right child is a subselect so test for correct leaf ** placement */ if (!subp->opv_eqcrequired || !BTsubset( (char *)subp->opv_eqcrequired, (char *)&nodep->opn_eqm, (i4)BITS_IN(OPE_BMEQCLS) ) ) return (FALSE); /* cannot evaluate all boolean ** factors with SEJOIN nodes ** in this configuration */ else { /* check if all corelated variables are available in the ** outer - FIXME create another pointer field which ** contains this information so this loop is avoided */ for (;subp; subp = subp->opv_nsubselect) { if (!BTsubset( (char *)&subp->opv_eqcmp, (char *)&leftchildp->opn_eqm, (i4)BITS_IN(OPE_BMEQCLS) ) ) return (FALSE); /* not all correlated ** equivalence classes ** are available from the outer ** so return */ } } } if ( (leftchildp->opn_nleaves == 1) && (subp = subquery->ops_vars.opv_base-> opv_rt[leftchildp->opn_prb[0]]->opv_subselect) ) { if (!subp->opv_eqcrequired || !BTsubset( (char *)subp->opv_eqcrequired, (char *)&nodep->opn_eqm, (i4)BITS_IN(OPE_BMEQCLS) ) ) return (FALSE); /* cannot evaluate all boolean ** factors with SEJOIN nodes ** in this configuration */ else { /* check if all corelated variables are available in the ** outer - FIXME create another pointer field which ** contains this information so this loop is avoided */ for (;subp; subp = subp->opv_nsubselect) { if (!BTsubset( (char *)&subp->opv_eqcmp, (char *)&rightchildp->opn_eqm, (i4)BITS_IN(OPE_BMEQCLS) ) ) return (FALSE); /* not all correlated ** equivalence classes ** are available from the outer ** so return */ } } } } if ((subquery->ops_oj.opl_lv > 0) && (nodep->opn_ojid != OPL_NOOUTER)) { OPV_IVARS maxvar; /* this is an outer join function attribute which should ** only appear at the point that the outer join is ** actually performed */ maxvar = subquery->ops_vars.opv_rv; outerp = subquery->ops_oj.opl_base->opl_ojt[nodep->opn_ojid]; if ((outerp->opl_type == OPL_LEFTJOIN) || (outerp->opl_type == OPL_FULLJOIN)) { OPV_BMVARS tempvmap; OPV_IVARS innervar; MEcopy((PTR)outerp->opl_maxojmap, sizeof(tempvmap), (PTR)&tempvmap); BTand((i4)BITS_IN(tempvmap), (char *)&nodep->opn_rlmap, (char *)&tempvmap); for (innervar = -1; (innervar = BTnext((i4)innervar, (char *)&tempvmap, (i4)maxvar))>=0;) { OPE_IEQCLS ojeqcls; ojeqcls = subquery->ops_vars.opv_base->opv_rt [innervar]->opv_ojeqc; if (ojeqcls != OPE_NOEQCLS) BTset((i4)ojeqcls, (char *)&nodep->opn_eqm); } } } { /* determine multi-variable functions that can first be calculated ** at this node */ OPZ_IFATTS fattr; /* current function attribute ** being analyzed */ OPZ_IFATTS maxfattr; /* maximum number of function ** attributes defined */ OPZ_FT *fbase; /* ptr to base of array of ptrs ** to function attribute ** elements */ OPZ_AT *abase; /* ptr to base of array of ptrs ** to joinop attribute elements */ maxfattr = subquery->ops_funcs.opz_fv; /* number of function ** attributes defined */ fbase = subquery->ops_funcs.opz_fbase; /* ptr to base of array ** of function attributes */ abase = subquery->ops_attrs.opz_base; /* ptr to base of array ** of ptrs to joinop attribute ** elements */ for (fattr = 0; fattr < maxfattr ; fattr++) { OPZ_FATTS *fattrp; /* ptr to current function ** attribute being analyzed */ OPE_IEQCLS eqcls; /* equivalence class of the ** multi-variable function ** attribute */ fattrp = fbase->opz_fatts[fattr]; eqcls = abase->opz_attnums[fattrp->opz_attno]->opz_equcls; /* ** equivalence class associated ** with the multi-variable ** function attribute */ /* check for WHERE clause (OPL_NOOUTER opz_ojid) OJSVAR func att ** and then make sure that all OJs that this func att is inner ** to are executed before the join involving this func att */ if (fattrp->opz_type == OPZ_SVAR && (fattrp->opz_mask & OPZ_OJSVAR) && nodep->opn_ojid != OPL_NOOUTER && fattrp->opz_ojid == OPL_NOOUTER && (fattrp->opz_ijmap && BTtest((i4)nodep->opn_ojid, (char *)fattrp->opz_ijmap) && !(outerp->opl_mask & OPL_BOJCARTPROD))) { OPZ_IATTS attno; bool allinrlmap = TRUE; OPE_EQCLIST *eqclsp; eqclsp = subquery->ops_eclass.ope_base->ope_eqclist[eqcls]; for (attno = -1; (attno = BTnext((i4)attno, (PTR)&eqclsp->ope_attrmap, (i4)subquery->ops_attrs.opz_av)) != -1; ) { if (!BTtest((i4)abase->opz_attnums[attno]->opz_varnm, (char *)&nodep->opn_rlmap)) { allinrlmap = FALSE; break; } } if (allinrlmap) return(FALSE); } if ((fattrp->opz_type != OPZ_MVAR) && !(fattrp->opz_type == OPZ_SVAR && fattrp->opz_mask & OPZ_OJSVAR && fattrp->opz_ojid == nodep->opn_ojid) || (fattrp->opz_mask & OPZ_OJFA)) continue; /* only multi-variable ** functions are assigned here ** (and SVARs in ON clauses ** of OJs eval'd at this node) ** since others where assigned ** earlier, ... also outer join ** special eqc were assigned ** prior to this loop */ if (fattrp->opz_type == OPZ_SVAR) { /* Must be ON clause ref'ed SVAR. Set bit in whichever ** child node covers the SVAR eqcmap. */ if (BTsubset((char *)&fattrp->opz_eqcm, (char *)&nodep->opn_child[1]->opn_eqm, (i4)BITS_IN(nodep->opn_eqm))) BTset((i4)eqcls, (char *)&nodep->opn_child[1]->opn_eqm); else if (BTsubset((char *)&fattrp->opz_eqcm, (char *)&nodep->opn_child[0]->opn_eqm, (i4)BITS_IN(nodep->opn_eqm))) BTset((i4)eqcls, (char *)&nodep->opn_child[0]->opn_eqm); /* set fattr eqcls bit in ** proper child opn_eqm */ continue; } if (BTtest( (i4)eqcls, (char *)&nodep->opn_eqm)) continue; /* if function attribute has ** been added then ** continue */ if (BTsubset((char *)&fattrp->opz_eqcm, (char *)&nodep->opn_eqm, (i4)BITS_IN(nodep->opn_eqm)) ) BTset((i4)eqcls, (char *)&nodep->opn_eqm); /* set bit if all ** the required equivalence ** classes are available ** for the function attribute */ } } } return(TRUE); }