/*{ ** Name: OPC_EXNHEADCNT - count array entriesfor node headers ** ** Description: Counts array entries for node headers - allows code to be ** called from several places in module ** ** Inputs: ** ** Outputs: ** Returns: ** Exceptions: ** ** Side Effects: ** ** History: ** 22-july-04 (inkdo01) ** Written for || query processing. */ static VOID opc_exnheadcnt( OPS_STATE *global, QEN_NODE *node, i4 *arrcnts, PTR rowmap) { /* Simply analyze possible array entries for node header and ** add (as necessary) to counts. */ if (node == (QEN_NODE *) NULL) return; /* just in case */ arrcnts[IX_STAT]++; if (node->qen_row >= 0) BTset(node->qen_row, rowmap); if (node->qen_frow >= 0) BTset(node->qen_frow, rowmap); if (node->qen_fatts != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (node->qen_prow != (QEN_ADF *) NULL) arrcnts[IX_CX]++; }
/*{ ** Name: ope_neweqcls - create a new equivalence class for joinop attribute ** ** Description: ** This routine will create a new equivalence class for the joinop ** attribute. It is assumed that the joinop attribute is not already ** assigned to an equivalence class. ** ** Inputs: ** subquery ptr to subquery be analyzed ** attr joinop attribute to be placed ** in equivalence class ** ** Outputs: ** Returns: ** equivalence class which was assigned ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 24-apr-86 (seputis) ** initial creation ** 24-oct-88 (seputis) ** init ope_nulljoin field ** 21-dec-88 (seputis) ** init ope_mask field, check for non-nullable attributes ** 13-may-91 (seputis) ** - fix for b37172 - added ope_sargp ** 31-jan-94 (rganski) ** Removed ope_sargp, due to change to oph_sarglist. ** 9-aug-05 (inkdo01) ** Add support of OJ & EQC bit maps. ** 24-aug-05 (inkdo01) ** Drop ope_ojs - problem was solved differently. ** 21-jul-06 (hayke02) ** Re-introcude and initialize ope_ojs. This change fixes bug 116406. [@history_line@]... */ OPE_IEQCLS ope_neweqcls( OPS_SUBQUERY *subquery, OPZ_IATTS attr) { OPE_IEQCLS eqcls; /* index into equivalence class array */ OPE_ET *ebase; /* ptr to base of equivalence class array */ ebase = subquery->ops_eclass.ope_base; eqcls = subquery->ops_eclass.ope_ev++; /* find first available unassigned ** equivalence class, allocate it */ if (eqcls >= OPE_MAXEQCLS) opx_error(E_OP0301_EQCLS_OVERFLOW); { /* eqcls now contains the unassigned equivalence class index */ OPE_EQCLIST *eqcls_ptr; eqcls_ptr = (OPE_EQCLIST *) opu_memory(subquery->ops_global, (i4) sizeof(OPE_EQCLIST)); ebase->ope_eqclist[eqcls] = eqcls_ptr; /* assign memory for the ** equivalence class element */ MEfill(sizeof(OPZ_BMATTS), (u_char) 0, (PTR) &eqcls_ptr->ope_attrmap); /* initialize equivalence class ** element attribute bit map to zero */ eqcls_ptr->ope_bfindex = OPB_NOBF; /* no constant predicates found yet*/ eqcls_ptr->ope_nbf = OPB_NOBF; /* no sargable predicates found yet */ eqcls_ptr->ope_nulljoin = TRUE; /* keep nulls in joins unless user ** explicitly joins two attributes */ eqcls_ptr->ope_mask = 0; /* mask of various booleans */ if (attr != OPZ_NOATTR) { /* update information in attribute to reference equivalence class*/ OPZ_ATTS *attr_ptr; /* ptr to attribute element to ** be placed in equivalence class */ BTset((i4) attr, (char *)&eqcls_ptr->ope_attrmap); /* set the appropriate ** bit to indicate attribute */ attr_ptr = subquery->ops_attrs.opz_base->opz_attnums[attr]; if (attr_ptr->opz_dataval.db_datatype > 0) /* NULLs will not exist, if this eventually becomes a joining eqcls */ eqcls_ptr->ope_nulljoin = FALSE; eqcls_ptr->ope_eqctype = (attr_ptr->opz_attnm.db_att_id ==DB_IMTID) ? OPE_TID : OPE_NONTID; /* set type of this eqclass to ** OPE_TID if the att is an implicit TID */ MEfill((u_i2) sizeof(OPL_BMOJ), (u_char) 0, (PTR) &eqcls_ptr->ope_ojs); /* init OJ bit map */ attr_ptr->opz_equcls = eqcls; /* remember that this att has ** been assigned to an eqclass */ BTset((i4)eqcls, (char *)&attr_ptr->opz_eqcmap); /* & set eqc in map (for multi-EQC atts) */ } else eqcls_ptr->ope_eqctype = OPE_NONTID; } return(eqcls); }
/* First, a tiny helper routine to deal with the mini-program pointed ** to by a QEN_PART_QUAL. */ static void opc_arrcnt_pqe(QEN_PQ_EVAL *pqe, i4 *arrcnts, PTR rowmap) { i4 ninstrs; arrcnts[IX_CX]++; BTset(pqe->un.hdr.pqe_value_row, rowmap); BTset(pqe->pqe_result_row, rowmap); ninstrs = pqe->un.hdr.pqe_ninstrs; while (--ninstrs > 0) { pqe = (QEN_PQ_EVAL *) ((char *)pqe + pqe->pqe_size_bytes); BTset(pqe->pqe_result_row, rowmap); if (pqe->pqe_eval_op == PQE_EVAL_ANDMAP || pqe->pqe_eval_op == PQE_EVAL_ORMAP) BTset(pqe->un.andor.pqe_source_row, rowmap); } } /* opc_arrcnt_pqe */
/*{ ** Name: opv_mapvar - update varmap of a query tree fragment ** ** Description: ** This routine will recursively descent the query tree and update the ** varmap i.e. bitmap of all range variables referenced in this query tree ** given to this routine. ** ** Inputs: ** nodep ptr to current query tree node being ** analyzed ** map ptr to bitmap of vars to be updated ** ** Outputs: ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 3-jul-86 (seputis) ** initial creation ** 28-jan-04 (wanfr01) ** Bug 36414, INGSRV582 ** Constant node doesn't need it's children recursed; ** all children will also be constants. [@history_line@]... */ VOID opv_mapvar( PST_QNODE *nodep, OPV_GBMVARS *map) { while (nodep) { if (nodep->pst_sym.pst_type == PST_VAR) { /* var node found so set bit map */ BTset( (i4)nodep->pst_sym.pst_value.pst_s_var.pst_vno, (char *)map ); return; } if ((nodep->pst_left) && (nodep->pst_sym.pst_type != PST_CONST)) opv_mapvar( nodep->pst_left, map); /* recurse down left */ nodep = nodep->pst_right; /* iterate down right side */ }; }
VOID psy_vcount( PST_QNODE *tree, PST_VRMAP *bitmap) { PSY_STK stk = {0, 0, {0, }}; STATUS sts; (VOID)MEfill(sizeof(PST_VRMAP), (u_char)0, (PTR)bitmap); while (tree) { /* If this is a VAR node supplement the bitmap */ switch (tree->pst_sym.pst_type) { case PST_VAR: (VOID)BTset((i4)tree->pst_sym.pst_value.pst_s_var.pst_vno, (char *)bitmap); break; case PST_ROOT: /* check union nodes */ if (tree->pst_sym.pst_value.pst_s_root.pst_union.pst_next) psy_push(&stk, (PTR)tree->pst_sym.pst_value.pst_s_root.pst_union.pst_next, &sts); break; default: break; } /* scan left and right branches if any */ if (tree->pst_right) psy_push(&stk, (PTR)tree->pst_right, &sts); if (!(tree = tree->pst_left)) tree = (PST_QNODE*)psy_pop(&stk); } }
/*{ ** 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: psy_integ - Apply integrity constraints ** ** Description: ** This function applies integrity constraints. It gets the constraints ** from RDF and puts them in the query where appropriate. ** ** Inputs: ** mstream QSF memory stream to allocate from ** root Root of query tree to constrain ** rngtab Pointer to the range table ** resvar Pointer to the result range variable ** qmode Query mode of user's query ** sess_cb session control block ** result Place to put pointer to constrained tree ** err_blk Filled in if an error happens ** ** Outputs: ** root Integrity constraints may be appended ** result Filled in with pointer to constrained ** tree ** err_blk Filled in if an error happened ** Returns: ** E_DB_OK Success ** E_DB_ERROR Failure ** Exceptions: ** none ** ** Side Effects: ** Allocates memory ** ** History: ** 19-jun-86 (jeff) ** Adapted from integrity.c in 4.0. ** 02-sep-86 (seputis) ** changes for new RDF interface ** fixed bug for no integrity case ** 04-dec-86 (daved) ** process define and replace cursor. Use copy of saved qual for ** replace cursor. ** 03-dec-87 (stec) ** Change psy_apql interface. ** 11-may-88 (stec) ** Make changes for db procs. ** 06-feb-89 (ralph) ** Modified to use DB_COL_WORDS*2 as extent of dset array ** 23-Feb-89 (andre) ** Changed the way the tree consisting of qualifications obtained from ** the integrity trees is constructed and merged with the ** qualifications found in the original tree. ** 18-may-89 (neil) ** Use session memory for cursors with integrities (bug fix). ** 12-mar-90 (andre) ** set rdr_2types_mask to 0. ** 22-may-90 (teg) ** init rdr_instr to RDF_NO_INSTR ** 04-sep-90 (andre) ** fixed bug 32976: for OPEN CURSOR (qmode==PSQ_DEFCURS) we need to ** compare attribute number(s) found in the integrity ** tuple with attribute number found in the VAR ** node(s) found in the portion of the target list ** which was built to represent the FOR UPDATE list ** (such node(s) are right children of RESDOM nodes ** with pst_rsupdt set to TRUE; note that for ** RESDOM nodes built to represent the real target ** list of SELECT, this field is set to FALSE.) ** 29-sep-92 (andre) ** RDF may choose to allocate a new info block and return its address ** in rdf_info_blk - we need to copy it over to pss_rdrinfo to avoid ** memory leak and other assorted unpleasantries ** 05-dec-92 (rblumer) ** ignore FIPS constraints during INGRES integrity processing ** 10-jan-93 (andre) ** after calling rdf_call() for IIINTEGRITY tuples, compare status to ** E_DB_OK instead of using DB_FAILURE_MACRO() since if there are fewer ** than 20 rows, rdf_call() sets err_code to E_RD0011 and status to ** E_DB_WARN ** 10-Feb-93 (Teresa) ** Changed RDF_GETINFO to RDF_READTUPLES for new RDF I/F ** 23-May-1997 (shero03) ** Save the rdr_info_blk after an UNFIX ** 22-Jul-2004 (schka24) ** Delete old ifdef'ed out normalization call ** 28-nov-2007 (dougi) ** Add PSQ_REPDYN to PSQ_DEFCURS test (cached dynamic). */ DB_STATUS psy_integ( PSF_MSTREAM *mstream, PST_QNODE *root, PSS_USRRANGE *rngtab, PSS_RNGTAB *resvar, i4 qmode, PSS_SESBLK *sess_cb, PST_QNODE **result, DB_ERROR *err_blk) { register PST_QNODE *r; PSC_CURBLK *curblk = sess_cb->pss_crsr; CS_SID sessid = sess_cb->pss_sessid; PTR db_id = sess_cb->pss_dbid; i2 dset[DB_COL_WORDS*2]; i2 doms; i2 *domset; bool subset; PST_QNODE *p; register i4 i; PST_QNODE *iqual; PST_QNODE **tmp1; DB_INTEGRITY *inttup; i4 err_code; PST_QTREE *qtree; PST_PROCEDURE *pnode; DB_STATUS status = E_DB_OK; i4 found; RDF_CB rdf_cb; QEF_DATA *qp; RDF_CB rdf_tree_cb; PSS_DUPRB dup_rb; i4 tupcount; i4 map[PST_NUMVARS]; /* Map for reassigning varnos */ r = root; /* Initialize fields in dup_rb */ dup_rb.pss_op_mask = 0; dup_rb.pss_num_joins = PST_NOJOIN; dup_rb.pss_tree_info = (i4 *) NULL; dup_rb.pss_mstream = mstream; dup_rb.pss_err_blk = err_blk; if (qmode == PSQ_REPCURS) { if (!curblk->psc_integ) { *result = r; return (E_DB_OK); } /* ** On a replace cursor, get the query tree that was stored in the ** cursor control block. */ /* copy the qual fragment so we can change below. */ dup_rb.pss_tree = curblk->psc_integ; dup_rb.pss_dup = &iqual; status = pst_treedup(sess_cb, &dup_rb); if (DB_FAILURE_MACRO(status)) { return (status); } } else { /* ** Check to see if we should apply the integrity ** algorithm. ** ** This means checking to insure that we have an update ** and seeing if any integrity constraints apply. */ if ( resvar == 0 || (resvar->pss_tabdesc->tbl_status_mask & DMT_INTEGRITIES) == 0 ) { *result = r; return (E_DB_OK); } /* ** Create a set of the domains updated in this query. */ for (i = 0; i < DB_COL_WORDS*2; i++) dset[i] = 0; for (p = r->pst_left, doms = 0; p != (PST_QNODE *) NULL && p->pst_sym.pst_type != PST_TREE; p = p->pst_left) { if (p->pst_sym.pst_type != PST_RESDOM) { psf_error(E_PS0D0C_NOT_RESDOM, 0L, PSF_INTERR, &err_code, err_blk, 0); return (E_DB_SEVERE); } /* ** if we are defining a cursor, RESDOM numbers are meaningless as ** at best they reflect position of an attribute in the target list ** of a subselect or, at worst, they are set to 1 for all columns ** mentioned in the FOR UPDATE LIST. We really are interested only ** in the VAR nodes which are children of RESDOM nodes built to ** repersent columns appearing in the FOR UPDATE list, so we will ** skip over RESDOM nodes which represent a true subselect ** (i.e. pst_rsupdt == FALSE) */ if (qmode == PSQ_DEFCURS) { if (p->pst_sym.pst_value.pst_s_rsdm.pst_rsupdt) { PST_QNODE *r_child = p->pst_right; /* ** this RESDOM was built to represent the column in the FOR ** UPDATE list */ /* ** make sure the right child is a VAR node; otherwise flag ** an error */ if (r_child == (PST_QNODE *) NULL || r_child->pst_sym.pst_type != PST_VAR) { psf_error(E_PS0C04_BAD_TREE, 0L, PSF_INTERR, &err_code, err_blk, 0); return (E_DB_SEVERE); } BTset((i4) r_child-> pst_sym.pst_value.pst_s_var.pst_atno.db_att_id, (char *) dset); ++doms; } } else { BTset((i4) p->pst_sym.pst_value.pst_s_rsdm.pst_rsno, (char *) dset); ++doms; } } /* ** Note if we are appending a subset of the relation's domains. ** If we are, we'll need to be extra careful to avoid violating ** constraints on those attributes not being explicitly appended: */ subset = ((doms < resvar->pss_tabdesc->tbl_attr_count) && (qmode == PSQ_APPEND)); /* ** Scan integrity catalog for possible tuples. If found, ** include them in the integrity qualification. */ iqual = (PST_QNODE *) NULL; /* Set up constant part of rdf query tree control block */ pst_rdfcb_init(&rdf_tree_cb, sess_cb); STRUCT_ASSIGN_MACRO(resvar->pss_tabid, rdf_tree_cb.rdf_rb.rdr_tabid); rdf_tree_cb.rdf_rb.rdr_types_mask = RDR_INTEGRITIES | RDR_QTREE; rdf_tree_cb.rdf_rb.rdr_qtuple_count = 1; /* Get 1 integ tree at a time */ rdf_tree_cb.rdf_info_blk = resvar->pss_rdrinfo; /* Get all integrity tuples */ pst_rdfcb_init(&rdf_cb, sess_cb); STRUCT_ASSIGN_MACRO(resvar->pss_tabid, rdf_cb.rdf_rb.rdr_tabid); rdf_cb.rdf_rb.rdr_types_mask = RDR_INTEGRITIES; rdf_cb.rdf_rb.rdr_update_op = RDR_OPEN; rdf_cb.rdf_rb.rdr_rec_access_id = NULL; rdf_cb.rdf_rb.rdr_qtuple_count = 20; /* Get 20 integrities at a time */ rdf_cb.rdf_info_blk = resvar->pss_rdrinfo; /* For each group of 20 integrities */ while (rdf_cb.rdf_error.err_code == 0) { status = rdf_call(RDF_READTUPLES, (PTR) &rdf_cb); /* ** RDF may choose to allocate a new info block and return its address ** in rdf_info_blk - we need to copy it over to pss_rdrinfo to avoid ** memory leak and other assorted unpleasantries */ if (rdf_cb.rdf_info_blk != resvar->pss_rdrinfo) { resvar->pss_rdrinfo = rdf_tree_cb.rdf_info_blk = rdf_cb.rdf_info_blk; } /* ** Must not use DB_FAILURE_MACRO because E_RD0011 returns E_DB_WARN ** that would be missed. */ if (status != E_DB_OK) { if ( rdf_cb.rdf_error.err_code == E_RD0011_NO_MORE_ROWS || rdf_cb.rdf_error.err_code == E_RD0013_NO_TUPLE_FOUND) { status = E_DB_OK; if (rdf_cb.rdf_error.err_code == E_RD0013_NO_TUPLE_FOUND) continue; } else if (rdf_cb.rdf_error.err_code == E_RD0002_UNKNOWN_TBL) { (VOID) psf_error(2117L, 0L, PSF_USERERR, &err_code, err_blk, 1, psf_trmwhite(sizeof(DB_TAB_NAME), (char *) &resvar->pss_tabname), &resvar->pss_tabname); status = E_DB_ERROR; goto exit; } else { (VOID) psf_rdf_error(RDF_READTUPLES, &rdf_cb.rdf_error, err_blk); goto exit; } } rdf_cb.rdf_rb.rdr_update_op = RDR_GETNEXT; /* FOR EACH INTEGRITY */ for ( qp = rdf_cb.rdf_info_blk->rdr_ituples->qrym_data, tupcount = 0; tupcount < rdf_cb.rdf_info_blk->rdr_ituples->qrym_cnt; qp = qp->dt_next, tupcount++ ) { inttup = (DB_INTEGRITY*) qp->dt_data; /* ** Ignore FIPS constraints (i.e. SQL92 constraints) ** (they are implemented via rules instead) */ if (inttup->dbi_consflags != 0) { continue; } /* check for some domain set overlap */ domset = (i2*) inttup->dbi_columns.db_domset; for (i = 0; i < DB_COL_WORDS*2; i++) { if ((dset[i] & domset[i]) != 0) break; } /* ** Check for appends where defaults don't satisfy integrity. */ if ((i >= DB_COL_WORDS*2) && !subset) { continue; } /* Get integrity tree and make qtree point to it */ STRUCT_ASSIGN_MACRO(inttup->dbi_tree, rdf_tree_cb.rdf_rb.rdr_tree_id); rdf_tree_cb.rdf_rb.rdr_qrymod_id = inttup->dbi_number; rdf_tree_cb.rdf_rb.rdr_update_op = 0; rdf_tree_cb.rdf_rb.rdr_rec_access_id = NULL; rdf_tree_cb.rdf_info_blk = resvar->pss_rdrinfo; rdf_tree_cb.rdf_rb.rdr_integrity = NULL; STRUCT_ASSIGN_MACRO(inttup->dbi_tabid, rdf_tree_cb.rdf_rb.rdr_tabid); rdf_tree_cb.rdf_rb.rdr_sequence = inttup->dbi_number; status = rdf_call(RDF_GETINFO, (PTR) &rdf_tree_cb); /* ** RDF may choose to allocate a new info block and return its ** address in rdf_info_blk - we need to copy it over to pss_rdrinfo ** to avoid memory leak and other assorted unpleasantries */ if (rdf_tree_cb.rdf_info_blk != resvar->pss_rdrinfo) { resvar->pss_rdrinfo = rdf_cb.rdf_info_blk = rdf_tree_cb.rdf_info_blk; } if (DB_FAILURE_MACRO(status)) { if (rdf_tree_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(DB_TAB_NAME), (char *) &resvar->pss_tabname), &resvar->pss_tabname); } else { (VOID) psf_rdf_error(RDF_GETINFO, &rdf_tree_cb.rdf_error, err_blk); } goto exit; } pnode = (PST_PROCEDURE *) rdf_tree_cb.rdf_rb.rdr_integrity->qry_root_node; qtree = pnode->pst_stmts->pst_specific.pst_tree; /* trim off (null) target list */ p = qtree->pst_qtree->pst_right; /* use a copy of the qtree because the qtree is in RDF memory */ dup_rb.pss_tree = qtree->pst_qtree->pst_right; dup_rb.pss_dup = &p; status = pst_treedup(sess_cb, &dup_rb); { /* unfix the query tree no matter what the above status is */ DB_STATUS temp_status; temp_status = rdf_call(RDF_UNFIX, (PTR) &rdf_tree_cb); resvar->pss_rdrinfo = rdf_cb.rdf_info_blk = rdf_tree_cb.rdf_info_blk; if (DB_FAILURE_MACRO(temp_status)) { (VOID) psf_rdf_error(RDF_UNFIX, &rdf_tree_cb.rdf_error, err_blk); status = temp_status; } } if (DB_FAILURE_MACRO(status)) goto exit; /* close integrity file */ /* ** Make the result variable for the integrity the same as the result ** variable for the user query. ** I AM NOT SURE THE FOLLOWING COMMENT APPLIES SO I AM MERGING ** THE RANGE VAR FOR APPENDS. ** This is not done for append because ** append doesn't have a result range variable. */ for (i = 0; i < PST_NUMVARS; i++) map[i] = i; i = inttup->dbi_resvar; map[i] = resvar->pss_rgno; status = psy_mapvars(p, map, err_blk); if (DB_FAILURE_MACRO(status)) goto exit; /* add to integrity qual */ if (iqual == NULL) { status = pst_node(sess_cb, mstream, p, (PST_QNODE *) NULL, PST_AND, (PTR) NULL, sizeof(PST_OP_NODE), DB_NODT, (i2) 0, (i4) 0, (DB_ANYTYPE *) NULL, &iqual, err_blk, (i4) 0); if (DB_FAILURE_MACRO(status)) goto exit; /* close integrity file */ /* ** Note that tmp1 will contain address of the pst_right ptr ** of the bottom AND node in the tree constructed from the ** qualifications found in the integrities */ tmp1 = &iqual->pst_right; } else { PST_QNODE *newnode; status = pst_node(sess_cb, mstream, p, iqual, PST_AND, (PTR) NULL, sizeof(PST_OP_NODE), DB_NODT, (i2) 0, (i4) 0, (DB_ANYTYPE *) NULL, &newnode, err_blk, (i4) 0); if (DB_FAILURE_MACRO(status)) goto exit; /* close integrity file */ iqual = newnode; } } } if (rdf_cb.rdf_rb.rdr_rec_access_id != NULL) { DB_STATUS temp_status; /* unfix integrity tuples */ rdf_cb.rdf_rb.rdr_update_op = RDR_CLOSE; temp_status = rdf_call(RDF_READTUPLES, (PTR) &rdf_cb); resvar->pss_rdrinfo = rdf_tree_cb.rdf_info_blk = rdf_cb.rdf_info_blk; if (DB_FAILURE_MACRO(temp_status)) { (VOID) psf_rdf_error(RDF_READTUPLES, &rdf_cb.rdf_error, err_blk); status = temp_status; } } } if (qmode == PSQ_DEFCURS || qmode == PSQ_REPDYN) { /* ** On a "define cursor", keep a copy of the integrity qualification ** for later use. */ if (iqual == NULL) { curblk->psc_integ = (PST_QNODE *) NULL; } else { status = pst_trdup(curblk->psc_stream, iqual, &curblk->psc_integ, &sess_cb->pss_memleft, err_blk); if (DB_FAILURE_MACRO(status)) { return (status); } } } else { /* ** Clean up the integrity qualification so that it will merge ** nicely into the tree, and then append it to the user's ** qualification. */ if (iqual != NULL) { /* replace VAR nodes by corresponding user afcn from user's ** query. For example, if the integ says r.a > 1 and the user's ** query says resdom 4 = 4 + 3, the integ is modified to 4 + 3 > 1. ** ** The following paragraph refers to the case where an integrity ** refers to a variable not updated in the target list. ** ** If there is no resdom for r.a, if the user didn't specify r.a ** in the target list of the query, and the query is an append, ** r.a > 1 is replaced with 'default val for col a' > 1. If the ** query is a replace statement, we do nothing because r.a will ** be retrieved but is not in the targ list. We will be verifying ** what we know should already hold (ie the integrity constraint). ** If we have replace cursor, we wan't to replace r.a with a value ** so a retrieve can be avoided on the replace command. We can't ** have col a = 5 where r.a > 1. We do, however, have the value r.a ** in the row to be updated. QEF has this value. We replace r.a with ** a curval node that refers to the value r.a. */ /* will pass dup_rb, but first null out pss_tree and pss_dup */ dup_rb.pss_tree = (PST_QNODE *) NULL; dup_rb.pss_dup = (PST_QNODE **) NULL; status = psy_subsvars(sess_cb, &iqual, resvar, r->pst_left, qmode, (PST_QNODE *) NULL, resvar, (i4) 0, qmode, &curblk->psc_blkid, &found, &dup_rb); if (DB_FAILURE_MACRO(status)) { return (status); } /* ** for REPLACE CURSOR, we need to traverse down the pst_right's ** until we encounter NULL in place of which we will append the ** qualification from the ROOT ** Note that iqual is guaranteed to be an AND node with BOP for a ** left child and either AND or NULL for the right child. */ if (qmode == PSQ_REPCURS) { for (tmp1 = &iqual->pst_right; (*tmp1) != (PST_QNODE *) NULL; tmp1 = &(*tmp1)->pst_right) ; } /* ** append qualification from the tree to the integrities and ** make the result the new qualification for the tree */ (*tmp1) = r->pst_right; r->pst_right = iqual; } } exit: if ((qmode != PSQ_REPCURS) && (rdf_cb.rdf_rb.rdr_rec_access_id != NULL)) { DB_STATUS temp_status; /* unfix integrity tuples */ rdf_cb.rdf_rb.rdr_update_op = RDR_CLOSE; temp_status = rdf_call(RDF_READTUPLES, (PTR) &rdf_cb); resvar->pss_rdrinfo = rdf_tree_cb.rdf_info_blk = rdf_cb.rdf_info_blk; if (DB_FAILURE_MACRO(temp_status)) { (VOID) psf_rdf_error(RDF_READTUPLES, &rdf_cb.rdf_error, err_blk); status = temp_status; } } if (status == E_DB_OK) { *result = r; } return (status); }
/*{ ** Name: opv_agrv - allocate new global range variable ** ** Description: ** Find a free slot in the global range table for an ** aggregate function, or implicitly referenced index. ** If there are no free slots for the aggregate function, then the ** optimization is aborted and an error reported. There will be ** one global range table per optimization and there will be no ** overlapping of range table assignments i.e. it is conceivable ** the two temporary relations could use the same range table ** entry since they do not exist at the same... this will not be ** done. ** ** Inputs: ** global ptr to global state variable ** name ptr to table name ** NULL- indicates a temporary table ** owner ptr to owner name ** abort TRUE if optimization should be aborted ** in case of error ** ** Outputs: ** Returns: ** - index into global range table representing the allocated ** variable ** Exceptions: ** Will generate an internal exception if the global range table ** is full. This will abort the query and report an error. ** ** Side Effects: ** none ** ** History: ** 7-apr-86 (seputis) ** initial creation ** 11-apr-91 (seputis) ** ask for RDF info if name, or table ID is given so ** that explicit secondary index substitution can get ** histograms ** 18-sep-92 (ed) ** bug 44850 - added parameter to allow multi-to-one mapping ** so a common aggregate temp can be used ** 17-Jan-2004 (schka24) ** Rename RDR_BLD_KEY to RDR_BLD_PHYS, gives us partition info too. [@history_line@]... */ OPV_IGVARS opv_agrv( OPS_STATE *global, DB_TAB_NAME *name, DB_OWN_NAME *owner, DB_TAB_ID *table_id, OPS_SQTYPE sqtype, bool abort, OPV_GBMVARS *gbmap, OPV_IGVARS gvarno) { OPV_IGVARS grv_index; /* index into global range table */ OPV_IGVARS empty_index; /* index into global range table of ** free element */ OPV_GRT *gbase; /* ptr to base of array of ptrs to global ** range table elements */ bool lookup; /* look for existing definition if ** names are available */ lookup = name && owner; /* TRUE - if RDF table ID given */ empty_index = OPV_NOGVAR; gbase = global->ops_rangetab.opv_base; for ( grv_index = 0; grv_index < OPV_MAXVAR; grv_index++) { OPV_GRV *existing_var; if (!(existing_var = gbase->opv_grv[grv_index])) { if (empty_index == OPV_NOGVAR) { empty_index = grv_index; if (!lookup) break; /* empty slot found and we do not need ** to continue searching for an existing ** table entry of the same name */ } } else { if (lookup && existing_var->opv_relation && existing_var->opv_relation->rdr_rel && ( existing_var->opv_relation->rdr_rel->tbl_name.db_tab_name[0] == name->db_tab_name[0] ) && ( existing_var->opv_relation->rdr_rel->tbl_owner.db_own_name[0] == owner->db_own_name[0] ) && !MEcmp((PTR)&existing_var->opv_relation->rdr_rel->tbl_name, (PTR)name, sizeof(*name)) && !MEcmp((PTR)&existing_var->opv_relation->rdr_rel->tbl_owner, (PTR)owner, sizeof(*owner)) && ( !gbmap || !BTtest((i4)grv_index, (char *)gbmap) /* do not use the ** same global range variable ** in the same subquery twice ** or OPC will complain */ ) ) { /* a match has been found */ if (gbmap) BTset((i4)grv_index, (char *)gbmap); /* set the global ** bit map so that this ** range variable is not ** reused in the same subquery */ return(grv_index); } } } if (empty_index != OPV_NOGVAR) { if (gvarno == OPV_NOGVAR) { /* empty slot found - allocate and initialize slot and return */ OPV_GRV *grv; /* pointer to global range table element */ RDF_CB *rdfcb; rdfcb = &global->ops_rangetab.opv_rdfcb; if (name || table_id) { /* if name is available then table ID might be available */ if (table_id) { /* use table ID if available */ STRUCT_ASSIGN_MACRO((*table_id), rdfcb->rdf_rb.rdr_tabid); /* ** need table name */ rdfcb->rdf_rb.rdr_types_mask = RDR_RELATION | 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_PHYS info. The ** attribute info does not need to ** be requested if RDF is changed.*/ } else { /* table ID not available so use name */ MEfill( (i4)sizeof(DB_TAB_ID), (u_char)0, (PTR)&rdfcb->rdf_rb.rdr_tabid); rdfcb->rdf_rb.rdr_types_mask = RDR_RELATION | RDR_ATTRIBUTES | RDR_BLD_PHYS | RDR_BY_NAME; STRUCT_ASSIGN_MACRO((*name), rdfcb->rdf_rb.rdr_name.rdr_tabname);/* need ** table name */ STRUCT_ASSIGN_MACRO((*owner), rdfcb->rdf_rb.rdr_owner);/* ** need table owner */ } } else rdfcb->rdf_info_blk = NULL; /* get new ptr to info ** associated with global var */ /* allocate and initialize global range table element */ if (opv_parser(global, empty_index, sqtype, (name != NULL) || (table_id != NULL), /* TRUE - if rdf info needs to be retrieved */ FALSE, /* TRUE - if this is a parser range table element */ abort) /* TRUE - if error occurs then otherwise FALSE ** means return varinit == TRUE */ ) return(OPV_NOGVAR); /* ignore variable if error occurs */ grv = gbase->opv_grv[empty_index]; /* get ptr to element */ grv->opv_qrt = OPV_NOGVAR; /* indicates that this table was not ** explicitly referenced in the query */ grv->opv_relation = rdfcb->rdf_info_blk; /* save ptr to RDF info */ } else { gbase->opv_grv[empty_index] = gbase->opv_grv[gvarno]; /* used ** for aggregate temporaries which ** require "2 cursors" */ } if (gbmap) BTset((i4)empty_index, (char *)gbmap); /* set the global ** bit map so that this ** range variable is not ** reused in the same subquery */ } else if (abort) /* the entire table is full so report and error */ opx_error(E_OP0005_GRANGETABLE); return (empty_index); /* return with no range table entry */ }
/*{ ** 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: opv_tproc - Load RDF defs for table procedure range table entry ** ** Description: ** This function allocates and formats simulated RDF structures for ** a table procedure, including result column descriptors that are ** used to resolve "column" references to the procedure. ** ** Inputs: ** sess_cb Pointer to session control block ** rngtable Pointer to the user range table ** scope scope that range variable belongs in ** -1 means don't care. ** corrname Correlation name of table procedure ** dbp Ptr to PSF procedure descriptor ** rngvar Place to put pointer to new range ** variable ** root Ptr to RESDOM list of parameter specs ** err_blk Place to put error information ** ** Outputs: ** rngvar Set to point to the range variable ** err_blk Filled in if an error happens ** Returns: ** E_DB_OK Success ** E_DB_ERROR Non-catastrophic failure ** E_DB_FATAL Catastrophic failure ** Exceptions: ** none ** ** Side Effects: ** Can allocate memory ** ** History: ** 17-april-2008 (dougi) ** Written for table procedures(semi-cloned from pst_stproc). ** 15-dec-2008 (dougi) BUG 121381 ** Fix computation of result column offsets. */ DB_STATUS opv_tproc( OPS_STATE *global, OPV_IGVARS gvar, OPV_GRV *grvp, PST_RNGENTRY *rngentry) { DMT_ATT_ENTRY **attarray, *attrp; DMT_ATT_ENTRY **parmarray, **rescarray; DMT_TBL_ENTRY *tblptr; RDR_INFO *rdrinfop; QEF_DATA *qp; RDF_CB *rdf_cb; RDR_RB *rdf_rb; DB_PROCEDURE *dbp; DB_DBP_NAME proc_name; DB_OWN_NAME proc_owner; i4 parameterCount, rescolCount, resrowWidth; u_i4 created; i4 i, j, totlen, offset; DB_STATUS status; i4 err_code; /* First retrieve iiprocedure row using id from range table entry. */ rdf_cb = &global->ops_rangetab.opv_rdfcb; rdf_rb = &rdf_cb->rdf_rb; STRUCT_ASSIGN_MACRO(rngentry->pst_rngvar, rdf_rb->rdr_tabid); rdf_rb->rdr_types_mask = RDR_PROCEDURE; rdf_rb->rdr_2types_mask = 0; rdf_rb->rdr_instr = RDF_NO_INSTR; /* ** need to set rdf_info_blk to NULL for otherwise RDF assumes that we ** already have the info_block */ rdf_cb->rdf_info_blk = (RDR_INFO *) NULL; status = rdf_call(RDF_GETINFO, rdf_cb); if (DB_FAILURE_MACRO(status)) { (VOID) opx_verror(status, E_OP0004_RDF_GETDESC, rdf_cb->rdf_error.err_code); } dbp = rdf_cb->rdf_info_blk->rdr_dbp; /* Before proceeding - assure this is the same proc that we think it is. */ if (rngentry->pst_timestamp.db_tab_high_time != dbp->db_created) { /* If not, bounce back to SCF to re-parse query. */ opx_vrecover(E_DB_ERROR, E_OP008F_RDF_MISMATCH, rdf_cb->rdf_error.err_code); } /* Save procedure stuff for later. */ parameterCount = dbp->db_parameterCount; rescolCount = dbp->db_rescolCount; resrowWidth = dbp->db_resrowWidth; created = dbp->db_created; STRUCT_ASSIGN_MACRO(dbp->db_dbpname, proc_name); STRUCT_ASSIGN_MACRO(dbp->db_owner, proc_owner); /* Allocate attr descriptors and address from ptr arrays. */ i = dbp->db_parameterCount + dbp->db_rescolCount; attarray = (DMT_ATT_ENTRY **) opu_memory(global, (sizeof(PTR) + sizeof(DMT_ATT_ENTRY)) * i + sizeof(PTR)); /* 1 extra ptr because array is 1-origin */ /* Set up attr pointer arrays for both parms and result columns. */ for (j = 1, attrp = (DMT_ATT_ENTRY *)&attarray[i+1], attarray[0] = (DMT_ATT_ENTRY *)NULL; j <= i; j++, attrp = &attrp[1]) { attarray[j] = attrp; MEfill(sizeof(DMT_ATT_ENTRY), (u_char)0, (char *)attrp); } rescarray = attarray; parmarray = &attarray[rescolCount+1]; /* Load iiprocedure_parameter rows for both parms and result cols. */ rdf_rb->rdr_types_mask = 0; rdf_rb->rdr_2types_mask = RDR2_PROCEDURE_PARAMETERS; rdf_rb->rdr_instr = RDF_NO_INSTR; rdf_rb->rdr_update_op = RDR_OPEN; rdf_rb->rdr_qrymod_id = 0; /* get all tuples */ rdf_rb->rdr_qtuple_count = 20; /* get 20 at a time */ rdf_cb->rdf_error.err_code = 0; /* ** must set rdr_rec_access_id since otherwise RDF will barf when we ** try to RDR_OPEN */ rdf_rb->rdr_rec_access_id = NULL; while (rdf_cb->rdf_error.err_code == 0) { status = rdf_call(RDF_READTUPLES, rdf_cb); rdf_rb->rdr_update_op = RDR_GETNEXT; /* Must not use DB_FAILURE_MACRO because E_RD0011 returns ** E_DB_WARN that would be missed. */ if (status != E_DB_OK) { switch(rdf_cb->rdf_error.err_code) { case E_RD0011_NO_MORE_ROWS: status = E_DB_OK; break; case E_RD0013_NO_TUPLE_FOUND: status = E_DB_OK; continue; default: opx_error(E_OP0013_READ_TUPLES); break; } /* switch */ } /* if status != E_DB_OK */ /* For each dbproc parameter tuple */ for (qp = rdf_cb->rdf_info_blk->rdr_pptuples->qrym_data, j = 0; j < rdf_cb->rdf_info_blk->rdr_pptuples->qrym_cnt; qp = qp->dt_next, j++) { DB_PROCEDURE_PARAMETER *param_tup = (DB_PROCEDURE_PARAMETER *) qp->dt_data; if (i-- == 0) { opx_error(E_OP0013_READ_TUPLES); } if (param_tup->dbpp_flags & DBPP_RESULT_COL) { attrp = rescarray[param_tup->dbpp_number]; attrp->att_number = param_tup->dbpp_number; } else { attrp = parmarray[param_tup->dbpp_number-1]; attrp->att_flags = DMT_F_TPROCPARM; attrp->att_number = param_tup->dbpp_number + dbp->db_rescolCount; } STRUCT_ASSIGN_MACRO(param_tup->dbpp_name, attrp->att_name); attrp->att_type = param_tup->dbpp_datatype; attrp->att_width = param_tup->dbpp_length; attrp->att_prec = param_tup->dbpp_precision; attrp->att_offset = param_tup->dbpp_offset; } } /* Reset result column offsets to remove effect of parms. */ offset = rescarray[1]->att_offset; for (j = 1; j <= rescolCount; j++) rescarray[j]->att_offset -= offset; if (rdf_rb->rdr_rec_access_id != NULL) { rdf_rb->rdr_update_op = RDR_CLOSE; status = rdf_call(RDF_READTUPLES, rdf_cb); if (DB_FAILURE_MACRO(status)) { opx_error(E_OP0013_READ_TUPLES); } } /* now unfix the dbproc description */ rdf_rb->rdr_types_mask = RDR_PROCEDURE | RDR_BY_NAME; rdf_rb->rdr_2types_mask = 0; rdf_rb->rdr_instr = RDF_NO_INSTR; status = rdf_call(RDF_UNFIX, rdf_cb); if (DB_FAILURE_MACRO(status)) { opx_error(E_OP008D_RDF_UNFIX); } /* Allocate table descriptor. */ tblptr = (DMT_TBL_ENTRY *) opu_memory(global, sizeof(DMT_TBL_ENTRY)); /* Allocate RDR_INFO block. */ rdrinfop = (RDR_INFO *) opu_memory(global, sizeof(RDR_INFO)+sizeof(PTR)); /* Now format them all. */ MEfill(sizeof(DMT_TBL_ENTRY), (u_char) 0, tblptr); MEcopy((char *)&proc_name.db_dbp_name, sizeof(DB_DBP_NAME), (char *)&tblptr->tbl_name.db_tab_name); STRUCT_ASSIGN_MACRO(proc_owner, tblptr->tbl_owner); MEfill(sizeof(DB_LOC_NAME), (u_char)' ', (char *)&tblptr->tbl_location); tblptr->tbl_attr_count = rescolCount + parameterCount; tblptr->tbl_width = resrowWidth; tblptr->tbl_date_modified.db_tab_high_time = created; tblptr->tbl_storage_type = DB_TPROC_STORE; /* Load cost parameters (if there). */ tblptr->tbl_record_count = (dbp->db_estRows) ? dbp->db_estRows : DBP_ROWEST; tblptr->tbl_page_count = (dbp->db_estCost) ? dbp->db_estCost : DBP_DIOEST; tblptr->tbl_pgsize = 2048; /* All the other DMT_TBL_ENTRY fields are being left 0 until ** something happens that suggests other values. */ /* Finally fill in the RDR_INFO structure. */ MEfill(sizeof(RDR_INFO), (u_char) 0, rdrinfop); rdrinfop->rdr_rel = tblptr; rdrinfop->rdr_attr = rescarray; rdrinfop->rdr_no_attr = tblptr->tbl_attr_count; rdrinfop->rdr_dbp = dbp; grvp->opv_relation = rdrinfop; BTset((i4)gvar, (char *)&global->ops_rangetab.opv_mrdf); /* indicate ** that RDF info is fixed */ grvp->opv_gmask |= OPV_TPROC; global->ops_gmask |= OPS_TPROCS; /* show query has table procs */ return (E_DB_OK); }
/*{ ** 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: opz_addatts - add joinop attributes to the array ** ** Description: ** This routine will add or find the appropriate joinop attribute ** to the joinop attributes array given the ** (range variable, range variable attribute number) pair. ** ** The exception to this rule occurs with function attributes which ** always results in a new joinop attribute being allocated. A ** function attribute uses "OPZ_FANUM" as the input attribute number. ** ** Inputs: ** subquery ptr to subquery being analyzed ** joinopvar index into joinop range table ** dmfattr dmf attribute number ** datatype ptr to ADT datatype ** ** Outputs: ** Returns: ** joinop attribute number ** Exceptions: ** none ** ** Side Effects: ** An entry may be allocated in the joinop attributes array ** (OPS_SUBQUERY->ops_attrs) ** ** History: ** 20-apr-86 (seputis) ** initial creation addatts ** 06-nov-89 (fred) ** Added support for non-histogrammable datatypes. This support ** entails providing a simple, coordinated replacement for this ** histogram. In our case, the datatype & length will be varchar, ** minimum & maximum being those for varchar, and adc_helem() ** replacement will be the histogram for "nohistos". ** ** This code is done in OPF as opposed to ADF because I think it ** desirable that OPF manage the histogram replacement. ADF will not ** know the appropriate values. Furthermore, it is more desirable to ** teach OPF to manage w/out histograms; however, that change is ** beyond the scope of this project. ** 26-dec-90 (seputis) ** init mask of booleans associated with attribute descriptor ** 18-jan-93 (rganski) ** Character Histogram Enhancements project: ** Initialize attr_ptr->opz_histogram.oph_dataval.db_length to 8 ** before call to adc_hg_dtln(), which has been changed; this gives ** old behavior (limit for char types was 8). This length may be ** changed later when RDF gets info from iistatistics. ** 24-may-93 (rganski) ** Character Histograms Enhancements project: ** Initialize attr_ptr->opz_histogram.oph_dataval.db_length to 0 ** before call to adc_hg_dtln(), which causes histogram value to be ** same length as attribute. This is necessary because otherwise the ** histogram value, which is used to determine selectivity of boolean ** factors, is truncated; this removes the benefits of having long ** histogram values. The histogram value length is adjusted in ** oph_catalog(), which reads the actual length from iistatistics (or ** truncates to 8, for old histograms). ** 6-dec-93 (ed) ** - bug 56139 - project union view before it is instantiated ** 09-oct-98 (matbe01) ** Added NO_OPTIM for NCR to eliminate runtime error that produces the ** message: E_OP0889 Eqc is not available at a CO node. ** 6-sep-04 (hayke02) ** Add OPZ_COLLATT attribute if OPS_COLLATION is on (resdom list, ** collation sequence). This change fixes problem INGSRV 2940, bug ** 112873. ** 8-nov-05 (hayke02) ** Add OPZ_VAREQVAR if OPS_VAREQVAR is on. Return if OPZ_VAREQVAR is ** not set and the attribute is found. This ensures that the fix for ** 110675 is limited to OPZ_VAREQVAR PST_BOP nodes. This change ** fixes bug 115420 problem INGSRV 3465. ** 15-aug-2007 (huazh01) ** ensure the datatype/length info of a created attribute matches ** the corresponding column type/length info on the user table. ** bug 117316. ** [@history_line@]... */ OPZ_IATTS opz_addatts( OPS_SUBQUERY *subquery, OPV_IVARS joinopvar, OPZ_DMFATTR dmfattr, DB_DATA_VALUE *datatype) { OPZ_IATTS attribute; /* used to save joinop attribute ** number if found by opz_findatt */ RDR_INFO *rel; /* FIXME - need to deal with unsigned compare problem here */ if ( (dmfattr != OPZ_FANUM) && (dmfattr != OPZ_SNUM) && (dmfattr != OPZ_CORRELATED) && (attribute = opz_findatt(subquery, joinopvar, dmfattr)) >= 0 && !(subquery->ops_attrs.opz_base->opz_attnums[attribute]->opz_mask & OPZ_VAREQVAR && subquery->ops_mask2 & OPS_COLLATION && (abs(datatype->db_datatype) == DB_CHA_TYPE || abs(datatype->db_datatype) == DB_VCH_TYPE || abs(datatype->db_datatype) == DB_CHR_TYPE || abs(datatype->db_datatype) == DB_TXT_TYPE)) ) return( attribute ); /* if this (joinopvar,dmfattr) pair ** exists and is not a function ** attribute, or subselect attribute ** then return */ { /* create new joinop attribute */ OPZ_IATTS nextattr; /* next available joinop attribute ** number */ OPS_STATE *global; /* ptr to global state variable */ global = subquery->ops_global; /* get ptr to global state variable */ if ( (nextattr = subquery->ops_attrs.opz_av++) >= OPZ_MAXATT) opx_error(E_OP0300_ATTRIBUTE_OVERFLOW); /* exit with error ** if no more room in attributes ** array */ { OPZ_ATTS *attr_ptr; /* ptr to newly created joinop ** attribute element */ OPV_VARS *var_ptr; /* ptr to respective joinop ** variable containing attribute */ var_ptr = subquery->ops_vars.opv_base->opv_rt[joinopvar]; /* allocate new joinop attribute structure and initialize */ subquery->ops_attrs.opz_base->opz_attnums[nextattr] = attr_ptr = (OPZ_ATTS *) opu_memory( global, (i4) sizeof(OPZ_ATTS)); MEfill(sizeof(*attr_ptr), (u_char)0, (PTR)attr_ptr); /* b117316: ** ** the fix to b109879 set PST_VAR node under the RESDOM to ** to nullable and increase the db_length by one, though ** the column corresponds to PST_VAR is defined as not null. ** Need to reset them to not null in order to prevent ** wrong db_type and db_length being used during query ** execution. e.g., wrong db_type and db_length could cause OPC ** to generate a set of ADF code which materialize tuples ** using incorrect offset and cause wrong result. ** */ rel = var_ptr->opv_grv->opv_relation; if (datatype->db_datatype < 0 && dmfattr > 0 && rel && rel->rdr_attr[dmfattr]->att_type * -1 == datatype->db_datatype && rel->rdr_attr[dmfattr]->att_width + 1 == datatype->db_length) { datatype->db_datatype = rel->rdr_attr[dmfattr]->att_type; datatype->db_length = rel->rdr_attr[dmfattr]->att_width; } attr_ptr->opz_varnm = joinopvar; /* index into local range table */ BTset( (i4)nextattr, (char *)&var_ptr->opv_attrmap); /* indicate ** that this joinop ** attribute belongs to this range ** variable */ attr_ptr->opz_gvar = var_ptr->opv_gvar; /* move global range ** variable number to attribute */ attr_ptr->opz_attnm.db_att_id = dmfattr;/* dmf attribute of ** range variable */ STRUCT_ASSIGN_MACRO((* datatype), attr_ptr->opz_dataval); attr_ptr->opz_equcls = OPE_NOEQCLS; /* this attribute has not been ** assigned an equivalence class yet */ attr_ptr->opz_func_att = OPZ_NOFUNCATT; /* this indicates that a ** function attribute is not ** associated with this joinop ** attribute element */ if ((dmfattr != OPZ_FANUM) && (dmfattr != OPZ_SNUM) && (dmfattr != OPZ_CORRELATED) && (attribute >= 0)) attr_ptr->opz_mask |= OPZ_COLLATT; if (subquery->ops_mask2 & OPS_VAREQVAR) attr_ptr->opz_mask |= OPZ_VAREQVAR; if ((var_ptr->opv_grv->opv_gmask & OPV_UVPROJECT) && (dmfattr >= 0)) { /* if union view exists in which a projection is possible ** (i.e. a UNION ALL view) then ** mark the attribute which are referenced in the union view*/ BTset((i4)dmfattr, (char *)var_ptr->opv_grv->opv_attrmap); } { /* initialize the histogram information associated with this ** attribute */ DB_STATUS hgstatus; /* ADT return status */ i4 dt_bits; hgstatus = adi_dtinfo(global->ops_adfcb, datatype->db_datatype, &dt_bits); if ((hgstatus == E_AD0000_OK) && (dt_bits & AD_NOHISTOGRAM)) { attr_ptr->opz_histogram.oph_dataval.db_datatype = OPH_NH_TYPE; attr_ptr->opz_histogram.oph_dataval.db_prec = OPH_NH_PREC; attr_ptr->opz_histogram.oph_dataval.db_length = OPH_NH_LENGTH; } else { attr_ptr->opz_histogram.oph_dataval.db_length = 0; hgstatus = adc_hg_dtln(global->ops_adfcb, datatype, &attr_ptr->opz_histogram.oph_dataval ); } # ifdef E_OP0780_ADF_HISTOGRAM if ((hgstatus != E_AD0000_OK) || (attr_ptr->opz_histogram.oph_dataval.db_datatype < 0) /* do not expect a nullable ** histogram datatype */ ) opx_verror( hgstatus, E_OP0780_ADF_HISTOGRAM, global->ops_adfcb->adf_errcb.ad_errcode); /* ** abort if unexpected error occurs */ # endif attr_ptr->opz_histogram.oph_dataval.db_data = NULL; /* init the ** data value ptr */ attr_ptr->opz_histogram.oph_numcells = OPH_NOCELLS; /* histogram ** has not been fetched so no cells ** exist at this point */ attr_ptr->opz_histogram.oph_histmm = NULL; /* no histogram ** exists at this point */ attr_ptr->opz_histogram.oph_mindomain = NULL; /* no minimum ** value for this histogram yet */ attr_ptr->opz_histogram.oph_maxdomain = NULL; /* no maximum ** value for this histogram yet */ } if (dmfattr == DB_IMTID) { /* implicit TID found so an equivalence class needs to be ** created ... so we know the equivalence class of the ** implicit TID for this (since the implicit TID is referenced ** by an index or explicitly in the qualification) */ var_ptr->opv_primary.opv_tid = ope_neweqcls( subquery, nextattr ); } } return( nextattr ); /* return joinop attribute which has ** been assigned */ } }
/*{ ** Name: opa_checkopt - check for possibility of optimization ** ** Description: ** This routine will create variable map of what the outer aggregate ** would appear like if all possible substitutions of the inner aggregate ** bylist attributes were made. The map of the all the inner aggregate ** bylist elements used in the substitution is also created. Thus, if ** no substitutions are made then this map would be empty. ** ** Inputs: ** global global state variable ** root root of query tree which will ** be analyzed for possible substitutions ** - this root is a subtree of the outer ** aggregate. ** bylist base of bylist which will be ** used for substitutions ** ** Outputs: ** usedmap ptr to map of all variables found ** in the "hit list" i.e. map of variables ** which will be replaced if the ** substitution were actually made ** newmap ptr to varmap of root, filled in if ** the substitutions were actually made ** Returns: ** varmap of root if the substitutions were actually made ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 15-apr-86 (seputis) ** initial creation ** 4-dec-02 (inkdo01) ** Return variable changed to a call-by-ref parm to support ** range table expansion. [@history_line@]... */ static VOID opa_checkopt( OPS_STATE *global, PST_QNODE *bylist, PST_QNODE *root, OPV_GBMVARS *usedmap, OPV_GBMVARS *newmap) { PST_QNODE *node; /* ptr to current by list element being ** substituted */ MEfill(sizeof(*newmap), 0, (char *)newmap); /* init the bit map */ if ( root ) { for(node = bylist; /* get first element of by list */ node && node->pst_sym.pst_type != PST_TREE; /* at the end of ** the bylist ? */ node = node->pst_left) /* get next bydom attribute */ { if ( opv_ctrees( global, root, node->pst_right ) ) { opv_mapvar( root, usedmap); /* update map of global range ** variables used in subtree */ return; /* substitution is made so no vars will ** be contributed to the map of the tree ** after substitution i.e. return 0 */ } } /* try the subtrees if none of the bylist elements matched */ { OPV_GBMVARS tempmap; /* used to create bit map for var node*/ if (root->pst_sym.pst_type == PST_VAR) { OPV_GRV *gvarp; /* global range var associated ** with this node */ BTset( (i4)root->pst_sym.pst_value.pst_s_var.pst_vno, (char *)newmap); gvarp = global->ops_rangetab.opv_base-> opv_grv[root->pst_sym.pst_value.pst_s_var.pst_vno]; if (gvarp->opv_gsubselect && gvarp->opv_gsubselect->opv_subquery) { MEcopy((char *)&gvarp->opv_gsubselect->opv_subquery-> ops_correlated, sizeof(tempmap), (char *)&tempmap); BTor(OPV_MAXVAR, (char *)&gvarp->opv_gsubselect->opv_subquery-> ops_fcorelated, (char *)&tempmap); if (BTcount((char *)&tempmap, OPV_MAXVAR)) { /* if a correlated subquery is referenced then the ** correlated variables cannot be substituted ** - FIXME need to find all correlated vars, and see if ** enough attributes exist to supply correlated values ** this can be done by running opa_subselect in OPAFINAL.C ** prior to this optimization ** ... also need to make sure the correlated subquery ** is not used in another context as determined by ** opa_compat ** - for now do not substitute correlated variables */ BTor( (i4)BITS_IN(OPV_GBMVARS), (char *) &gvarp->opv_gsubselect->opv_subquery->ops_correlated, (char *)newmap); BTor( (i4)BITS_IN(OPV_GBMVARS), (char *) &gvarp->opv_gsubselect->opv_subquery->ops_fcorelated, (char *)newmap); } } return; /* return map with bit set for this var ** since a substition will not be made, so ** the var will appear in the optimized tree ** if it is created */ } else { opa_checkopt( global, bylist, root->pst_left, usedmap, newmap); MEcopy((char *)newmap, sizeof(tempmap), (char *)&tempmap); BTand(OPV_MAXVAR, (char *)usedmap, (char *)&tempmap); if (BTcount((char *)&tempmap, OPV_MAXVAR)) /* if the maps have an intersection then abort the search ** since there will not be a commit made */ return; /* traverse the right side of the tree */ opa_checkopt( global, bylist, root->pst_right,usedmap, &tempmap); BTor(OPV_MAXVAR, (char *)&tempmap, (char *)newmap); return; } } } MEfill(sizeof(*newmap), 0, (char *)newmap); /* return empty map */ return; }
/* And now the real thing */ static VOID opc_exnodearrcnt( OPS_STATE *global, QEN_NODE *node, i4 *arrcnts, PTR rowmap) { QEN_OJINFO *ojinfop; QEN_PART_INFO *partp; QEN_PART_QUAL *pqual; QEN_SJOIN *sjnp; QEN_KJOIN *kjnp; QEN_TJOIN *tjnp; QEN_HJOIN *hjnp; QEN_SEJOIN *sejnp; QEN_SORT *srtp; QEN_TPROC *tprocp; QEN_TSORT *tsrtp; QEN_ORIG *origp; QEN_QP *qpp; QEN_EXCH *exchp; QEF_QP_CB *qp = global->ops_cstate.opc_qp; QEF_RESOURCE *resp; QEF_VALID *vlp; QEF_AHD *act; i4 i, j, k; i4 dmrix; bool endloop; /* Loop (recurse on left, iterate on right) and switch to process ** each node in subtree. */ for ( ; ; ) { if (node == (QEN_NODE *) NULL) return; /* just in case */ opc_exnheadcnt(global, node, arrcnts, rowmap); /* count node header indexes */ ojinfop = (QEN_OJINFO *) NULL; partp = (QEN_PART_INFO *) NULL; pqual = NULL; dmrix = -1; switch (node->qen_type) { case QE_CPJOIN: case QE_FSMJOIN: case QE_ISJOIN: sjnp = &node->node_qen.qen_sjoin; ojinfop = sjnp->sjn_oj; if (sjnp->sjn_krow >= 0) BTset(sjnp->sjn_krow, rowmap); if (sjnp->sjn_hfile >= 0) arrcnts[IX_HLD]++; if (sjnp->sjn_itmat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sjnp->sjn_okmat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sjnp->sjn_okcompare != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sjnp->sjn_joinkey != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sjnp->sjn_jqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; opc_exnodearrcnt(global, sjnp->sjn_out, arrcnts, rowmap); node = sjnp->sjn_inner; break; case QE_KJOIN: kjnp = &node->node_qen.qen_kjoin; ojinfop = kjnp->kjoin_oj; partp = kjnp->kjoin_part; pqual = kjnp->kjoin_pqual; if ((dmrix = kjnp->kjoin_get) >= 0) arrcnts[IX_DMR]++; if (kjnp->kjoin_krow >= 0) BTset(kjnp->kjoin_krow, rowmap); if (kjnp->kjoin_key != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (kjnp->kjoin_kqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (kjnp->kjoin_kcompare != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (kjnp->kjoin_iqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (kjnp->kjoin_jqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; node = kjnp->kjoin_out; break; case QE_TJOIN: tjnp = &node->node_qen.qen_tjoin; ojinfop = tjnp->tjoin_oj; partp = tjnp->tjoin_part; pqual = tjnp->tjoin_pqual; if ((dmrix = tjnp->tjoin_get) >= 0) arrcnts[IX_DMR]++; if (tjnp->tjoin_orow >= 0) BTset(tjnp->tjoin_orow, rowmap); if (tjnp->tjoin_irow >= 0) BTset(tjnp->tjoin_irow, rowmap); if (tjnp->tjoin_jqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (tjnp->tjoin_isnull != (QEN_ADF *) NULL) arrcnts[IX_CX]++; node = tjnp->tjoin_out; break; case QE_HJOIN: hjnp = &node->node_qen.qen_hjoin; ojinfop = hjnp->hjn_oj; pqual = hjnp->hjn_pqual; arrcnts[IX_HSH]++; if (hjnp->hjn_brow >= 0) BTset(hjnp->hjn_brow, rowmap); /* prow is probably already counted as qen_row but make sure */ if (hjnp->hjn_prow >= 0) BTset(hjnp->hjn_prow, rowmap); if (hjnp->hjn_dmhcb >= 0) arrcnts[IX_DMH]++; if (hjnp->hjn_btmat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (hjnp->hjn_ptmat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (hjnp->hjn_jqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; opc_exnodearrcnt(global, hjnp->hjn_out, arrcnts, rowmap); node = hjnp->hjn_inner; break; case QE_SEJOIN: sejnp = &node->node_qen.qen_sejoin; ojinfop = (QEN_OJINFO *) NULL; partp = (QEN_PART_INFO *) NULL; /* if (sejnp->sejn_hget >= 0) - these aren't ref'ed in QEF arrcnts[IX_DMR]++; */ if (sejnp->sejn_hfile >= 0) arrcnts[IX_HLD]++; if (sejnp->sejn_itmat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sejnp->sejn_ccompare != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sejnp->sejn_oqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sejnp->sejn_okmat != NULL) arrcnts[IX_CX]++; if (sejnp->sejn_kcompare != NULL) arrcnts[IX_CX]++; if (sejnp->sejn_kqual != NULL) arrcnts[IX_CX]++; opc_exnodearrcnt(global, sejnp->sejn_out, arrcnts, rowmap); node = sejnp->sejn_inner; break; case QE_TSORT: tsrtp = &node->node_qen.qen_tsort; pqual = tsrtp->tsort_pqual; if (tsrtp->tsort_get >= 0) arrcnts[IX_DMR]++; if (tsrtp->tsort_load >= 0) arrcnts[IX_DMR]++; if (tsrtp->tsort_create >= 0) arrcnts[IX_DMT]++; if (tsrtp->tsort_shd >= 0) arrcnts[IX_SHD]++; if (tsrtp->tsort_mat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; node = tsrtp->tsort_out; break; case QE_SORT: srtp = &node->node_qen.qen_sort; if (srtp->sort_load >= 0) arrcnts[IX_DMR]++; if (srtp->sort_create >= 0) arrcnts[IX_DMT]++; if (srtp->sort_shd >= 0) arrcnts[IX_SHD]++; if (srtp->sort_mat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; node = srtp->sort_out; break; case QE_ORIG: case QE_ORIGAGG: origp = &node->node_qen.qen_orig; if ((dmrix = origp->orig_get) >= 0) { arrcnts[IX_DMR]++; } partp = origp->orig_part; pqual = origp->orig_pqual; if (origp->orig_qual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; node = (QEN_NODE *) NULL; break; case QE_QP: qpp = &node->node_qen.qen_qp; if (qpp->qp_qual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; /* Process action headers anchored in QP node. */ for (act = node->node_qen.qen_qp.qp_act; act; act = act->ahd_next) opc_exactarrcnt(global, act, arrcnts, rowmap); return; case QE_EXCHANGE: exchp = &node->node_qen.qen_exch; if (exchp->exch_mat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; /* Don't probe below 1:N exchanges, they'll do their own setup. ** 1:1 exchange depends on parent, so keep going. */ if (exchp->exch_ccount > 1) return; node = exchp->exch_out; break; case QE_TPROC: tprocp = &node->node_qen.qen_tproc; if (tprocp->tproc_parambuild != NULL) arrcnts[IX_CX]++; if (tprocp->tproc_qual != NULL) arrcnts[IX_CX]++; return; /* Nothing else interesting */ default: TRdisplay("Unexpected QP node type %d under exch\n",node->qen_type); opx_error(E_OP068E_NODE_TYPE); } /* end of switch */ /* Node specific bits have been set - now go over OJ and ** partition stuff (if any). */ if (ojinfop) { if (ojinfop->oj_heldTidRow >= 0) BTset(ojinfop->oj_heldTidRow, rowmap); if (ojinfop->oj_ijFlagsRow >= 0) BTset(ojinfop->oj_ijFlagsRow, rowmap); if (ojinfop->oj_resultEQCrow >= 0) BTset(ojinfop->oj_resultEQCrow, rowmap); if (ojinfop->oj_specialEQCrow >= 0) BTset(ojinfop->oj_specialEQCrow, rowmap); if (ojinfop->oj_tidHoldFile >= 0) arrcnts[IX_HLD]++; if (ojinfop->oj_ijFlagsFile >= 0) arrcnts[IX_HLD]++; if (ojinfop->oj_innerJoinedTIDs) arrcnts[IX_TTAB]++; if (ojinfop->oj_oqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (ojinfop->oj_equal != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (ojinfop->oj_lnull != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (ojinfop->oj_rnull != (QEN_ADF *) NULL) arrcnts[IX_CX]++; } if (partp) { if (partp->part_groupmap_ix >= 0) BTset(partp->part_groupmap_ix, rowmap); if (partp->part_ktconst_ix >= 0) BTset(partp->part_ktconst_ix, rowmap); if (partp->part_knokey_ix >= 0) BTset(partp->part_knokey_ix, rowmap); } /* Part-qual structures contain mini-programs that must be ** scanned to gather up row numbers. */ if (pqual != NULL) { QEN_PQ_EVAL *pqe; arrcnts[IX_PQUAL]++; if (pqual->part_constmap_ix >= 0) BTset(pqual->part_constmap_ix, rowmap); if (pqual->part_lresult_ix >= 0) BTset(pqual->part_lresult_ix, rowmap); if (pqual->part_work1_ix >= 0) BTset(pqual->part_work1_ix, rowmap); pqe = pqual->part_const_eval; if (pqe != NULL) opc_arrcnt_pqe(pqe, arrcnts, rowmap); pqe = pqual->part_join_eval; if (pqe != NULL) opc_arrcnt_pqe(pqe, arrcnts, rowmap); } /* If TJOIN, KJOIN or ORIG, locate DMT_CB index in valid's. */ if (dmrix >= 0) for (resp = qp->qp_resources, endloop = FALSE; resp && !endloop; resp = resp->qr_next) if (resp->qr_type == QEQR_TABLE) for (vlp = resp->qr_resource.qr_tbl.qr_lastvalid; vlp && !endloop; vlp = vlp->vl_next) if (dmrix == vlp->vl_dmr_cb) { arrcnts[IX_DMT]++; endloop = TRUE; if (vlp->vl_partition_cnt > 1) arrcnts[IX_DMR]++; /* set master DMR_CB, too */ } if (node == (QEN_NODE *) NULL) return; } /* end of for ( ; ; ) */ }
/*{ ** 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); } }
/*{ ** Name: opl_ioutjoin - init outer join IDs for this subquery ** ** Description: ** This routine will initialize an outer join descriptor for ** this subquery. Note that a PSF var may be referenced in several ** subqueries due to link backs in aggregates, etc. but only ** one of these subqueries would contain the outer join ID. So ** that OPF delays inserting the join ID into the ** subquery until a parse tree node is discovered which references ** the join ID. ** ** Inputs: ** subquery subquery which contains outer ** join ** varp ptr to range table entry ** containing the outer join var ** ** Outputs: ** ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 14-sep-89 (seputis) ** initial creation ** 15-feb-93 (ed) ** fix outer join placement bug 49317 ** 19-jan-94 (ed) ** remove obsolete structures ** 15-sep-00 (inkdo01) ** Init opl_translate to NOINIT to distinguish from legitimate ** parent of OPL_NOOUTER (to fix bug 87175). [@history_line@]... [@history_template@]... */ OPL_IOUTER opl_ioutjoin( OPS_SUBQUERY *subquery, PST_J_ID joinid) { OPS_STATE *global; OPL_OUTER *outjoinp; global = subquery->ops_global; { /* this ID has not been added to the current set */ i4 ojsize; /* size of structures needed to support ** the outer join descriptor */ ojsize = sizeof(*outjoinp) + sizeof(*outjoinp->opl_innereqc) + sizeof(*outjoinp->opl_ojtotal) + sizeof(*outjoinp->opl_onclause) + sizeof(*outjoinp->opl_ovmap) + sizeof(*outjoinp->opl_ivmap) + sizeof(*outjoinp->opl_bvmap) + sizeof(*outjoinp->opl_ojbvmap) + sizeof(*outjoinp->opl_idmap) + sizeof(*outjoinp->opl_bfmap) + sizeof(*outjoinp->opl_minojmap) + sizeof(*outjoinp->opl_maxojmap) + sizeof(*outjoinp->opl_ojattr) + sizeof(*outjoinp->opl_reqinner); outjoinp = (OPL_OUTER *)opu_memory(global, ojsize); MEfill(ojsize, (u_char)0, (PTR)outjoinp); outjoinp->opl_innereqc = (OPE_BMEQCLS *)&outjoinp[1]; /* ptr to set of equivalence classes ** which represent all the inner relations ** to this outer join */ outjoinp->opl_id = subquery->ops_oj.opl_lv++; /* query tree ID associated with this ** outer join, which should be found in ** the op node of any qualification */ outjoinp->opl_gid = joinid; /* global ID used in query tree produced ** by parser, this is different from opl_id ** since opl_id is local to the subquery ** and the op nodes were renamed to ** be opl_id */ outjoinp->opl_ojtotal= (OPV_BMVARS *)&outjoinp->opl_innereqc[1]; /* map of ** all variables which are considered ** "inner" to this join ID, this map is used ** for legal placement of variables, this ** includes all secondaries etc. */ outjoinp->opl_onclause = (OPV_BMVARS *)&outjoinp->opl_ojtotal[1]; ; outjoinp->opl_ovmap = (OPV_BMVARS *)&outjoinp->opl_onclause[1]; outjoinp->opl_ivmap = (OPV_BMVARS *)&outjoinp->opl_ovmap[1]; outjoinp->opl_bvmap = (OPV_BMVARS *)&outjoinp->opl_ivmap[1]; outjoinp->opl_ojbvmap = (OPV_BMVARS *)&outjoinp->opl_bvmap[1]; outjoinp->opl_idmap = (OPL_BMOJ *)&outjoinp->opl_ojbvmap[1]; outjoinp->opl_bfmap = (OPB_BMBF *)&outjoinp->opl_idmap[1]; outjoinp->opl_minojmap = (OPV_BMVARS *)&outjoinp->opl_bfmap[1]; outjoinp->opl_maxojmap = (OPV_BMVARS *)&outjoinp->opl_minojmap[1]; outjoinp->opl_ojattr = (OPZ_BMATTS *)&outjoinp->opl_maxojmap[1]; outjoinp->opl_reqinner = (OPL_BMOJ *)&outjoinp->opl_ojattr[1]; outjoinp->opl_type = OPL_UNKNOWN; /* type of outer join will ** be determined later */ outjoinp->opl_mask = 0; /* mask of various booleans */ outjoinp->opl_translate = OPL_NOINIT; if ((subquery->ops_oj.opl_lv >= OPL_MAXOUTER) || (joinid > global->ops_goj.opl_glv)) opx_error(E_OP038E_MAXOUTERJOIN); subquery->ops_oj.opl_base->opl_ojt[outjoinp->opl_id] = outjoinp; if (joinid == PST_NOJOIN) return (outjoinp->opl_id); /* in the case of TID joins this routine ** would be called to create a new joinid ** descriptor for a left join */ if (joinid > global->ops_goj.opl_glv) opx_error(E_OP0395_PSF_JOINID); /* join id is out of range */ if (global->ops_goj.opl_gbase->opl_gojt[joinid] != OPL_NOOUTER) opx_error(E_OP038F_OUTERJOINSCOPE); /* not expecting another translation ** for the same outer join */ BTset( (i4)joinid, (char *)&subquery->ops_oj.opl_jmap); /* mark ** outer join ID as being processed */ global->ops_goj.opl_gbase->opl_gojt[joinid] = outjoinp->opl_id; } { /* traverse range table and check for any variables which reference ** this join id */ OPV_IVARS varno; OPV_RT *vbase; /* ptr to base of array of ptrs ** to joinop variables */ OPL_PARSER *pouter; OPL_PARSER *pinner; vbase = subquery->ops_vars.opv_base; /* init ptr to base of array of ** ptrs to joinop variables */ pouter = subquery->ops_oj.opl_pouter; pinner = subquery->ops_oj.opl_pinner; for (varno = subquery->ops_vars.opv_rv; --varno>=0;) { OPV_VARS *varp; varp = vbase->opv_rt[varno]; if (varp->opv_grv /* && (varp->opv_grv->opv_gmask & OPV_GOJVAR) OPV_GOJVAR is not reliably set */ ) { OPV_IGVARS gvar; gvar = varp->opv_gvar; /* outer join semantics are defined with ** the primary */ if (BTtest((i4)joinid, (char *)&pouter->opl_parser[gvar])) { /* found variable which is an outer to this join id */ if (!varp->opv_ojmap) { /* allocate a bit map for the outer join */ varp->opv_ojmap = (OPL_BMOJ *)opu_memory(global, (i4)sizeof(*varp->opv_ojmap)); MEfill(sizeof(*varp->opv_ojmap), (u_char)0, (PTR)varp->opv_ojmap); } BTset((i4)outjoinp->opl_id, (char *)varp->opv_ojmap); } if (BTtest((i4)joinid, (char *)&pinner->opl_parser[gvar])) { /* found variable which is an inner to this join id */ if (!varp->opv_ijmap) { /* allocate a bit map for the outer join */ varp->opv_ijmap = (OPL_BMOJ *)opu_memory(global, (i4)sizeof(*varp->opv_ijmap)); MEfill(sizeof(*varp->opv_ijmap), (u_char)0, (PTR)varp->opv_ijmap); } BTset((i4)outjoinp->opl_id, (char *)varp->opv_ijmap); BTset((i4)varno, (char *)outjoinp->opl_ojtotal); } } } } return (outjoinp->opl_id); }
/*{ ** 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: 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 */ } } }