/*{ ** 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_uvar - get the attribute number of the union view var node ** ** Description: ** This routine will calculate the attribute number of the union view ** given the equivalence class of this var node ** ** Inputs: ** subquery ptr to subquery containing equivalence ** class of var node ** qual ptr to var node ** ** Outputs: ** attidp ptr to attribute ID for union view ** Returns: ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 28-jun-89 (seputis) ** initial creation [@history_template@]... */ static VOID opj_uvar( OPS_SUBQUERY *subquery, PST_QNODE *qual, OPV_IVARS varno, DB_ATT_ID *attidp) { OPZ_BMATTS *attrmap; /* bit map of attributes in the ** equivalence class */ OPZ_IATTS attno; /* attribute currently being looked at */ OPZ_IATTS maxattr; /* maximum attribute */ OPZ_AT *abase; maxattr = subquery->ops_attrs.opz_av; abase = subquery->ops_attrs.opz_base; /* ptr to base of array of ptrs ** to joinop attributes */ attrmap = &subquery->ops_eclass.ope_base->ope_eqclist [subquery->ops_attrs.opz_base->opz_attnums [qual->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id ]->opz_equcls ]->ope_attrmap; for (attno = -1; (attno = BTnext((i4)attno, (char *)attrmap, maxattr) ) >= 0;) { if (abase->opz_attnums[attno]->opz_varnm == varno) { /* found the union view attribute number so copy it in */ STRUCT_ASSIGN_MACRO(abase->opz_attnums[attno]->opz_attnm, *attidp); break; } } if (attno < 0) opx_error(E_OP0384_NOATTS); /* attribute not found when ** expected */ }
/*{ ** Name: opn_impliedtid - check for implied TID join between two relations ** ** Description: ** Check if there is an implied TID join between the two subtrees ** ** Inputs: ** subquery ptr to subquery being analyzed ** lp ptr to left join subtree ** rp ptr to right join subtree ** eqcls joining equivalence class to test ** ** Outputs: ** lfflag TRUE if left subtree "rp" contains ** the implicit TID ** Returns: ** TRUE - if there is an implied TID join between the two relations ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 30-may-86 (seputis) ** initial creation [@history_line@]... */ bool opn_imtidjoin( OPS_SUBQUERY *subquery, OPN_JTREE *lp, OPN_JTREE *rp, OPE_IEQCLS eqcls, bool *lfflag) { OPE_EQCLIST *eqclsp; /* ptr to joining equivalence class */ OPZ_BMATTS *amap; /* ptr to attribute map for ** this equivalence class */ OPZ_IATTS attr; /* current attribute of equivalence ** class being analyzed */ OPZ_IATTS maxattr; /* number of joinop attributes ** defined */ OPZ_AT *abase; /* ptr to base of array of ptrs ** to joinop attributes */ bool rightleaf; /* TRUE if right subtree is a leaf */ bool leftleaf; /* TRUE if left subtree is a leaf */ eqclsp = subquery->ops_eclass.ope_base->ope_eqclist[eqcls]; /* get ptr to ** joining equivalence class */ if (eqclsp->ope_eqctype != OPE_TID) /* is this a TID equivalence class*/ return (FALSE); amap = &eqclsp->ope_attrmap; /* get equivalence class attribute ** map */ maxattr = subquery->ops_attrs.opz_av; /* maximum number of joinop ** attributes defined */ abase = subquery->ops_attrs.opz_base; /* ptr to base of array of ptrs ** to joinop attributes */ leftleaf = (lp->opn_nleaves == 1); /* TRUE if the left subtree is a ** leaf */ rightleaf = (rp->opn_nleaves == 1); /* TRUE if the right subtree is a ** leaf */ for (attr = -1; (attr = BTnext((i4)attr, (char *)amap, (i4)maxattr)) >= 0;) { OPZ_ATTS *attrp; /* ptr to joinop attribute element*/ attrp = abase->opz_attnums[attr]; /* if on one side the TID is implied, then on the other side ** the TID must be explicit */ if ((*lfflag = ( (leftleaf) && BTtest((i4)attrp->opz_varnm, (char *)&lp->opn_rlmap) && attrp->opz_attnm.db_att_id == DB_IMTID)) || ( (rightleaf) && BTtest((i4)attrp->opz_varnm, (char *)&rp->opn_rlmap) && attrp->opz_attnm.db_att_id == DB_IMTID)) return (TRUE); } return (FALSE); }
CS_SCB * pick_new_thread(CS_SCB * curr_scb, int ready_mask) { int rdymsk, ffs; rdymsk = ready_mask; ffs = 15 - BTnext(-1, &rdymsk, 16); return CS_xchng_thread(curr_scb, ffs); }
/*{ ** Name: OPC_EEXCATTS - Build atts from eqcs that go to exchange ** buffer ** ** Description: {@comment_line@}... ** ** Inputs: [@PARAM_DESCR@]... ** ** Outputs: [@PARAM_DESCR@]... ** Returns: ** {@return_description@} ** Exceptions: ** [@description_or_none@] ** ** Side Effects: ** [@description_or_none@] ** ** History: ** 13-nov-03 (inkdo01) ** Written for parallel query processing (cloned from opcsorts.c). ** 7-Apr-2004 (schka24) ** Set attr default stuff just in case someone looks at it. [@history_template@]... */ static VOID opc_eexatts( OPS_STATE *global, OPE_BMEQCLS *eeqcmp, OPC_EQ *ceq, DMF_ATTR_ENTRY ***patts, /* ptr to a ptr to an array of ptrs to atts */ i4 *pacount ) { OPS_SUBQUERY *subqry = global->ops_cstate.opc_subqry; DMF_ATTR_ENTRY **atts; DMF_ATTR_ENTRY *att; i4 attno; OPZ_AT *at = global->ops_cstate.opc_subqry->ops_attrs.opz_base; OPE_IEQCLS eqcno; *pacount = BTcount((char *)eeqcmp, (i4)subqry->ops_eclass.ope_ev); if (*pacount == 0) return; /* count(*) or sommat - no atts to materialize */ atts = (DMF_ATTR_ENTRY **) opu_qsfmem(global, *pacount * sizeof (DMF_ATTR_ENTRY *)); for (attno = 0, eqcno = -1; (eqcno = BTnext((i4)eqcno, (char *)eeqcmp, (i4)subqry->ops_eclass.ope_ev)) != -1; attno += 1 ) { att = (DMF_ATTR_ENTRY *) opu_qsfmem(global, sizeof (DMF_ATTR_ENTRY)); STprintf(att->attr_name.db_att_name, "a%x", eqcno); STmove(att->attr_name.db_att_name, ' ', sizeof(att->attr_name.db_att_name), att->attr_name.db_att_name); att->attr_type = ceq[eqcno].opc_eqcdv.db_datatype; att->attr_size = ceq[eqcno].opc_eqcdv.db_length; att->attr_precision = ceq[eqcno].opc_eqcdv.db_prec; att->attr_flags_mask = 0; SET_CANON_DEF_ID(att->attr_defaultID, DB_DEF_NOT_DEFAULT); att->attr_defaultTuple = NULL; atts[attno] = att; } *patts = atts; }
/*{ ** 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: opv_parser - init global range table element given parser varno ** ** Description: ** This routine will initialize the global range table element in OPF ** corresponding to the PSF range table element. ** ** Inputs: ** global ptr to global range table ** gvar element in parser range table ** which is referenced in query ** ** Outputs: ** global->ops_rangetab.opv_base[gvar] initialize corresponding element ** in optimizer range table ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 2-jul-86 (seputis) ** initial creation ** 6-nov-88 (seputis) ** change RDF invalidation to include all indexes on base relation ** since this is only notification that OPF gets that its' time ** stamp is out of date ** 25-sep-89 (seputis) ** - made addition to timestamp check, to refresh if this is a ** multi-variable query and the tuple count is zero ** 9-nov-89 (seputis) ** - added OPA_NOVID initialization for b8160, and corrected ** sep 25 fix for timestamps ** 12-jan-90 (seputis) ** - detect table ID's which are the same for different range vars ** 26-feb-91 (seputis) ** - add improved diagnostics for b35862 ** 31-dec-91 (seputis) ** - flush cache entry if tuple count is zero and more than one ** range table entry, since aggregate queries and flattened queries ** are not getting handled. ** 12-feb-93 (jhahn) ** Added support for statement level rules. (FIPS) ** 12-apr-93 (ed) ** - fix bug 50673, relax range variable check for larger OPF table ** 7-dec-93 (ed) ** b56139 - add OPZ_TIDHISTO to mark tid attributes which ** need histograms,... needed since target list is traversed ** earlier than before ** 16-feb-95 (inkdo01) ** b66907 - check for explicit refs to inconsistent tabs/idxes ** 23-feb-95 (wolf) ** Recent addition of MEcopy call should have been accompanied by ** an include of me.h ** 10-aug-98 (stial01) ** opv_parser() Remove code to invalidate indexes. The invalidate ** table that follows will do the job. Invalidate indexes by table id ** can get E_OP008E_RDF_INVALIDATE, E_RD0045_ULH_ACCESS ** 31-oct-1998 (nanpr01) ** Reset the rdfcb->infoblk ptr before checking the error code. ** 18-june-99 (inkdo01) ** Init opv_ttmodel for temp table model histogram feature. ** 19-Jan-2000 (thaju02) ** relation descriptor may be out of date and contain a stale ** secondary index count, use rdfcb->rdf_info_block->rdr_no_index ** which reflects the number of index entries in the rdr_indx ** array. (b100060) ** 17-Jan-2004 (schka24) ** Rename RDR_BLD_KEY to RDR_BLD_PHYS. ** 3-Sep-2005 (schka24) ** Remove if-0'ed out section that included a ref to a member ** that is going away (opv_ghist). ** 17-Nov-2005 (schka24) ** Don't propagate RDF invalidates that we cause. Our RDF cache ** is out of date but that's not other servers' problem. ** 14-Mar-2007 (kschendel) SIR 122513 ** Light "partitioned" flag if partitioned table seen. ** 18-april-2008 (dougi) ** Add support for table procedures. ** 8-Jul-2010 (wanfr01) b123949 ** If rdf_gdesc failed with an error, don't use the column ** information - it may not be fully initialized. */ bool opv_parser( OPS_STATE *global, OPV_IGVARS gvar, OPS_SQTYPE sqtype, bool rdfinfo, bool psf_table, bool abort) { OPV_GRT *gbase; /* ptr to base of global range table */ # ifdef E_OP0387_VARNO if ((gvar < 0) || ((gvar >= PST_NUMVARS) && (gvar >= OPV_MAXVAR))) opx_error(E_OP0387_VARNO ); /* range var out of range - ** consistency check */ # endif gbase = global->ops_rangetab.opv_base; /* get base of global range table ** ptr to array of ptrs */ if ( !gbase->opv_grv[gvar] ) { /* if global range variable element has not been allocated */ OPV_GRV *grvp; /* ptr to new range var element */ if (global->ops_rangetab.opv_gv <= gvar) global->ops_rangetab.opv_gv = gvar+1; /* update the largest range ** table element assigned so far */ grvp = (OPV_GRV *) opu_memory(global, sizeof( OPV_GRV ) ); /* save ** and allocate ptr to global ** var */ /* Explicitly zero out the grv entry */ MEfill(sizeof(*grvp), (u_char)0, (PTR)grvp); grvp->opv_qrt = gvar; /* save index to ** parser range table element */ grvp->opv_created = sqtype; /* save global range table type */ grvp->opv_gvid = OPA_NOVID; /* if this base table was implicitly ** referenced then the view id of the ** explicitly reference view will be ** saved */ grvp->opv_siteid = OPD_NOSITE; /* initialize distributed site location */ grvp->opv_same = OPV_NOGVAR; /* used to map tables with similiar ** IDs */ grvp->opv_compare = OPV_NOGVAR; /* used to map tables with similiar ** IDs */ grvp->opv_ttmodel = NULL; /* RDR_INFO ptr for temp table model ** histograms */ gbase->opv_grv[gvar] = grvp; /* place into table */ /* get RDF information about the table */ if (rdfinfo) { RDF_CB *rdfcb; /* ptr to RDF control block which ** has proper db_id and sessionid ** info */ PST_RNGENTRY *rngentry; /* ptr to parse tree range entry */ DB_STATUS status; /* RDF return status */ i4 ituple; rdfcb = &global->ops_rangetab.opv_rdfcb; if (psf_table) { /* Snag table procedures and handle them elsewhere. */ if ((rngentry = global->ops_qheader->pst_rangetab[gvar]) ->pst_rgtype == PST_TPROC) { if (opv_tproc(global, gvar, grvp, rngentry) == E_DB_OK) return(FALSE); else return(TRUE); } STRUCT_ASSIGN_MACRO(global->ops_qheader->pst_rangetab[gvar]-> pst_rngvar, rdfcb->rdf_rb.rdr_tabid); /* need ** table id from parser's table */ #if 0 if ((BTnext((i4)-1, (char *)&rangep->pst_outer_rel, (i4)BITS_IN(rangep->pst_outer_rel)) >= 0) || (BTnext((i4)-1, (char *)&rangep->pst_inner_rel, (i4)BITS_IN(rangep->pst_outer_rel)) >= 0) ) grvp->opv_gmask |= OPV_GOJVAR; /* mark whether this ** variable is within the scope of an ** outer join */ /* OPV_GOJVAR is not reliably set since the subquery bitmap should be tested ** also for an aggregate temp, multi-to-one mappings may exist for global range ** variables */ #endif if (global->ops_qheader->pst_rangetab[gvar]-> pst_rgtype == PST_SETINPUT) grvp->opv_gmask |= OPV_SETINPUT; /* is this the set input ** parameter for a procedure */ rdfcb->rdf_rb.rdr_types_mask = RDR_RELATION | RDR_INDEXES | RDR_ATTRIBUTES | RDR_BLD_PHYS; /* get relation info ** - The optimizer uses attribute ** info in query tree directly ** but it is needed to be requested ** since the RDF uses attr info to ** build RDR_BLK_KEY info. The ** attribute info does not need to ** requested if RDF is changed. ** - ask for indexes so that ** invalidating the cache can also ** invalidate any indexes in the ** cache */ /* When we're ready to enable column comparison stats, uncomment the following statement. ** rdfcb->rdf_rb.rdr_2types_mask = RDR2_COLCOMPARE; /* column ** comparison stats, too */ } rdfcb->rdf_info_blk = NULL; /* get new ptr to info ** associated with global var */ status = rdf_call( RDF_GETDESC, (PTR)rdfcb); grvp->opv_relation = rdfcb->rdf_info_blk; /* save ptr to ** new info block */ if ((status != E_RD0000_OK) && (rdfcb->rdf_error.err_code != E_RD026A_OBJ_INDEXCOUNT) /* this ** is a check for distributed ** index information, which could ** be inconsistent but should not ** cause the query to be aborted ** it will cause indexes to be ** avoided */ ) { gbase->opv_grv[gvar] = NULL; if (abort) opx_verror( status, E_OP0004_RDF_GETDESC, rdfcb->rdf_error.err_code); else { return (TRUE); /* indicate failure to get RDF ** descriptor */ } } BTset( (i4)gvar, (char *)&global->ops_rangetab.opv_mrdf); /* indicate ** that RDF information is fixed */ /* Check table and all associated indexes to see if there is a ** statement level index associated with this variable */ if (grvp->opv_relation->rdr_rel->tbl_2_status_mask & DMT_STATEMENT_LEVEL_UNIQUE) grvp->opv_gmask |= OPV_STATEMENT_LEVEL_UNIQUE; for( ituple = grvp->opv_relation->rdr_no_index; ituple-- ;) { if (grvp->opv_relation->rdr_indx[ituple]->idx_status & DMT_I_STATEMENT_LEVEL_UNIQUE) grvp->opv_gmask |= OPV_STATEMENT_LEVEL_UNIQUE; } if (psf_table) { /* check if timestamp matches and invalidate RDF cache ** date of last modify do not match, this is only done ** for tables passed by PSF, the other tables should ** be dependent on the PSF time stamp */ DB_TAB_TIMESTAMP *timeptr; /* ptr to last modify ** date that RDF has cached */ DB_TAB_TIMESTAMP *psftimeptr; /* ptr to last modify ** date which parser used for the ** table */ psftimeptr = &global->ops_qheader->pst_rangetab[gvar]-> pst_timestamp; timeptr = &grvp->opv_relation->rdr_rel->tbl_date_modified; if (timeptr->db_tab_high_time != psftimeptr->db_tab_high_time || timeptr->db_tab_low_time != psftimeptr->db_tab_low_time || ( !grvp->opv_relation->rdr_rel->tbl_record_count && (global->ops_qheader->pst_rngvar_count > 1) ) /* special zero tuple count ** case check to see if tuple count ** is way off, i.e. a table create ** followed by a number of appends ** will cause a 0 tuple count, check ** for more than one variable since ** refresh is only useful when joins occur */ ) { PTR save_fcb = rdfcb->rdf_rb.rdr_fcb; /* Don't propagate this invalidate to other DBMS servers. ** There's no reason to think that they are as out of ** date as we are. (plus this might be a session temp ** which is strictly local!) */ rdfcb->rdf_rb.rdr_fcb = NULL; status = rdf_call( RDF_INVALIDATE, (PTR)rdfcb); rdfcb->rdf_rb.rdr_fcb = save_fcb; # ifdef E_OP008E_RDF_INVALIDATE if (status != E_RD0000_OK) opx_verror( E_DB_ERROR, E_OP008E_RDF_INVALIDATE, rdfcb->rdf_error.err_code); # endif status = rdf_call( RDF_GETDESC, (PTR)rdfcb); grvp->opv_relation = rdfcb->rdf_info_blk; /* save ptr to ** new info block */ if (status != E_RD0000_OK) { gbase->opv_grv[gvar] = NULL; opx_verror( E_DB_ERROR, E_OP0004_RDF_GETDESC, rdfcb->rdf_error.err_code); } timeptr = &grvp->opv_relation->rdr_rel->tbl_date_modified; if (timeptr->db_tab_high_time != psftimeptr->db_tab_high_time || timeptr->db_tab_low_time != psftimeptr->db_tab_low_time ) opx_vrecover( E_DB_ERROR, E_OP008F_RDF_MISMATCH, rdfcb->rdf_error.err_code); /* PSF timestamp is ** still out of date, so tell ** SCF to reparse the query */ } { /* search thru existing tables to discover if any ** tables have the same table ID */ OPV_IGVARS gvno; DB_TAB_ID *tabidp; OPV_IGVARS target_vno; tabidp = &global->ops_qheader->pst_rangetab[gvar]->pst_rngvar; target_vno = OPV_NOGVAR; for (gvno = 0; gvno < OPV_MAXVAR; gvno++) { OPV_GRV *grv1p; grv1p = gbase->opv_grv[gvno]; if (grv1p && grv1p->opv_relation) { DB_TAB_ID *gtabidp; if (gvno == gvar) continue; gtabidp = &grv1p->opv_relation->rdr_rel->tbl_id; if ((tabidp->db_tab_base == gtabidp->db_tab_base) && (tabidp->db_tab_index == gtabidp->db_tab_index) ) { /* found 2 table ID's which are identical */ global->ops_gmask |= OPS_IDSAME; if (target_vno == OPV_NOGVAR) { /* map all table id's vars to the lowest ** global range number of the group */ if (gvno > gvar) target_vno = gvar; else target_vno = gvno; } grv1p->opv_same = target_vno; grvp->opv_same = target_vno; if (target_vno != gvar) break; } } } } } if (global->ops_cb->ops_smask & OPS_MDISTRIBUTED) opd_addsite(global, grvp); /* add site information if a ** distributed thread */ /* Check for partitioned table, turns on additional ** analysis after enumeration. */ if (grvp->opv_relation != NULL && grvp->opv_relation->rdr_parts != NULL) global->ops_gmask |= OPS_PARTITION; if (grvp->opv_relation && grvp->opv_relation->rdr_rel-> tbl_2_status_mask & DMT_INCONSIST) /* check for inconsistent table ** (due to partial back/recov) */ { OPT_NAME tabname; i2 i; MEcopy(&grvp->opv_relation->rdr_rel->tbl_name, sizeof(grvp->opv_relation->rdr_rel->tbl_name), &tabname); /* table name is msg token */ for (i = sizeof(grvp->opv_relation->rdr_rel->tbl_name); i > 1 && tabname.buf[i-1] == ' '; i--); tabname.buf[i] = 0; /* lop blanks off name */ opx_1perror(E_OP009A_INCONSISTENT_TAB,(PTR)&tabname); } } else grvp->opv_relation = NULL; /* set to NULL if not used */ } return(FALSE); }
/*{ ** Name: ops_deallocate - deallocate resources for an optimization ** ** Description: ** This routine will deallocate the resources used for an optimization. ** Resources include any memory requested from the optimizer memory pool ** and any RDF cache objects which were locked in the global range ** table ** ** Inputs: ** global ptr to global state variable ** report TRUE if errors should be reported ** via the user's control block. ** partial_dbp partial deallocation required for ** statement within a procedure ** ** Outputs: ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** memory resources released, RDF unfixed, ** QSF query tree memory released ** ** History: ** 29-jun-86 (seputis) ** initial creation ** 8-nov-88 (seputis) ** if no query run trace point is set then destroy the QP since ** SCF assumes optimizer cleans up after error ** 8-nov-88 (seputis) ** turn off CPU accounting if was off originally ** 28-jan-91 (seputis) ** added support for OPF ACTIVE flag ** 20-jul-93 (ed) ** changed name ops_lock for solaris, due to OS conflict ** 29-jul-93 (andre) ** rdr_types_mask must be initialized (to RDR_RELATION) before ** calling RDF_UNFIX. Otherwise RDF may end up complaining because we ** ask it to destroy a relation cache entry while RDR_PROCEDURE bit is ** set. ** 12-aug-93 (swm) ** Cast first parameter of CSaltr_session() to CS_SID to match ** revised CL interface specification. ** 02-Jun-1997 (shero03) ** Update the saved rdf_info_block after calling RDF. ** 02-Aug-2001 (hanal04) Bug 105360 INGSRV 1505 ** Plug the RDF memory leak introduced by inkdo01's new ** function oph_temphist(). ** 17-Dec-2003 (jenjo02) ** Added (CS_SID)NULL to CScnd_signal prototype. ** 6-Feb-2006 (kschendel) ** Fix some squirrely looking code that purported to avoid dangling ** references, but didn't really. (No symptoms known.) ** 14-nov-2007 (dougi) ** Add support for cached dynamic query plans. ** 20-may-2008 (dougi) ** Add support for table procedures. ** 29-may-2009 (wanfr01) Bug 122125 ** Need to add dbid to cache_dynamic queries for db uniqueness */ VOID ops_deallocate( OPS_STATE *global, bool report, bool partial_dbp) { DB_STATUS finalstatus; /* this status is returned to the user ** - it will contain the first error ** during resource deallocation */ DB_ERROR error; /* error code from offending facility */ finalstatus = E_DB_OK; error.err_code = 0; { /* close any fixed RDF objects - deallocate prior to closing the ** global memory stream */ OPV_IGVARS gvar; /* index into global range variable ** table */ OPV_GRT *gbase; /* ptr to base of array of ptrs ** to global range table elements */ OPV_IGVARS maxgvar; /* number of global range table ** elements allocated */ RDF_CB *rdfcb; /* ptr to rdf control block used ** unfix the relation info */ OPV_GBMVARS *rdfmap; /* ptr to map of global range ** variables which have RDF info ** fixed */ gbase = global->ops_rangetab.opv_base; maxgvar = global->ops_rangetab.opv_gv; rdfcb = &global->ops_rangetab.opv_rdfcb; rdfmap = &global->ops_rangetab.opv_mrdf; /* ** rdr_types_mask needs to be initialized - since we will be unfixing ** relation entries, RDR_RELATION seems like a good choice, although 0 ** would suffice as well */ rdfcb->rdf_rb.rdr_types_mask = RDR_RELATION; if (global->ops_cstate.opc_relation) { /* OPC allocates a RDF descriptor for cursors so deallocate ** if this is the case */ DB_STATUS opcrdfstatus; /* RDF return status */ rdfcb->rdf_info_blk = global->ops_cstate.opc_relation; opcrdfstatus = rdf_call( RDF_UNFIX, (PTR)rdfcb ); if ( (DB_FAILURE_MACRO(opcrdfstatus)) && (DB_SUCCESS_MACRO(finalstatus)) ) { finalstatus = opcrdfstatus; error.err_code = rdfcb->rdf_error.err_code; } global->ops_cstate.opc_relation = NULL; } if (maxgvar) { for ( gvar = -1; (gvar = BTnext((i4)gvar, (char *)rdfmap, (i4)maxgvar)) >=0;) { OPV_GRV *gvarp; /* ptr to global range variable to ** be deallocated */ if ((gvarp = gbase->opv_grv[gvar]) /* NULL if not allocated */ && (gvarp->opv_relation) /* not NULL if RDF has been ** called for this range variable */ && !(gvarp->opv_gmask & OPV_TPROC) /* not table procedure */ ) { /* if this element has been allocated and if it has an RDF ** cache element associated with it */ DB_STATUS rdfstatus; /* RDF return status */ gbase->opv_grv[gvar] = NULL; /* so we do not try to deallocate ** twice in case of an error */ rdfcb->rdf_info_blk = gvarp->opv_relation; rdfstatus = rdf_call( RDF_UNFIX, (PTR)rdfcb ); if ( (DB_FAILURE_MACRO(rdfstatus)) && (DB_SUCCESS_MACRO(finalstatus)) ) { finalstatus = rdfstatus; error.err_code = rdfcb->rdf_error.err_code; } gvarp->opv_relation = NULL; } if ((gvarp) && (gvarp->opv_ttmodel)) { /* if this element has been allocated and if it has an RDF ** cache element associated with a persistent table ** which provides histogram models. */ DB_STATUS rdfstatus; /* RDF return status */ rdfcb->rdf_info_blk = gvarp->opv_ttmodel; gvarp->opv_ttmodel = NULL; rdfstatus = rdf_call( RDF_UNFIX, (PTR)rdfcb ); if ( (DB_FAILURE_MACRO(rdfstatus)) && (DB_SUCCESS_MACRO(finalstatus)) ) { finalstatus = rdfstatus; error.err_code = rdfcb->rdf_error.err_code; } } } global->ops_rangetab.opv_gv = 0; } } if (partial_dbp) return; /* only deallocate the global range table ** for DBP, and keep the memory streams ** until the end */ if (global->ops_estate.opn_statistics && global->ops_estate.opn_reset_statistics) { /* statistics CPU accounting was turned on, and needs to be reset */ STATUS cs_status; i4 turn_off; turn_off = FALSE; /* turn off accounting */ global->ops_estate.opn_statistics = FALSE; cs_status = CSaltr_session((CS_SID)0, CS_AS_CPUSTATS, (PTR)&turn_off); if (cs_status != OK) { finalstatus = E_DB_ERROR; error.err_code = cs_status; } } /* deallocate ULM memory stream */ if (global->ops_mstate.ops_streamid == NULL) /* non-zero if allocated */ { /* check if ULM stream does not exist then this deallocation has ** already occurred so just return */ return; } else { DB_STATUS ulm1status;/* ULM return status */ global->ops_mstate.ops_ulmrcb.ulm_streamid_p = &global->ops_mstate.ops_streamid; /* ulm will NULL ops_streamid */ ulm1status = ulm_closestream( &global->ops_mstate.ops_ulmrcb ); if ( (DB_FAILURE_MACRO(ulm1status)) && (DB_SUCCESS_MACRO(finalstatus)) ) { finalstatus = ulm1status; error.err_code = global->ops_mstate.ops_ulmrcb.ulm_error.err_code; } } /* deallocate ULM temp buffer memory stream */ if ( global->ops_mstate.ops_tstreamid ) /* non-zero if allocated */ { DB_STATUS ulm2status; /* ULM return status */ global->ops_mstate.ops_ulmrcb.ulm_streamid_p = &global->ops_mstate.ops_tstreamid; /* ulm will NULL ops_tstreamid */ ulm2status = ulm_closestream( &global->ops_mstate.ops_ulmrcb ); if ( (DB_FAILURE_MACRO(ulm2status)) && (DB_SUCCESS_MACRO(finalstatus)) ) { finalstatus = ulm2status; error.err_code = global->ops_mstate.ops_ulmrcb.ulm_error.err_code; } } /* deallocate OPC ULM buffer memory stream */ if ( global->ops_mstate.ops_sstreamid ) /* non-zero if allocated */ { DB_STATUS ulm3status; /* ULM return status */ global->ops_mstate.ops_ulmrcb.ulm_streamid_p = &global->ops_mstate.ops_sstreamid; /* ulm will NULL ops_sstreamid */ ulm3status = ulm_closestream( &global->ops_mstate.ops_ulmrcb ); if ( (DB_FAILURE_MACRO(ulm3status)) && (DB_SUCCESS_MACRO(finalstatus)) ) { finalstatus = ulm3status; error.err_code = global->ops_mstate.ops_ulmrcb.ulm_error.err_code; } } if (!report #ifdef OPT_F032_NOEXECUTE || /* if trace flag is set then cleanup QSF memory since optimizer will ** generate an error to SCF and SCF assumes optimizer will cleanup */ (global->ops_cb->ops_check && (opt_strace( global->ops_cb, OPT_F032_NOEXECUTE) || opt_strace( global->ops_cb, OPT_F023_NOCOMP) ) ) #endif ) { /* an error or an asychronous abort has occurred so destroy the plan ** or shared plan , FIXME destroy the shared plan in the earlier ** exception handler */ DB_STATUS qsfqpstatus; /* QSF return status */ if(global->ops_qpinit) { /* deallocate QSF object for query plan if another error has occurred ** - in this case OPC has already created a new QP handle and has ** gotten a lock on it */ STRUCT_ASSIGN_MACRO(global->ops_caller_cb->opf_qep, global->ops_qsfcb.qsf_obj_id); /* get ** query plan id */ global->ops_qsfcb.qsf_lk_id = global->ops_qplk_id; /* get lock id for ** QSF */ qsfqpstatus = ops_qsfdestroy(global); /* destroy the query plan */ if ( (DB_FAILURE_MACRO(qsfqpstatus)) && (DB_SUCCESS_MACRO(finalstatus)) ) { finalstatus = qsfqpstatus; error.err_code = global->ops_qsfcb.qsf_error.err_code; } } else { /* OPC has not been reached so need to check for shared query plan */ if (!global->ops_procedure) { /* get query tree if it has not already been retrieved */ qsfqpstatus = ops_gqtree(global); if ( (DB_FAILURE_MACRO(qsfqpstatus)) && (DB_SUCCESS_MACRO(finalstatus)) ) { finalstatus = qsfqpstatus; error.err_code = global->ops_qsfcb.qsf_error.err_code; } } if (global->ops_qheader && (global->ops_qheader->pst_mask1 & PST_RPTQRY)) { /* shared query plan possible */ if (global->ops_procedure->pst_flags & PST_REPEAT_DYNAMIC) { char *p; global->ops_qsfcb.qsf_obj_id.qso_lname = sizeof(DB_CURSOR_ID) + sizeof(i4); MEfill(sizeof(global->ops_qsfcb.qsf_obj_id.qso_name), 0, global->ops_qsfcb.qsf_obj_id.qso_name); MEcopy((PTR)&global->ops_procedure-> pst_dbpid.db_cursor_id[0], sizeof (global->ops_procedure-> pst_dbpid.db_cursor_id[0]), (PTR)global->ops_qsfcb.qsf_obj_id.qso_name); p = (char *) global->ops_qsfcb.qsf_obj_id.qso_name + 2*sizeof(i4); MEcopy((PTR)"qp", sizeof("qp"), p); p = (char *) global->ops_qsfcb.qsf_obj_id.qso_name + sizeof(DB_CURSOR_ID); I4ASSIGN_MACRO(global->ops_caller_cb->opf_udbid, *(i4 *) p); } else /* must be proc or regular repeat query */ { global->ops_qsfcb.qsf_obj_id.qso_lname = sizeof (global->ops_procedure->pst_dbpid); MEcopy((PTR)&global->ops_procedure->pst_dbpid, sizeof (global->ops_procedure->pst_dbpid), (PTR)&global->ops_qsfcb.qsf_obj_id.qso_name[0]); } global->ops_qsfcb.qsf_obj_id.qso_type = QSO_QP_OBJ; global->ops_qsfcb.qsf_lk_state = QSO_SHLOCK; qsfqpstatus = qsf_call(QSO_GETHANDLE, &global->ops_qsfcb); if (DB_SUCCESS_MACRO(qsfqpstatus)) { qsfqpstatus = ops_qsfdestroy( global ); if ( (DB_FAILURE_MACRO(qsfqpstatus)) && (DB_SUCCESS_MACRO(finalstatus)) ) { finalstatus = qsfqpstatus; error.err_code = global->ops_qsfcb.qsf_error.err_code; } } else if (global->ops_qsfcb.qsf_error.err_code != E_QS0019_UNKNOWN_OBJ) { /* if object is not found then this is not a shared query */ finalstatus = qsfqpstatus; error.err_code = global->ops_qsfcb.qsf_error.err_code; } } } } /* release QSF memory allocated to query tree, make sure that this ** is done after the QP has been processed since pst_rptqry is still ** needed for above block */ { DB_STATUS qsfstatus; /* QSF return status */ STRUCT_ASSIGN_MACRO(global->ops_caller_cb->opf_query_tree, global->ops_qsfcb.qsf_obj_id); /* get ** query tree id */ global->ops_qsfcb.qsf_lk_id = global->ops_lk_id; /* get lock id for ** QSF */ qsfstatus = ops_qsfdestroy( global ); if ( (DB_FAILURE_MACRO(qsfstatus)) && (DB_SUCCESS_MACRO(finalstatus)) ) { finalstatus = qsfstatus; error.err_code = global->ops_qsfcb.qsf_error.err_code; } } /* signal that the session is exiting OPF and that another thread may enter */ if (global->ops_cb->ops_smask & OPS_MCONDITION) { DB_STATUS lockstatus; OPG_CB *servercb; servercb = global->ops_cb->ops_server; global->ops_cb->ops_smask &= (~OPS_MCONDITION); lockstatus = ops_exlock(global->ops_caller_cb, &servercb->opg_semaphore); /* check if server ** thread is available, obtain ** semaphore lock on critical variable */ servercb->opg_activeuser--; /* since exit is about to occur, and memory ** has already been deallocated, allow another ** user to enter OPF */ servercb->opg_waitinguser--; /* since exit is about to occur, and memory ** has already been deallocated, allow another ** user to enter OPF */ if (DB_FAILURE_MACRO(lockstatus) && (DB_SUCCESS_MACRO(finalstatus))) { finalstatus = lockstatus; error.err_code = global->ops_caller_cb->opf_errorblock.err_data; } else { if (servercb->opg_waitinguser > servercb->opg_activeuser) { STATUS csstatus; csstatus = CScnd_signal(&servercb->opg_condition, (CS_SID)NULL); /* signal only if some users are waiting */ if ((csstatus != OK) && (DB_SUCCESS_MACRO(finalstatus))) { finalstatus = E_DB_ERROR; error.err_code = csstatus; } } lockstatus = ops_unlock(global->ops_caller_cb, &servercb->opg_semaphore); /* check if server ** thread is available */ if (DB_FAILURE_MACRO(lockstatus) && (DB_SUCCESS_MACRO(finalstatus))) { finalstatus = lockstatus; error.err_code = global->ops_caller_cb->opf_errorblock.err_data; } } } if (DB_FAILURE_MACRO(finalstatus)) { if (report) opx_verror( finalstatus, E_OP0084_DEALLOCATION, error.err_code); /* report ** error and generate an exception */ else opx_rverror(global->ops_cb->ops_callercb, finalstatus, E_OP0084_DEALLOCATION, error.err_code); /* report error only but do not generate an ** exception */ } }
/*{ ** 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); }
/*{ ** Name: opn_ukey - useable key - can inner node be probed ** ** Description: ** ** Returns TRUE if there are enough attributes ** to use the key of the relation. ** ** The jeqc must be the first element in the ordering that is not ** satisfied by a boolfact. This is because all in order for this ** routine to return true, keys 0..N were present in the join node. ** In opn_sjeqc, we will select the join eqc after reviewing all ** possibilities. One of the joined eqc's will be the first in the ** order list. If we only return true for this one, it will be joined. ** This only matters in the BTREE case. The prnode, in this case is ** also sorted on the first equiv class. ** If we picked an arbitrary eqc as keyed, we may do a needless sort. ** ** Inputs: ** subquery ptr to subquery being analyzed ** oeqcmp available equivalence class map of ** outer node ** jeqc primary attribute's eqc of key ** keyedvar joinop range variable number of ** possibly keyed relation ** primkey TRUE - if jeqc must be first joined ** element of orderlist ** ** Outputs: ** Returns: ** TRUE - if keyed access can be used for join ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 31-may-86 (seputis) ** initial creation ** 10-jun-96 (inkdo01) ** Support for Rtree inners of Kjoins. [@history_line@]... */ bool opn_ukey( OPS_SUBQUERY *subquery, OPE_BMEQCLS *oeqcmp, OPO_ISORT jeqc, OPV_IVARS keyedvar, bool primkey) { OPO_STORAGE storage; /* storage structure of relation */ OPV_VARS *varp; /* ptr to range variable element to ** be checked */ i4 ordercount; /* number of attributes that the ** relation is ordered on */ i4 orderindex; /* index into the OPB_MBF structure ** of the range variable element */ OPZ_AT *abase; /* ptr to base of array of ptrs to ** joinop attribute elements */ bool ret_val; /* TRUE is we can do keyed lookup */ /* Relation must be disk resident */ if (keyedvar < 0) return (FALSE); ret_val = FALSE; varp = subquery->ops_vars.opv_base->opv_rt[keyedvar]; /* get ptr to range ** variable element */ storage = varp->opv_tcop->opo_storage; /* get storage structure ** of relation */ abase = subquery->ops_attrs.opz_base; /* ptr to base of array of ptrs to ** joinop attribute elements */ #if 0 /* FIXME - why is this check here */ if ((storage) < 0) storage = -storage; #endif # ifdef xNTR1 if (tTf (1, 0)) { TRdisplay("Checking for possible keyed lookup into %s.\n", Jn.Range[keyedvar]->xrelid); TRdisplay("We %s looking for primary join key eqc %d.\n", (primkey ? "are" : "are not"), jeqc); TRdisplay("The joinop order eqcs are:"); for (i = 0; (att = order[i]) > -1; i++) TRdisplay("%d ", (i4) Jn.Attnums[att]->equcls); TRdisplay("\n"); uflush(); } # endif /* Each attribute in the ordering must be available from the outer ** or from the boolfacts */ ordercount = varp->opv_mbf.opb_count; /* number of attributes that the ** relation is ordered on */ for (orderindex = 0; orderindex < ordercount; orderindex++) { OPZ_IATTS attr; /* current ordering attribute being ** analyzed */ OPE_IEQCLS eqcls; /* equivalence class of ** of ordering attribute */ attr = varp->opv_mbf.opb_kbase->opb_keyorder[orderindex].opb_attno; eqcls = abase->opz_attnums[attr]->opz_equcls; if (storage == DB_RTRE_STORE) /* special Rtree inner logic */ { OPB_IBF bfi; OPB_BOOLFACT *bfp; for (bfi = 0; bfi < subquery->ops_bfs.opb_bv; bfi++) if ((bfp = subquery->ops_bfs.opb_base->opb_boolfact[bfi])-> opb_mask & OPB_SPATJ && BTtest((i4)eqcls, (char *)&bfp->opb_eqcmap)) { OPE_IEQCLS eqc1; if ((eqc1 = BTnext((i4)-1, (char *)&bfp->opb_eqcmap, (i4)subquery->ops_eclass.ope_ev)) == eqcls) eqc1 = BTnext((i4)eqc1, (char *)&bfp->opb_eqcmap, (i4)subquery->ops_eclass.ope_ev); if (BTtest((i4)eqc1, (char *)oeqcmp)) { eqcls = eqc1; break; /* kinda silly to let this drop into ** same test again - any alternative will ** be even uglier! */ } } } if (BTtest((i4)eqcls, (char *)oeqcmp) == TRUE) { /* Look no further if the first eqcls in the outer ** that exists in the order for the relation is not the ** joining eqcls (if primkey) */ if (primkey && (jeqc != eqcls)) { #ifdef xNTR1 if (tTf(1, 0)) { TRdisplay("We tried to join on eqcls %d but realized that it\n", eqcls); TRdisplay("was not the the primary attribute in the ordering and\n"); TRdisplay("that the primary attribute is available from the outer.\n"); uflush(); } #endif break; } /* Condition satisfied. If non-hash we are keyed */ ret_val = TRUE; primkey = FALSE; if (storage != DB_HASH_STORE) break; /* we are hashed and need all attrs in the key */ continue; } if (subquery->ops_bfs.opb_bfeqc && BTtest((i4)eqcls, (char *)subquery->ops_bfs.opb_bfeqc)) continue; /* check if constant key is ** available for this attribute*/ /* eqcls is not available. If we haven't gotten all ** of the hashed key, we can't use the key. */ if (storage == DB_HASH_STORE) { # ifdef xNTR1 if (tTf(1, 0)) { TRdisplay("Not all keys for hashed lookup were found.\n"); uflush(); } # endif ret_val = FALSE; } break; } # ifdef xNTR1 if (tTf(1, 0)) { if (ret_val == TRUE) TRdisplay("We can do keyed lookup.\n"); else TRdisplay("We can NOT do keyed lookup.\n"); uflush(); } # endif return (ret_val); }
/*{ ** Name: ope_type - get datatype of equivalence class ** ** Description: ** Get attribute from the eqclass with the greatest length if ** the types are the same or if FLOATs and INTs, pick the ** FLOAT. ** ** this only works for interior nodes, not original nodes ** ** Inputs: ** subquery ptr to subquery being analyzed ** eqcls equivalence class ** ** Outputs: ** Returns: ** ptr to DB_DATA_VALUE datatype of the equivalence class ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 13-jun-86 (seputis) ** initial creation from newatt ** 4-nov-97 (inkdo01) ** Don't get mad at the dopey OPE_SPATJ eqclass (it can be ** heterogeneous). [@history_line@]... */ DB_DATA_VALUE * ope_type( OPS_SUBQUERY *subquery, OPE_IEQCLS eqcls) { OPZ_BMATTS *attrmap; /* ptr to bit map of attributes in the ** equilvalence class */ OPZ_IATTS attr; /* current attribute of equivalence ** class being analyzed */ OPZ_AT *abase; /* ptr to base of array of ptrs to ** joinop attributes */ DB_DATA_VALUE *bestdtp; /* ptr to data type of attribute with ** largest width in equivalence class ** map */ DB_DT_ID besttype; /* best type found so far */ OPZ_IATTS maxattr; /* number of attributes defined in ** subquery */ OPE_EQCLIST *eqcp; /* ptr to eqcls structure */ bestdtp = NULL; eqcp = subquery->ops_eclass.ope_base->ope_eqclist[eqcls]; attrmap = &eqcp->ope_attrmap; maxattr = subquery->ops_attrs.opz_av; /* number of joinop attributes defined */ abase = subquery->ops_attrs.opz_base; /* ptr to base of array of ptrs to ** joinop attributes */ for (attr = -1; (attr = BTnext(attr, (char *)attrmap, (i4)maxattr)) >= 0;) { DB_DATA_VALUE *datatypep; /* ptr to datatype of current ** attribute element being tested */ DB_DT_ID type; /* type of next attribute being tested */ datatypep = &abase->opz_attnums[attr]->opz_dataval; /* datatype of ** this attribute */ type = abs(datatypep->db_datatype); /* save type of new attribute */ if (!bestdtp) { /* if this is the first then use it */ bestdtp = datatypep; besttype = type; } else { if (( (type == DB_FLT_TYPE) && (besttype == DB_INT_TYPE) ) || ( (type == besttype || eqcp->ope_mask & OPE_SPATJ) && (datatypep->db_length > bestdtp->db_length) )) { /* new attribute has higher priority type */ bestdtp = datatypep; besttype = type; } #ifdef E_OP0383_TYPEMISMATCH if ((abs(datatypep->db_datatype) != abs(bestdtp->db_datatype) ) && !(eqcp->ope_mask & OPE_SPATJ) && (!opn_numeric(type) || !opn_numeric(besttype))) /* newatt: bad types:%d, %d */ opx_error(E_OP0383_TYPEMISMATCH); #endif } } #ifdef E_OP0384_NOATTS if (!bestdtp) /* newatt: nothing in att map, %d */ opx_error(E_OP0384_NOATTS); #endif return(bestdtp); }
/*{ ** Name: opa_obylist - replace variables in outer by inner ** ** Description: ** This procedure will attempt to replace the variables in the outer ** aggregate by using bylist attributes of the inner aggregate. ** ** Inputs: ** global global state variable ** inner inner function aggregate subquery ** whose bylist will be used to attempt ** to replace the outer aggregate variables ** outer outer aggregate subquery whose variables ** will possibly be replaced by the inner ** ** Outputs: ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 15-apr-86 (seputis) ** initial creation ** 16-may-96 (inkdo01) ** Change 409554 has been backed out to fix bug 74793. It claimed to ** eliminate obsolete code (because of change 409457), but the code ** appears to have still been necessary for queries involving outer ** joins of aggregate views. ** 3-dec-02 (inkdo01) ** Changes for range table expansion. ** 23-nov-05 (inkdo01) ** Fix a bug in one of the more complex expressions that derived from ** the range table expansion. [@history_line@]... */ static VOID opa_obylist( OPS_STATE *global, OPS_SUBQUERY *inner, OPS_SUBQUERY *outer) { OPV_GBMVARS outermap; /* var map of outer aggregate */ opv_smap(outer); /* get variable map of outer aggregate */ MEcopy((char *)&outer->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm, sizeof(outermap), (char *)&outermap); BTor(OPV_MAXVAR, (char *)&outer->ops_root->pst_sym.pst_value.pst_s_root.pst_rvrm, (char *)&outermap); opv_smap(inner); /* get variable map of inner aggregate */ BTand(OPV_MAXVAR, (char *)&inner->ops_agg.opa_blmap, (char *)&outermap); if (BTcount((char *)&outermap, OPV_MAXVAR) == 0) /* if the outer aggregate and the inner aggregate do not have any ** variables in common then there can be no replacement so return. */ return; BTor(OPV_MAXVAR, (char *)&inner->ops_root->pst_sym.pst_value.pst_s_root.pst_tvrm, (char *)&outer->ops_aggmap); BTor(OPV_MAXVAR, (char *)&inner->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm, (char *)&outer->ops_aggmap); BTor(OPV_MAXVAR, (char *)&inner->ops_root->pst_sym.pst_value.pst_s_root.pst_rvrm, (char *)&outer->ops_aggmap); /* this set of variables could be substituted ** in the outer, so they are not to be ** assumed to be in the from list for ** a cartesean product as in the query ** "select r.a from r,s" */ { OPV_GBMVARS usedmap; /* var map of variables which were ** replaced by substituting the ** bylist attributes of the inner */ OPV_GBMVARS newmap; /* var map of the outer aggregate ** after the inner aggregate bylist ** used to substitute expressions ** in the outer */ OPV_GBMVARS tempmap; MEfill(sizeof(usedmap), 0, (char *)&usedmap); /* initialize var map */ opa_checkopt(global, inner->ops_agg.opa_byhead->pst_left, outer->ops_root, &usedmap, &newmap); /* this routine will return information ** on what the query tree would ** look like if the inner aggregate ** was substituted (without actually ** doing the substitution) */ MEcopy((char *)&newmap, sizeof(newmap), (char *)&tempmap); BTand(OPV_MAXVAR, (char *)&usedmap, (char *)&tempmap); /* This replaces the old (32 bit varmap) test of "usedmap && ** !(newmap & usedmap)". */ if (BTcount((char *)&usedmap, OPV_MAXVAR) != 0 && /* non-zero implies some optimizations ** were found */ BTcount((char *)&tempmap, OPV_MAXVAR) == 0) /* the substitution would eliminate ** those variables entirely */ { /* COMMIT THE CHANGES ** the usedmap is non-zero so some variable subtitutions were ** found. Moreover, the substitutions would entirely eliminate ** the variables since newmap is non-zero ** ** First making a copy of the bylist for the outer aggregate ** if it exists (and if it is not the main query). This ** is done to avoid the problem of optimizing away the links ** made by the outer aggregate. For example, in the query ** RETRIEVE SUPPLIERS WHO SUPPLY ALL PARTS ** ret( s.sname, s.s) where any(p.p by s.s where any(sp.tid ** by p.p,s.s where p.p=sp.p and s.s=sp.s)=0)=0 ** In this query the outer aggregate references only attributes ** in the BY list of the inner aggregate, and won't have the ** aggregate result linked to the main query ... if we did ** not make a copy of the bylist ... remember that the outer ** aggregate was linked to the main query by using the by list ** subtrees directly! ** FIXME - OPA_LINK will copy the bylists anyways so this copy ** is not needed */ OPV_IGVARS innervarno; /* var number of inner ** aggregate which will be ** referenced for substitution */ if (outer->ops_sqtype == OPS_MAIN) global->ops_gmask |= OPS_TCHECK; else if (outer->ops_agg.opa_byhead) /* outer aggregate has a by list */ { PST_QNODE *bylist; /* used to traverse the bylist */ /* traverse the bylist and copy the subtrees */ for ( bylist = outer->ops_agg.opa_byhead->pst_left; bylist && bylist->pst_sym.pst_type != PST_TREE; bylist = bylist->pst_left) opv_copytree( global, &bylist->pst_right ); } /* Traverse the tree and actually perform the substitutions instead ** of only checking for them */ innervarno = (*inner->ops_agg.opa_graft)->pst_sym.pst_value. pst_s_var.pst_vno; if (outer->ops_global->ops_qheader->pst_numjoins > 0) { /* make sure that all the outer joins semantics are ** the same for all the relations referenced, or else ** semantics are lost, i.e. cannot substitute if variables ** have different maps */ PST_J_MASK pinner; PST_J_MASK pouter; bool first_time; OPV_IGVARS gvar; PST_J_MASK *ijmaskp; PST_J_MASK *ojmaskp; OPL_PARSER *pinnerp; OPL_PARSER *pouterp; first_time = TRUE; pinnerp = outer->ops_oj.opl_pinner; pouterp = outer->ops_oj.opl_pouter; for (gvar = -1; (gvar = BTnext((i4)gvar, (char *)&usedmap, (i4)BITS_IN(usedmap)))>=0;) { if (first_time) { MEcopy((PTR)&pinnerp->opl_parser[gvar], sizeof(pinner), (PTR)&pinner); MEcopy((PTR)&pouterp->opl_parser[gvar], sizeof(pouter), (PTR)&pouter); } else { if (MEcmp((PTR)&pinnerp->opl_parser[gvar], (PTR)&pinner, sizeof(pinner)) || MEcmp((PTR)&pouterp->opl_parser[gvar], (PTR)&pouter, sizeof(pouter)) ) return; /* outer joins semantics of ** variables to be substituted are ** different, FIXME, try to substitute ** one variable instead of 2 */ } } /* copy the outer join semantics to the substituted variable */ ijmaskp = &pinnerp->opl_parser[innervarno]; ojmaskp = &pouterp->opl_parser[innervarno]; if ((BTnext((i4)-1, (char *)ijmaskp, (i4)BITS_IN(*ijmaskp)) >= 0) || (BTnext((i4)-1, (char *)ojmaskp, (i4)BITS_IN(*ojmaskp)) >= 0) ) opx_error(E_OP0288_OJAGG); /* should not already have an ** outer join defined on this aggregate ** in this query */ MEcopy((PTR)&pinner, sizeof(*ijmaskp), (PTR)ijmaskp); MEcopy((PTR)&pouter, sizeof(*ojmaskp), (PTR)ojmaskp); } outer->ops_vmflag = FALSE; /* bitmaps need to be updated if a ** substitution on the outer is made */ opa_commit(global, inner->ops_agg.opa_byhead->pst_left, &outer->ops_root, innervarno); /* this routine will traverse ** the tree in the same way as ** opa_checkopt except that ** substitutions will actually be made */ global->ops_gmask &= (~OPS_TCHECK); } } }
DB_STATUS dmu_modify(DMU_CB *dmu_cb) { DM_SVCB *svcb = dmf_svcb; DMU_CB *dmu = dmu_cb; DML_XCB *xcb; DML_ODCB *odcb; DM2U_MOD_CB local_mcb, *mcb = &local_mcb; i4 recovery; i4 truncate; i4 duplicates; i4 i,j; i4 indicator; i4 error, local_error; DB_STATUS status; bool bad_loc; i4 blob_add_extend = 0; bool used_default_page_size = TRUE; i4 page_size; i4 verify_options; i4 mask; i4 has_extensions = 0; DB_OWN_NAME table_owner; DB_TAB_NAME table_name; bool got_action; bool is_table_debug; bool reorg; CLRDBERR(&dmu->error); /* Any modify should make table recovery disallowed except for the ** alter_status options which change logical, physical consistency ** and table recovery bit itself */ mcb->mcb_mod_options2 = DM2U_2_TBL_RECOVERY_DEFAULT; do { /* Check for bad flags. */ mask = ~(DMU_VGRANT_OK | DMU_INTERNAL_REQ | DMU_RETINTO | DMU_PARTITION | DMU_MASTER_OP | DMU_ONLINE_START | DMU_ONLINE_END | DMU_NO_PAR_INDEX | DMU_PIND_CHAINED | DMU_NODEPENDENCY_CHECK); if ( (dmu->dmu_flags_mask & mask) != 0) { SETDBERR(&dmu->error, 0, E_DM001A_BAD_FLAG); break; } /* Validate the transaction id. */ xcb = (DML_XCB *)dmu->dmu_tran_id; if (dm0m_check((DM_OBJECT *)xcb, (i4)XCB_CB) != E_DB_OK) { SETDBERR(&dmu->error, 0, E_DM003B_BAD_TRAN_ID); break; } /* Check for external interrupts */ if ( xcb->xcb_scb_ptr->scb_ui_state ) dmxCheckForInterrupt(xcb, &error); if ( xcb->xcb_state ) { if (xcb->xcb_state & XCB_USER_INTR) { SETDBERR(&dmu->error, 0, E_DM0065_USER_INTR); break; } if (xcb->xcb_state & XCB_FORCE_ABORT) { SETDBERR(&dmu->error, 0, E_DM010C_TRAN_ABORTED); break; } if (xcb->xcb_state & XCB_ABORT) { SETDBERR(&dmu->error, 0, E_DM0064_USER_ABORT); break; } } /* Check the database identifier. */ odcb = (DML_ODCB *)dmu->dmu_db_id; if (dm0m_check((DM_OBJECT *)odcb, (i4)ODCB_CB) != E_DB_OK) { SETDBERR(&dmu->error, 0, E_DM0010_BAD_DB_ID); break; } mcb->mcb_db_lockmode = DM2T_X; /* Check that this is a update transaction on the database ** that can be updated. */ if (odcb != xcb->xcb_odcb_ptr) { SETDBERR(&dmu->error, 0, E_DM005D_TABLE_ACCESS_CONFLICT); break; } /* Prime the MCB */ mcb->mcb_dcb = odcb->odcb_dcb_ptr; mcb->mcb_xcb = xcb; mcb->mcb_tbl_id = &dmu->dmu_tbl_id; mcb->mcb_omcb = (DM2U_OMCB*)NULL; mcb->mcb_dmu = dmu; mcb->mcb_structure = 0; mcb->mcb_i_fill = 0; mcb->mcb_l_fill = 0; mcb->mcb_d_fill = 0; mcb->mcb_unique = FALSE; mcb->mcb_compressed = TCB_C_NONE; mcb->mcb_index_compressed = FALSE; mcb->mcb_temporary = FALSE; mcb->mcb_merge = FALSE; mcb->mcb_clustered = FALSE; mcb->mcb_modoptions = 0; mcb->mcb_min_pages = 0; mcb->mcb_max_pages = 0; mcb->mcb_allocation = 0; mcb->mcb_extend = 0; mcb->mcb_page_type = TCB_PG_INVALID; mcb->mcb_page_size = svcb->svcb_page_size; mcb->mcb_tup_info = &dmu->dmu_tup_cnt; mcb->mcb_reltups = 0; mcb->mcb_tab_name = &table_name; mcb->mcb_tab_owner = &table_owner; mcb->mcb_has_extensions = &has_extensions; mcb->mcb_relstat2 = 0; mcb->mcb_flagsmask = dmu->dmu_flags_mask; mcb->mcb_tbl_pri = 0; mcb->mcb_rfp_entry = (DM2U_RFP_ENTRY*)NULL; mcb->mcb_new_part_def = (DB_PART_DEF*)dmu->dmu_part_def; mcb->mcb_new_partdef_size = dmu->dmu_partdef_size; mcb->mcb_verify = 0; dmu->dmu_tup_cnt = 0; truncate = 0; reorg = FALSE; duplicates = -1; verify_options = 0; got_action = FALSE; /* FIXME better messages (in general) */ /* If there's a partdef it has to be one-piece, else bad param */ if (dmu->dmu_part_def != NULL && dmu->dmu_part_def->ndims > 0 && (dmu->dmu_part_def->part_flags & DB_PARTF_ONEPIECE) == 0) { SETDBERR(&dmu->error, 0, E_DM002A_BAD_PARAMETER); break; } /* Disassemble the modify action. ** FIXME this used to be buried in the characteristics array. ** It would make much more sense to just carry the action ** code through, but that will have to wait for another day. */ got_action = FALSE; switch (dmu->dmu_action) { case DMU_ACT_STORAGE: if (BTtest(DMU_STRUCTURE, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_structure = dmu->dmu_chars.dmu_struct; } break; case DMU_ACT_ADDEXTEND: got_action = TRUE; mcb->mcb_mod_options2 |= DM2U_2_ADD_EXTEND; break; case DMU_ACT_ENCRYPT: got_action = TRUE; mcb->mcb_mod_options2 |= DM2U_2_ENCRYPT; break; case DMU_ACT_LOG_CONSISTENT: if (BTtest(DMU_ACTION_ONOFF, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_mod_options2 &= ~DM2U_2_TBL_RECOVERY_DEFAULT; if ( dmu->dmu_chars.dmu_flags & DMU_FLAG_ACTON ) mcb->mcb_mod_options2 |= DM2U_2_LOG_CONSISTENT; else mcb->mcb_mod_options2 |= DM2U_2_LOG_INCONSISTENT; } break; case DMU_ACT_MERGE: got_action = TRUE; mcb->mcb_merge = TRUE; break; case DMU_ACT_PERSISTENCE: if (BTtest(DMU_PERSISTS_OVER_MODIFIES, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_mod_options2 |= (dmu->dmu_chars.dmu_flags & DMU_FLAG_PERSISTENCE) ? DM2U_2_PERSISTS_OVER_MODIFIES : DM2U_2_NOPERSIST_OVER_MODIFIES; } break; case DMU_ACT_PHYS_CONSISTENT: if (BTtest(DMU_ACTION_ONOFF, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_mod_options2 &= ~DM2U_2_TBL_RECOVERY_DEFAULT; if ( dmu->dmu_chars.dmu_flags & DMU_FLAG_ACTON ) mcb->mcb_mod_options2 |= DM2U_2_PHYS_CONSISTENT; else mcb->mcb_mod_options2 |= DM2U_2_PHYS_INCONSISTENT; } break; case DMU_ACT_PRIORITY: if (BTtest(DMU_TABLE_PRIORITY, dmu->dmu_chars.dmu_indicators)) got_action = TRUE; /* flag setting when we hit the priority char */ break; case DMU_ACT_READONLY: if (BTtest(DMU_ACTION_ONOFF, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; if ( dmu->dmu_chars.dmu_flags & DMU_FLAG_ACTON ) mcb->mcb_mod_options2 |= DM2U_2_READONLY; else mcb->mcb_mod_options2 |= DM2U_2_NOREADONLY; } break; case DMU_ACT_REORG: got_action = TRUE; reorg = TRUE; break; case DMU_ACT_TABLE_RECOVERY: if (BTtest(DMU_ACTION_ONOFF, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_mod_options2 &= ~DM2U_2_TBL_RECOVERY_DEFAULT; if ( dmu->dmu_chars.dmu_flags & DMU_FLAG_ACTON ) mcb->mcb_mod_options2 |= DM2U_2_TBL_RECOVERY_ALLOWED; else mcb->mcb_mod_options2 |= DM2U_2_TBL_RECOVERY_DISALLOWED; } break; case DMU_ACT_TRUNC: got_action = TRUE; truncate++; break; case DMU_ACT_USCOPE: if (BTtest(DMU_STATEMENT_LEVEL_UNIQUE, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_mod_options2 |= DM2U_2_STATEMENT_LEVEL_UNIQUE; } break; case DMU_ACT_VERIFY: if (BTtest(DMU_VACTION, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_verify = dmu->dmu_chars.dmu_vaction; } break; } /* switch */ if (! got_action) { SETDBERR(&dmu->error, 0, E_DM000E_BAD_CHAR_VALUE); break; } /* Disassemble the characteristics. ** FIXME probably better to just carry it through, but one step ** at a time! */ indicator = -1; while ((indicator = BTnext(indicator, dmu->dmu_chars.dmu_indicators, DMU_CHARIND_LAST)) != -1) { switch (indicator) { case DMU_ACTION_ONOFF: case DMU_STRUCTURE: /* Already picked it up, just skip on */ continue; case DMU_IFILL: mcb->mcb_i_fill = dmu->dmu_chars.dmu_nonleaff; if (mcb->mcb_i_fill > 100) mcb->mcb_i_fill = 100; continue; case DMU_LEAFFILL: mcb->mcb_l_fill = dmu->dmu_chars.dmu_leaff; if (mcb->mcb_l_fill > 100) mcb->mcb_l_fill = 100; continue; case DMU_DATAFILL: mcb->mcb_d_fill = dmu->dmu_chars.dmu_fillfac; if (mcb->mcb_d_fill > 100) mcb->mcb_d_fill = 100; continue; case DMU_PAGE_SIZE: used_default_page_size = FALSE; mcb->mcb_page_size = dmu->dmu_chars.dmu_page_size; if (mcb->mcb_page_size != 2048 && mcb->mcb_page_size != 4096 && mcb->mcb_page_size != 8192 && mcb->mcb_page_size != 16384 && mcb->mcb_page_size != 32768 && mcb->mcb_page_size != 65536) { SETDBERR(&dmu->error, indicator, E_DM000E_BAD_CHAR_VALUE); break; } else if (!dm0p_has_buffers(mcb->mcb_page_size)) { SETDBERR(&dmu->error, 0, E_DM0157_NO_BMCACHE_BUFFERS); break; } else { continue; } case DMU_MINPAGES: mcb->mcb_min_pages = dmu->dmu_chars.dmu_minpgs; continue; case DMU_MAXPAGES: mcb->mcb_max_pages = dmu->dmu_chars.dmu_maxpgs; continue; case DMU_UNIQUE: mcb->mcb_unique = TRUE; continue; case DMU_DCOMPRESSION: /* Translate DMU_xxx to TCB compression types */ if (dmu->dmu_chars.dmu_dcompress == DMU_COMP_ON) mcb->mcb_compressed = TCB_C_DEFAULT; else if (dmu->dmu_chars.dmu_dcompress == DMU_COMP_HI) mcb->mcb_compressed = TCB_C_HICOMPRESS; continue; case DMU_KCOMPRESSION: mcb->mcb_index_compressed = (dmu->dmu_chars.dmu_kcompress != DMU_COMP_OFF); continue; case DMU_TEMP_TABLE: mcb->mcb_temporary = TRUE; continue; case DMU_RECOVERY: recovery = (dmu->dmu_chars.dmu_flags & DMU_FLAG_RECOVERY) != 0; if (recovery) { /* recovery isn't currently supported */ SETDBERR(&dmu->error, indicator, E_DM000D_BAD_CHAR_ID); break; } continue; case DMU_DUPLICATES: duplicates = 0; if (dmu->dmu_chars.dmu_flags & DMU_FLAG_DUPS) duplicates = 1; continue; case DMU_ALLOCATION: mcb->mcb_allocation = dmu->dmu_chars.dmu_alloc; continue; case DMU_EXTEND: mcb->mcb_extend = dmu->dmu_chars.dmu_extend; continue; case DMU_VACTION: /* Already got it, just skip on */ continue; case DMU_VOPTION: verify_options = dmu->dmu_chars.dmu_voption; continue; case DMU_STATEMENT_LEVEL_UNIQUE: if (dmu->dmu_chars.dmu_flags & DMU_FLAG_UNIQUE_STMT) mcb->mcb_relstat2 |= TCB_STATEMENT_LEVEL_UNIQUE; continue; case DMU_PERSISTS_OVER_MODIFIES: if (dmu->dmu_chars.dmu_flags & DMU_FLAG_PERSISTENCE) mcb->mcb_relstat2 |= TCB_PERSISTS_OVER_MODIFIES; continue; case DMU_SYSTEM_GENERATED: mcb->mcb_relstat2 |= TCB_SYSTEM_GENERATED; continue; case DMU_SUPPORTS_CONSTRAINT: mcb->mcb_relstat2 |= TCB_SUPPORTS_CONSTRAINT; continue; case DMU_NOT_UNIQUE: mcb->mcb_relstat2 |= TCB_NOT_UNIQUE; continue; case DMU_NOT_DROPPABLE: mcb->mcb_relstat2 |= TCB_NOT_DROPPABLE; continue; case DMU_ROW_SEC_AUDIT: mcb->mcb_relstat2 |= TCB_ROW_AUDIT; continue; case DMU_TABLE_PRIORITY: mcb->mcb_tbl_pri = dmu->dmu_chars.dmu_cache_priority; if (mcb->mcb_tbl_pri < 0 || mcb->mcb_tbl_pri > DB_MAX_TABLEPRI) { SETDBERR(&dmu->error, indicator, E_DM000E_BAD_CHAR_VALUE); break; } /* ** DMU_TABLE_PRIORITY is set if priority came from WITH clause. ** DMU_TO_TABLE_PRIORITY is set if priority came from MODIFY TO clause. */ if (dmu->dmu_action != DMU_ACT_PRIORITY) mcb->mcb_mod_options2 |= DM2U_2_TABLE_PRIORITY; else mcb->mcb_mod_options2 |= DM2U_2_TO_TABLE_PRIORITY; continue; case DMU_BLOBEXTEND: blob_add_extend = dmu->dmu_chars.dmu_blobextend; continue; case DMU_CLUSTERED: mcb->mcb_clustered = (dmu->dmu_chars.dmu_flags & DMU_FLAG_CLUSTERED) != 0; continue; case DMU_CONCURRENT_UPDATES: /* Translate from PSF flag to DMU internal flag */ if (dmu->dmu_chars.dmu_flags & DMU_FLAG_CONCUR_U) mcb->mcb_flagsmask |= DMU_ONLINE_START; continue; default: /* Ignore anything else, might be for CREATE, who knows */ continue; } break; } /* ** If no page size specified, set page_size to zero ** In this case the current page size will be used */ if (used_default_page_size) mcb->mcb_page_size = 0; /* Save a local copy for dmpe_modify, since dm2u_modify can alter mcb */ page_size = mcb->mcb_page_size; if (mcb->mcb_structure == TCB_HEAP) { if (mcb->mcb_d_fill == 0) mcb->mcb_d_fill = DM_F_HEAP; } else if (mcb->mcb_structure == TCB_ISAM) { if (mcb->mcb_i_fill == 0) mcb->mcb_i_fill = DM_FI_ISAM; if (mcb->mcb_d_fill == 0) { if (mcb->mcb_compressed != TCB_C_NONE) mcb->mcb_d_fill = DM_F_CISAM; else mcb->mcb_d_fill = DM_F_ISAM; } } else if (mcb->mcb_structure == TCB_HASH) { if (mcb->mcb_d_fill == 0) { if (mcb->mcb_compressed != TCB_C_NONE) mcb->mcb_d_fill = DM_F_CHASH; else mcb->mcb_d_fill = DM_F_HASH; } if (mcb->mcb_min_pages == 0) { if (mcb->mcb_compressed != TCB_C_NONE) mcb->mcb_min_pages = 1; else mcb->mcb_min_pages = 10; /* If user specified max pages, don't set minpages higher */ if (mcb->mcb_min_pages > mcb->mcb_max_pages && mcb->mcb_max_pages != 0) mcb->mcb_min_pages = mcb->mcb_max_pages; } if (mcb->mcb_max_pages == 0) mcb->mcb_max_pages = 8388607; } else if (mcb->mcb_structure == TCB_BTREE || mcb->mcb_merge) { if (DMZ_AM_MACRO(16) && !mcb->mcb_temporary) { /* DM616 -- forces index compression to be used: */ mcb->mcb_index_compressed = TRUE; } if (mcb->mcb_i_fill == 0) mcb->mcb_i_fill = DM_FI_BTREE; if (mcb->mcb_l_fill == 0) mcb->mcb_l_fill = DM_FL_BTREE; if (mcb->mcb_d_fill == 0) { if (mcb->mcb_compressed != TCB_C_NONE) mcb->mcb_d_fill = DM_F_CBTREE; else mcb->mcb_d_fill = DM_F_BTREE; } } else if (truncate) { if (mcb->mcb_d_fill == 0) mcb->mcb_d_fill = DM_F_HEAP; } if (mcb->mcb_structure == TCB_HASH && mcb->mcb_min_pages > mcb->mcb_max_pages) { SETDBERR(&dmu->error, 0, E_DM000D_BAD_CHAR_ID); break; } mcb->mcb_kcount = dmu->dmu_key_array.ptr_in_count; mcb->mcb_key = (DMU_KEY_ENTRY**) dmu->dmu_key_array.ptr_address; if (mcb->mcb_kcount && (mcb->mcb_key == (DMU_KEY_ENTRY**)NULL || dmu->dmu_key_array.ptr_size != sizeof(DMU_KEY_ENTRY))) { SETDBERR(&dmu->error, 0, E_DM002A_BAD_PARAMETER); break; } if (truncate) { mcb->mcb_kcount = 0; mcb->mcb_modoptions |= DM2U_TRUNCATE; } if (duplicates == 1) mcb->mcb_modoptions |= DM2U_DUPLICATES; else if (duplicates == 0) mcb->mcb_modoptions |= DM2U_NODUPLICATES; /* else duplicates == -1, set neither flag */ if (reorg) mcb->mcb_modoptions |= DM2U_REORG; /* CLUSTERED implies and requires Unique */ if ( mcb->mcb_clustered && mcb->mcb_structure == TCB_BTREE ) mcb->mcb_unique = TRUE; else mcb->mcb_clustered = FALSE; if (mcb->mcb_verify) { if (verify_options == 0) { /* Apply defaults. */ switch (mcb->mcb_verify) { case DMU_V_VERIFY: verify_options = DMU_T_LINK | DMU_T_RECORD | DMU_T_ATTRIBUTE; break; case DMU_V_REPAIR: case DMU_V_DEBUG: verify_options = DMU_T_BITMAP; break; case DMU_V_PATCH: case DMU_V_FPATCH: break; } } /* Shift modifiers into place */ mcb->mcb_verify |= (verify_options << DM1U_MODSHIFT); } is_table_debug = ((mcb->mcb_verify & DM1U_OPMASK) == DM1U_DEBUG); /* Check the location names for duplicates, too many. */ mcb->mcb_location = (DB_LOC_NAME*)NULL; mcb->mcb_l_count = 0; if (dmu->dmu_location.data_address && (dmu->dmu_location.data_in_size >= sizeof(DB_LOC_NAME)) && mcb->mcb_temporary == FALSE) { mcb->mcb_location = (DB_LOC_NAME *) dmu->dmu_location.data_address; mcb->mcb_l_count = dmu->dmu_location.data_in_size/sizeof(DB_LOC_NAME); if (mcb->mcb_l_count > DM_LOC_MAX) { SETDBERR(&dmu->error, 0, E_DM0071_LOCATIONS_TOO_MANY); break; } bad_loc = FALSE; for (i = 0; i < mcb->mcb_l_count; i++) { for (j = 0; j < i; j++) { /* ** Compare this location name against other ** already given, they cannot be the same. */ if (MEcmp(mcb->mcb_location[j].db_loc_name, mcb->mcb_location[i].db_loc_name, sizeof(DB_LOC_NAME)) == 0 ) { SETDBERR(&dmu->error, i, E_DM001E_DUP_LOCATION_NAME); bad_loc = TRUE; break; } } if (bad_loc == TRUE) break; } if (bad_loc == TRUE) break; } else { /* There must a location list if you are reorganizing ** to a different number of locations. */ if (reorg) { if (dmu->dmu_location.data_address && dmu->dmu_location.data_in_size) SETDBERR(&dmu->error, 0, E_DM001F_LOCATION_LIST_ERROR); else SETDBERR(&dmu->error, 0, E_DM0072_NO_LOCATION); break; } } mcb->mcb_partitions = (DMU_PHYPART_CHAR*)NULL; mcb->mcb_nparts = 0; if ( dmu->dmu_ppchar_array.data_address && dmu->dmu_ppchar_array.data_in_size >= sizeof(DMU_PHYPART_CHAR) ) { mcb->mcb_partitions = (DMU_PHYPART_CHAR*)dmu->dmu_ppchar_array.data_address; mcb->mcb_nparts = dmu->dmu_ppchar_array.data_in_size / sizeof(DMU_PHYPART_CHAR); } if ((xcb->xcb_x_type & XCB_RONLY) && !is_table_debug) { SETDBERR(&dmu->error, 0, E_DM006A_TRAN_ACCESS_CONFLICT); break; } /* ** If this is the first write operation for this transaction, ** then we need to write the begin transaction record. */ if ((xcb->xcb_flags & XCB_DELAYBT) != 0 && mcb->mcb_temporary == FALSE && !is_table_debug) { status = dmxe_writebt(xcb, TRUE, &dmu->error); if (status != E_DB_OK) { xcb->xcb_state |= XCB_TRANABORT; break; } } /* Calls the physical layer to process the rest of the modify */ status = dm2u_modify(mcb, &dmu->error); if (status == E_DB_OK && has_extensions) { if ((mcb->mcb_mod_options2 & DM2U_2_ADD_EXTEND) && blob_add_extend == 0) status = E_DB_OK; else { /* FIX ME make modify etabs optional !! */ /* Add flag to modify top make modify etabs optional */ /* Add sysmod dbname tablename blob-column-name */ #ifdef xDEBUG TRdisplay("Modify etabs for %~t %~t\n", sizeof(DB_TAB_NAME), table_name.db_tab_name, sizeof(DB_OWN_NAME), table_owner.db_own_name); #endif status = dmpe_modify(dmu, odcb->odcb_dcb_ptr, xcb, &dmu->dmu_tbl_id, mcb->mcb_db_lockmode, mcb->mcb_temporary, truncate, (i4)0, blob_add_extend, &dmu->error); } } /* Audit successful MODIFY/PATCH of TABLE. */ if ( status == E_DB_OK && dmf_svcb->svcb_status & SVCB_C2SECURE ) { i4 msgid; i4 access = SXF_A_SUCCESS; if ((mcb->mcb_verify & DM1U_OPMASK) == DM1U_PATCH || (mcb->mcb_verify & DM1U_OPMASK) == DM1U_FPATCH) { access |= SXF_A_ALTER; msgid = I_SX271A_TABLE_PATCH; } else { access |= SXF_A_MODIFY; msgid = I_SX270F_TABLE_MODIFY; } /* ** Audit success */ status = dma_write_audit( SXF_E_TABLE, access, table_name.db_tab_name, /* Table/view name */ sizeof(table_name.db_tab_name), /* Table/view name */ &table_owner, /* Table/view owner */ msgid, FALSE, /* Not force */ &dmu->error, NULL); } if (status == E_DB_OK) { /* If modify to reorg or merge then return no tuple count info. */ if (reorg || (mcb->mcb_merge) || (mcb->mcb_verify != 0)) { dmu->dmu_tup_cnt = DM_NO_TUPINFO; } return (E_DB_OK); } else { if (dmu->error.err_code > E_DM_INTERNAL) { uleFormat(&dmu->error, 0, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, (char * )NULL, (i4)0, (i4 *)NULL, &local_error, 0); SETDBERR(&dmu->error, 0, E_DM0091_ERROR_MODIFYING_TABLE); } switch (dmu->error.err_code) { case E_DM004B_LOCK_QUOTA_EXCEEDED: case E_DM0112_RESOURCE_QUOTA_EXCEED: case E_DM0091_ERROR_MODIFYING_TABLE: case E_DM009B_ERROR_CHK_PATCH_TABLE: case E_DM0045_DUPLICATE_KEY: case E_DM0137_GATEWAY_ACCESS_ERROR: case E_DM006A_TRAN_ACCESS_CONFLICT: xcb->xcb_state |= XCB_STMTABORT; break; case E_DM0042_DEADLOCK: case E_DM004A_INTERNAL_ERROR: case E_DM0100_DB_INCONSISTENT: xcb->xcb_state |= XCB_TRANABORT; break; case E_DM0065_USER_INTR: xcb->xcb_state |= XCB_USER_INTR; break; case E_DM010C_TRAN_ABORTED: xcb->xcb_state |= XCB_FORCE_ABORT; break; case E_DM007D_BTREE_BAD_KEY_LENGTH: dmu->dmu_tup_cnt = dmu->dmu_tup_cnt; /* same for now */ default: break; } } } while (FALSE); if (dmu->error.err_code > E_DM_INTERNAL) { uleFormat(&dmu->error, 0, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, (char * )NULL, (i4)0, (i4 *)NULL, &local_error, 0); SETDBERR(&dmu->error, 0, E_DM0091_ERROR_MODIFYING_TABLE); } return (E_DB_ERROR); }
/*{ ** Name: psy_subsvars - Scan query tree and replace VAR nodes ** ** Description: ** Scans a tree and finds all VAR nodes for this variable. ** These nodes are looked up in the translation tree and ** replaced by the value found there. If this is for a ** view, the corresponding node must exist in the translation ** tree. If not for a view, a 'zero' node (of a type appropriate based ** on the context) is created and inserted, or, if for a "replace ** cursor" command, a CURVAL node is substituted for the VAR ** node. ** ** This routine is one half of the guts of the whole view ** algorithm. ** ** VAR nodes are detached and replaced with the replacement ** as defined by the view. Note that there can never be any ** problems here with update anomalies, since VAR nodes are ** only used in retrieve contexts. ** ** It does some extra processing with RESDOM nodes with ** resno = 0. These nodes specify a 'tid' domain, and are ** included by the parser on REPLACE and DELETE commands ** Subsvars will allow this construct iff the right hand pointer is a ** VAR node with attno = 0. In pre-Jupiter versions, this used ** to update the variable number in these tid RESDOMs; in Jupiter, ** however, it was changed to keep the same result variable number ** throughout the qrymod process, so this should be unnecessary. ** This is because the resvar is the variable number of the one and ** only underlying base relation of the view on an update ** (which is presumably the only case where this can come ** up). Psy_vrscan has already insured that there can only be ** a single base relation in this case. ** ** This whole messy thing is only done with view substitutions. ** NOTE: THIS IS NOT TRUE! IT IS ALSO DONE FOR INTEGRITY SUBSTITUTIONS! ** I DON'T KNOW WHY THIS COMMENT IS HERE. ** ** In order to fix the handling of aggregates over views, subsvars ** calls psy_apql at the appropriate place to append a ** view qualification (if any). It is done here to handle nested ** aggregates correctly since after psy_subsvars we no longer know ** which nested aggregates actually contained the view variable. ** The view qual will be appended if and only if the view var ** appears explicitly within the immediate scope of a root node ** (NOT in a nested aggregate.) ** ** If at any scope we encounter a var node, we add the qualification ** to that scope. Once a var node has been found in a scope (and ** the qualifaction added), for example, a nested aggregate, the ** qualification is not added to an outer scope unless a var node ** in the view or integ has been found in that outer scope. ** ** ** Inputs: ** proot Pointer to pointer to root of tree ** to be updated ** rngvar view variable range table entry ** transtree Pointer to the target list of the ** translation tree ** vmode PSQ_VIEW if called from view processor, ** PSQ_APPEND is called from the integrity ** processor with an APPEND command, else ** something else. Mostly, changes ** handling of tid nodes, and forces an ** error on a view if the VAR node in the ** scanned tree does not exist in the ** vtree. ** vqual View qualification to be appended, ** if any. ** resvar Range table entry for result variable ** in query being modified. ** from_list from_list from view getting added. ** qmode Query mode of user query. ** cursid Cursor id of current cursor, if any ** result Pointer to indicator for result. ** dup_rb Ptr to dup. request block ** pss_op_mask -- 0 ** pss_num_joins -- PST_NOJOIN ** pss_tree_info -- NULL ** pss_mstream -- ptr to memory stream to be used ** pss_err_blk -- ptr to error block ** ** Outputs: ** proot User query tree can be updated ** result Filled in with TRUE if view variable ** was found, FALSE if not. Valid only ** if E_DB_OK returned. ** dup_rb ** pss_err_blk Filled in if an error happens. ** Returns: ** E_DB_OK Success ** E_DB_ERROR Failure ** Exceptions: ** none ** ** Side Effects: ** Can allocate memory ** ** History: ** 19-jun-86 (jeff) ** written ** 1-oct-86 (daved) ** set return to TRUE if a VAR node is found. ** 23-dec-86 (daved) ** copy qualification before appending it. this avoids graphs in ** the tree as well as, and more importantly, the re-use of the ** memory used by the qualification before its time. That is, ** if vqual is in temporary memory and gets deleted but the ** proot tree thinks the memory is still around, bad things happen. ** 12-aug-87 (stec) ** Removed test for special 'tid' attribute case, which, according ** to Jeff is no longer needed. ** Check for special 'tid' resdom now includes open cursor stmt. ** 15-oct-87 (stec) ** Added the removed test for special 'tid' attribute case; ** it's necessary for checking cases like "retrieve (viewname.tid)". ** 03-dec-87 (stec) ** Change psy_apql interface. ** 31-dec-87 (stec) ** Cleanup. ** 08-feb-88 (stec) ** Modify psy_subsvars to generate CURVAL nodes for replace cursor statement. ** 04-nov-88 (stec) ** Fix a view substitution bug. When visiting an AGHEAD node it may happen ** that count(*), or count(const) were defined, in which case there are ** no VAR nodes and the applicability of the view has to be determined from ** the relation bitmap in the node. This anomaly exists only in SQL. ** 14-dec-88 (stec) ** Fix correlated subqueries bug. ** 05-apr-89 (andre) ** simplify the test for when reference to the view in pst_tvrm is to ** be replaced with the tables used to define the view. We no longer ** care if there were any variables found below the root node, instead, ** we do it for every root node which has a bit corresponding to the ** view set in pst_tvrm. ** As a part of the fix, qualification of the view will be appended to ** the tree whenever the view is found in the pst_tvrm of the root ** node. ** Besides allowing us to get rid of calling recursive psy_fixmap() in ** psy_view, but it also fixes bugs such as: ** "select const from view" returning more rows than there are in the ** view. ** 04-may-89 (andre) ** for the time being, set pst_maks1 in all ROOT-type nodes to 0. ** 01-jun-89 (andre) ** The preceding fix was not perfect. ** "create view v as select * from t where <qual>; ** select <aggregate> from v\g" ** would result in as many rows containing result of applying ** <aggregate> as there are rows in v. This happens only in SQL. The ** problem is that <qual> gets appended to both AGGHEAD node and the ** ROOT node. The solution is as follows: ** For every node N s.t. N is of type ROOT or SUBSELECT, remember ** if <qual> has been applied to an AGGHEAD node in the left ** subtree of N (in SQL you can not have AGGHEADs in the ** "where-clause"). If <qual> has been applied to AGGHEAD(s) in ** the left subtree of N, do not append it to the right subtree of ** N. ** 22-jun-89 (andre) ** And yet another fix for the previous bug fix. I have incorrectly ** assumed that there may be no AGGHEADs in the right subtrre of ** ROOT/SUBSEL (select ... having agg(col)). Before setting *mask to ** indicate that an AGGHEAD has been seen, make sure that we are in the ** left subtree of the immediate ROOT/SUBSEL parent ** (mask != (i4 *) NULL). If mask is NULL, we must be in the right ** subtree, and the fact that we saw an AGGHEAD is of no importance ** (or shall I add "or so I believe"?) ** 13-sep-89 (andre) ** receive ptr to PSS_DUPRB which will point at memopry stream and ** error block + it will be used when calling pst_treedup(). The ** fields in dup_rb must be set as follows: ** pss_op_mask -- 0 ** pss_num_joins -- PST_NOJOIN ** pss_tree_info -- NULL ** pss_mstream -- ptr to memory stream to be used ** pss_err_blk -- ptr to error block ** 14-sep-92 (andre) ** do not zero out pst_mask1 in PST_ROOT and PST_SUBSEL node ** (fix for bug 45238) ** 11-feb-93 (andre) ** if a query tree involved a reference to a TID attribute of a view V, ** replace it with a reference to TID attribute of V's underlying table ** or view; this is accomplished by replacing variable number found in ** the PST_VAR node with the variable number of the view's underlying ** table/view (which can be found by looking for the first set bit in ** from_list) ** 27-nov-02 (inkdo01) ** Range table expansion (i4 changed to PSAT_J_MASK). ** 13-Jun-2006 (kschendel) ** Barf if we translate a var node to a seqop default in an INSERT. ** This only happens if we're translating an integrity where-clause ** tree, and a var in that where-clause isn't mentioned in the ** values list, so we stick the default in instead. Seqops aren't ** allowed in where clauses. (It would imply that the insert ** integrity-permission depends on the sequence value, which is ** silly at best.) ** 15-May-2007 (kiria01) b111992 ** Flatten out much of the recursion of this function to reduce ** runtime stack usage - especially bad with views containing ** massive IN clauses (>5K elements). ** 28-nov-2007 (dougi) ** Add PSQ_REPDYN to PSQ_DEFCURS test for cached dynamic qs. ** 05-Nov-2009 (kiria01) b122841 ** Use psl_mk_const_similar to cast default values directly. ** 12-Nov-2009 (kiria01) b122841 ** Corrected psl_mk_const_similar parameters with explicit ** mstream. ** 5-Feb-2010 (hanal04) Bug 123209 ** psy_integ() calls psy_subsvars() to subsitute VARs in the ** integrity tree with the corresponding nodes from the ** user query. When a VAR is replaced with a CONST cast the ** CONST to the VAR's datatype. This stops the substitution from ** breaking ADE_COMPAREN & ADE_NCOMPAREN processing if the ** VAR was part of an IN LIST. ** 18-May-2010 (kiria01) b123442 ** Force psl_mk_const_similar to generate coercion to cleanly ** enable correct datatype to be represented when substituting ** default values. */ DB_STATUS psy_subsvars( PSS_SESBLK *cb, PST_QNODE **proot, PSS_RNGTAB *rngvar, PST_QNODE *transtree, i4 vmode, PST_QNODE *vqual, PSS_RNGTAB *resvar, PST_J_MASK *from_list, i4 qmode, DB_CURSOR_ID *cursid, i4 *mask, PSS_DUPRB *dup_rb) { PSY_STK stk = {0, 0, {0, }};/* Backtrack stack */ PST_QNODE *t; /* Temporary for *proot */ i4 vn = rngvar ? rngvar->pss_rgno : -1; /* can be NULL on replace cursor statements */ i4 err_code; DB_STATUS status = E_DB_OK; while(proot && (t = *proot)) { /* These 3 mask variables are only used for ROOT, SUBSEL and AGHEAD */ i4 newmask; /* For receiving result from recursive call */ i4 *l_mask; /* For propagating state to recursive caller */ i4 *r_mask; /* .. */ /* ** The recursive nature of this function has been restructured to ** allow for most of the processing to be achieved in an iterative ** manner. Unlike with the other functions in this module, the ** flattening could not be complete due to the 'mask' output parameter ** which requires local storage for certain node types: ROOT, SUBSEL ** and AGHEAD. If we have one of these node types we recurse 1 level ** to process the left and right sub-trees with the correct scoping of ** the 'mask' variable. */ switch (t->pst_sym.pst_type) { case PST_ROOT: /* Handle any unions */ if (t->pst_sym.pst_value.pst_s_root.pst_union.pst_next) { /* ** Defer the tree representing the next subselect in the UNION ** status = psy_subsvars(cb, ** &t->pst_sym.pst_value.pst_s_root.pst_union.pst_next, rngvar, transtree, ** vmode, vqual, resvar, from_list, qmode, cursid, (i4 *) NULL, ** dup_rb); */ psy_push(&stk, (PTR)&t->pst_sym.pst_value.pst_s_root.pst_union.pst_next, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } } /*FALLTHROUGH*/ case PST_SUBSEL: /* ** The following applies when language is SQL: ** if this is a ROOT or a SUBSELECT node, we want to know if the left ** subtree contains AGGHEAD nodes to which view qualification have been ** applied; we are not concerned with AGGHEAD nodes in the ** qualification (there shouldn't be any, anyway) or in other members ** of the union. */ if (cb->pss_lang == DB_SQL) { newmask = 0; l_mask = &newmask; /* we don't care about the right subtree */ r_mask = (i4 *) NULL; } /*FALLTHROUGH*/ case PST_AGHEAD: /* ** The following applies when language is SQL: ** If this is an AGGHEAD node, set a bit in 'mask' to remember that ** we saw it. */ if (t->pst_sym.pst_type == PST_AGHEAD) { if (cb->pss_lang == DB_SQL) { if (l_mask = r_mask = mask) /* ** If we are in the right subtree of the immediate ROOT/SUBSELECT ** parent, mask will be NULL, since we are not concerned with ** AGHEADs in the right subtrees. */ *mask |= PSS_1SAW_AGG; } /* ** pst_mask1 in PST_AGHEAD node is neither used nor set; I think it ** would be a good idea to set it, but at this point there is not a heck ** of a lot that we can do. Here we will zero out PST_AGHEAD.pst_mask1 ** purely for esthetic reasons. */ t->pst_sym.pst_value.pst_s_root.pst_mask1 = 0; } /* ** Recurse 1 level to process the left & right subtrees completly ** so that we can complete the processing of this node */ status = psy_subsvars(cb, &t->pst_left, rngvar, transtree, vmode, vqual, resvar, from_list, qmode, cursid, l_mask, dup_rb); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* Process the right branch */ status = psy_subsvars(cb, &t->pst_right, rngvar, transtree, vmode, vqual, resvar, from_list, qmode, cursid, r_mask, dup_rb); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* Add `from' list to bitmap, remove entry for replaced var */ if (BTtest(vn, (char*)&t->pst_sym.pst_value.pst_s_root.pst_tvrm)) { BTclear(vn, (char*)&t->pst_sym.pst_value.pst_s_root.pst_tvrm); BTor(PST_NUMVARS, (char *)from_list, (char *)&t->pst_sym.pst_value.pst_s_root.pst_tvrm); t->pst_sym.pst_value.pst_s_root.pst_tvrc = BTcount((char*)&t->pst_sym.pst_value.pst_s_root.pst_tvrm, BITS_IN(t->pst_sym.pst_value.pst_s_root.pst_tvrm)); /* ** We will append qualification (if there is one) if the ** following holds: ** 1) This is not an SQL (must be QUEL) query OR ** 2) if node is ROOT or SUBSEL (i.e. not an AGHEAD) then there ** were no AGHEADs found in its left subtree ** ** Let QUAL <==> there is a qualification, ** SQL <==> language is SQL ** ROOT <==> node type is ROOT ** SUBSEL <==> node type is SUBSEL ** AGG <==> node type is AGHEAD ** SAW_AGG <==> mask & PSS_1SAW_AGG. Then ** ** (Do not apply qualification) <==> ** !QUAL + SQL * (ROOT + SUBSEL) * SAW_AGG --> ** (Apply qualification) <==> ** !(!QUAL + SQL * (ROOT + SUBSEL) * SAW_AGG) <==> ** QUAL * !(SQL * (ROOT + SUBSEL) * SAW_AGG) <==> ** QUAL * (!SQL + !((ROOT + SUBSEL) * SAW_AGG)) <==> ** QUAL * (!SQL + !(ROOT + SUBSEL) + !SAW_AGG) <==> ** QUAL * (!SQL + AGG + !SAW_AGG) */ if (vqual && (cb->pss_lang != DB_SQL || t->pst_sym.pst_type == PST_AGHEAD || (*l_mask & PSS_1SAW_AGG) == 0)) { PST_QNODE *vqual_copy; dup_rb->pss_tree = vqual; dup_rb->pss_dup = &vqual_copy; status = pst_treedup(cb, dup_rb); dup_rb->pss_tree = (PST_QNODE *)NULL; dup_rb->pss_dup = (PST_QNODE **)NULL; if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* append view qualification */ status = psy_apql(cb, dup_rb->pss_mstream, vqual_copy, t, dup_rb->pss_err_blk); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } } } /* left & right have been processed */ break; case PST_VAR: /* ** This is a terminal node - the expectation is that left & right are 0 */ /* ** Check for a VAR node but of a different variable than the one ** we are substituting for. REPLACE CURSOR (quel version) is an ** exception because the substitution variable (resvar) is not ** defined, in that case we do not want to execute the code ** below, but want to continue the translation process. */ if (vn != -1 && t->pst_sym.pst_value.pst_s_var.pst_vno != vn) break; /* ** if this is a reference to a TID attribute of a view (which is not ** a "real" attribute), it needs to be translated into a reference ** to the TID attribute of the view's underlying table or view */ if (t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id == 0 && vmode == PSQ_VIEW) { t->pst_sym.pst_value.pst_s_var.pst_vno = BTnext(-1, (char *) from_list, sizeof(*from_list)); } else { PST_QNODE *v; /* find var in vtree */ status = psy_vfind((u_i2)t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id, transtree, &v, dup_rb->pss_err_blk); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } if (v == (PST_QNODE *)NULL) { /* attribute not defined in view */ if (vmode == PSQ_VIEW) { psf_error(E_PS0D03_ATT_NOT_FOUND, 0L, PSF_INTERR, &err_code, dup_rb->pss_err_blk, 0); status = E_DB_SEVERE; proot = NULL; /* Exiting to return error */ break; } /* append defaults for integrity. Integrity might exist on a value ** we are appending by default. I.e., the attribute was not mentioned ** in the target list. We replace the var node in the integrity with ** a default value so that the integrity will read 'default value' ? ** value. */ else if (vmode == PSQ_APPEND) { status = psl_make_default_node(cb, dup_rb->pss_mstream, resvar, t->pst_sym.pst_value .pst_s_var.pst_atno.db_att_id, &v, dup_rb->pss_err_blk); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* Try to cast to column type */ status = psl_mk_const_similar(cb, dup_rb->pss_mstream, &t->pst_sym.pst_dataval, &v, dup_rb->pss_err_blk, NULL); if (DB_FAILURE_MACRO(status)) return(status); /* If we ended up with a sequence default, fail. This is an ** unreasonable situation, integrity where tests should not apply ** to sequence defaults. */ if (v->pst_sym.pst_type == PST_SEQOP) { psf_error(6319, 0, PSF_USERERR, &err_code, dup_rb->pss_err_blk, 0); status = E_DB_ERROR; proot = NULL; /* Exiting to return error */ break; } } /* we would like to delete the qualification for this node since the ** value is not changing and thus we don't need to worry about integrity ** constaints on it. However, we don't do that. Instead we have the ** integrity refer to the value in the current row that reflects the ** value for the attribute. In the replace statement (not replace ** cursor) we just perform the retrieve, the qualification is unneeded ** but doesn't hurt anything. We want to avoid causing a retrieve for ** each update cursor; therefore, we change the varnode to refer to the ** current value (ie the retrieve has already been done). */ else if (vmode == PSQ_REPCURS) { PST_CRVAL_NODE curval; /* Create a CURVAL node for the corresponding column */ curval.pst_curcol.db_att_id = t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id; STRUCT_ASSIGN_MACRO(*cursid, curval.pst_cursor); status = pst_node(cb, dup_rb->pss_mstream, (PST_QNODE *)NULL, (PST_QNODE *)NULL, PST_CURVAL, (PTR)&curval, sizeof(curval), t->pst_sym.pst_dataval.db_datatype, t->pst_sym.pst_dataval.db_prec, t->pst_sym.pst_dataval.db_length, (DB_ANYTYPE *)t->pst_sym.pst_dataval.db_data, &v, dup_rb->pss_err_blk, (i4) 0); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } } } else { dup_rb->pss_tree = v; dup_rb->pss_dup = &v; status = pst_treedup(cb, dup_rb); dup_rb->pss_tree = (PST_QNODE *)NULL; dup_rb->pss_dup = (PST_QNODE **)NULL; if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* When called from psy_integ() v will be found but ** may still need constants to be cast. */ if ((v != (PST_QNODE *)NULL) && (v->pst_sym.pst_type == PST_CONST)) { bool handled; /* Try to cast to column type */ status = psl_mk_const_similar(cb, dup_rb->pss_mstream, &t->pst_sym.pst_dataval, &v, dup_rb->pss_err_blk, &handled); if (DB_FAILURE_MACRO(status)) return(status); } } /* replace VAR node */ if (v != (PST_QNODE *)NULL) { *proot = v; } } /* left and right should have been null as we are on a terminal */ break; case PST_RESDOM: /* Process `TID' resdom used by DELETE, REPLACE and OPEN CURSOR */ if (t->pst_sym.pst_value.pst_s_rsdm.pst_rsno == 0 && (qmode == PSQ_DELETE || qmode == PSQ_REPLACE || qmode == PSQ_DEFCURS || qmode == PSQ_REPDYN)) { /* ** If resvar not specified, or if not resvar, ignore leaf. */ if (resvar && vn == resvar->pss_rgno) { /* t->right better be VAR node, attno 0 */ t = t->pst_right; if (t->pst_sym.pst_type != PST_VAR || t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id != 0 || t->pst_sym.pst_value.pst_s_var.pst_vno != vn) { (VOID) psf_error(E_PS0D02_BAD_TID_NODE, 0L, PSF_INTERR, &err_code, dup_rb->pss_err_blk, 0); status = E_DB_SEVERE; proot = NULL; /* Exiting to return error */ break; } } else if (t->pst_right) /* Process the right branch */ psy_push(&stk, (PTR)&t->pst_right, &status); } else if (t->pst_right) /* Process the right branch */ psy_push(&stk, (PTR)&t->pst_right, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* Process left branch */ if (t->pst_left) psy_push(&stk, (PTR)&t->pst_left, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } break; default: /* ** Just ensure that we traverse the tree */ if (t->pst_right) psy_push(&stk, (PTR)&t->pst_right, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } if (t->pst_left) psy_push(&stk, (PTR)&t->pst_left, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } break; } if (!proot) /* We're breaking out early to exit */ break; /* ** Get next deferred node */ proot = (PST_QNODE**)psy_pop(&stk); } /* Release any outstanding entries */ psy_pop_all(&stk); return status; }
/*{ ** 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: psy_dpermit - Define a permit. ** ** INTERNAL PSF call format: status = psy_dpermit(&psy_cb, sess_cb); ** ** EXTERNAL call format: status = psy_call(PSY_DPERMIT, &psy_cb, sess_cb); ** ** Description: ** Given all of the parameters necessary to CREATE/DEFINE a permit on a ** table or view, this function will store the permission in the system ** catalogs. This will include storing the query tree in the tree table, ** storing the text of the query in the iiqrytext table (really done by ** QEF), storing a row in the protect table, and issuing an "alter table" ** operation to DMF to indicate that there are permissions on the given ** table. ** ** Inputs: ** psy_cb ** .psy_qrytext Id of query text as stored in QSF. ** .psy_cols[] Array of columns on which to grant ** permission ** .psy_numcols Number of columns listed above; 0 means ** give permission on all columns ** .psy_intree QSF id of query tree representing the ** where clause in the permit ** .psy_opctl Bit map of defined operations ** .psy_opmap Bit map of permitted operations ** .psy_user Name of user who will get permission ** .psy_terminal Terminal at which permission is given ** (blank if none specified) ** .psy_timbgn Time of day at which the permission ** begins (minutes since 00:00) ** .psy_timend Time of day at which the permission ends ** (minutes since 00:00) ** .psy_daybgn Day of week at which the permission ** begins (0 = Sunday) ** .psy_dayend Day of week at which the permission ends ** (0 = Sunday) ** .psy_grant ** PSY_CPERM CREATE/DEFINE PERMIT ** .psy_tblq head of table queue ** .psy_colq head of column queue ** .psy_usrq head of user queue ** .psy_qlen length of first iiqrytext ** .psy_flags useful info ** PSY_EXCLUDE_COLUMNS user specified a list of columns to ** which privilege should not apply ** sess_cb Pointer to session control block ** (Can be NULL) ** ** Outputs: ** psy_cb ** .psy_txtid Id of query text as stored in the ** iiqrytext system relation. ** .psy_error Filled in if error happens ** Returns: ** E_DB_OK Function completed normally. ** E_DB_WARN Function completed with warning(s); ** E_DB_ERROR Function failed; non-catastrophic error ** E_DB_FATAL Function failed; catastrophic error ** Exceptions: ** none ** ** Side Effects: ** Stores text of query in iiqrytext relation, query tree in tree ** relation, row in protect relation identifying the permit. Does ** an alter table DMF operation to indicate that there are permissions ** on the table. ** ** History: ** 02-oct-85 (jeff) ** written ** 03-sep-86 (seputis) ** changed some psy_cb. to psy_cb-> ** added .db_att_id reference ** changed rdr_cb. rdr_cb-> ** 02-dec-86 (daved) ** bug fixing. check for permit on tables owned by user and not ** view. ** 29-apr-87 (stec) ** Implemented changes for GRANT statement. ** 10-may-88 (stec) ** Make changes for db procs. ** 03-oct-88 (andre) ** Modified call to pst_rgent to pass 0 as a query mode since it is ** clearly not PSQ_DESTROY ** 06-feb-89 (ralph) ** Added support for 300 attributes: ** Use DB_COL_BITS in place of DB_MAX_COLS ** Loop over domset array using DB_COL_WORDS ** 06-mar-89 (ralph) ** GRANT Enhancements, Phase 1: ** Initialize new DB_PROTECTION fields, dbp_seq and dbp_gtype ** 03-apr-89 (ralph) ** GRANT Enhancements, Phase 2: ** Use DBGR_USER when initializing dbp_gtype ** 08-may-89 (ralph) ** Initialize reserved field to blanks (was \0) ** 04-jun-89 (ralph) ** Initialize dbp_fill1 to zero ** Fix unix portability problems ** 02-nov-89 (neil) ** Alerters: Allowed privileges for events. ** 1-mar-90 (andre) ** If processing a GRANT on tables, check if ** ALL-TO-ALL or RETRIEVE-TO-ALL has already been granted, and if so, ** mark psy_mask appropriately. ** If user tried to CREATE ALL/RETRIEVE-TO-ALL, and one already exists, ** skip to the exit. ** 12-mar-90 (andre) ** set rdr_2types_mask to 0. ** 22-may-90 (teg) ** init rdr_instr to RDF_NO_INSTR ** 08-aug-90 (ralph) ** Initialize new fields in iiprotect tuple ** 14-dec-90 (ralph) ** Disallow use of GRANT by non-DBA if xORANGE ** 11-jan-90 (ralph) ** Allow user "$ingres" to use GRANT if xORANGE. ** This was done for CREATEDB (UPGRADEFE). ** 20-feb-91 (andre) ** For CREATE/DEFINE PERMIT, grantee type was stored in ** psy_cb->psy_gtype. ** 24-jun-91 (andre) ** IIPROTECT tuples for table permits will contain exactly one ** privilege. IIQRYTEXT template built for table-wide privileges ** contains a placeholder for a privilege name which will be filled in ** with each of the table-wide privileges being granted, one at a time. ** PSY_CB.psy_opmap will be set to correspond with privilege name ** stored in the IIQRYTEXT permit. ** 16-jul-91 (andre) ** responsibility for splitting permit tuples will passed on to ** qeu_cprot(). If a permit specified only one privilege, we will ** substitute the appropriate privilege name here and will not ask ** qeu_cprot() to split tuples. ** 06-aug-91 (andre) ** before proceeding to CREATE a permit on a view owned by the current ** user, we will call psy_tbl_grant_check() to ensure that this user ** may create a permit on his view. If the object is not owned by the ** current user, we will not try to verify that the user may ** CREATE/DEFINE a permit since (until the relevant FE changes are ** made) we intend to continue allowing any user with CATUPD to ** CREATE/DEFINE permits on catalogs and the dba will be allowed to ** CREATE/DEFINE permits on extended catalogs ** 11-nov-91 (rblumer) ** merged from 6.4: 26-feb-91 (andre) ** PST_QTREE was changed to store the range table as an array of ** pointers to PST_RNGENTRY structure. ** 14-feb-92 (andre) ** we will no longer have to fill in privilege name for permits ** specifying one privilege - it will be handled in respective ** grammars. ** 15-jun-92 (barbara) ** For Sybil, change interface to pst_rgent(), Star returns from ** psy_dpermit before permits get stored. ** 07-jul-92 (andre) ** DB_PROTECTION tuple will contain an indicator of how the permit was ** created, i.e. whether it was created using SQL or QUEL and if the ** former, then whether it was created using GRANT statement. Having ** this information will facilitate merging similar and identical ** permit tuples. ** 14-jul-92 (andre) ** semantics of GRANT ALL [PRIVILEGES] is different from that of ** CREATE PERMIT ALL in that the former (as dictated by SQL92) means ** "grant all privileges which the current auth id posesses WGO" ** whereas the latter (as is presently interpreted) means "grant all ** privileges that can be defined on the object" which in case of ** tables and views means SELECT, INSERT, DELETE, UPDATE. ** psy_tbl_grant_check() (function responsible for determining whether ** a user may grant specified privilege on a specified table or view) ** will have to be notified whether we are processing GRANT ALL. Its ** behaviour will change as follows: ** - if processing GRANT ALL and psy_tbl_grant_check() determines ** that the user does not possess some (but not all) of the ** privileges passed to it by the caller it will not treat it as an ** error, but will instead inform the caller of privileges that the ** user does not posess, ** - if processing GRANT ALL and psy_tbl_grant_check() determines ** that the user does not possess any of the privileges passed to ** it by the caller it will treat it as an error ** - if processing a statement other than GRANT ALL and ** psy_tbl_grant_check() determines that the user does not possess ** some of the privileges passed to it by the caller it will treat ** it as an error ** 16-jul-92 (andre) ** if a permit being created depends on some privileges, build a ** structure describing these privileges and store its address in ** rdf_cb->rdr_indep. ** 18-jul-92 (andre) ** we will no longer be telling QEF to turn off DMT_ALL_PROT or ** DMT_RETRIEVE_PRO when a user creates ALL/RETRIEVE TO ALL permit. ** QEF will figure out on its own whether PUBLIC now has RETRIEVE or ** ALL on a table/view ** 20-jul-92 (andre) ** if user specified a list of columns to which privilege(s) should ** not apply, set dbp_domset correctly ** 03-aug-92 (barbara) ** Invalidate base table infoblk from RDF cache for CREATE PERMIT ** and CREATE SEC_ALARM. ** 16-sep-92 (andre) ** privilege maps are build using bitwise ops, so care should be ** exercised when accessing it using BT*() functions ** 17-jun-93 (andre) ** changed interface of psy_secaudit() to accept PSS_SESBLK ** 5-jul-93 (robf) ** changed interface of psy_secaudit() to accept security label ** 7-jan-94 (swm) ** Bug #58635 ** Added PTR cast for qsf_owner which has changed type to PTR. ** 06-mar-96 (nanpr01) ** Move the QSF request block initialization up. because if ** pst_rgnent returns a failure status code, subsequent QSF ** calls get bad control block error. */ DB_STATUS psy_dpermit( PSY_CB *psy_cb, PSS_SESBLK *sess_cb) { RDF_CB rdf_cb; register RDR_RB *rdf_rb = &rdf_cb.rdf_rb; QSF_RCB qsf_rb; DB_STATUS status; DB_STATUS stat; DB_PROTECTION ptuple; register DB_PROTECTION *protup = &ptuple; i4 *domset = ptuple.dbp_domset; register i4 i, j; i4 err_code; PSS_RNGTAB *rngvar; PSS_USRRANGE *rngtab; PST_PROCEDURE *pnode; PST_QTREE *qtree; DB_ERROR *err_blk = &psy_cb->psy_error; i4 textlen; i4 tree_lock = 0; i4 text_lock = 0; DB_TAB_ID tabids[PST_NUMVARS]; PSQ_INDEP_OBJECTS indep_objs; PSQ_OBJPRIV obj_priv; /* space for independent DELETE */ PSQ_COLPRIV col_privs[2]; /* ** space for independent INSERT and ** UPDATE */ PST_VRMAP varmap; PSY_TBL *psy_tbl; DB_TIME_ID timeid; DB_NAME *objname; /* ** For CREATE/DEFINE PERMIT execute code below. */ /* initialize the QSF control block */ qsf_rb.qsf_type = QSFRB_CB; qsf_rb.qsf_ascii_id = QSFRB_ASCII_ID; qsf_rb.qsf_length = sizeof(qsf_rb); qsf_rb.qsf_owner = (PTR)DB_PSF_ID; qsf_rb.qsf_sid = sess_cb->pss_sessid; rngtab = &sess_cb->pss_auxrng; /* table info is stored in the only entry in the table queue */ psy_tbl = (PSY_TBL *) psy_cb->psy_tblq.q_next; status = pst_rgent(sess_cb, rngtab, -1, "", PST_SHWID, (DB_TAB_NAME *) NULL, (DB_TAB_OWN *) NULL, &psy_tbl->psy_tabid, TRUE, &rngvar, (i4) 0, err_blk); if (DB_FAILURE_MACRO(status)) goto exit; /* In STAR, we do not actually store permits */ if (sess_cb->pss_distrib & DB_3_DDB_SESS) { qsf_rb.qsf_lk_state = QSO_EXLOCK; goto exit; } /* Fill in the RDF request block */ pst_rdfcb_init(&rdf_cb, sess_cb); /* The table which is receiving the permit */ STRUCT_ASSIGN_MACRO(psy_tbl->psy_tabid, rdf_rb->rdr_tabid); /* Tell RDF we're doing a permit definition */ rdf_rb->rdr_update_op = RDR_APPEND; rdf_rb->rdr_types_mask = RDR_PROTECT; rdf_rb->rdr_qrytuple = (PTR) protup; /* initialize independent object structure */ indep_objs.psq_objs = (PSQ_OBJ *) NULL; indep_objs.psq_objprivs = (PSQ_OBJPRIV *) NULL; indep_objs.psq_colprivs = (PSQ_COLPRIV *) NULL; indep_objs.psq_grantee = &sess_cb->pss_user; rdf_rb->rdr_indep = (PTR) &indep_objs; /* ** populate the IIPROTECT tuple */ /* Zero out the template */ (VOID)MEfill(sizeof(ptuple), (u_char) 0, (PTR) protup); /* store grantee type */ protup->dbp_gtype = psy_cb->psy_gtype; /* Init reserved block */ (VOID)MEfill(sizeof(protup->dbp_reserve), (u_char) ' ', (PTR) protup->dbp_reserve); /* Init obj name */ STRUCT_ASSIGN_MACRO(psy_tbl->psy_tabnm, protup->dbp_obname); /*@FIX_ME@ Where does this come from? */ protup->dbp_obstat = ' '; /* store the object type indicator */ if (psy_tbl->psy_mask & PSY_OBJ_IS_TABLE) { protup->dbp_obtype = DBOB_TABLE; } else if (psy_tbl->psy_mask & PSY_OBJ_IS_VIEW) { protup->dbp_obtype = DBOB_VIEW; } else { protup->dbp_obtype = DBOB_INDEX; } STRUCT_ASSIGN_MACRO(psy_tbl->psy_owner, protup->dbp_obown); STRUCT_ASSIGN_MACRO(sess_cb->pss_user, protup->dbp_grantor); TMnow((SYSTIME *)&timeid); protup->dbp_timestamp.db_tim_high_time = timeid.db_tim_high_time; protup->dbp_timestamp.db_tim_low_time = timeid.db_tim_low_time; /* The table on which we're giving permission */ STRUCT_ASSIGN_MACRO(psy_tbl->psy_tabid, protup->dbp_tabid); /* Beginning and ending times of day */ protup->dbp_pdbgn = psy_cb->psy_timbgn; protup->dbp_pdend = psy_cb->psy_timend; /* Beginning and ending days of week */ protup->dbp_pwbgn = psy_cb->psy_daybgn; protup->dbp_pwend = psy_cb->psy_dayend; if (psy_cb->psy_numcols != 0 && ~psy_cb->psy_flags & PSY_EXCLUDE_COLUMNS) { /* user specified a list of columns to which privilege(s) will apply */ /* Bit map of permitted columns */ psy_fill_attmap(domset, ((i4) 0)); for (i = 0; i < psy_cb->psy_numcols; i++) { BTset((i4)psy_cb->psy_cols[i].db_att_id, (char *) domset); } } else { /* ** user specified table-wide privilege(s) or a list of columns L s.t. ** privilege(s) will apply to the entire table except for columns in L */ psy_fill_attmap(domset, ~((i4) 0)); if (psy_cb->psy_flags & PSY_EXCLUDE_COLUMNS) { /* ** exclude specified columns from the list of columns to which ** privilege(s) will apply */ for (i = 0; i < psy_cb->psy_numcols; i++) { BTclear((i4) psy_cb->psy_cols[i].db_att_id, (char *) domset); } } } if (rngvar->pss_tabdesc->tbl_status_mask & DMT_VIEW) { /* ** if view is owned by the current user, psy_tbl_grant_check() will ** determine if the permit can, indeed, be created; as long as we are ** preserving the kludge that allows users with CATUPD create permits on ** catalogs and DBAs to create permits on extended catalogs, we shall ** not call psy_tbl_grant_check() on view not owned by the current user, ** since it is likely to result in psy_tbl_grant_check() complaining ** about inadequate permissions */ if (!MEcmp((PTR) &rngvar->pss_ownname, (PTR) &sess_cb->pss_user, sizeof(sess_cb->pss_user))) { i4 tbl_wide_privs; PSY_COL_PRIVS col_specific_privs, *csp, indep_col_specific_privs; DB_TAB_ID indep_id; i4 indep_tbl_wide_privs; bool insuf_privs, quel_view; i4 val1, val2; /* ** build maps of table-wide and column-specific privileges for ** psy_tbl_grant_check() ** if a column list was specified with CREATE PERMIT and ** privileges specified in the statement include a set of ** privileges S s.t. for all P in S, P can only be specified as ** table-wide with GRANT statement (currently this includes ** SELECT, INSERT, DELETE), we will make ** psy_tbl_grant_check() think that privileges in S are ** table-wide. ** This will work correctly since if the view was defined over ** some objects owned by other user(s), for every P in S we ** would need table-wide privilege WGO on the underlying object. ** ** For the purposes of providing more descriptive output for ** trace point ps131, if column-list was specified, we will pass ** the map of attributes even if column-specific UPDATE was not ** specified */ if (psy_cb->psy_numcols != 0 && (psy_cb->psy_opmap & DB_REPLACE || ult_check_macro(&sess_cb->pss_trace, 3, &val1, &val2) ) ) { i4 *ip; csp = &col_specific_privs; /* ** column-specific UPDATE privilege will not be translated into ** a table-wide privilege since GRANT allows for specification ** of column-specific UPDATE privilege */ csp->psy_col_privs = psy_cb->psy_opmap & DB_REPLACE; tbl_wide_privs = psy_cb->psy_opmap & ~DB_REPLACE; /* ** if creating a permit on a set of columns and UPDATE is not ** one of the privileges named in the statement, store the ** attribute map in the first element of the attribute map list */ ip = (csp->psy_col_privs) ? csp->psy_attmap[PSY_UPDATE_ATTRMAP].map : csp->psy_attmap->map; /* copy the attribute map */ for (i = 0; i < DB_COL_WORDS; i++, ip++) { *ip = domset[i]; } } else { tbl_wide_privs = psy_cb->psy_opmap; csp = (PSY_COL_PRIVS *) NULL; } status = psy_tbl_grant_check(sess_cb, (i4) PSQ_PROT, &rngvar->pss_tabid, &tbl_wide_privs, csp, &indep_id, &indep_tbl_wide_privs, &indep_col_specific_privs, psy_cb->psy_flags, &insuf_privs, &quel_view, &psy_cb->psy_error); if (DB_FAILURE_MACRO(status)) { goto exit; } if (insuf_privs) { /* must audit failure to create a permit */ if ( Psf_srvblk->psf_capabilities & PSF_C_C2SECURE ) { DB_ERROR e_error; /* Must audit CREATE PERMIT failure. */ status = psy_secaudit(FALSE, sess_cb, (char *)&rngvar->pss_tabdesc->tbl_name, &rngvar->pss_tabdesc->tbl_owner, sizeof(DB_TAB_NAME), SXF_E_TABLE, I_SX2016_PROT_TAB_CREATE, SXF_A_FAIL | SXF_A_CREATE, &e_error); status = (status > E_DB_ERROR) ? status : E_DB_ERROR; } goto exit; } else if (quel_view) { goto exit; } /* ** If user is trying to grant one or more of ** INSERT/DELETE/UPDATE on his/her view whose underlying table ** or view is owned by another user, psy_tbl_grant_check() will ** return id of the underlying object along with map of ** privileges. We will convert maps of independent privileges ** into elements of independent privilege list and pass them ** along to QEF */ if ( indep_id.db_tab_base != (i4) 0 && ( indep_id.db_tab_base != rngvar->pss_tabid.db_tab_base || indep_id.db_tab_index != rngvar->pss_tabid.db_tab_index ) ) { if (indep_tbl_wide_privs & DB_DELETE) { /* ** the only expected independent table-wide privilege ** is DELETE */ obj_priv.psq_next = (PSQ_OBJPRIV *) NULL; obj_priv.psq_objtype = PSQ_OBJTYPE_IS_TABLE; obj_priv.psq_privmap = (i4) DB_DELETE; obj_priv.psq_objid.db_tab_base = indep_id.db_tab_base; obj_priv.psq_objid.db_tab_index = indep_id.db_tab_index; indep_objs.psq_objprivs = &obj_priv; } if (indep_col_specific_privs.psy_col_privs) { i4 i, j; PSQ_COLPRIV *csp; i4 *att_map, *p; i4 priv_map = 0; /* ** privilege map is built using bitwise operators, but ** here using BTnext() makes code much more palatable, ** so convert a privilege map */ if (indep_col_specific_privs.psy_col_privs & DB_APPEND) BTset(DB_APPP, (char *) &priv_map); if (indep_col_specific_privs.psy_col_privs & DB_REPLACE) BTset(DB_REPP, (char *) &priv_map); for (i = -1, csp = col_privs; (i = BTnext(i, (char *) &priv_map, BITS_IN(priv_map))) != -1; csp++ ) { csp->psq_next = indep_objs.psq_colprivs; indep_objs.psq_colprivs = csp; csp->psq_objtype = PSQ_OBJTYPE_IS_TABLE; csp->psq_tabid.db_tab_base = indep_id.db_tab_base; csp->psq_tabid.db_tab_index = indep_id.db_tab_index; switch (i) { case DB_APPP: /* INSERT privilege */ { csp->psq_privmap = (i4) DB_APPEND; att_map = indep_col_specific_privs. psy_attmap[PSY_INSERT_ATTRMAP].map; break; } case DB_REPP: { csp->psq_privmap = (i4) DB_REPLACE; att_map = indep_col_specific_privs. psy_attmap[PSY_UPDATE_ATTRMAP].map; break; } } for (p = csp->psq_attrmap, j = 0; j < DB_COL_WORDS; j++) { *p++ = *att_map++; } } } } } else { /* ** either this is a catalog and the user has CATUPD or ** this is an extended catalog and the user is the DBA; ** since we may be allowing a user to create a permit by ** circumventing the permit system, we only need to ascertain that ** this is an SQL view */ i4 issql = 0; status = psy_sqlview(rngvar, sess_cb, err_blk, &issql); if (status) { goto exit; } if (!issql) { /* can only have permits on SQL views */ psf_error(3598L, 0L, PSF_USERERR, &err_code, err_blk, 1, psf_trmwhite(sizeof(rngvar->pss_tabname), (char *) &rngvar->pss_tabname), &rngvar->pss_tabname); status = E_DB_ERROR; goto exit; } } } /* Name of user getting permission */ STRUCT_ASSIGN_MACRO(psy_cb->psy_user, protup->dbp_owner); /* Terminal at which permission given */ STRUCT_ASSIGN_MACRO(psy_cb->psy_terminal, protup->dbp_term); /* Give RDF pointer to query tree, if any */ if (!psy_cb->psy_istree) { rdf_rb->rdr_qry_root_node = (PTR) NULL; } else { PST_VRMAP varset; i4 j; STRUCT_ASSIGN_MACRO(psy_cb->psy_intree, qsf_rb.qsf_obj_id); qsf_rb.qsf_lk_state = QSO_EXLOCK; status = qsf_call(QSO_LOCK, &qsf_rb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_error(E_PS0D19_QSF_INFO, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, err_blk, 0); goto exit; } tree_lock = qsf_rb.qsf_lk_id; pnode = (PST_PROCEDURE *) qsf_rb.qsf_root; qtree = (PST_QTREE *) pnode->pst_stmts->pst_specific.pst_tree; rdf_rb->rdr_qry_root_node = (PTR) pnode; /* check for no views in the qualification. */ (VOID)psy_varset(qtree->pst_qtree, &varset); j = BTnext(-1, (char *) &varset, BITS_IN(varset)); for ( ; j >= 0; j = BTnext(j, (char *) &varset, BITS_IN(varset))) { status = pst_rgent(sess_cb, rngtab, -1, "", PST_SHWID, (DB_TAB_NAME *) NULL, (DB_TAB_OWN *) NULL, &qtree->pst_rangetab[j]->pst_rngvar, TRUE, &rngvar, (i4) 0, err_blk); if (status) goto exit; if (rngvar->pss_tabdesc->tbl_status_mask & DMT_VIEW) { psf_error(3597L, 0L, PSF_USERERR, &err_code, err_blk, 1, psf_trmwhite(sizeof(rngvar->pss_tabname), (char *) &rngvar->pss_tabname), &rngvar->pss_tabname); status = E_DB_ERROR; goto exit; } } } /* Give RDF a pointer to the query text to be stored in iiqrytext */ STRUCT_ASSIGN_MACRO(psy_cb->psy_qrytext, qsf_rb.qsf_obj_id); qsf_rb.qsf_lk_state = QSO_EXLOCK; status = qsf_call(QSO_LOCK, &qsf_rb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_error(E_PS0D19_QSF_INFO, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, err_blk, 0); goto exit; } text_lock = qsf_rb.qsf_lk_id; MEcopy((char *) qsf_rb.qsf_root, sizeof(i4), (char *) &textlen); rdf_rb->rdr_l_querytext = textlen; rdf_rb->rdr_querytext = ((char *) qsf_rb.qsf_root) + sizeof(i4); rdf_rb->rdr_status = (sess_cb->pss_lang == DB_SQL) ? DB_SQL : 0; /* determine if the permit specifies exactly one privilege */ if (BTcount((char *) &psy_cb->psy_opmap, BITS_IN(psy_cb->psy_opmap)) > 1) { /* ** if permit specified more than one privilege, notify QEF that it will ** have to split the permit into multiple IIPROTECT tuples */ rdf_rb->rdr_instr |= RDF_SPLIT_PERM; } else if (psy_cb->psy_opmap & DB_RETRIEVE) { /* ** if qeu_cprot() will not be splitting the permit into multiple tuples ** and RETRIEVE is the privilege mentioned in it, set the two bits ** associated with DB_RETRIEVE */ psy_cb->psy_opmap |= DB_TEST | DB_AGGREGATE; psy_cb->psy_opctl |= DB_TEST | DB_AGGREGATE; } /* Null out the DMU control block pointer, just in case */ rdf_rb->rdr_dmu_cb = (PTR) NULL; /* produce list of dependent tables */ rdf_rb->rdr_cnt_base_id = 0; if (psy_cb->psy_istree && qtree->pst_qtree) { j = 0; (VOID)psy_varset(qtree->pst_qtree, &varmap); for (i = -1; (i = BTnext(i, (char*) &varmap, PST_NUMVARS)) > -1;) { /* if this is the table that is getting the permit, ignore */ if (qtree->pst_rangetab[i]->pst_rngvar.db_tab_base != psy_tbl->psy_tabid.db_tab_base || qtree->pst_rangetab[i]->pst_rngvar.db_tab_index != psy_tbl->psy_tabid.db_tab_index ) { rdf_rb->rdr_cnt_base_id++; STRUCT_ASSIGN_MACRO(qtree->pst_rangetab[i]->pst_rngvar, tabids[j++]); } } rdf_rb->rdr_base_id = tabids; } protup->dbp_popctl = psy_cb->psy_opctl; protup->dbp_popset = psy_cb->psy_opmap; /* ** store an indication of whether this permit is being created using SQL or ** QUEL */ protup->dbp_flags = (sess_cb->pss_lang == DB_SQL) ? DBP_SQL_PERM : (i2) 0; protup->dbp_flags |= DBP_65_PLUS_PERM; /* Now let RDF do all the work of the permit definition */ status = rdf_call(RDF_UPDATE, (PTR) &rdf_cb); if (DB_FAILURE_MACRO(status)) { if (rdf_cb.rdf_error.err_code == E_RD0002_UNKNOWN_TBL) { (VOID) psf_error(E_PS0903_TAB_NOTFOUND, 0L, PSF_USERERR, &err_code, err_blk, 1, psf_trmwhite(sizeof(psy_tbl->psy_tabnm), (char *) &psy_tbl->psy_tabnm), &psy_tbl->psy_tabnm); } else { (VOID) psf_rdf_error(RDF_UPDATE, &rdf_cb.rdf_error, &psy_cb->psy_error); } goto exit; } /* ** Invalidate base object's infoblk from RDF cache. */ pst_rdfcb_init(&rdf_cb, sess_cb); STRUCT_ASSIGN_MACRO(psy_cb->psy_tables[0], rdf_rb->rdr_tabid); status = rdf_call(RDF_INVALIDATE, (PTR) &rdf_cb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_rdf_error(RDF_INVALIDATE, &rdf_cb.rdf_error, &psy_cb->psy_error); } exit: qsf_rb.qsf_lk_state = QSO_EXLOCK; if (psy_cb->psy_istree) { /* Destroy query tree */ STRUCT_ASSIGN_MACRO(psy_cb->psy_intree, qsf_rb.qsf_obj_id); if ((qsf_rb.qsf_lk_id = tree_lock) == 0) { stat = qsf_call(QSO_LOCK, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_error(E_PS0D18_QSF_LOCK, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (!status || stat == E_DB_FATAL) status = stat; } tree_lock = qsf_rb.qsf_lk_id; } stat = qsf_call(QSO_DESTROY, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_error(E_PS0D1A_QSF_DESTROY, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (!status || stat == E_DB_FATAL) status = stat; } tree_lock = 0; } /* Destroy query text */ STRUCT_ASSIGN_MACRO(psy_cb->psy_qrytext, qsf_rb.qsf_obj_id); if ((qsf_rb.qsf_lk_id = text_lock) == 0) { stat = qsf_call(QSO_LOCK, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_error(E_PS0D18_QSF_LOCK, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (!status || stat == E_DB_FATAL) status = stat; } text_lock = qsf_rb.qsf_lk_id; } stat = qsf_call(QSO_DESTROY, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_error(E_PS0D1A_QSF_DESTROY, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (!status || stat == E_DB_FATAL) status = stat; } return (status); }
/*{ ** Name: OPC_EXCH_BUILD - Build a QEN_EXCH node. ** ** Description: {@comment_line@}... ** ** Inputs: [@PARAM_DESCR@]... ** ** Outputs: [@PARAM_DESCR@]... ** Returns: ** {@return_description@} ** Exceptions: ** [@description_or_none@] ** ** Side Effects: ** [@description_or_none@] ** ** History: ** 13-nov-03 (inkdo01) ** Written for parallel query processing (cloned from opcsorts.c). ** 1-mar-04 (inkdo01) ** Allocate and format bit map to identify DSH-based structures ** to allocate with child DSHs. ** 21-july-04 (inkdo01) ** Change to use index arrays, rather than bit map. ** 6-aug-04 (inkdo01) ** Change setting of || union to opo_punion. ** 31-dec-04 (inkdo01) ** Added parm to opc_materialize to assure there's always exch_mat ** code (fixes 113694). ** 27-Aug-2007 (kschendel) SIR 122512 ** Count subplan headers, info used by hashop reservation. ** 14-May-2010 (kschendel) b123565 ** Node nthreads now defaults to zero, push counts even for 1:1 ** exchange so that QEF knows whether a given node is under an ** exchange or not. ** 19-May-2010 (kschendel) b123759 ** Include (new style) partition qualification data in exch counts. ** DSH rows can pop up multiple times, especially in pquals, use ** a bitmap to avoid listing a row more than once (would leak ** memory in QEF, at least for 1:N exchanges). */ VOID opc_exch_build( OPS_STATE *global, OPC_NODE *cnode) { OPS_SUBQUERY *subqry = global->ops_cstate.opc_subqry; OPO_CO *co = cnode->opc_cco; OPC_EQ *ceq = cnode->opc_ceq; QEN_NODE *qn = cnode->opc_qennode; QEN_EXCH *ex = &cnode->opc_qennode->node_qen.qen_exch; QEN_QP *qpp; QEF_QP_CB *qp = global->ops_cstate.opc_qp; OPC_NODE outer_cnode; QEF_AHD *union_action = NULL; PTR rowmap; /* DSH row tracker */ i4 arr1size, arr2size, i, j, ucount; i4 arrcnts[IX_MAX]; i4 mapbits, mapbytes; bool weset_exchn = FALSE; /* compile the outer tree */ STRUCT_ASSIGN_MACRO(*cnode, outer_cnode); if (!(global->ops_cstate.opc_flags & OPC_EXCH_N) && co->opo_ccount > 1) { weset_exchn = TRUE; global->ops_cstate.opc_flags |= OPC_EXCH_N; } qp->qp_status |= QEQP_PARALLEL_PLAN; outer_cnode.opc_cco = co->opo_outer; outer_cnode.opc_invent_sort = FALSE; opc_qentree_build(global, &outer_cnode); ex->exch_out = outer_cnode.opc_qennode; ex->exch_ccount = co->opo_ccount; ex->exch_flag = 0; cnode->opc_below_rows += outer_cnode.opc_below_rows; if (global->ops_cstate.opc_flags & OPC_EXCH_N) ex->exch_flag |= EXCH_NCHILD; if (weset_exchn) global->ops_cstate.opc_flags &= ~OPC_EXCH_N; if (co->opo_punion) { ex->exch_flag |= EXCH_UNION; ex->exch_out->node_qen.qen_qp.qp_flag |= QP_PARALLEL_UNION; ucount = ex->exch_ccount; } else ucount = 1; /* Count up subplan headers below here, normally 1, but N if this is ** a parallel union. This is purely to help out join reservation, ** so don't bother if child is just an orig. */ if (ex->exch_out->qen_type != QE_ORIG) qp->qp_pqhead_cnt += ucount; /* Post exch thread-counts to the sub-plan underneath this exch. ** (even if it's a 1:1 exch). This tells everyone that the sub-plan ** will be executed in a child thread. In addition, for the 1:n ** case, the ORIG and partition-compatible joins need to know N. */ opc_pushcount(ex->exch_out, ex->exch_ccount); /* materialize the inner tuple */ opc_materialize(global, cnode, &ex->exch_mat, &co->opo_maps->opo_eqcmap, ceq, &qn->qen_row, &qn->qen_rsz, (i4)FALSE, (bool)TRUE, (bool)TRUE); /* only drop dups if explicitly told to do so. This also disables the ** "only sort on key columns" change (because of odd cases where it seems ** to rely on the additional sort keys in its dups dropping strategies), ** so it's to our advantage to limit the dups dropping cases. */ if ((subqry->ops_duplicates == OPS_DKEEP || subqry->ops_duplicates == OPS_DUNDEFINED) && co->opo_odups == OPO_DDEFAULT ) { ex->exch_dups = 0; } else if (co->opo_odups == OPO_DREMOVE || subqry->ops_duplicates == OPS_DREMOVE ) { ex->exch_dups = DMR_NODUPLICATES; } else { /* error */ } /* CB number for exchange context CB */ opc_ptcb(global, &ex->exch_exchcb, sizeof(EXCH_CB)); /* Fill in the attributes to place in the exchange buffer. */ opc_eexatts(global, &co->opo_maps->opo_eqcmap, ceq, &ex->exch_atts, &ex->exch_acount); /* Fill in the atts to merge on. */ ex->exch_macount = 0; ex->exch_matts = (DMT_KEY_ENTRY **) NULL; /* To avoid double-counting a row, which is especially easy if PQUAL's ** are around, use a bitmap to track rows. There usually aren't a ** huge number of rows, even in complex query plans. ** Double-counting a row is nonfatal, but it leaks memory for unused ** row buffers in QEF for 1:N plans. */ mapbits = global->ops_cstate.opc_qp->qp_row_cnt; mapbytes = (mapbits + 7) / 8; rowmap = opu_Gsmemory_get(global, mapbytes); /* Build buffer/structure index arrays for each child (if || union, ** one set of arrays per child, else just one set of arrays). */ for (i = 0; i < ucount; i++) { /* Allocate arrays to identify buffers and structures to attach ** to child DSHs resulting from this EXCH node. First build vector ** of subarray counts for each class of buffers/structs. */ for (j = 0; j < IX_MAX; j++) arrcnts[j] = 0; /* init count array */ MEfill(mapbytes, 0, rowmap); /* and row tracker */ /* Set subarray counts. */ opc_exnheadcnt(global, qn, arrcnts, rowmap); /* count for EXCH node hdr */ arrcnts[IX_CX]++; /* + 1 for the exch_mat CX */ BTset(OPC_CTEMP, rowmap); /* + the temp row under exch */ if (ex->exch_flag & EXCH_UNION) opc_exunion_arrcnt(global, qn, &union_action, arrcnts, rowmap); /* counts for curr union select */ else opc_exnodearrcnt(global, qn->node_qen.qen_exch.exch_out, arrcnts, rowmap); /* counts everything under exch */ /* Determine total size of i2 and i4 arrays and alloc memory. */ arrcnts[IX_ROW] = BTcount(rowmap, mapbits); for (j = 0, arr1size = 0; j < IX_CX; j++) arr1size += arrcnts[j]; for (j = IX_CX, arr2size = 0; j < IX_MAX; j++) arr2size += arrcnts[j]; ex->exch_ixes[i].exch_array1 = (i2 *)opu_qsfmem(global, arr1size * sizeof(i2)); ex->exch_ixes[i].exch_array2 = (i4 *)opu_qsfmem(global, arr2size * sizeof(i4)); /* Set counts into EXCH. */ ex->exch_ixes[i].exch_row_cnt = arrcnts[IX_ROW]; ex->exch_ixes[i].exch_hsh_cnt = arrcnts[IX_HSH]; ex->exch_ixes[i].exch_hsha_cnt = arrcnts[IX_HSHA]; ex->exch_ixes[i].exch_stat_cnt = arrcnts[IX_STAT]; ex->exch_ixes[i].exch_ttab_cnt = arrcnts[IX_TTAB]; ex->exch_ixes[i].exch_hld_cnt = arrcnts[IX_HLD]; ex->exch_ixes[i].exch_shd_cnt = arrcnts[IX_SHD]; ex->exch_ixes[i].exch_pqual_cnt = arrcnts[IX_PQUAL]; ex->exch_ixes[i].exch_cx_cnt = arrcnts[IX_CX]; ex->exch_ixes[i].exch_dmr_cnt = arrcnts[IX_DMR]; ex->exch_ixes[i].exch_dmt_cnt = arrcnts[IX_DMT]; ex->exch_ixes[i].exch_dmh_cnt = arrcnts[IX_DMH]; /* Determine start of each subarray. */ for (j = IX_MAX; j > IX_CX; j--) { arr2size -= arrcnts[j-1]; arrcnts[j-1] = arr2size; } for (j = IX_CX; j > 0; j--) { arr1size -= arrcnts[j-1]; arrcnts[j-1] = arr1size; } /* Load subarrays - note EXCH nodes stop array loading, so this ** EXCH node is loaded here, not in opc_ex...arrset(). Likewise, ** the QP node on top of a || union is loaded here. The subtrees ** below the EXCH and QP nodes are handled by the opc_ex...arrset() ** functions. The row indexes are listed via the row bitmap. */ j = BTnext(-1, rowmap, mapbits); while (j != -1) { ex->exch_ixes[i].exch_array1[arrcnts[IX_ROW]++] = j; j = BTnext(j, rowmap, mapbits); } opc_exnheadset(global, qn, ex->exch_ixes[i].exch_array1, ex->exch_ixes[i].exch_array2, arrcnts); /* for EXCH node header */ ex->exch_ixes[i].exch_array2[arrcnts[IX_CX]++] = ex->exch_mat-> qen_pos; /* exch_mat CX */ if (ex->exch_flag & EXCH_UNION) { qpp = &ex->exch_out->node_qen.qen_qp; opc_exnheadset(global, ex->exch_out, ex->exch_ixes[i].exch_array1, ex->exch_ixes[i].exch_array2, arrcnts); /* for QP node header */ if (qpp->qp_qual != (QEN_ADF *) NULL) ex->exch_ixes[i].exch_array2[arrcnts[IX_CX]++] = qpp->qp_qual->qen_pos; opc_exactarrset(global, union_action, ex->exch_ixes[i].exch_array1, ex->exch_ixes[i].exch_array2, arrcnts); } else opc_exnodearrset(global, qn->node_qen.qen_exch.exch_out, ex->exch_ixes[i].exch_array1, ex->exch_ixes[i].exch_array2, arrcnts); } }