/*{ ** Name: psq_jrel_dmp - Dump inner and outer relation mask ** ** Description: ** This function dumps inner and outer relation mask in a somewhat ** readable form. ** ** Inputs: ** inner_rel ptr to the inner relation mask ** outer_rel ptr to the outer relation mask ** ** Outputs: ** None ** Returns: ** E_DB_OK Success ** E_DB_ERROR Non-catastrophic failure ** E_DB_FATAL Catastrophic failure ** Exceptions: ** none ** ** Side Effects: ** Sends output to terminal and/or log file. ** ** History: ** 19-sep-89 (andre) ** written */ DB_STATUS psq_jrel_dmp( PST_J_MASK *inner_rel, PST_J_MASK *outer_rel) { char inner_buf[sizeof(PST_J_MASK) * BITSPERBYTE + 1]; char outer_buf[sizeof(PST_J_MASK) * BITSPERBYTE + 1]; register i4 i; register char *cur_inner = inner_buf; register char *cur_outer = outer_buf; for (i = sizeof(PST_J_MASK) * BITSPERBYTE - 1; i >= 0; i--, cur_inner++, cur_outer++) { *cur_inner = (BTtest(i, (char *) inner_rel)) ? '1' : '0'; *cur_outer = (BTtest(i, (char *) outer_rel)) ? '1' : '0'; } *cur_inner = *cur_outer = '\0'; if (TRdisplay("\n\t\t\tInner relation mask: %s", inner_buf) != TR_OK) { return (E_DB_ERROR); } if (TRdisplay("\n\t\t\tOuter relation mask: %s", outer_buf) != TR_OK) { return (E_DB_ERROR); } return (E_DB_OK); }
/*{ ** Name: opn_impliedtid - check for implied TID join between two relations ** ** Description: ** Check if there is an implied TID join between the two subtrees ** ** Inputs: ** subquery ptr to subquery being analyzed ** lp ptr to left join subtree ** rp ptr to right join subtree ** eqcls joining equivalence class to test ** ** Outputs: ** lfflag TRUE if left subtree "rp" contains ** the implicit TID ** Returns: ** TRUE - if there is an implied TID join between the two relations ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 30-may-86 (seputis) ** initial creation [@history_line@]... */ bool opn_imtidjoin( OPS_SUBQUERY *subquery, OPN_JTREE *lp, OPN_JTREE *rp, OPE_IEQCLS eqcls, bool *lfflag) { OPE_EQCLIST *eqclsp; /* ptr to joining equivalence class */ OPZ_BMATTS *amap; /* ptr to attribute map for ** this equivalence class */ OPZ_IATTS attr; /* current attribute of equivalence ** class being analyzed */ OPZ_IATTS maxattr; /* number of joinop attributes ** defined */ OPZ_AT *abase; /* ptr to base of array of ptrs ** to joinop attributes */ bool rightleaf; /* TRUE if right subtree is a leaf */ bool leftleaf; /* TRUE if left subtree is a leaf */ eqclsp = subquery->ops_eclass.ope_base->ope_eqclist[eqcls]; /* get ptr to ** joining equivalence class */ if (eqclsp->ope_eqctype != OPE_TID) /* is this a TID equivalence class*/ return (FALSE); amap = &eqclsp->ope_attrmap; /* get equivalence class attribute ** map */ maxattr = subquery->ops_attrs.opz_av; /* maximum number of joinop ** attributes defined */ abase = subquery->ops_attrs.opz_base; /* ptr to base of array of ptrs ** to joinop attributes */ leftleaf = (lp->opn_nleaves == 1); /* TRUE if the left subtree is a ** leaf */ rightleaf = (rp->opn_nleaves == 1); /* TRUE if the right subtree is a ** leaf */ for (attr = -1; (attr = BTnext((i4)attr, (char *)amap, (i4)maxattr)) >= 0;) { OPZ_ATTS *attrp; /* ptr to joinop attribute element*/ attrp = abase->opz_attnums[attr]; /* if on one side the TID is implied, then on the other side ** the TID must be explicit */ if ((*lfflag = ( (leftleaf) && BTtest((i4)attrp->opz_varnm, (char *)&lp->opn_rlmap) && attrp->opz_attnm.db_att_id == DB_IMTID)) || ( (rightleaf) && BTtest((i4)attrp->opz_varnm, (char *)&rp->opn_rlmap) && attrp->opz_attnm.db_att_id == DB_IMTID)) return (TRUE); } return (FALSE); }
DB_STATUS adi_ficoerce( ADF_CB *adf_scb, DB_DT_ID adi_from_did, DB_DT_ID adi_into_did, ADI_FI_ID *adi_fid) { DB_DT_ID from_bdt = abs(adi_from_did); DB_DT_ID into_bdt = abs(adi_into_did); DB_DT_ID minto_bdt; DB_DT_ID mfrom_bdt; ADI_DATATYPE *dt; ADI_COERC_ENT *cent; i4 found = FALSE; mfrom_bdt = ADI_DT_MAP_MACRO(from_bdt); minto_bdt = ADI_DT_MAP_MACRO(into_bdt); for (;;) { /* Check the validity of the datatype ids */ if ( (mfrom_bdt <= 0 || mfrom_bdt > ADI_MXDTS) || (minto_bdt <= 0 || minto_bdt > ADI_MXDTS) || Adf_globs->Adi_dtptrs[minto_bdt] == NULL || (dt = Adf_globs->Adi_dtptrs[mfrom_bdt]) == NULL ) { return (adu_error(adf_scb, E_AD2004_BAD_DTID, 0)); } /* First check the datatypes table to see if a coercion exists */ if (!BTtest((i4) minto_bdt, adf_scb->adf_qlang == DB_SQL ? (char *) &dt->adi_dtcoerce_sql : (char *) &dt->adi_dtcoerce_quel)) return (adu_error(adf_scb, E_AD2009_NOCOERCION, 0)); /* Search for the appropriate function instance id */ for (cent = dt->adi_coerc_ents; cent->adi_from_dt == from_bdt; cent++) { if (cent->adi_into_dt == into_bdt) { *adi_fid = cent->adi_fid_coerc; found = TRUE; break; } } break; } /* end of for(;;) stmt */ if (!found) return (adu_error(adf_scb, E_AD2009_NOCOERCION, 0)); else return (E_DB_OK); }
/*{ ** Name: opj_uboolfact - copy boolean factors into all parts of the union ** ** Description: ** A previous pass of all the subqueries determined the equivalence classes ** and that some conjuncts could be evaluated in the union view instead of ** the parent query. This routine will attach this qualification list to ** all parts of the union. ** ** Inputs: ** subquery ptr to beginning of union list ** ** Outputs: ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 28-jun-89 (seputis) ** initial creation ** 7-dec-93 (ed) ** b56139 - remove unused attributes from union views, i.e. ** create a projection ** 01-Oct-2001 (hanal04) Bug 105464 INGSRV 1517 ** After calling opv_copytree() or opj_subboolfact() mark the ** target subquery as non-CNF if the source subquery was non-CNF. [@history_template@]... */ VOID opj_uboolfact( OPS_SUBQUERY *subquery) { OPS_SUBQUERY *unionp; OPV_GRV *grvp; for (unionp = subquery->ops_next; (unionp->ops_sqtype == OPS_UNION) && unionp->ops_union; unionp = unionp->ops_next) ; /* search for the first union ** element of the list */ subquery = unionp; grvp = subquery->ops_global->ops_rangetab.opv_base->opv_grv[subquery->ops_gentry]; if (( !subquery->ops_sunion.opu_qual && !(grvp->opv_gmask & OPV_UVPROJECT) ) || (subquery->ops_sunion.opu_mask & OPU_PROCESSED)) return; /* return if no qualifications ** exist to propagate */ subquery->ops_sunion.opu_mask |= OPU_PROCESSED; for (; unionp; unionp=unionp->ops_union) { PST_QNODE *qual; qual = subquery->ops_sunion.opu_qual; if (qual) { PST_QNODE **qualpp; if (unionp->ops_union) /* if there is still another union then make a copy ** of the query tree, so that the original can be used ** for subsequent unions */ opv_copytree(subquery->ops_global, &qual); qualpp = opj_subboolfact(unionp, &qual); unionp->ops_sunion.opu_mask |= OPU_EQCLS; if(subquery->ops_mask & OPS_NONCNF) { unionp->ops_mask |= OPS_NONCNF; } #if 0 /* cannot assign equivalence classes here since the ope_ebase array ** does not have any more room */ ope_aseqcls(unionp, (OPE_BMEQCLS *) NULL, qual); /* assign ** equivalence classes for vars in ** the qualification which have not ** been assigned yet */ #endif *qualpp = unionp->ops_root->pst_right; unionp->ops_root->pst_right = qual; } unionp->ops_gentry = subquery->ops_gentry; if (grvp->opv_gmask & OPV_UVPROJECT) { /* remove attributes from target list which are not needed ** by parent query */ PST_QNODE **qnodepp; /* used to traverse resdom list ** to create projection */ PST_QNODE *savep; /* save at least one resdom in ** case entire list is eliminated */ bool insert_resdom; /* TRUE if all resdoms are eliminated */ savep = NULL; insert_resdom = TRUE; for (qnodepp = &unionp->ops_root->pst_left; (*qnodepp) && ((*qnodepp)->pst_sym.pst_type == PST_RESDOM);) { if ((*qnodepp)->pst_sym.pst_value.pst_s_rsdm.pst_rsflags&PST_RS_PRINT && !BTtest((i4)(*qnodepp)->pst_sym.pst_value.pst_s_rsdm.pst_rsno, (char *)grvp->opv_attrmap)) { savep = *qnodepp; *qnodepp = (*qnodepp)->pst_left; /* eliminate unneeded ** resdom */ } else { qnodepp = &(*qnodepp)->pst_left; /* resdom needed by parent */ insert_resdom = FALSE; } } if (insert_resdom && savep) { /* make sure at least one printing resdom remains */ savep->pst_left = unionp->ops_root->pst_left; unionp->ops_root->pst_left = savep; savep->pst_right = opv_i1const(subquery->ops_global, 0); STRUCT_ASSIGN_MACRO(savep->pst_right->pst_sym.pst_dataval, savep->pst_sym.pst_dataval); /* create i1 constant since only ** a place holder is needed and the ** parent query does not reference ** this attribute so change it to a ** one byte integer constant */ savep->pst_sym.pst_dataval.db_data = NULL; } } } }
/*{ ** Name: opj_union - optimize unions ** ** Description: ** The routine determines which qualifications of a parent query ** can be copied to the union, so that the union can be more restrictive ** or partitions of the union can be eliminated entirely in some cases. ** ** Inputs: ** subquery ptr to subquery be analyzed ** ** Outputs: ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 21-jun-89 (seputis) ** initial creation ** 01-Oct-2001 (hanal04) Bug 105464 INGSRV 1517 ** After calling opv_copytree() mark the target subquery ** as non-CNF if the source suquery was non-CNF. ** 11-may-05 (inkdo01) ** Don't copy ON clause BFs to union subqueries - fixes 114493. ** 20-apr-07 (hayke02) ** Limit the fix for bug 114493 (test for a OPL_NOOUTER pst_joinid) ** to those subqueries containing outer joins, otherwise pst_joinid ** will be PST_NOJOIN (0). This change fixes bug 118088. ** 27-mar-2009 (huazh01) ** don't copy OPZ_FANUM, OPZ_SNUM, OPZ_CORRELATED attribute into ** 'unionbm'. Doing so will cause opj_utree() to convert the ** union_subquery->ops_sunion.opu_qual qualification into a ** PST_QNODE node containing negative attribute id, and causes ** E_OP0384 error. (bug 121602) [@history_template@]... */ VOID opj_union( OPS_SUBQUERY *subquery) { OPV_IVARS lvar; for (lvar = subquery->ops_vars.opv_prv; --lvar>=0;) { /* look at the union views referenced in this subquery */ OPV_VARS *unionp; unionp = subquery->ops_vars.opv_base->opv_rt[lvar]; if (unionp->opv_grv && (unionp->opv_grv->opv_created == OPS_VIEW) && unionp->opv_grv->opv_gquery && unionp->opv_grv->opv_gquery->ops_union ) { /* a union view has been found, so search the list of boolean ** factors to determine which terms can be placed into the qualifications ** of the union */ OPE_BMEQCLS unionbm; /* bitmap of the equivalence classes which ** are available in the union view */ OPS_SUBQUERY *union_subquery; OPB_BMBF bfbm; OPB_IBF maxbf; /* current boolean factor number */ maxbf = BITS_IN(bfbm); union_subquery = unionp->opv_grv->opv_gquery; if (union_subquery->ops_sunion.opu_mask & OPU_QUAL) { if (union_subquery->ops_sunion.opu_qual == (PST_QNODE *)NULL) continue; /* since entire union is required by ** another subquery, no need to proceed */ MEfill(sizeof(bfbm), (u_char)0, (PTR)&bfbm); BTnot((i4)BITS_IN(bfbm), (char *)&bfbm); /* bits will get ** reset as identical qualifications are ** found */ } MEfill(sizeof(unionbm), (u_char)0, (PTR)&unionbm); { /* search thru list of attributes and mark ** appropriate equivalence classes*/ /* opv_eqcmp not initialized yet */ OPZ_IATTS attr; OPZ_AT *abase; abase = subquery->ops_attrs.opz_base; /* ptr to base of array of ptrs ** to joinop attributes */ for (attr = subquery->ops_attrs.opz_av; --attr>=0;) { /* opv_eqcmp not initialized yet */ OPZ_ATTS *attrp; attrp = abase->opz_attnums[attr]; if ((attrp->opz_varnm == lvar) && (attrp->opz_equcls >= 0) && attrp->opz_attnm.db_att_id >= 0 ) BTset ((i4)attrp->opz_equcls, (char *)&unionbm); } } { /* search list of conjuncts, boolean factor structure has ** not been created at this point */ OPB_IBF total_bfs; /* number of qualifications placed into ** union view list */ PST_QNODE *qual; total_bfs = 0; for (qual = subquery->ops_root->pst_right; qual->pst_sym.pst_type != PST_QLEND; qual = qual->pst_right) { OPE_BMEQCLS qual_eqcmp; MEfill(sizeof(qual_eqcmp), (u_char)0, (PTR)&qual_eqcmp); ope_aseqcls(subquery, &qual_eqcmp, qual->pst_left); /* assign ** equivalence classes for vars in ** the qualification which have not ** been assigned yet */ if (BTsubset((char *)&qual_eqcmp, (char *)&unionbm, (i4)BITS_IN(unionbm)) && !(subquery->ops_oj.opl_base && (qual->pst_left->pst_sym.pst_value.pst_s_op.pst_joinid != OPL_NOOUTER))) { /* this boolean factor contains equivalence classes which ** are all in the union view, so it can be evaluated inside ** the union view */ if (union_subquery->ops_sunion.opu_mask & OPU_QUAL) { /* qualification must already be in the list ** so compare to existing list of entries */ PST_QNODE *old_qual; OPB_IBF bfcount; bfcount = 0; for (old_qual = union_subquery->ops_sunion.opu_qual; old_qual; old_qual = old_qual->pst_right) { if (BTtest((i4)bfcount, (char *)&bfbm) /* if this ** boolean factor has not been ** matched */ && opj_ctree(subquery, qual->pst_left, lvar, union_subquery, old_qual->pst_left) /* ** and if the qualification ** matches the current one */ ) { /* found a match so mark the boolean ** factor as being in common */ BTclear((i4)bfcount, (char *)&bfbm); break; } bfcount++; } } else { /* if this is the first time then just copy the ** qualification and place into the list */ PST_QNODE *and_node; total_bfs++; if (total_bfs >= BITS_IN(unionbm)) break; /* cannot add anymore qualifications ** since the max boolean factor ** count has been reached */ and_node = opv_opnode(subquery->ops_global, PST_AND, (ADI_OP_ID)0, (OPL_IOUTER)OPL_NOOUTER); and_node->pst_left = qual->pst_left; opv_copytree(subquery->ops_global, &and_node->pst_left); and_node->pst_right = union_subquery->ops_sunion.opu_qual; union_subquery->ops_sunion.opu_qual = and_node; if(subquery->ops_mask & OPS_NONCNF) { union_subquery->ops_mask |= OPS_NONCNF; } } } } } if (union_subquery->ops_sunion.opu_mask & OPU_QUAL) { /* previous qualification exists so need to get common qualifications, ** by using boolean factor bitmap of those qualifications which are ** to be eliminated */ OPB_IBF bfno; PST_QNODE **qualpp; OPB_IBF current; current = 0; qualpp = &union_subquery->ops_sunion.opu_qual; for (bfno = -1; (bfno = BTnext((i4)bfno, (char *)&bfbm, (i4)maxbf)) >= 0;) { while ((current < bfno) && *qualpp) { /* find next qualification to eliminate */ current++; qualpp = &(*qualpp)->pst_right; } if (*qualpp) *qualpp = (*qualpp)->pst_right; /* remove this qual since ** no match was found for this subquery */ else break; /* end of qual list reached */ } } else { /* this is the first qualification in the list, so it can be applied */ union_subquery->ops_sunion.opu_mask |= OPU_QUAL; if (union_subquery->ops_sunion.opu_qual) opj_utree(subquery, union_subquery->ops_sunion.opu_qual, lvar); /* ** convert the ** qualification to reference var nodes ** of the union view, so that comparisons ** can be made later */ } } } }
/*{ ** Name: opv_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 */ }
VOID opc_querycomp( OPS_STATE *global) { DB_STATUS ret; global->ops_gmask |= OPS_OPCEXCEPTION; /* mark facility as being in OPC */ #ifdef OPT_F033_OPF_TO_OPC if (opt_strace(global->ops_cb, OPT_F033_OPF_TO_OPC) == TRUE) { char temp[OPT_PBLEN + 1]; bool init = 0; if (global->ops_cstate.opc_prbuf == NULL) { global->ops_cstate.opc_prbuf = temp; init++; } /* Trace all of 'global' */ if (global->ops_statement != NULL) { opt_state(global); } if (init) { global->ops_cstate.opc_prbuf = NULL; } } #endif if ( opt_strace(global->ops_cb, OPT_F071_QEP_WITHOUT_COST ) == TRUE && global->ops_subquery) { opt_cotree_without_stats( global ); } /* If this is CREATE TABLE, check for primary, unique, foreign key ** constraints to use for default base table structure. */ if (global->ops_statement && global->ops_statement->pst_type == PST_CREATE_TABLE_TYPE && global->ops_statement->pst_specific.pst_createTable. pst_createTableFlags == PST_CRT_TABLE) { QEU_CB *qeucb = global->ops_statement->pst_specific.pst_createTable.pst_createTableQEUCB; DMU_CB *dmucb = (DMU_CB *) qeucb->qeu_d_cb; bool checkit = FALSE; if (BTtest(DMU_AUTOSTRUCT, dmucb->dmu_chars.dmu_indicators)) checkit = (dmucb->dmu_chars.dmu_flags & DMU_FLAG_AUTOSTRUCT) != 0; else checkit = opt_strace(global->ops_cb, OPT_F084_TBLAUTOSTRUCT ) || global->ops_cb->ops_alter.ops_autostruct != 0; if (checkit) opc_checkcons(global->ops_statement, dmucb); } /* On entry for rule processing, assume ops_qpinit == TRUE. There */ /* is no need to allocate a memory stream since we are contuing */ /* processing on the QP that was started by the triggering statement. */ if (global->ops_qpinit == FALSE) { /* First, lets open the stack ULM memory stream that OPC uses */ opu_Osmemory_open(global); /* Tell QSF that we want to store an object; */ global->ops_qsfcb.qsf_obj_id.qso_type = QSO_QP_OBJ; if (global->ops_procedure->pst_flags & PST_REPEAT_DYNAMIC) { char *p; global->ops_qsfcb.qsf_obj_id.qso_lname = sizeof(DB_CURSOR_ID) + sizeof(i4); MEfill(sizeof(global->ops_qsfcb.qsf_obj_id.qso_name), 0, global->ops_qsfcb.qsf_obj_id.qso_name); MEcopy((PTR)&global->ops_procedure->pst_dbpid.db_cursor_id[0], sizeof (global->ops_procedure->pst_dbpid.db_cursor_id[0]), (PTR)global->ops_qsfcb.qsf_obj_id.qso_name); p = (char *) global->ops_qsfcb.qsf_obj_id.qso_name + 2*sizeof(i4); if (global->ops_caller_cb->opf_locator) MEcopy((PTR)"ql", sizeof("ql"), p); else MEcopy((PTR)"qp", sizeof("qp"), p); p = (char *) global->ops_qsfcb.qsf_obj_id.qso_name + sizeof(DB_CURSOR_ID); I4ASSIGN_MACRO(global->ops_caller_cb->opf_udbid, *(i4 *) p); } else if ( global->ops_procedure->pst_isdbp == TRUE || ( global->ops_qheader != NULL && (global->ops_qheader->pst_mask1 & PST_RPTQRY) ) ) { global->ops_qsfcb.qsf_obj_id.qso_lname = sizeof (global->ops_procedure->pst_dbpid); MEcopy((PTR)&global->ops_procedure->pst_dbpid, sizeof (global->ops_procedure->pst_dbpid), (PTR)global->ops_qsfcb.qsf_obj_id.qso_name); } else { global->ops_qsfcb.qsf_obj_id.qso_lname = 0; } /* Also allow for the case where a concurrent clash causes a new ** object at an awkward point */ if ((ret = qsf_call(QSO_CREATE, &global->ops_qsfcb)) != E_DB_OK && !(ret == E_DB_ERROR && global->ops_qsfcb.qsf_error.err_code == E_QS001C_EXTRA_OBJECT) && !((global->ops_procedure->pst_flags & PST_SET_INPUT_PARAM) && global->ops_qsfcb.qsf_error.err_code == E_QS001C_EXTRA_OBJECT)) { /* if object exists and we have a named query plan. */ if (global->ops_qsfcb.qsf_error.err_code == E_QS000A_OBJ_ALREADY_EXISTS ) { /* Log query info */ QSO_OBID *obj = &global->ops_qsfcb.qsf_obj_id; char *qrytype; char *objtype; char *objname; char *qrytext; char tmp[(DB_OWN_MAXNAME + DB_CURSOR_MAXNAME) + 3 + 1]; DB_STATUS status; QSF_RCB qsf_rb; PSQ_QDESC *qdesc; if (global->ops_procedure->pst_isdbp == TRUE) qrytype = "database procedure"; else if (global->ops_qheader != NULL && (global->ops_qheader->pst_mask1 & PST_RPTQRY) ) qrytype = "repeat query"; else qrytype = "non-repeat query"; objtype = "QSO_QP_OBJ"; if (obj->qso_lname == 0) { objname = "QSF object has no name"; } else { char fmt[30]; DB_CURSOR_ID *curid; char *user; i4 *dbid; curid = (DB_CURSOR_ID *)obj->qso_name; user = curid->db_cur_name + DB_CURSOR_MAXNAME; dbid = (i4 *)(user + DB_OWN_MAXNAME); STprintf(fmt, ":%%lx:%%lx:%%.%ds:%%.%ds:%%lx:", DB_CURSOR_MAXNAME, DB_OWN_MAXNAME); STprintf(tmp, fmt, (i4)curid->db_cursor_id[0], (i4)curid->db_cursor_id[1], curid->db_cur_name, user, (i4)(*dbid)); objname = tmp; } 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_OPF_ID; qsf_rb.qsf_obj_id.qso_handle = global->ops_caller_cb->opf_thandle; qrytext = "Query text was not available."; if (qsf_rb.qsf_obj_id.qso_handle != NULL) { status = qsf_call(QSO_INFO, &qsf_rb); if (DB_SUCCESS_MACRO(status)) { qdesc = (PSQ_QDESC*) qsf_rb.qsf_root; qrytext = qdesc->psq_qrytext; } } /* log an error */ opx_lerror((OPX_ERROR)E_OP089F_QSF_FAILCREATE, (i4)4, (PTR)qrytype, (PTR)objtype, (PTR)objname, (PTR)qrytext); } opx_verror(ret, E_OP0882_QSF_CREATE, global->ops_qsfcb.qsf_error.err_code); } /* Put the handle for the QEP into the callers CB; ** - will be used for deallocation in case of an error ** - both the object id and the lock id are needed in order to destroy ** the object */ STRUCT_ASSIGN_MACRO(global->ops_qsfcb.qsf_obj_id, global->ops_caller_cb->opf_qep); global->ops_qplk_id = global->ops_qsfcb.qsf_lk_id; global->ops_qpinit = TRUE; /* Allocate and initialize the QP. */ opc_iqp_init(global); } /* Continue the QP compilation by adding the current statement */ if (global->ops_statement != NULL) { opc_cqp_continue(global); } /* if it's time to stop compiling the query, then lets close stuff. */ /* The caller is responsible for making one additional call to OPC */ /* with ops_statement == NULL after all statements in the QP we are */ /* currently building have been compiled. Note that this is a change */ /* from the previous version of this routine which required the extra */ /* call only if a db procedure was being compiled. Such a call must */ /* also be made after the last statement in each rule list. This allows */ /* OPC to link all conditionals statements in the rule list together */ /* before continuing with the next user statement to be compiled. */ if (global->ops_statement == NULL) { /* We're finished compiling all of the statements, so lets finish ** the QP */ opc_fqp_finish(global); /* The QP is only associated with the outer query, not a rule list */ if (!global->ops_inAfterRules && !global->ops_inBeforeRules) { /* Tell QSF what the root of the QEP is; */ global->ops_qsfcb.qsf_root = (PTR) global->ops_cstate.opc_qp; if ((ret = qsf_call(QSO_SETROOT, &global->ops_qsfcb)) != E_DB_OK) { opx_verror(ret, E_OP0883_QSF_SETROOT, global->ops_qsfcb.qsf_error.err_code); } if ((ret = qsf_call(QSO_UNLOCK, &global->ops_qsfcb)) != E_DB_OK) { opx_verror(ret, E_OP089E_QSF_UNLOCK, global->ops_qsfcb.qsf_error.err_code); } /* Now lets close the stack ULM memory stream that OPC used */ opu_Csmemory_close(global); } } global->ops_gmask &= (~OPS_OPCEXCEPTION); /* mark facility as leaving OPC */ }
/*{ ** Name: psq_crdump - Dump cursor control block given cursor and session ids ** ** INTERNAL PSF call format: status = psq_crdump(&psq_cb, &sess_cb); ** ** EXTERNAL call format: status = psq_call(PSQ_CURDUMP, &psq_cb, &sess_cb); ** ** Description: ** The psq_crdump function will format and print a cursor control block ** given the cursor id and the session id that identify it. The output ** will go to the output terminal and/or file named by the user in the ** "SET TRACE TERMINAL" and "SET TRACE OUTPUT" commands. ** ** Inputs: ** psq_cb ** .psq_cursid Cursor id ** sess_cb Pointer to session control block ** (Can be NULL) ** ** Outputs: ** psq_cb ** .psq_error Error information ** .err_code What error occurred ** E_PS0000_OK Success ** E_PS0002_INTERNAL_ERROR Internal inconsistency in PSF ** E_PS0205_SRV_NOT_INIT Server not initialized ** E_PS0401_CUR_NOT_FOUND Cursor not found ** 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: ** Writes to output file and/or terminal as specified in the "set trace ** output" and "set trace terminal" commands. ** ** History: ** 02-oct-85 (jeff) ** written ** 27-oct-88 (stec) ** Dump psc_iupdmap. ** 10-may-89 (neil) ** Tracing of new rule-related objects. ** 22-dec-92 (rblumer) ** Added tracing of new statement-level rules. ** 10-mar-93 (andre) ** dump psc_expmap ** 07-apr-93 (andre) ** psc_tbl_mask, psc_rchecked, psc_rules, and psc_stmt_rules have all ** been moved from PSC_CURBLK into PSC_TBL_DESCR. A list of one or ** more PSC_TBL_DESCR structures will hang off PSC_CURBLK for ** updatable cursors ** 11-oct-1993 (tad) ** Bug #56449 ** Changed %x to %p for pointer values. ** 15-june-06 (dougi) ** Add support for "before" triggers. */ DB_STATUS psq_crdump( PSQ_CB *psq_cb, PSS_SESBLK *sess_cb) { PSC_CURBLK *cursor; DB_STATUS status; i4 i; i4 thisline; PSC_RESCOL *column; extern PSF_SERVBLK *Psf_srvblk; /* ** Make sure server is initialized. */ if (!Psf_srvblk->psf_srvinit) { psq_cb->psq_error.err_code = E_PS0205_SRV_NOT_INIT; return (E_DB_ERROR); } /* ** Get pointer to cursor control block. */ status = psq_crfind(sess_cb, &psq_cb->psq_cursid, &cursor, &psq_cb->psq_error); if (status != E_DB_OK) return (status); /* ** NULL means no such cursor. */ if (cursor == (PSC_CURBLK *) NULL) { psq_cb->psq_error.err_code = E_PS0401_CUR_NOT_FOUND; return (E_DB_ERROR); } /* ** Now print out everything in the cursor control block. */ TRdisplay("Cursor Control Block for Cursor:"); status = psq_ciddmp(&psq_cb->psq_cursid); if (status != E_DB_OK) { psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR; return (status); } TRdisplay("\n\n"); /* First, the control block header */ if ((status = psq_headdmp((PSQ_CBHEAD *) cursor)) != E_DB_OK) { psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR; return (status); } /* The cursor id */ TRdisplay("\tpsc_blkid:\n"); if ((status = psq_ciddmp(&cursor->psc_blkid)) != E_DB_OK) { psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR; return (status); } /* Used / Not Used flag */ TRdisplay("\tpsc_used:\t"); if ((status = psq_booldmp(cursor->psc_used)) != E_DB_OK) { psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR; return (status); } TRdisplay("\n"); /* Stream pointer */ TRdisplay("\tpsc_stream:\t%p\n", cursor->psc_stream); /* Query language */ TRdisplay("\tpsc_lang:\t(%d) ", cursor->psc_lang); if ((status = psq_lngdmp(cursor->psc_lang)) != E_DB_OK) { psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR; return (status); } TRdisplay("\n"); /* Delete permission flag */ TRdisplay("\tpsc_delall:\t"); if ((status = psq_booldmp(cursor->psc_delall)) != E_DB_OK) { psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR; return (status); } TRdisplay("\n"); /* For update flag */ TRdisplay("\tpsc_forupd:\t"); if ((status = psq_booldmp(cursor->psc_forupd)) != E_DB_OK) { psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR; return (status); } TRdisplay("\n"); /* Readonly flag */ TRdisplay("\tpsc_readonly:\t"); if ((status = psq_booldmp(cursor->psc_readonly)) != E_DB_OK) { psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR; return (status); } TRdisplay("\n"); /* Repeat cursor flag */ TRdisplay("\tpsc_repeat:\t"); if ((status = psq_booldmp(cursor->psc_repeat)) != E_DB_OK) { psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR; return (status); } TRdisplay("\n"); /* Open flag */ TRdisplay("\tpsc_open:\t"); if ((status = psq_booldmp(cursor->psc_open)) != E_DB_OK) { psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR; return (status); } TRdisplay("\n"); TRdisplay("\tList of descriptions of cursor's underlying table/views:\n"); if (cursor->psc_tbl_descr_queue.q_next == &cursor->psc_tbl_descr_queue) { TRdisplay("\t\tNONE\n"); } else { PSC_TBL_DESCR *descr, *last_descr; i4 i = 0; descr = (PSC_TBL_DESCR *) cursor->psc_tbl_descr_queue.q_next; last_descr = (PSC_TBL_DESCR *) cursor->psc_tbl_descr_queue.q_prev; do { if (i++) descr = (PSC_TBL_DESCR *) descr->psc_queue.q_next; /* element number (starting at 1) */ TRdisplay("\t\telement %d:\n", i); /* table/view id */ TRdisplay("\t\t\t\tpsc_tabid:\t(%d,%d)\n", descr->psc_tabid.db_tab_base, descr->psc_tabid.db_tab_index); /* table mask */ TRdisplay("\t\t\tpsc_tbl_mask:\t0x%x\n", descr->psc_tbl_mask); /* Row-level after user-defined rules */ TRdisplay("\t\t\tpsc_row_lvl_usr_rules:\t(address) 0x%p\n", descr->psc_row_lvl_usr_rules); /* Row-level after system-generated rules */ TRdisplay("\t\t\tpsc_row_lvl_sys_rules:\t(address) 0x%p\n", descr->psc_row_lvl_sys_rules); /* statement-level after user-defined rules */ TRdisplay("\t\t\tpsc_stmt_lvl_usr_rules:\t(address) 0x%p\n", descr->psc_stmt_lvl_usr_rules); /* statement-level after system-generated rules */ TRdisplay("\t\t\tpsc_stmt_lvl_sys_rules:\t(address) 0x%p\n", descr->psc_stmt_lvl_sys_rules); /* Row-level before user-defined rules */ TRdisplay("\t\t\tpsc_row_lvl_usr_before_rules:\t(address) 0x%p\n", descr->psc_row_lvl_usr_before_rules); /* Row-level before system-generated rules */ TRdisplay("\t\t\tpsc_row_lvl_sys_before_rules:\t(address) 0x%p\n", descr->psc_row_lvl_sys_before_rules); /* statement-level before user-defined rules */ TRdisplay("\t\t\tpsc_stmt_lvl_usr_before_rules:\t(address) 0x%p\n", descr->psc_stmt_lvl_usr_before_rules); /* statement-level before system-generated rules */ TRdisplay("\t\t\tpsc_stmt_lvl_sys_before_rules:\t(address) 0x%p\n", descr->psc_stmt_lvl_sys_before_rules); /* psc_flags */ TRdisplay("\t\t\tpsc_flags:\t"); TRdisplay("\t\t\t\tPSC_RULES_CHECKED:\t"); if ((status = psq_booldmp(descr->psc_flags & PSC_RULES_CHECKED)) != E_DB_OK) { psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR; return (status); } } while (descr != last_descr); } TRdisplay("\n"); /* Now do the set of columns for update. psc_updmap is a bit map. */ TRdisplay("\tpsc_updmap:\t"); thisline = 0; for (i = 0; i < (i4)sizeof(cursor->psc_updmap) * BITSPERBYTE; i++) { if (BTtest(i, (char *) &cursor->psc_updmap)) { TRdisplay("%d ", i); thisline++; /* Limit to 10 numbers per row */ if (thisline >= 10) { TRdisplay("\n\t\t"); thisline = 0; } } } TRdisplay("\n"); /* Now do the column set */ TRdisplay("\tpsc_restab:\n"); TRdisplay("\t\tpsc_tabsize: %d\n", cursor->psc_restab.psc_tabsize); TRdisplay("\t\tpsc_coltab:\n"); for (i = 0; i < cursor->psc_restab.psc_tabsize; i++) { for (column = cursor->psc_restab.psc_coltab[i]; column != (PSC_RESCOL *) NULL; column = column->psc_colnext) { TRdisplay("\n"); TRdisplay("\t\t\tpsc_attname:\t%#s\n", sizeof (column->psc_attname), &column->psc_attname); TRdisplay("\t\t\tpsc_type:\t"); if ((status = psq_dtdump(column->psc_type)) != E_DB_OK) { psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR; return (status); } TRdisplay("\n\t\t\tpsc_len:\t%d\n", column->psc_len); TRdisplay("\t\t\tpsc_prec:\t%d\n", column->psc_prec); TRdisplay("\t\t\tpsc_attid:\t%d\n", column->psc_attid.db_att_id); TRdisplay("\n"); } } /* Now do the internal set of columns for update, ** psc_iupdmap is a bit map. */ TRdisplay("\tpsc_iupdmap:\t"); thisline = 0; for (i = 0; i < (i4)sizeof(cursor->psc_iupdmap) * BITSPERBYTE; i++) { if (BTtest(i, (char *) &cursor->psc_iupdmap)) { TRdisplay("%d ", i); thisline++; /* Limit to 10 numbers per row */ if (thisline >= 10) { TRdisplay("\n\t\t"); thisline = 0; } } } TRdisplay("\n"); /* ** Now dump a map of attributes of a view on which a cursor is declared ** which (attributes that is) are based on an expression ** psc_expmap is a bit map. */ TRdisplay("\tpsc_expmap:\t"); thisline = 0; for (i = 0; i < (i4)sizeof(cursor->psc_expmap) * BITSPERBYTE; i++) { if (BTtest(i, (char *) &cursor->psc_expmap)) { TRdisplay("%d ", i); thisline++; /* Limit to 10 numbers per row */ if (thisline >= 10) { TRdisplay("\n\t\t"); thisline = 0; } } } TRdisplay("\n"); return (E_DB_OK); }
DB_STATUS dmu_modify(DMU_CB *dmu_cb) { DM_SVCB *svcb = dmf_svcb; DMU_CB *dmu = dmu_cb; DML_XCB *xcb; DML_ODCB *odcb; DM2U_MOD_CB local_mcb, *mcb = &local_mcb; i4 recovery; i4 truncate; i4 duplicates; i4 i,j; i4 indicator; i4 error, local_error; DB_STATUS status; bool bad_loc; i4 blob_add_extend = 0; bool used_default_page_size = TRUE; i4 page_size; i4 verify_options; i4 mask; i4 has_extensions = 0; DB_OWN_NAME table_owner; DB_TAB_NAME table_name; bool got_action; bool is_table_debug; bool reorg; CLRDBERR(&dmu->error); /* Any modify should make table recovery disallowed except for the ** alter_status options which change logical, physical consistency ** and table recovery bit itself */ mcb->mcb_mod_options2 = DM2U_2_TBL_RECOVERY_DEFAULT; do { /* Check for bad flags. */ mask = ~(DMU_VGRANT_OK | DMU_INTERNAL_REQ | DMU_RETINTO | DMU_PARTITION | DMU_MASTER_OP | DMU_ONLINE_START | DMU_ONLINE_END | DMU_NO_PAR_INDEX | DMU_PIND_CHAINED | DMU_NODEPENDENCY_CHECK); if ( (dmu->dmu_flags_mask & mask) != 0) { SETDBERR(&dmu->error, 0, E_DM001A_BAD_FLAG); break; } /* Validate the transaction id. */ xcb = (DML_XCB *)dmu->dmu_tran_id; if (dm0m_check((DM_OBJECT *)xcb, (i4)XCB_CB) != E_DB_OK) { SETDBERR(&dmu->error, 0, E_DM003B_BAD_TRAN_ID); break; } /* Check for external interrupts */ if ( xcb->xcb_scb_ptr->scb_ui_state ) dmxCheckForInterrupt(xcb, &error); if ( xcb->xcb_state ) { if (xcb->xcb_state & XCB_USER_INTR) { SETDBERR(&dmu->error, 0, E_DM0065_USER_INTR); break; } if (xcb->xcb_state & XCB_FORCE_ABORT) { SETDBERR(&dmu->error, 0, E_DM010C_TRAN_ABORTED); break; } if (xcb->xcb_state & XCB_ABORT) { SETDBERR(&dmu->error, 0, E_DM0064_USER_ABORT); break; } } /* Check the database identifier. */ odcb = (DML_ODCB *)dmu->dmu_db_id; if (dm0m_check((DM_OBJECT *)odcb, (i4)ODCB_CB) != E_DB_OK) { SETDBERR(&dmu->error, 0, E_DM0010_BAD_DB_ID); break; } mcb->mcb_db_lockmode = DM2T_X; /* Check that this is a update transaction on the database ** that can be updated. */ if (odcb != xcb->xcb_odcb_ptr) { SETDBERR(&dmu->error, 0, E_DM005D_TABLE_ACCESS_CONFLICT); break; } /* Prime the MCB */ mcb->mcb_dcb = odcb->odcb_dcb_ptr; mcb->mcb_xcb = xcb; mcb->mcb_tbl_id = &dmu->dmu_tbl_id; mcb->mcb_omcb = (DM2U_OMCB*)NULL; mcb->mcb_dmu = dmu; mcb->mcb_structure = 0; mcb->mcb_i_fill = 0; mcb->mcb_l_fill = 0; mcb->mcb_d_fill = 0; mcb->mcb_unique = FALSE; mcb->mcb_compressed = TCB_C_NONE; mcb->mcb_index_compressed = FALSE; mcb->mcb_temporary = FALSE; mcb->mcb_merge = FALSE; mcb->mcb_clustered = FALSE; mcb->mcb_modoptions = 0; mcb->mcb_min_pages = 0; mcb->mcb_max_pages = 0; mcb->mcb_allocation = 0; mcb->mcb_extend = 0; mcb->mcb_page_type = TCB_PG_INVALID; mcb->mcb_page_size = svcb->svcb_page_size; mcb->mcb_tup_info = &dmu->dmu_tup_cnt; mcb->mcb_reltups = 0; mcb->mcb_tab_name = &table_name; mcb->mcb_tab_owner = &table_owner; mcb->mcb_has_extensions = &has_extensions; mcb->mcb_relstat2 = 0; mcb->mcb_flagsmask = dmu->dmu_flags_mask; mcb->mcb_tbl_pri = 0; mcb->mcb_rfp_entry = (DM2U_RFP_ENTRY*)NULL; mcb->mcb_new_part_def = (DB_PART_DEF*)dmu->dmu_part_def; mcb->mcb_new_partdef_size = dmu->dmu_partdef_size; mcb->mcb_verify = 0; dmu->dmu_tup_cnt = 0; truncate = 0; reorg = FALSE; duplicates = -1; verify_options = 0; got_action = FALSE; /* FIXME better messages (in general) */ /* If there's a partdef it has to be one-piece, else bad param */ if (dmu->dmu_part_def != NULL && dmu->dmu_part_def->ndims > 0 && (dmu->dmu_part_def->part_flags & DB_PARTF_ONEPIECE) == 0) { SETDBERR(&dmu->error, 0, E_DM002A_BAD_PARAMETER); break; } /* Disassemble the modify action. ** FIXME this used to be buried in the characteristics array. ** It would make much more sense to just carry the action ** code through, but that will have to wait for another day. */ got_action = FALSE; switch (dmu->dmu_action) { case DMU_ACT_STORAGE: if (BTtest(DMU_STRUCTURE, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_structure = dmu->dmu_chars.dmu_struct; } break; case DMU_ACT_ADDEXTEND: got_action = TRUE; mcb->mcb_mod_options2 |= DM2U_2_ADD_EXTEND; break; case DMU_ACT_ENCRYPT: got_action = TRUE; mcb->mcb_mod_options2 |= DM2U_2_ENCRYPT; break; case DMU_ACT_LOG_CONSISTENT: if (BTtest(DMU_ACTION_ONOFF, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_mod_options2 &= ~DM2U_2_TBL_RECOVERY_DEFAULT; if ( dmu->dmu_chars.dmu_flags & DMU_FLAG_ACTON ) mcb->mcb_mod_options2 |= DM2U_2_LOG_CONSISTENT; else mcb->mcb_mod_options2 |= DM2U_2_LOG_INCONSISTENT; } break; case DMU_ACT_MERGE: got_action = TRUE; mcb->mcb_merge = TRUE; break; case DMU_ACT_PERSISTENCE: if (BTtest(DMU_PERSISTS_OVER_MODIFIES, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_mod_options2 |= (dmu->dmu_chars.dmu_flags & DMU_FLAG_PERSISTENCE) ? DM2U_2_PERSISTS_OVER_MODIFIES : DM2U_2_NOPERSIST_OVER_MODIFIES; } break; case DMU_ACT_PHYS_CONSISTENT: if (BTtest(DMU_ACTION_ONOFF, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_mod_options2 &= ~DM2U_2_TBL_RECOVERY_DEFAULT; if ( dmu->dmu_chars.dmu_flags & DMU_FLAG_ACTON ) mcb->mcb_mod_options2 |= DM2U_2_PHYS_CONSISTENT; else mcb->mcb_mod_options2 |= DM2U_2_PHYS_INCONSISTENT; } break; case DMU_ACT_PRIORITY: if (BTtest(DMU_TABLE_PRIORITY, dmu->dmu_chars.dmu_indicators)) got_action = TRUE; /* flag setting when we hit the priority char */ break; case DMU_ACT_READONLY: if (BTtest(DMU_ACTION_ONOFF, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; if ( dmu->dmu_chars.dmu_flags & DMU_FLAG_ACTON ) mcb->mcb_mod_options2 |= DM2U_2_READONLY; else mcb->mcb_mod_options2 |= DM2U_2_NOREADONLY; } break; case DMU_ACT_REORG: got_action = TRUE; reorg = TRUE; break; case DMU_ACT_TABLE_RECOVERY: if (BTtest(DMU_ACTION_ONOFF, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_mod_options2 &= ~DM2U_2_TBL_RECOVERY_DEFAULT; if ( dmu->dmu_chars.dmu_flags & DMU_FLAG_ACTON ) mcb->mcb_mod_options2 |= DM2U_2_TBL_RECOVERY_ALLOWED; else mcb->mcb_mod_options2 |= DM2U_2_TBL_RECOVERY_DISALLOWED; } break; case DMU_ACT_TRUNC: got_action = TRUE; truncate++; break; case DMU_ACT_USCOPE: if (BTtest(DMU_STATEMENT_LEVEL_UNIQUE, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_mod_options2 |= DM2U_2_STATEMENT_LEVEL_UNIQUE; } break; case DMU_ACT_VERIFY: if (BTtest(DMU_VACTION, dmu->dmu_chars.dmu_indicators)) { got_action = TRUE; mcb->mcb_verify = dmu->dmu_chars.dmu_vaction; } break; } /* switch */ if (! got_action) { SETDBERR(&dmu->error, 0, E_DM000E_BAD_CHAR_VALUE); break; } /* Disassemble the characteristics. ** FIXME probably better to just carry it through, but one step ** at a time! */ indicator = -1; while ((indicator = BTnext(indicator, dmu->dmu_chars.dmu_indicators, DMU_CHARIND_LAST)) != -1) { switch (indicator) { case DMU_ACTION_ONOFF: case DMU_STRUCTURE: /* Already picked it up, just skip on */ continue; case DMU_IFILL: mcb->mcb_i_fill = dmu->dmu_chars.dmu_nonleaff; if (mcb->mcb_i_fill > 100) mcb->mcb_i_fill = 100; continue; case DMU_LEAFFILL: mcb->mcb_l_fill = dmu->dmu_chars.dmu_leaff; if (mcb->mcb_l_fill > 100) mcb->mcb_l_fill = 100; continue; case DMU_DATAFILL: mcb->mcb_d_fill = dmu->dmu_chars.dmu_fillfac; if (mcb->mcb_d_fill > 100) mcb->mcb_d_fill = 100; continue; case DMU_PAGE_SIZE: used_default_page_size = FALSE; mcb->mcb_page_size = dmu->dmu_chars.dmu_page_size; if (mcb->mcb_page_size != 2048 && mcb->mcb_page_size != 4096 && mcb->mcb_page_size != 8192 && mcb->mcb_page_size != 16384 && mcb->mcb_page_size != 32768 && mcb->mcb_page_size != 65536) { SETDBERR(&dmu->error, indicator, E_DM000E_BAD_CHAR_VALUE); break; } else if (!dm0p_has_buffers(mcb->mcb_page_size)) { SETDBERR(&dmu->error, 0, E_DM0157_NO_BMCACHE_BUFFERS); break; } else { continue; } case DMU_MINPAGES: mcb->mcb_min_pages = dmu->dmu_chars.dmu_minpgs; continue; case DMU_MAXPAGES: mcb->mcb_max_pages = dmu->dmu_chars.dmu_maxpgs; continue; case DMU_UNIQUE: mcb->mcb_unique = TRUE; continue; case DMU_DCOMPRESSION: /* Translate DMU_xxx to TCB compression types */ if (dmu->dmu_chars.dmu_dcompress == DMU_COMP_ON) mcb->mcb_compressed = TCB_C_DEFAULT; else if (dmu->dmu_chars.dmu_dcompress == DMU_COMP_HI) mcb->mcb_compressed = TCB_C_HICOMPRESS; continue; case DMU_KCOMPRESSION: mcb->mcb_index_compressed = (dmu->dmu_chars.dmu_kcompress != DMU_COMP_OFF); continue; case DMU_TEMP_TABLE: mcb->mcb_temporary = TRUE; continue; case DMU_RECOVERY: recovery = (dmu->dmu_chars.dmu_flags & DMU_FLAG_RECOVERY) != 0; if (recovery) { /* recovery isn't currently supported */ SETDBERR(&dmu->error, indicator, E_DM000D_BAD_CHAR_ID); break; } continue; case DMU_DUPLICATES: duplicates = 0; if (dmu->dmu_chars.dmu_flags & DMU_FLAG_DUPS) duplicates = 1; continue; case DMU_ALLOCATION: mcb->mcb_allocation = dmu->dmu_chars.dmu_alloc; continue; case DMU_EXTEND: mcb->mcb_extend = dmu->dmu_chars.dmu_extend; continue; case DMU_VACTION: /* Already got it, just skip on */ continue; case DMU_VOPTION: verify_options = dmu->dmu_chars.dmu_voption; continue; case DMU_STATEMENT_LEVEL_UNIQUE: if (dmu->dmu_chars.dmu_flags & DMU_FLAG_UNIQUE_STMT) mcb->mcb_relstat2 |= TCB_STATEMENT_LEVEL_UNIQUE; continue; case DMU_PERSISTS_OVER_MODIFIES: if (dmu->dmu_chars.dmu_flags & DMU_FLAG_PERSISTENCE) mcb->mcb_relstat2 |= TCB_PERSISTS_OVER_MODIFIES; continue; case DMU_SYSTEM_GENERATED: mcb->mcb_relstat2 |= TCB_SYSTEM_GENERATED; continue; case DMU_SUPPORTS_CONSTRAINT: mcb->mcb_relstat2 |= TCB_SUPPORTS_CONSTRAINT; continue; case DMU_NOT_UNIQUE: mcb->mcb_relstat2 |= TCB_NOT_UNIQUE; continue; case DMU_NOT_DROPPABLE: mcb->mcb_relstat2 |= TCB_NOT_DROPPABLE; continue; case DMU_ROW_SEC_AUDIT: mcb->mcb_relstat2 |= TCB_ROW_AUDIT; continue; case DMU_TABLE_PRIORITY: mcb->mcb_tbl_pri = dmu->dmu_chars.dmu_cache_priority; if (mcb->mcb_tbl_pri < 0 || mcb->mcb_tbl_pri > DB_MAX_TABLEPRI) { SETDBERR(&dmu->error, indicator, E_DM000E_BAD_CHAR_VALUE); break; } /* ** DMU_TABLE_PRIORITY is set if priority came from WITH clause. ** DMU_TO_TABLE_PRIORITY is set if priority came from MODIFY TO clause. */ if (dmu->dmu_action != DMU_ACT_PRIORITY) mcb->mcb_mod_options2 |= DM2U_2_TABLE_PRIORITY; else mcb->mcb_mod_options2 |= DM2U_2_TO_TABLE_PRIORITY; continue; case DMU_BLOBEXTEND: blob_add_extend = dmu->dmu_chars.dmu_blobextend; continue; case DMU_CLUSTERED: mcb->mcb_clustered = (dmu->dmu_chars.dmu_flags & DMU_FLAG_CLUSTERED) != 0; continue; case DMU_CONCURRENT_UPDATES: /* Translate from PSF flag to DMU internal flag */ if (dmu->dmu_chars.dmu_flags & DMU_FLAG_CONCUR_U) mcb->mcb_flagsmask |= DMU_ONLINE_START; continue; default: /* Ignore anything else, might be for CREATE, who knows */ continue; } break; } /* ** If no page size specified, set page_size to zero ** In this case the current page size will be used */ if (used_default_page_size) mcb->mcb_page_size = 0; /* Save a local copy for dmpe_modify, since dm2u_modify can alter mcb */ page_size = mcb->mcb_page_size; if (mcb->mcb_structure == TCB_HEAP) { if (mcb->mcb_d_fill == 0) mcb->mcb_d_fill = DM_F_HEAP; } else if (mcb->mcb_structure == TCB_ISAM) { if (mcb->mcb_i_fill == 0) mcb->mcb_i_fill = DM_FI_ISAM; if (mcb->mcb_d_fill == 0) { if (mcb->mcb_compressed != TCB_C_NONE) mcb->mcb_d_fill = DM_F_CISAM; else mcb->mcb_d_fill = DM_F_ISAM; } } else if (mcb->mcb_structure == TCB_HASH) { if (mcb->mcb_d_fill == 0) { if (mcb->mcb_compressed != TCB_C_NONE) mcb->mcb_d_fill = DM_F_CHASH; else mcb->mcb_d_fill = DM_F_HASH; } if (mcb->mcb_min_pages == 0) { if (mcb->mcb_compressed != TCB_C_NONE) mcb->mcb_min_pages = 1; else mcb->mcb_min_pages = 10; /* If user specified max pages, don't set minpages higher */ if (mcb->mcb_min_pages > mcb->mcb_max_pages && mcb->mcb_max_pages != 0) mcb->mcb_min_pages = mcb->mcb_max_pages; } if (mcb->mcb_max_pages == 0) mcb->mcb_max_pages = 8388607; } else if (mcb->mcb_structure == TCB_BTREE || mcb->mcb_merge) { if (DMZ_AM_MACRO(16) && !mcb->mcb_temporary) { /* DM616 -- forces index compression to be used: */ mcb->mcb_index_compressed = TRUE; } if (mcb->mcb_i_fill == 0) mcb->mcb_i_fill = DM_FI_BTREE; if (mcb->mcb_l_fill == 0) mcb->mcb_l_fill = DM_FL_BTREE; if (mcb->mcb_d_fill == 0) { if (mcb->mcb_compressed != TCB_C_NONE) mcb->mcb_d_fill = DM_F_CBTREE; else mcb->mcb_d_fill = DM_F_BTREE; } } else if (truncate) { if (mcb->mcb_d_fill == 0) mcb->mcb_d_fill = DM_F_HEAP; } if (mcb->mcb_structure == TCB_HASH && mcb->mcb_min_pages > mcb->mcb_max_pages) { SETDBERR(&dmu->error, 0, E_DM000D_BAD_CHAR_ID); break; } mcb->mcb_kcount = dmu->dmu_key_array.ptr_in_count; mcb->mcb_key = (DMU_KEY_ENTRY**) dmu->dmu_key_array.ptr_address; if (mcb->mcb_kcount && (mcb->mcb_key == (DMU_KEY_ENTRY**)NULL || dmu->dmu_key_array.ptr_size != sizeof(DMU_KEY_ENTRY))) { SETDBERR(&dmu->error, 0, E_DM002A_BAD_PARAMETER); break; } if (truncate) { mcb->mcb_kcount = 0; mcb->mcb_modoptions |= DM2U_TRUNCATE; } if (duplicates == 1) mcb->mcb_modoptions |= DM2U_DUPLICATES; else if (duplicates == 0) mcb->mcb_modoptions |= DM2U_NODUPLICATES; /* else duplicates == -1, set neither flag */ if (reorg) mcb->mcb_modoptions |= DM2U_REORG; /* CLUSTERED implies and requires Unique */ if ( mcb->mcb_clustered && mcb->mcb_structure == TCB_BTREE ) mcb->mcb_unique = TRUE; else mcb->mcb_clustered = FALSE; if (mcb->mcb_verify) { if (verify_options == 0) { /* Apply defaults. */ switch (mcb->mcb_verify) { case DMU_V_VERIFY: verify_options = DMU_T_LINK | DMU_T_RECORD | DMU_T_ATTRIBUTE; break; case DMU_V_REPAIR: case DMU_V_DEBUG: verify_options = DMU_T_BITMAP; break; case DMU_V_PATCH: case DMU_V_FPATCH: break; } } /* Shift modifiers into place */ mcb->mcb_verify |= (verify_options << DM1U_MODSHIFT); } is_table_debug = ((mcb->mcb_verify & DM1U_OPMASK) == DM1U_DEBUG); /* Check the location names for duplicates, too many. */ mcb->mcb_location = (DB_LOC_NAME*)NULL; mcb->mcb_l_count = 0; if (dmu->dmu_location.data_address && (dmu->dmu_location.data_in_size >= sizeof(DB_LOC_NAME)) && mcb->mcb_temporary == FALSE) { mcb->mcb_location = (DB_LOC_NAME *) dmu->dmu_location.data_address; mcb->mcb_l_count = dmu->dmu_location.data_in_size/sizeof(DB_LOC_NAME); if (mcb->mcb_l_count > DM_LOC_MAX) { SETDBERR(&dmu->error, 0, E_DM0071_LOCATIONS_TOO_MANY); break; } bad_loc = FALSE; for (i = 0; i < mcb->mcb_l_count; i++) { for (j = 0; j < i; j++) { /* ** Compare this location name against other ** already given, they cannot be the same. */ if (MEcmp(mcb->mcb_location[j].db_loc_name, mcb->mcb_location[i].db_loc_name, sizeof(DB_LOC_NAME)) == 0 ) { SETDBERR(&dmu->error, i, E_DM001E_DUP_LOCATION_NAME); bad_loc = TRUE; break; } } if (bad_loc == TRUE) break; } if (bad_loc == TRUE) break; } else { /* There must a location list if you are reorganizing ** to a different number of locations. */ if (reorg) { if (dmu->dmu_location.data_address && dmu->dmu_location.data_in_size) SETDBERR(&dmu->error, 0, E_DM001F_LOCATION_LIST_ERROR); else SETDBERR(&dmu->error, 0, E_DM0072_NO_LOCATION); break; } } mcb->mcb_partitions = (DMU_PHYPART_CHAR*)NULL; mcb->mcb_nparts = 0; if ( dmu->dmu_ppchar_array.data_address && dmu->dmu_ppchar_array.data_in_size >= sizeof(DMU_PHYPART_CHAR) ) { mcb->mcb_partitions = (DMU_PHYPART_CHAR*)dmu->dmu_ppchar_array.data_address; mcb->mcb_nparts = dmu->dmu_ppchar_array.data_in_size / sizeof(DMU_PHYPART_CHAR); } if ((xcb->xcb_x_type & XCB_RONLY) && !is_table_debug) { SETDBERR(&dmu->error, 0, E_DM006A_TRAN_ACCESS_CONFLICT); break; } /* ** If this is the first write operation for this transaction, ** then we need to write the begin transaction record. */ if ((xcb->xcb_flags & XCB_DELAYBT) != 0 && mcb->mcb_temporary == FALSE && !is_table_debug) { status = dmxe_writebt(xcb, TRUE, &dmu->error); if (status != E_DB_OK) { xcb->xcb_state |= XCB_TRANABORT; break; } } /* Calls the physical layer to process the rest of the modify */ status = dm2u_modify(mcb, &dmu->error); if (status == E_DB_OK && has_extensions) { if ((mcb->mcb_mod_options2 & DM2U_2_ADD_EXTEND) && blob_add_extend == 0) status = E_DB_OK; else { /* FIX ME make modify etabs optional !! */ /* Add flag to modify top make modify etabs optional */ /* Add sysmod dbname tablename blob-column-name */ #ifdef xDEBUG TRdisplay("Modify etabs for %~t %~t\n", sizeof(DB_TAB_NAME), table_name.db_tab_name, sizeof(DB_OWN_NAME), table_owner.db_own_name); #endif status = dmpe_modify(dmu, odcb->odcb_dcb_ptr, xcb, &dmu->dmu_tbl_id, mcb->mcb_db_lockmode, mcb->mcb_temporary, truncate, (i4)0, blob_add_extend, &dmu->error); } } /* Audit successful MODIFY/PATCH of TABLE. */ if ( status == E_DB_OK && dmf_svcb->svcb_status & SVCB_C2SECURE ) { i4 msgid; i4 access = SXF_A_SUCCESS; if ((mcb->mcb_verify & DM1U_OPMASK) == DM1U_PATCH || (mcb->mcb_verify & DM1U_OPMASK) == DM1U_FPATCH) { access |= SXF_A_ALTER; msgid = I_SX271A_TABLE_PATCH; } else { access |= SXF_A_MODIFY; msgid = I_SX270F_TABLE_MODIFY; } /* ** Audit success */ status = dma_write_audit( SXF_E_TABLE, access, table_name.db_tab_name, /* Table/view name */ sizeof(table_name.db_tab_name), /* Table/view name */ &table_owner, /* Table/view owner */ msgid, FALSE, /* Not force */ &dmu->error, NULL); } if (status == E_DB_OK) { /* If modify to reorg or merge then return no tuple count info. */ if (reorg || (mcb->mcb_merge) || (mcb->mcb_verify != 0)) { dmu->dmu_tup_cnt = DM_NO_TUPINFO; } return (E_DB_OK); } else { if (dmu->error.err_code > E_DM_INTERNAL) { uleFormat(&dmu->error, 0, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, (char * )NULL, (i4)0, (i4 *)NULL, &local_error, 0); SETDBERR(&dmu->error, 0, E_DM0091_ERROR_MODIFYING_TABLE); } switch (dmu->error.err_code) { case E_DM004B_LOCK_QUOTA_EXCEEDED: case E_DM0112_RESOURCE_QUOTA_EXCEED: case E_DM0091_ERROR_MODIFYING_TABLE: case E_DM009B_ERROR_CHK_PATCH_TABLE: case E_DM0045_DUPLICATE_KEY: case E_DM0137_GATEWAY_ACCESS_ERROR: case E_DM006A_TRAN_ACCESS_CONFLICT: xcb->xcb_state |= XCB_STMTABORT; break; case E_DM0042_DEADLOCK: case E_DM004A_INTERNAL_ERROR: case E_DM0100_DB_INCONSISTENT: xcb->xcb_state |= XCB_TRANABORT; break; case E_DM0065_USER_INTR: xcb->xcb_state |= XCB_USER_INTR; break; case E_DM010C_TRAN_ABORTED: xcb->xcb_state |= XCB_FORCE_ABORT; break; case E_DM007D_BTREE_BAD_KEY_LENGTH: dmu->dmu_tup_cnt = dmu->dmu_tup_cnt; /* same for now */ default: break; } } } while (FALSE); if (dmu->error.err_code > E_DM_INTERNAL) { uleFormat(&dmu->error, 0, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, (char * )NULL, (i4)0, (i4 *)NULL, &local_error, 0); SETDBERR(&dmu->error, 0, E_DM0091_ERROR_MODIFYING_TABLE); } return (E_DB_ERROR); }
/*{ ** Name: opn_ukey - useable key - can inner node be probed ** ** Description: ** ** Returns TRUE if there are enough attributes ** to use the key of the relation. ** ** The jeqc must be the first element in the ordering that is not ** satisfied by a boolfact. This is because all in order for this ** routine to return true, keys 0..N were present in the join node. ** In opn_sjeqc, we will select the join eqc after reviewing all ** possibilities. One of the joined eqc's will be the first in the ** order list. If we only return true for this one, it will be joined. ** This only matters in the BTREE case. The prnode, in this case is ** also sorted on the first equiv class. ** If we picked an arbitrary eqc as keyed, we may do a needless sort. ** ** Inputs: ** subquery ptr to subquery being analyzed ** oeqcmp available equivalence class map of ** outer node ** jeqc primary attribute's eqc of key ** keyedvar joinop range variable number of ** possibly keyed relation ** primkey TRUE - if jeqc must be first joined ** element of orderlist ** ** Outputs: ** Returns: ** TRUE - if keyed access can be used for join ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 31-may-86 (seputis) ** initial creation ** 10-jun-96 (inkdo01) ** Support for Rtree inners of Kjoins. [@history_line@]... */ bool opn_ukey( OPS_SUBQUERY *subquery, OPE_BMEQCLS *oeqcmp, OPO_ISORT jeqc, OPV_IVARS keyedvar, bool primkey) { OPO_STORAGE storage; /* storage structure of relation */ OPV_VARS *varp; /* ptr to range variable element to ** be checked */ i4 ordercount; /* number of attributes that the ** relation is ordered on */ i4 orderindex; /* index into the OPB_MBF structure ** of the range variable element */ OPZ_AT *abase; /* ptr to base of array of ptrs to ** joinop attribute elements */ bool ret_val; /* TRUE is we can do keyed lookup */ /* Relation must be disk resident */ if (keyedvar < 0) return (FALSE); ret_val = FALSE; varp = subquery->ops_vars.opv_base->opv_rt[keyedvar]; /* get ptr to range ** variable element */ storage = varp->opv_tcop->opo_storage; /* get storage structure ** of relation */ abase = subquery->ops_attrs.opz_base; /* ptr to base of array of ptrs to ** joinop attribute elements */ #if 0 /* FIXME - why is this check here */ if ((storage) < 0) storage = -storage; #endif # ifdef xNTR1 if (tTf (1, 0)) { TRdisplay("Checking for possible keyed lookup into %s.\n", Jn.Range[keyedvar]->xrelid); TRdisplay("We %s looking for primary join key eqc %d.\n", (primkey ? "are" : "are not"), jeqc); TRdisplay("The joinop order eqcs are:"); for (i = 0; (att = order[i]) > -1; i++) TRdisplay("%d ", (i4) Jn.Attnums[att]->equcls); TRdisplay("\n"); uflush(); } # endif /* Each attribute in the ordering must be available from the outer ** or from the boolfacts */ ordercount = varp->opv_mbf.opb_count; /* number of attributes that the ** relation is ordered on */ for (orderindex = 0; orderindex < ordercount; orderindex++) { OPZ_IATTS attr; /* current ordering attribute being ** analyzed */ OPE_IEQCLS eqcls; /* equivalence class of ** of ordering attribute */ attr = varp->opv_mbf.opb_kbase->opb_keyorder[orderindex].opb_attno; eqcls = abase->opz_attnums[attr]->opz_equcls; if (storage == DB_RTRE_STORE) /* special Rtree inner logic */ { OPB_IBF bfi; OPB_BOOLFACT *bfp; for (bfi = 0; bfi < subquery->ops_bfs.opb_bv; bfi++) if ((bfp = subquery->ops_bfs.opb_base->opb_boolfact[bfi])-> opb_mask & OPB_SPATJ && BTtest((i4)eqcls, (char *)&bfp->opb_eqcmap)) { OPE_IEQCLS eqc1; if ((eqc1 = BTnext((i4)-1, (char *)&bfp->opb_eqcmap, (i4)subquery->ops_eclass.ope_ev)) == eqcls) eqc1 = BTnext((i4)eqc1, (char *)&bfp->opb_eqcmap, (i4)subquery->ops_eclass.ope_ev); if (BTtest((i4)eqc1, (char *)oeqcmp)) { eqcls = eqc1; break; /* kinda silly to let this drop into ** same test again - any alternative will ** be even uglier! */ } } } if (BTtest((i4)eqcls, (char *)oeqcmp) == TRUE) { /* Look no further if the first eqcls in the outer ** that exists in the order for the relation is not the ** joining eqcls (if primkey) */ if (primkey && (jeqc != eqcls)) { #ifdef xNTR1 if (tTf(1, 0)) { TRdisplay("We tried to join on eqcls %d but realized that it\n", eqcls); TRdisplay("was not the the primary attribute in the ordering and\n"); TRdisplay("that the primary attribute is available from the outer.\n"); uflush(); } #endif break; } /* Condition satisfied. If non-hash we are keyed */ ret_val = TRUE; primkey = FALSE; if (storage != DB_HASH_STORE) break; /* we are hashed and need all attrs in the key */ continue; } if (subquery->ops_bfs.opb_bfeqc && BTtest((i4)eqcls, (char *)subquery->ops_bfs.opb_bfeqc)) continue; /* check if constant key is ** available for this attribute*/ /* eqcls is not available. If we haven't gotten all ** of the hashed key, we can't use the key. */ if (storage == DB_HASH_STORE) { # ifdef xNTR1 if (tTf(1, 0)) { TRdisplay("Not all keys for hashed lookup were found.\n"); uflush(); } # endif ret_val = FALSE; } break; } # ifdef xNTR1 if (tTf(1, 0)) { if (ret_val == TRUE) TRdisplay("We can do keyed lookup.\n"); else TRdisplay("We can NOT do keyed lookup.\n"); uflush(); } # endif return (ret_val); }
/*{ ** Name: opn_jmaps - set various join operator tree maps ** ** Description: {@comment_line@}... ** ** Inputs: ** subquery ptr to subquery being analyzed ** nodep ptr to current operator node which ** will have maps initialized ** ojmap NULL if no outer joins exist ** - map of outer joins which are ** evaluated in parent nodes. ** ** Outputs: ** nodep->opn_eqm set of equivalence class available ** from subtree ** nodep->opn_rlmap bitmap of relations in the subtree ** nodep->opn_rlasg order in which the relations in ** opn_rlmap are assigned to the leaves ** Returns: ** TRUE if there is a valid placement of subselect nodes with ** all required equivalence classes available for execution of the ** boolean factor used for the subselect ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 11-jun-86 (seputis) ** initial creation from setjmaps ** 29-mar-90 (seputis) ** fix byte alignment problems ** 2-apr-91 (seputis) ** only partially copied relation assignment causing run_all diffs ** fix for b35461, this would cause the OPF cache of query plans ** to not be effectively used, and could cause performance problems ** on non-VMS systems. This fix should be used whenever a poor ** query plan bug is reported which cannot be reproduced on VMS. ** 15-feb-94 (ed) ** - bug 59598 - correct mechanism in which boolean factors are ** evaluated at several join nodes ** 11-apr-94 (ed) ** - bug 59937 - E_OP0489 consistency check due to inner join ojid ** not being visible in boolean factor placement maps ** 31-jul-97 (inkdo01) ** Fix to force SVAR func atts ref'ed in ON clauses to materialize ** just before needed (to incorporate proper null semantics). ** 9-jul-99 (hayke02 for inkdo01) ** Set opn_eqm bits in lower level nodes (e.g. leaf nodes) when ** func attrs are found in higher level OJ nodes. This change ** fixes bug 92749. ** 20-Mar-02 (wanfr01) ** Bug 106678, INGSRV 1633 ** Confirm function attribute as an ojid before associating it ** with an outer join node. ** 16-sep-03 (hayke02) ** Check for OPN_OJINNERIDX in nodep->opn_jmask to indicate that ** placement of a coverqual inner index outer join needs to be ** checked. ** 28-apr-04 (hayke02) ** Modify previous change so that we return FALSE and reject the QEP ** if the index outer join node (node->LEFT) has a non-zero opn_nchild ** for node->LEFT->RIGHT. This will allow only QEPs where the OJ ** inner is the index only and not the index joined to another ** relation. This change fixes problem INGSRV 2808, bug 112211. ** 05-Oct-2004 (huazh01) ** Remove the above fix. The fix for 111627 handles b106678 ** as well. This fixes b113160, INGSRV2984. ** 14-mar-05 (hayke02) ** Modify the fix for problem INGSRV 2808, bug 112211 so that we now ** check for more than 1 var in the opl_ivmap. This now allows the ** correct rejection of plans that have had their opn_child's set up ** with left joins 'reversed' into right joins. This change fixes ** problems INGSRV 3049 and 3094, bugs 113457 and 113990. ** 23-sep-05 (hayke02) ** Check for a WHERE clause (OPL_NOOOUTER opz_ojid) OJSVAR func att, ** and make sure that all OJs that this func att is inner to are ** executed before the join involving this func att. This change fixes ** bug 114912. ** 30-jan-07 (hayke02) ** Disable the fix for bug 114912 for cart prod (OPL_BOJCARTPROD) OJs. ** This change fixes bug 117513. ** 27-Oct-2009 (kiria01) SIR 121883 ** Scalar sub-selects - protect subp->opv_eqcrequired from bad de-ref. [@history_line@]... */ bool opn_jmaps( OPS_SUBQUERY *subquery, OPN_JTREE *nodep, OPL_BMOJ *ojmap) { OPL_OUTER *outerp; if (nodep->opn_nleaves == 1) { /* leaf node */ OPV_IVARS varno; /* joinop range variable number ** of leaf */ varno = nodep->opn_prb[0]; /* by definition of leaf - only ** one variable in partition */ nodep->opn_rlasg[0] = varno; /* trivial ordering for leaf */ MEfill( sizeof(nodep->opn_rlmap), (u_char)0, (PTR)&nodep->opn_rlmap ); if (subquery->ops_oj.opl_lv > 0) { MEfill( sizeof(nodep->opn_ojinnermap), (u_char)0, (PTR)&nodep->opn_ojinnermap); MEfill( sizeof(nodep->opn_ojevalmap), (u_char)0, (PTR)&nodep->opn_ojevalmap); opl_sjij(subquery, varno, &nodep->opn_ojinnermap, ojmap); } BTset((i4)varno, (char *)&nodep->opn_rlmap); /* only one bit set for leaf*/ MEcopy ((PTR)&subquery->ops_vars.opv_base->opv_rt[varno]->opv_maps.opo_eqcmap, sizeof(nodep->opn_eqm), (PTR)&nodep->opn_eqm ); /* copy map of ** equivalence classes ** associated with varno of this ** leaf */ return(TRUE); } else { /* non-leaf node */ OPN_JTREE *leftchildp; /* ptr to left child node */ OPN_JTREE *rightchildp; /* ptr to right child node */ leftchildp = nodep->opn_child[OPN_LEFT]; rightchildp = nodep->opn_child[OPN_RIGHT]; if (ojmap) { MEfill(sizeof(nodep->opn_ojevalmap), (u_char)0, (PTR)&nodep->opn_ojevalmap); if( (nodep->opn_ojid >= 0) && !BTtest((i4)nodep->opn_ojid, (char *)ojmap)) { /* setup the outer join map which contains all outer joins ** which are completely evaluated within this subtree */ BTset((i4)nodep->opn_ojid, (char *)&nodep->opn_ojevalmap); BTset((i4)nodep->opn_ojid, (char *)ojmap); } } if (!opn_jmaps (subquery, leftchildp, ojmap)) /* get info on left child */ return(FALSE); if (!opn_jmaps (subquery, rightchildp, ojmap)) /* get info on right child */ return(FALSE); MEcopy ((PTR)&leftchildp->opn_rlmap, sizeof(nodep->opn_rlmap), (PTR)&nodep->opn_rlmap ); /* get var bitmap from left ** child */ BTor( (i4)BITS_IN(nodep->opn_rlmap), (char *)&rightchildp->opn_rlmap, (char *)&nodep->opn_rlmap ); /* "OR" rightchildp->opn_rlmap ** into nodep->opn_rlmap */ if (ojmap) { /* setup ojinnermap which contains all outer joins which are ** partially or totally evaluated within this subtree, but ** not at this node unless it is in the subtree */ MEcopy((PTR)&leftchildp->opn_ojinnermap, sizeof(leftchildp->opn_ojinnermap), (PTR)&nodep->opn_ojinnermap); BTor((i4)BITS_IN(rightchildp->opn_ojinnermap), (char *)&rightchildp->opn_ojinnermap, (char *)&nodep->opn_ojinnermap); if (leftchildp->opn_ojid >= 0) BTset((i4)leftchildp->opn_ojid, (char *)&nodep->opn_ojinnermap); if (rightchildp->opn_ojid >= 0) BTset((i4)rightchildp->opn_ojid, (char *)&nodep->opn_ojinnermap); if (subquery->ops_mask & OPS_IJCHECK) opl_ijcheck(subquery, &leftchildp->opn_ojinnermap, &rightchildp->opn_ojinnermap, &nodep->opn_ojinnermap, &leftchildp->opn_ojevalmap, &rightchildp->opn_ojevalmap, &nodep->opn_rlmap); /* map of outerjoins which are entirely evaluated within ** this subtree */ BTor((i4)BITS_IN(leftchildp->opn_ojevalmap), (char *)&leftchildp->opn_ojevalmap, (char *)&nodep->opn_ojevalmap); BTor((i4)BITS_IN(rightchildp->opn_ojevalmap), (char *)&rightchildp->opn_ojevalmap, (char *)&nodep->opn_ojevalmap); if ((nodep->opn_jmask & OPN_OJINNERIDX) && ((nodep->opn_ojid != nodep->opn_child[OPN_LEFT]->opn_ojid) || ((nodep->opn_ojid == nodep->opn_child[OPN_LEFT]->opn_ojid) && (nodep->opn_ojid >= 0) && (BTcount((char *)subquery->ops_oj.opl_base->opl_ojt [nodep->opn_ojid]->opl_ivmap, subquery->ops_vars.opv_rv) > 1)))) return(FALSE); } MEcopy ((PTR)leftchildp->opn_rlasg, leftchildp->opn_nleaves * sizeof(nodep->opn_rlasg[0]), (PTR)nodep->opn_rlasg); /* get relations from left ** child */ MEcopy ((PTR)rightchildp->opn_rlasg, rightchildp->opn_nleaves * sizeof(nodep->opn_rlasg[0]), (PTR)(nodep->opn_rlasg + leftchildp->opn_nleaves)); /* get ** relations from right child ** and place them beside the ** ones from the left child */ MEcopy ((PTR)&leftchildp->opn_eqm, sizeof(nodep->opn_eqm), (PTR)&nodep->opn_eqm ); /* copy equivalence class map ** from left child to this node */ BTor( (i4)BITS_IN(nodep->opn_eqm), (char *)&rightchildp->opn_eqm, (char *)&nodep->opn_eqm ); /* "OR" right child equivalence ** map into this to produce ** map for this node */ if (subquery->ops_joinop.opj_virtual) { /* there is a subselect in this query so check for availability ** of equivalence classes used for evaluation of boolean factors ** containing the subselect */ OPV_SUBSELECT *subp; /* ptr to subselect descriptor ** for range variable */ if ( (rightchildp->opn_nleaves == 1) && ( subp = subquery->ops_vars.opv_base-> opv_rt[rightchildp->opn_prb[0]]->opv_subselect ) ) { /* right child is a subselect so test for correct leaf ** placement */ if (!subp->opv_eqcrequired || !BTsubset( (char *)subp->opv_eqcrequired, (char *)&nodep->opn_eqm, (i4)BITS_IN(OPE_BMEQCLS) ) ) return (FALSE); /* cannot evaluate all boolean ** factors with SEJOIN nodes ** in this configuration */ else { /* check if all corelated variables are available in the ** outer - FIXME create another pointer field which ** contains this information so this loop is avoided */ for (;subp; subp = subp->opv_nsubselect) { if (!BTsubset( (char *)&subp->opv_eqcmp, (char *)&leftchildp->opn_eqm, (i4)BITS_IN(OPE_BMEQCLS) ) ) return (FALSE); /* not all correlated ** equivalence classes ** are available from the outer ** so return */ } } } if ( (leftchildp->opn_nleaves == 1) && (subp = subquery->ops_vars.opv_base-> opv_rt[leftchildp->opn_prb[0]]->opv_subselect) ) { if (!subp->opv_eqcrequired || !BTsubset( (char *)subp->opv_eqcrequired, (char *)&nodep->opn_eqm, (i4)BITS_IN(OPE_BMEQCLS) ) ) return (FALSE); /* cannot evaluate all boolean ** factors with SEJOIN nodes ** in this configuration */ else { /* check if all corelated variables are available in the ** outer - FIXME create another pointer field which ** contains this information so this loop is avoided */ for (;subp; subp = subp->opv_nsubselect) { if (!BTsubset( (char *)&subp->opv_eqcmp, (char *)&rightchildp->opn_eqm, (i4)BITS_IN(OPE_BMEQCLS) ) ) return (FALSE); /* not all correlated ** equivalence classes ** are available from the outer ** so return */ } } } } if ((subquery->ops_oj.opl_lv > 0) && (nodep->opn_ojid != OPL_NOOUTER)) { OPV_IVARS maxvar; /* this is an outer join function attribute which should ** only appear at the point that the outer join is ** actually performed */ maxvar = subquery->ops_vars.opv_rv; outerp = subquery->ops_oj.opl_base->opl_ojt[nodep->opn_ojid]; if ((outerp->opl_type == OPL_LEFTJOIN) || (outerp->opl_type == OPL_FULLJOIN)) { OPV_BMVARS tempvmap; OPV_IVARS innervar; MEcopy((PTR)outerp->opl_maxojmap, sizeof(tempvmap), (PTR)&tempvmap); BTand((i4)BITS_IN(tempvmap), (char *)&nodep->opn_rlmap, (char *)&tempvmap); for (innervar = -1; (innervar = BTnext((i4)innervar, (char *)&tempvmap, (i4)maxvar))>=0;) { OPE_IEQCLS ojeqcls; ojeqcls = subquery->ops_vars.opv_base->opv_rt [innervar]->opv_ojeqc; if (ojeqcls != OPE_NOEQCLS) BTset((i4)ojeqcls, (char *)&nodep->opn_eqm); } } } { /* determine multi-variable functions that can first be calculated ** at this node */ OPZ_IFATTS fattr; /* current function attribute ** being analyzed */ OPZ_IFATTS maxfattr; /* maximum number of function ** attributes defined */ OPZ_FT *fbase; /* ptr to base of array of ptrs ** to function attribute ** elements */ OPZ_AT *abase; /* ptr to base of array of ptrs ** to joinop attribute elements */ maxfattr = subquery->ops_funcs.opz_fv; /* number of function ** attributes defined */ fbase = subquery->ops_funcs.opz_fbase; /* ptr to base of array ** of function attributes */ abase = subquery->ops_attrs.opz_base; /* ptr to base of array ** of ptrs to joinop attribute ** elements */ for (fattr = 0; fattr < maxfattr ; fattr++) { OPZ_FATTS *fattrp; /* ptr to current function ** attribute being analyzed */ OPE_IEQCLS eqcls; /* equivalence class of the ** multi-variable function ** attribute */ fattrp = fbase->opz_fatts[fattr]; eqcls = abase->opz_attnums[fattrp->opz_attno]->opz_equcls; /* ** equivalence class associated ** with the multi-variable ** function attribute */ /* check for WHERE clause (OPL_NOOUTER opz_ojid) OJSVAR func att ** and then make sure that all OJs that this func att is inner ** to are executed before the join involving this func att */ if (fattrp->opz_type == OPZ_SVAR && (fattrp->opz_mask & OPZ_OJSVAR) && nodep->opn_ojid != OPL_NOOUTER && fattrp->opz_ojid == OPL_NOOUTER && (fattrp->opz_ijmap && BTtest((i4)nodep->opn_ojid, (char *)fattrp->opz_ijmap) && !(outerp->opl_mask & OPL_BOJCARTPROD))) { OPZ_IATTS attno; bool allinrlmap = TRUE; OPE_EQCLIST *eqclsp; eqclsp = subquery->ops_eclass.ope_base->ope_eqclist[eqcls]; for (attno = -1; (attno = BTnext((i4)attno, (PTR)&eqclsp->ope_attrmap, (i4)subquery->ops_attrs.opz_av)) != -1; ) { if (!BTtest((i4)abase->opz_attnums[attno]->opz_varnm, (char *)&nodep->opn_rlmap)) { allinrlmap = FALSE; break; } } if (allinrlmap) return(FALSE); } if ((fattrp->opz_type != OPZ_MVAR) && !(fattrp->opz_type == OPZ_SVAR && fattrp->opz_mask & OPZ_OJSVAR && fattrp->opz_ojid == nodep->opn_ojid) || (fattrp->opz_mask & OPZ_OJFA)) continue; /* only multi-variable ** functions are assigned here ** (and SVARs in ON clauses ** of OJs eval'd at this node) ** since others where assigned ** earlier, ... also outer join ** special eqc were assigned ** prior to this loop */ if (fattrp->opz_type == OPZ_SVAR) { /* Must be ON clause ref'ed SVAR. Set bit in whichever ** child node covers the SVAR eqcmap. */ if (BTsubset((char *)&fattrp->opz_eqcm, (char *)&nodep->opn_child[1]->opn_eqm, (i4)BITS_IN(nodep->opn_eqm))) BTset((i4)eqcls, (char *)&nodep->opn_child[1]->opn_eqm); else if (BTsubset((char *)&fattrp->opz_eqcm, (char *)&nodep->opn_child[0]->opn_eqm, (i4)BITS_IN(nodep->opn_eqm))) BTset((i4)eqcls, (char *)&nodep->opn_child[0]->opn_eqm); /* set fattr eqcls bit in ** proper child opn_eqm */ continue; } if (BTtest( (i4)eqcls, (char *)&nodep->opn_eqm)) continue; /* if function attribute has ** been added then ** continue */ if (BTsubset((char *)&fattrp->opz_eqcm, (char *)&nodep->opn_eqm, (i4)BITS_IN(nodep->opn_eqm)) ) BTset((i4)eqcls, (char *)&nodep->opn_eqm); /* set bit if all ** the required equivalence ** classes are available ** for the function attribute */ } } } return(TRUE); }
/*{ ** Name: opn_deltrl - delete a OPN_RLS struct and its associated histograms ** ** Description: ** This routine will delete an OPN_RLS structure and its' associated ** histograms, including the "cell count" array of any histogram on ** the joining equivalence class. ** ** Inputs: ** subquery ptr to subquery being analyzed ** trl OPN_RLS struct to delete ** jeqc joining equivalence class which ** will have a histogram to delete ** ** Outputs: ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 14-jun-86 (seputis) ** initial creation from deltrl ** 20-dec-90 (seputis) ** add support for deleting multi-attribute join histograms ** 19-nov-01 (inkdo01) ** Add logic to resolve linkage around OPN_RLS deleted from chain. [@history_line@]... */ VOID opn_deltrl( OPS_SUBQUERY *subquery, OPN_RLS *trl, OPE_IEQCLS jeqc, OPN_LEAVES nleaves) { OPZ_AT *abase; /* ptr to base of array of ptrs ** to joinop attributes */ OPH_HISTOGRAM *histp; /* histogram element to ** deallocate */ OPH_HISTOGRAM *nexthistp; /* need to save next histogram ** ptr in case memory manager ** munches the deallocated ** previous histogram */ OPE_BMEQCLS *eqcmap; /* map of joining equivalence classes */ OPN_RLS *prevrls; abase = subquery->ops_attrs.opz_base; /* ptr to base of array of ptrs ** to joinop attributes */ if (jeqc >= subquery->ops_eclass.ope_ev) { /* multi-attribute join found */ eqcmap = subquery->ops_msort.opo_base->opo_stable[jeqc-subquery->ops_eclass.ope_ev]->opo_bmeqcls; } else eqcmap = (OPE_BMEQCLS *)NULL; for (histp = trl->opn_histogram; histp; histp = nexthistp) { OPZ_ATTS *attrp; /* ptr to joinop attribute ** associated with histogram */ nexthistp = histp->oph_next; /* save next histogram prior ** to deallocation */ attrp = abase->opz_attnums[histp->oph_attribute]; if ((attrp->opz_equcls == jeqc) || ( eqcmap && BTtest((i4)attrp->opz_equcls, (char *)eqcmap) )) { oph_dccmemory( subquery, &attrp->opz_histogram, histp->oph_fcnt); /* can only free the fcnt array if it is on the joining ** eqclass because then we know it was created brand new by ** opn_h2 and does not point to another hist's oph_fcnt. (see ** line "newhp->oph_fcnt=lnp->oph_fcnt" in opn_h2.c */ } oph_dmemory(subquery, histp); } /* Locate trl in chain and remove it before deleting. */ prevrls = subquery->ops_global->ops_estate.opn_sbase->opn_savework[nleaves]; if (prevrls && prevrls == trl) subquery->ops_global->ops_estate.opn_sbase->opn_savework[nleaves] = trl->opn_rlnext; else if (prevrls) { for (; prevrls && prevrls->opn_rlnext != trl; prevrls = prevrls->opn_rlnext); if (prevrls) prevrls->opn_rlnext = trl->opn_rlnext; /* skip dropped OPN_RLS */ } opn_drmemory(subquery, trl); }
/*{ ** Name: opn_prleaf - project restrict for leaf ** ** Description: ** ** This routine generates a CO-tree forest for a single leaf. The ** forest will contain a tree with just an ORIG in it, and another ** tree with PR + ORIG. (unless the caller requests that the latter ** be omitted, i.e. wants keyed strategies only.) The bare ORIG ** will typically be used for K-join or T-join plans, while the ** PR/ORIG subtree can be used to cost out simple-join plans. ** ** [the below apparently dates from the Jurassic age, and it's not ** entirely clear to me how much of it is accurate or useful...] ** ** General: ** ** an index is useful if it is hash (and single attribute) or isam and ** a) it matches a joining clause ** or b) it matches a boolean factor ** ** To match a joining clause- ** attribute of index must equal join attribute. if isam, the ** attribute must be the most significant. if hash, the rel must ** be keyed on only this attribute. ** ** To match a boolean factor- ** there must exist a boolean factor such that ** a) for hash, all preds are equality sargs on the attribute or ** b) for isam, all preds are sargs on the most significant ** attribute. We do not worry about sort because it can ** only exist as an intermediate relation and hence will ** never need a project-restrict (since it would have ** been done when the tuple was created). ** ** Operation of project-restrict when relation is isam or hash: ** ** a) hash: find a matching boolean factor (all preds are equality sargs) ** then for each sarg hash it's value and for all elements ** in the resulting linked list apply the whole predicate ** ** b) isam: if there is a boolean factor where all preds are equality ** sargs then process similar to hash. the only difference ** is that a directory search is performed instead of ** hashing to determine the beginning and ending of the ** linked list. ** ** otherwise, for all matching boolean factors find a max and ** a min value as follows: ** . if all preds in this boolean factor are <, <= or = then set ** mx to max of the constants. if mx is greater then Gmax, set ** Gmax to mx. ** . if all preds in this bf are >, >= or = then set mn to min of ** the constants. if mn is greater than Gmin set Gmin to mn. ** . do a directory search with Gmin and Gmax. ** . for all elements in this linked list apply the whole ** predicate. ** ** ** COST ** ** cost of project-restrict-reformat is composed of the ** project-restrict cost and ** reformat cost. ** ** for the reformat cost see procedure "costreformat.c" ** ** project restrict cost is: ** hash and matching boolean factor ** ((#_pgs)/(#_primary) + hashcost) * (#_sargs_in_bf) ** hashcost is usually 0. ** isam and matching boolean factor ** ((#_pgs)/(#_primary) + dirsearchcost) * (#_sargs_in_bf) ** isam and matching boolean factor with range (at least one ** inequality sarg) ** 2 * (dirsearchcost) + integrate that part of hist to find ** that portion of relation which must be accessed ** ** ** SPECIFIC ** ** operation of prr- ** ** We are given a list of co's. this list contains one element if we are ** working on a leaf. if we are working on the result of a join then ** there may be zero or more co's. (there could be zero if for every co ** generated by calcost, there was a cheaper one in an associated co ** list with the same opo_storage and ordeqc.) ** ** let stp refer to this node in the tree ** if stp is a leaf then there is only one co. it represents the original ** relation as it is stored on disk. there can be no associated ** co lists because for a leaf there is only one structure and relation ** assignment possible. so for this stp's co, do simple pr and ** reformat to isam, hash and sort on all interesting orders. ** the reformat co's point to the pr co which points to the original co ** (the couter pointer does the pointing). ** ** If stp is the output of a join then the procedure is more complex. ** If there are no co's then nothing is done, so return ** otherwise find the minimum cost co in stp's co list. ** Find the minimum cost from all the other associated co's, if any. ** if there are other co's and a minimum cost is found which is less ** than stp's minimum cost then do nothing, return ** if there are other co's and a they have a higher mincost, then ** delete all reformat co's from them cause they can be done cheaper here ** specifically: ** using the minimum cost co as a base, do reformats to isam, hash ** and sort on all interesting orders. for each structure/ordering ** check this stp and all associated co lists. If this co is the best use ** it and delete the other with same struct/ord. if this is not the ** cheapest then do not use it, try next struct/ord combo. ** ** Default case will be to not do reformat to isam or hash unless the ** trace flags are turned on. Also, do not reformat to isam or hash ** if the tuple is too wide (>MAXTUP) cause modify cannot handle it. ** ** ctups is multiplied by sel so QEP will print correctly and ** set_qep_lock will know how many result tuples there are so it can ** set result relation locking appropriately. ** ** Always create PR nodes so that the cost of their use can be evaluated. ** This fixes a bug where cross products would become excessively large ** because the restriction on the outer node would only happen ** after the join. In the query "ret (r.a)where s.a =5" ** s.a will be the outer. We should do the restriction before the ** cross product. ** ** There will be no eqclasses in eqsp->eqcmp at this point if ** this query is from a single variable integrity constraint (right ** now all integrity constraints are single variable). This is because ** integrity constraints have no target list because they just want to ** see if there exists at least one element the query and do not want ** to return any attributes. We could do a ** special case in the optimizer to find the fastest way to get the ** first tuple (this could also be useful for QBF) but this is not done ** now. ** ** Note on trace points op188/op215/op216. ** Trace points op188/op215/op216 and op145 (set qep) both use opt_cotree(). ** op188/op215/op216 expect opt_conode to point to the fragment being ** traced, and op145 expects it to be NULL (so that ops_bestco is used). ** The NULL/non-NULL value of opt_conode also results in different ** display behaviour. For these reasons opt_conode is also NULLed ** after the call to opt_cotree(). ** ** Inputs: ** subquery ptr to subquery being analyzed ** jsbtp set of cost orderings already calculated ** .opn_coforw co tree representing leaf ** eqsp equivalence class list header structure ** rlsp represents the relation being restricted ** and projected ** blocks estimated number of blocks in relation ** represented by this leaf after restrict ** project applied ** selectivity selectivity of predicates on relation ** ** Outputs: ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 28-may-86 (seputis) ** initial creation ** 19-sep-88 (seputis) ** fix double application of selectivity ** 17-nov-88 (seputis) ** non-zero PR cpu cost required ** 29-mar-90 (seputis) ** fix metaphor performance problem - be smarter about search space ** 20-dec-90 (seputis) ** add top sort node removal checks ** 22-apr-91 (seputis) ** - fix float exception handling problem b36920 ** 15-dec-91 (seputis) ** fix b33551 make better guess for initial set of relations to enumerate ** 26-dec-91 (seputis) ** - 41526 - initialize sortrequired since opn_checksort now needs this ** initialized as part of bug fix ** 28-jan-91 (seputis) ** - fix 40923 - remove parameter to opn_prcost ** 1-apr-92 (seputis) ** - b40687 - part of tid join estimate fix. ** 8-may-92 (seputis) ** - b44212, check for overflow in opo_pointercnt ** 5-jan-93 (ed) ** - bug 48049 - move definition of ordering to make it more localized ** 30-mar-94 (ed) ** - bug 59461 - correct estimate of index pages in fanout causing ** overestimates and potential overflows ** 06-mar-96 (nanpr01) ** - Changes for variable page size. Do not use DB_MAXTUP constant ** rather use the opn_maxreclen function to calculate the ** max tuple size given the page size. ** 06-may-96 (nanpr01) ** Changes for New Page Format project - account for tuple header. ** 17-jun-96 (inkdo01) ** Treat Rtree like Btree for relprim computation. ** 21-jan-1997 (nanpr01) ** opo_pagesize was not getting set and consequently in opn_prcost/ ** opn_readahead was getting floating point exception in VMS. ** 1-apr-99 (hayke02) ** Use tbl_id.db_tab_index and tbl_storage_type rather than ** tbl_dpage_count <= 1 to check for a btree or rtree index. As of ** 1.x, dmt_show() returns a tbl_dpage_count of 1 for single page ** tables, rather than 2 in 6.x. This change fixes bug 93436. ** 17-aug-99 (inkdo01) ** Minor, but ugly, change to detect descending top sort where ** first column of multi-attr key struct is also in constant BF. ** read backwards doesn't work in this case. ** 28-sep-00 (inkdo01) ** Code for composite histograms (avoid using them when attr no ** is expected in histogram) ** 13-nov-00 (inkdo01) ** Added CO-tree addr to opn_checksort call. Seems no reason to ** omit it and new unique index top sort removal heuristic (100482) ** requires it. ** 12-aug-2002 (huazh01) ** Do not mark ncop->opo_storage as sorted on a hash structure base ** table. This fixes bug 107351, INGSRV 1727 ** 26-aug-2003 (wanfr01) ** Bug 111062, INGSRV 2545 ** Do not mark storage sorted unless it is a btree. ** 5-july-05 (inkdo01) ** Call opn_modcost() to alter row count for agg queries. ** 30-Jun-2006 (kschendel) ** Remove modcost, done now after subquery enumerated so that we ** don't affect the agg-input co tree. ** 30-Aug-2006 (kschendel) ** Eliminate redundant opo_topsort bool. ** 31-Aug-2006 (kschendel) ** Watch for HFAGG as well as RFAGG. ** 22-Oct-2007 (kibro01) b119313 ** Ensure that pr_tuples does not fall below 1. ** 7-Mar-2008 (kschendel) ** Always generate PR/ORIG co tree if new enumeration, regardless ** of plan ops_cost considerations. Failure to do this can cause ** a new enum pass to not find any valid plans until it clears the ** subtree cache with opn-recover. See inline. ** 04-nov-08 (hayke02 for kschendel) ** Modify the previous change to prevent single var substitution index ** QEPs always being chosen, despite the base table QEP being cheaper. ** This change fixes bug 121051. ** 3-Jun-2009 (kschendel) b122118 ** OJ filter cleanup, fix here. ** 26-feb-10 (smeke01) b123349 b123350 ** Copy over the op255 and op188 trace point code from opn_corput(). ** Amend opn_prleaf() to return OPN_STATUS in order to make trace point ** op255 work with set joinop notimeout. ** 01-mar-10 (smeke01) b123333 b123373 ** Improve op188. Replace call to uld_prtree() with call to ** opt_cotree() so that the same formatting code is used in op188 as ** in op145 (set qep). Save away the current fragment number and ** the best fragment number so far for use in opt_cotree(). Also ** set ops_trace.opt_subquery to make sure we are tracing the current ** subquery. ** 13-may-10 (smeke01) b123727 ** Add new trace point op215 to output trace for all query plan ** fragments and best plans considered by the optimizer. Add a new ** trace point op216 that prints out only the best plans as they are ** Also cleaned up some confused variable usage: replaced dsortcop ** with sortcop; and removed usage of ncop after it has been assigned ** to cop, replacing it with cop. ** 01-jun-10 (smeke01) b123838 ** Prevent oph_origtuples/oph_odefined from being set for the ** histograms of a multi-attribute key when there are no boolean ** factors for the key. This in turn prevents the ** 'delayed multi-attribute' code in oph_jtuples() from being skewed ** by an unreasonably high number for oph_origtuples (up to 50% of ** the number of rows in the base table). */ OPN_STATUS opn_prleaf( OPS_SUBQUERY *subquery, OPN_SUBTREE *jsbtp, OPN_EQS *eqsp, OPN_RLS *rlsp, OPO_BLOCKS blocks, bool selapplied, OPN_PERCENT selectivity, OPV_VARS *varp, bool keyed_only) { OPS_STATE *global; OPO_BLOCKS dio; /* estimated number of disk block ** reads/writes to produce tuples ** represented by this cost ordering ** - after the project restrict */ OPO_CPU cpu; /* estimate of cpu cost to produce ** this cost ordering */ OPO_TUPLES pr_tuples; /* number of tuples in the project ** restrict */ OPB_SARG sargtype; /* type of keyed access which can be ** used on relation */ OPO_BLOCKS pblk; /* estimated number of blocks read including ** blocks in primary index */ OPH_HISTOGRAM *hp; /* histogram returned only if TID access */ OPO_CO *cop; /* ptr to cost-ordering representing ** the leaf node */ OPO_CO *ncop; /* ptr to new cost-ordering of a project ** - restrict of "*cop" */ bool pt_valid; /* TRUE if pblk can be used as an estimate ** for pages touched, which is used for ** lock mode calculations */ bool imtid; /* TRUE if implicit TID instead of ** keyed attributes were used */ bool distributed; /* TRUE is this is a distributed ** thread */ OPO_COST prcost; /* project restrict cost of node */ i4 pagesize; /* estimate the page size */ OPO_CO *sortcop = NULL; /* set to not NULL if a sort node is required */ bool op188 = FALSE; bool op215 = FALSE; bool op216 = FALSE; global = subquery->ops_global; if (global->ops_cb->ops_check) { if (opt_strace( global->ops_cb, OPT_F060_QEP)) op188 = TRUE; if (opt_strace( global->ops_cb, OPT_F087_ALLFRAGS)) op215 = TRUE; if (opt_strace( global->ops_cb, OPT_F088_BESTPLANS)) op216 = TRUE; } distributed = global->ops_cb->ops_smask & OPS_MDISTRIBUTED; cop = jsbtp->opn_coback = jsbtp->opn_coforw = opn_cmemory( subquery ); /* ** get a new CO node */ STRUCT_ASSIGN_MACRO(*(varp->opv_tcop), *(jsbtp->opn_coback)); /* copy the node since it may be ** changed */ jsbtp->opn_coback->opo_coforw = jsbtp->opn_coback->opo_coback = (OPO_CO *) jsbtp; /* ptr to jsbtp used to terminate list */ jsbtp->opn_coback->opo_maps = &eqsp->opn_maps; /* save a ptr to the ** equivalence classes supplied by this ** CO subtree (i.e. node) */ if (varp->opv_grv->opv_relation) { DMT_TBL_ENTRY *dmfptr; /* ptr to DMF table info */ dmfptr = varp->opv_grv->opv_relation->rdr_rel; /* get ptr to RDF ** relation description info */ pagesize = dmfptr->tbl_pgsize; /* ** Note if distributed, the DMT_TBL_ENTRY is not totally initialized ** (it was filled in by RDF rdd_alterdate -- which did ** 'select ... from iitables...' against the local database ** However the information available by querying iitables does ** not include all information put in the DMT_TBL_ENTRY by DMT_SHOW */ if (distributed && ((dmfptr->tbl_storage_type == DB_BTRE_STORE) || (dmfptr->tbl_storage_type == DB_RTRE_STORE) || (dmfptr->tbl_storage_type == DB_ISAM_STORE)) ) { i4 pgtype; varp->opv_dircost = 1.0; /* ** Make assumptions about page type -> page/row overheads ** The overheads used are data page, not btree index page overheads ** That's okay - we're just looking for rough estimates */ pgtype = (dmfptr->tbl_pgsize == DB_COMPAT_PGSIZE) ? DB_PG_V1: DB_PG_V3; varp->opv_kpb = (dmfptr->tbl_pgsize - DB_VPT_PAGE_OVERHEAD_MACRO(pgtype)) / (varp->opv_mbf.opb_keywidth + DB_VPT_SIZEOF_LINEID(pgtype)); if (varp->opv_kpb < 2.0) varp->opv_kpb = 2.0; if (dmfptr->tbl_storage_type == DB_ISAM_STORE) { varp->opv_kpleaf = 0; varp->opv_leafcount = 0; } else { /* kpleaf calculation assumes no non-key columns */ varp->opv_kpleaf = varp->opv_kpb; if (dmfptr->tbl_record_count) { varp->opv_leafcount = dmfptr->tbl_record_count/varp->opv_kpleaf; if (varp->opv_leafcount < 1.0) varp->opv_leafcount = 1.0; } else varp->opv_leafcount = 1.0; } } else if ((dmfptr->tbl_storage_type == DB_BTRE_STORE) || (dmfptr->tbl_storage_type == DB_RTRE_STORE)) { /* ** Let dmf work this out, since it knows about page and row ** overhead and also takes into consideration potentially ** different entry lengths for leaf and index pages */ varp->opv_kpb = dmfptr->tbl_kperpage; varp->opv_kpleaf = dmfptr->tbl_kperleaf; varp->opv_dircost = dmfptr->tbl_ixlevels; varp->opv_leafcount = dmfptr->tbl_lpage_count; } else if (dmfptr->tbl_storage_type == DB_ISAM_STORE) { OPO_TUPLES tupleperblock; /* estimated number of tuples in an index ** block where size of one tuple is the ** length of the attribute being ordered ** on ... plus the size a block ptr */ OPO_BLOCKS blocks; /* number of blocks accessed in index */ OPO_BLOCKS previous; /* total blocks accessed in the index */ i4 dirsearch; OPO_BLOCKS indexpages; OPO_BLOCKS root_blocks; /* number of index pages which are not ** on the final fanout level */ OPO_BLOCKS index_blocks; /* number of index pages for btree */ OPO_BLOCKS leaf_blocks; /* number of index and leaf pages for ** btree */ indexpages = dmfptr->tbl_ipage_count; if (indexpages < 1.0) indexpages = 1.0; /* ** Let dmf work out keys-per-page since it know about ** page and row overhead for different table types */ varp->opv_kpb = dmfptr->tbl_kperpage; tupleperblock = dmfptr->tbl_kperpage; root_blocks = index_blocks = 0.0; previous = 1.0; leaf_blocks = blocks = tupleperblock; for (dirsearch = 1; previous < indexpages; ) { root_blocks = index_blocks; index_blocks = previous; /* previous level contain only index blocks */ previous += blocks; /* calculate total blocks used so far */ leaf_blocks = blocks; /* lowest level contain leaf pages for btree */ blocks = blocks * blocks; dirsearch += 1; /* probably faster than using logs */ } blocks = leaf_blocks; /* get previous value, i.e. leaf page ** level for btree */ varp->opv_dircost = dirsearch - (previous - indexpages)/blocks; /* ** calculate the average directory ** search cost; if fanout is incomplete ** then subtract the probabilistic fraction ** in which one less directory search would be ** made - FIXME ** get fill factor involved in this ** calculation */ blocks = (dmfptr->tbl_dpage_count < 1.0) ? 1.0 : dmfptr->tbl_dpage_count; varp->opv_leafcount = blocks; /* opv_leafcount is probably not used anymore since DMF ** returns an leaf page count in the tbl_dpage_count field ** for a secondary btree index now */ /* used for BTREE secondary indexes which have no leaf pages ** to put an upper bound on the number of pages that ** can be hit on any particular level of the btree, which ** can be thought of as data pages */ } } else { /* Temporary relation */ /* ** Determine the best possible page size for this relation ** Try to fit at least 20 rows in the temp relation if possible. */ pagesize = opn_pagesize(global->ops_cb->ops_server, eqsp->opn_relwid); } opo_orig(subquery, cop); /* initialize the ordeqc of the DB_ORIG ** node */ if (distributed) opd_orig(subquery, cop); /* initialize the distributed portion of ** the orig node */ if (keyed_only) return(OPN_SIGOK); if (cop->opo_cost.opo_pagesize == 0) cop->opo_cost.opo_pagesize = pagesize; /* FIXME - is not opo_dio 0 here ? */ { OPO_ISORT ordering; /* this is the ordering which can be ** assumed after keying has been done in ** the relation, - OPE_NOEQLCS if no ** ordering can be assumed ** not used here */ opn_prcost (subquery, cop, &sargtype, &pblk, &hp, &imtid, FALSE, &ordering, &pt_valid, &dio, &cpu); } dio += cop->opo_cost.opo_dio; /* add disk i/o ** cost to restrict/project ** i.e. cost of reading tuples using ** keyed access or cost of reading ** entire relation */ if (selapplied) pr_tuples = rlsp->opn_reltups; /* duplicate this info here ** to help lock chooser. We will ** use this to guess how many ** pages in the result relation ** will be touched. It is also ** printed out in the QEP */ else pr_tuples = rlsp->opn_reltups * selectivity; /* do not ** apply selectivity twice */ if (pr_tuples < 1.0) pr_tuples = 1.0; /* FIXME - same thing */ cpu += cop->opo_cost.opo_cpu; /* get CPU time used to project and ** restrict the node */ prcost = opn_cost(subquery, dio, cpu); /* count another fragment for trace point op188/op215/op216 */ subquery->ops_currfragment++; /* With traditional enum, the only way to have an ops_cost is to have ** a best plan, which implies we looked at all leaves already. So ** presumably enum memory was dumped so that the search could continue. ** It's safe to assume that a PR/ORIG subtree that costs more than the ** entire best plan so far is not useful, and we might as well reduce ** the search space. ** ** Things are very different with the new enumeration. New enumeration ** can and will look for best-plans that do NOT encompass all leaves. ** An early pass may generate a very cheap best-fragment that does not ** involve some expensive leaf. A later pass (that includes that ** fragment) may require the expensive leaf to be able to form a plan. ** If all we make is the ORIG, it makes it look like the var can only ** be keyed to, which may in fact not be right. This could prevent ** new enum from finding a valid plan for the larger set of tables, until ** it flushes enumeration memory and tries again. Avoid this by always ** saving both the ORIG and PR/ORIG CO-tree forest. ** ** (If somehow we got here as root, act normally; shouldn't happen ** during greedy enum.) */ if ((global->ops_estate.opn_rootflg || (subquery->ops_mask & OPS_LAENUM) == 0) && prcost >= subquery->ops_cost) { if (op215) { global->ops_trace.opt_subquery = subquery; global->ops_trace.opt_conode = cop; opt_cotree(cop); global->ops_trace.opt_conode = NULL; } return(OPN_SIGOK); /* do not do anything if the cost is larger ** than the current best plan */ } ncop = opn_cmemory(subquery); /* get space for a project-restrict CO node */ ncop->opo_storage = DB_HEAP_STORE; /* no reformat for this CO node */ { OPV_GRV *grvp; /* ptr to global range var descriptor ** for ORIG node */ if ((cop->opo_storage == DB_HEAP_STORE) && (grvp = varp->opv_grv) ) { if (grvp->opv_gsubselect && ( (grvp->opv_gsubselect->opv_subquery->ops_sqtype == OPS_RFAGG) || (grvp->opv_gsubselect->opv_subquery->ops_sqtype == OPS_HFAGG) || (grvp->opv_gsubselect->opv_subquery->ops_sqtype == OPS_RSAGG) || (grvp->opv_gsubselect->opv_subquery->ops_sqtype == OPS_VIEW) ) ) # if 0 check for noncorrelated rfagg removed, removed since correlated aggregates need an SEJOIN and these need to be projected/restrict always because OPC has a restriction in which the parent node of an aggregate result needs to be a PST_BOP with a "subselect indicator" # endif { /* eliminate node from list to reduce cost calculations needed */ jsbtp->opn_coback = jsbtp->opn_coforw = (OPO_CO *)jsbtp; } if (grvp->opv_gquery && ( (grvp->opv_created == OPS_RFAGG) || (grvp->opv_created == OPS_FAGG) )) ncop->opo_storage = DB_SORT_STORE; /* function aggregate has ** ordered by lists */ } } /* create a new CO project restrict node ncop which points to cop */ cop->opo_pointercnt++; /* cop has one more CO pointer to it */ if (!(cop->opo_pointercnt)) opx_error(E_OP04A8_USAGEOVFL); /* report error is usage count overflows */ ncop->opo_ordeqc = OPE_NOEQCLS; switch (sargtype) /* define adjacent duplicate factor and ** sortedness factor */ { case ADC_KEXACTKEY: /* isam or hash or explicit equality TID ** clause in the qualification, (assume the ** boolfact sarg constants are sorted and ** then used to access the relation, this is ** done by opb_create) */ { if (hp) ncop->opo_cost.opo_adfactor = hp->oph_reptf; /* this may happen ** with tid access */ else ncop->opo_cost.opo_adfactor = 1.0; /* we do not have an ordering ** equivalence class yet so worst case ** adjacent duplicate factor */ ncop->opo_cost.opo_sortfact = rlsp->opn_reltups; /* number of ** tuples in the relation */ /* b107351 */ if ( varp->opv_grv->opv_relation->rdr_rel->tbl_storage_type == DB_HASH_STORE ) ncop->opo_storage = DB_HASH_STORE; else { if (varp->opv_grv->opv_relation->rdr_rel->tbl_storage_type == DB_BTRE_STORE) ncop->opo_storage = DB_SORT_STORE; /* mark node as sorted on the ** ordering equivalence class if we ** can guarantee it, this will cause ** opo_pr to lookup the ordering which ** is contained in range var descriptor ** FIXME- need to check multi-attribute ** ordering portion which is not ordered */ else ncop->opo_storage = varp->opv_grv->opv_relation->rdr_rel->tbl_storage_type; } break; } case ADC_KRANGEKEY: /* must be isam or btree */ { /*static*/opn_adfsf (ncop, cop, blocks, pblk, rlsp->opn_reltups); break; } default: /* read whole relation */ /*static*/opn_adfsf (ncop, cop, blocks, cop->opo_cost.opo_reltotb, rlsp->opn_reltups); } ncop->opo_outer = cop; /* outer node is leaf i.e. original ** relation */ ncop->opo_sjpr = DB_PR; /* project restrict node */ ncop->opo_union.opo_ojid = OPL_NOOUTER; /* not an outer join */ ncop->opo_maps = &eqsp->opn_maps; /* save ptr to equivalence class ** map of attributes which will ** be returned */ ncop->opo_cost.opo_pagesize = pagesize; ncop->opo_cost.opo_tpb = (i4)opn_tpblk(global->ops_cb->ops_server, pagesize, eqsp->opn_relwid); /* tuples per block */ ncop->opo_cost.opo_cvar.opo_cache = OPN_SCACHE; /* project restrict will use ** the single page cache prior to ** accessing the block reads */ ncop->opo_cost.opo_reltotb = blocks; /* estimated number of blocks in ** relation after project restrict*/ ncop->opo_cost.opo_dio = dio; /* disk i/o to do this project ** restrict */ ncop->opo_cost.opo_cpu = cpu; /* cpu required for project ** restrict*/ if (pt_valid) ncop->opo_cost.opo_pagestouched = pblk; /* number of primary blocks ** read is an accurate estimate of ** pages touched */ else ncop->opo_cost.opo_pagestouched = 0.0; /* if it is not accurate then ** estimate 0.0 pages touched so ** that page level locking will ** be used */ ncop->opo_cost.opo_tups = pr_tuples; /* number of tuples in PR result */ if( (cop->opo_storage == DB_BTRE_STORE) /* if a btree */ || (cop->opo_storage == DB_ISAM_STORE) /* if a isam, then ordering ** can be used for partial ** sort merge, note that ** ncop->opo_storage is ** DB_HEAP_STORE so that ** ordering is not used for ** a full sort merge */ || (ncop->opo_storage == DB_SORT_STORE)) /* or sorted due to exact ** keys */ opo_pr(subquery, ncop); /* initialize the ordering ** for the project restrict ** node */ /* In the following condition we check first whether the relation: ** (1) has a multi-attribute key with matching attributes in the ** query (opb_count > 1). ** (2) has a multi-attribute key with at least one matching boolean ** factor in the query (opb_bfcount > 0). ** If either is false then there is either no multi-attribute key, ** or a file scan is preferable to using the multi-attribute key. */ if ( varp->opv_mbf.opb_count > 1 && varp->opv_mbf.opb_bfcount > 0 ) { /* look at all histograms in the relation and determine which ** have a requirement for special processing due to multi-attribute ** index restrictivity rules, FIXME, this should be replaced ** by TRUE multi-attribute processing */ OPH_HISTOGRAM *histp; OPZ_AT *abase; /* base of array of ptrs to joinop ** attribute elements */ abase = subquery->ops_attrs.opz_base; /* base of array of ptrs to ** joinop attribute elements */ for (histp = rlsp->opn_histogram; histp; histp = histp->oph_next) if (!(histp->oph_mask & OPH_COMPOSITE) && abase->opz_attnums[histp->oph_attribute]->opz_mask & OPZ_MAINDEX) { histp->oph_origtuples = rlsp->opn_reltups; histp->oph_odefined = TRUE; } } cop = ncop; /* all reformats go from cop ** (we must make a copy of the ** original relation before ** reformatting) */ varp->opv_pr = ncop; /* save project-restrict for calculation ** of initial guess of best set of ** relations */ varp->opv_prtuples = pr_tuples; { bool root_flag; root_flag = global->ops_estate.opn_rootflg; if (distributed) { bool useful; /* TRUE if distributed plan ** is useful for at least one site */ if (root_flag) { OPO_COST cpu_sortcost; /* total cpu cost for sort node */ OPO_COST dio_sortcost; /* total dio cost for sort node */ if (subquery->ops_msort.opo_mask & OPO_MTOPSORT) { cpu_sortcost = eqsp->opn_cpu; dio_sortcost = eqsp->opn_dio; } else { cpu_sortcost = 0.0; dio_sortcost = 0.0; } useful = opd_prleaf(subquery, rlsp, cop, eqsp->opn_relwid, cpu_sortcost, dio_sortcost); /* add ** distributed costs for this node*/ if (!useful || opd_bestplan(subquery, cop, ((subquery->ops_msort.opo_mask & OPO_MTOPSORT) != 0), cpu_sortcost, dio_sortcost, &sortcop)) { if (op215) { global->ops_trace.opt_subquery = subquery; global->ops_trace.opt_conode = cop; opt_cotree(cop); global->ops_trace.opt_conode = NULL; } return(OPN_SIGOK); } if (subquery->ops_msort.opo_mask & OPO_MTOPSORT) opn_createsort(subquery, sortcop, cop, eqsp); /* initialize the fields of ** the top sort node, opd_bestplan has already ** placed the top sort node in the appropriate ** CO plan locations */ } else { op216 = FALSE; /* op216 traces only the best plans */ useful = opd_prleaf(subquery, rlsp, cop, eqsp->opn_relwid, (OPO_CPU)0.0, (OPO_BLOCKS)0.0); /* add ** distributed costs for this node*/ } } else if (root_flag) { OPO_COST prcost1; prcost1 = opn_cost(subquery, dio, cpu); if (subquery->ops_msort.opo_mask & OPO_MTOPSORT) { /* FIXME - can detect user specified orderings here and ** avoid a sort node */ OPO_ISORT sordering; bool sortrequired; if (cop->opo_storage == DB_SORT_STORE) sordering = cop->opo_ordeqc; else sordering = OPE_NOEQCLS; sortrequired = FALSE; /* no deferred semantics problem for ** single table lookups */ if (subquery->ops_msort.opo_mask & OPO_MDESC) { /* Single node and descending order by. Assure the key ** key structure isn't multi-attr in which the 1st column ** is in a constant BF. Read backwards doesn't work. */ OPO_CO *orig = cop; OPO_EQLIST *orig_list; if ((orig->opo_sjpr == DB_ORIG || (orig = orig->opo_outer) && orig->opo_sjpr == DB_ORIG) && !(orig->opo_ordeqc < subquery->ops_eclass.ope_ev || subquery->ops_bfs.opb_bfeqc == NULL) && (orig->opo_ordeqc > OPE_NOEQCLS || (orig_list = subquery->ops_msort.opo_base->opo_stable [orig->opo_ordeqc-subquery->ops_eclass.ope_ev]->opo_eqlist) == NULL || BTtest((i4)orig_list->opo_eqorder[0], (char *)subquery->ops_bfs.opb_bfeqc))) sortrequired = TRUE; } if (opn_checksort(subquery, &subquery->ops_bestco, subquery->ops_cost, dio, cpu, eqsp, &prcost1, sordering, &sortrequired, cop) ) { if (op215) { global->ops_trace.opt_subquery = subquery; global->ops_trace.opt_conode = cop; opt_cotree(cop); global->ops_trace.opt_conode = NULL; } return(OPN_SIGOK); /* return if added cost of sort node, or creating ** relation is too expensive */ } if (sortrequired) sortcop = opn_cmemory(subquery); /* allocate memory here so that ** we do not run out of memory prior to ** calling opn_dcmemory, which would delete ** the previous best plan */ } opn_dcmemory(subquery, subquery->ops_bestco); if (sortcop) { opn_createsort(subquery, sortcop, cop, eqsp); subquery->ops_bestco = sortcop; } else subquery->ops_bestco = cop; subquery->ops_besthisto = rlsp->opn_histogram; if (global->ops_gmask & OPS_FPEXCEPTION) global->ops_gmask |= OPS_BFPEXCEPTION; /* a plan was found which ** did not have a floating point exception ** so skip over subsequent plans with ** floating point exceptions */ else global->ops_gmask &= (~OPS_BFPEXCEPTION); /* reset exception ** flag if plan was found to be free ** of float exceptions */ subquery->ops_tcurrent++; /* increment plan number */ subquery->ops_tplan = subquery->ops_tcurrent; /* since this is **a single leaf tree, opn_ceval ** will detect a timeout */ /* save the best fragment so far for trace point op188/op215/op216 */ subquery->ops_bestfragment = subquery->ops_currfragment; subquery->ops_cost = prcost1; global->ops_estate.opn_search = FALSE; /* new best CO ** found so memory garbage ** collection routines may be ** useful */ } if (!root_flag) { op216 = FALSE; /* op216 traces only the best plans */ opn_coinsert (jsbtp, cop); /* put in co list, do not place into CO ** if at the root since it may be deallocated ** when a new best plan is found, and the PR ** is worthless if it was once the best plan ** so it does not need to be in the linked list ** of CO nodes */ } } if (op188 || op215 || op216) { global->ops_trace.opt_subquery = subquery; if (!sortcop) { global->ops_trace.opt_conode = cop; opt_cotree(cop); } else { global->ops_trace.opt_conode = sortcop; opt_cotree(sortcop); } global->ops_trace.opt_conode = NULL; } if (global->ops_cb->ops_check) { i4 first; i4 second; /* If we have a usable plan, check for trace point op255 timeout setting */ if( subquery->ops_bestco && opt_svtrace( global->ops_cb, OPT_F127_TIMEOUT, &first, &second) && !(subquery->ops_mask & OPS_LAENUM) && (first <= subquery->ops_tcurrent) && /* check if all subqueries should be timed out OR a particular subquery is being searched for */ ( (second == 0) || (second == subquery->ops_tsubquery) ) ) { opx_verror(E_DB_WARN, E_OP0006_TIMEOUT, (OPX_FACILITY)0); opn_exit(subquery, FALSE); /* ** At this point we return the subquery->opn_bestco tree. ** This could also happen in freeco and enumerate */ return(OPN_SIGEXIT); } } return(OPN_SIGOK); }
/* ** NOTE: in SQL grammar target_list of a subselect is processed BEFORE the ** from_list; consequently, data types of target list elements are not ** known when we build RESDOM nodes for the target list elements of form ** [<corr_name>.]<col_name>. In psl_p_tlist(), we revisit the prototype ** tree and fill in the newly available information (type, length, ** precision, etc.) ** ** When making changes to pst_adresdom(), please take time to understand ** the effect these changes may have on the processing of prototype trees. */ DB_STATUS pst_adresdom( char *attname, PST_QNODE *left, PST_QNODE *right, PSS_SESBLK *cb, PSQ_CB *psq_cb, PST_QNODE **newnode) { DB_STATUS status; DMT_ATT_ENTRY *coldesc; DMT_ATT_ENTRY column; PSS_RNGTAB *resrange; char colname[sizeof(DB_ATT_NAME) + 1]; /* null term. */ PST_RSDM_NODE resdom; i4 err_code; PSC_RESCOL *rescol; ADF_CB *adf_scb; i2 null_adjust = 0; i4 temp_collID; /* Convert column name to a null-terminated string. */ (VOID) MEcopy((PTR) attname, sizeof(DB_ATT_NAME), (PTR) colname); colname[sizeof(DB_ATT_NAME)] = '\0'; (VOID) STtrmwhite(colname); /* For these operations, the result domain comes from the result table */ if (psq_cb->psq_mode == PSQ_APPEND || psq_cb->psq_mode == PSQ_PROT) { /* Get the result range variable */ if (psq_cb->psq_qlang == DB_SQL) { resrange = &cb->pss_auxrng.pss_rsrng; } else { resrange = &cb->pss_usrrange.pss_rsrng; } /* "tid" result column not allowed with these operations */ if (!STcasecmp(((*cb->pss_dbxlate & CUI_ID_REG_U) ? "TID" : "tid"), colname )) { psf_error(2100L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 4, (i4) sizeof(cb->pss_lineno), &cb->pss_lineno, psf_trmwhite(sizeof(DB_TAB_NAME), (char *) &resrange->pss_tabname), &resrange->pss_tabname, psf_trmwhite(sizeof(DB_OWN_NAME), (char *) &resrange->pss_ownname), &resrange->pss_ownname, psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname); return (E_DB_ERROR); } /* Get the column description */ coldesc = pst_coldesc(resrange, (DB_ATT_NAME *) attname); if (coldesc == (DMT_ATT_ENTRY *) NULL) { psf_error(2100L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 4, (i4) sizeof(cb->pss_lineno), &cb->pss_lineno, psf_trmwhite(sizeof(DB_TAB_NAME), (char *) &resrange->pss_tabname), &resrange->pss_tabname, psf_trmwhite(sizeof(DB_OWN_NAME), (char *) &resrange->pss_ownname), &resrange->pss_ownname, psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname); return (E_DB_ERROR); } if (coldesc->att_flags & DMU_F_SYS_MAINTAINED) { psf_error(E_US1900_6400_UPD_LOGKEY, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 4, (i4) sizeof(cb->pss_lineno), &cb->pss_lineno, psf_trmwhite(sizeof(DB_TAB_NAME), (char *) &resrange->pss_tabname), &resrange->pss_tabname, psf_trmwhite(sizeof(DB_OWN_NAME), (char *) &resrange->pss_ownname), &resrange->pss_ownname, psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname); return (E_DB_ERROR); } } else if (psq_cb->psq_mode == PSQ_REPLACE) { /* ** For the "replace" command, use the result range variable that's ** in the normal user range table, not the special slot that's ** reserved for the result table in the append command. */ /* Get the result range variable */ resrange = cb->pss_resrng; /* "tid" result column not allowed with these operations */ if (!STcasecmp(((*cb->pss_dbxlate & CUI_ID_REG_U) ? "TID" : "tid"), colname)) { psf_error(2100L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 4, (i4) sizeof(cb->pss_lineno), &cb->pss_lineno, psf_trmwhite(sizeof(DB_TAB_NAME), (char *) &resrange->pss_tabname), &resrange->pss_tabname, psf_trmwhite(sizeof(DB_OWN_NAME), (char *) &resrange->pss_ownname), &resrange->pss_ownname, psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname); return (E_DB_ERROR); } /* Get the column description */ coldesc = pst_coldesc(resrange, (DB_ATT_NAME *) attname); if (coldesc == (DMT_ATT_ENTRY *) NULL) { psf_error(2100L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 4, (i4) sizeof(cb->pss_lineno), &cb->pss_lineno, psf_trmwhite(sizeof(DB_TAB_NAME), (char *) &resrange->pss_tabname), &resrange->pss_tabname, psf_trmwhite(sizeof(DB_OWN_NAME), (char *) &resrange->pss_ownname), &resrange->pss_ownname, psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname); return (E_DB_ERROR); } if (coldesc->att_flags & DMU_F_SYS_MAINTAINED) { psf_error(E_US1900_6400_UPD_LOGKEY, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 4, (i4) sizeof(cb->pss_lineno), &cb->pss_lineno, psf_trmwhite(sizeof(DB_TAB_NAME), (char *) &resrange->pss_tabname), &resrange->pss_tabname, psf_trmwhite(sizeof(DB_OWN_NAME), (char *) &resrange->pss_ownname), &resrange->pss_ownname, psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname); return (E_DB_ERROR); } } else if (psq_cb->psq_mode == PSQ_REPCURS) { /* ** For the "replace cursor" command, the info comes from the cursor ** control block. Cursor column list and update map should always ** specify same column set, so the second if statemnt (BTtest) could, ** perhaps, be removed. */ rescol = psq_ccol(cb->pss_crsr, (DB_ATT_NAME *) attname); if (rescol == (PSC_RESCOL *) NULL) { psf_error(2207L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 3, sizeof(cb->pss_lineno), &cb->pss_lineno, psf_trmwhite(DB_CURSOR_MAXNAME, cb->pss_crsr->psc_blkid.db_cur_name), cb->pss_crsr->psc_blkid.db_cur_name, psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname); return (E_DB_ERROR); } /* Make sure the column was declared "for update" */ if (!BTtest((i4) rescol->psc_attid.db_att_id, (char *) &cb->pss_crsr->psc_updmap)) { psf_error(2207L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 3, sizeof(cb->pss_lineno), &cb->pss_lineno, psf_trmwhite(DB_CURSOR_MAXNAME, cb->pss_crsr->psc_blkid.db_cur_name), cb->pss_crsr->psc_blkid.db_cur_name, psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname); return (E_DB_ERROR); } /* Set up column descriptor */ coldesc = &column; MEcopy((char *) attname, sizeof(DB_ATT_NAME), (char *) &coldesc->att_name); #ifdef NO /* ** Count columns. Give error if too many. One extra for tid. */ cb->pss_rsdmno++; if (cb->pss_rsdmno > (DB_MAX_COLS + 1)) { psf_error(2130L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 1, (i4) sizeof(cb->pss_lineno), &cb->pss_lineno); return (E_DB_ERROR); } coldesc->att_number = cb->pss_rsdmno; #endif coldesc->att_number = rescol->psc_attid.db_att_id; coldesc->att_type = rescol->psc_type; coldesc->att_width = rescol->psc_len; coldesc->att_prec = rescol->psc_prec; coldesc->att_collID = -1; coldesc->att_geomtype = -1; coldesc->att_srid = -1; coldesc->att_encflags = 0; coldesc->att_encwid = 0; } else { /* ** In all other cases, just take the datatype info ** from the right child. */ coldesc = &column; MEcopy((char *) attname, sizeof(DB_ATT_NAME), (char *) &coldesc->att_name); /* ** Count columns. Give error if too many. One extra for tid. */ cb->pss_rsdmno++; if (cb->pss_rsdmno > (DB_MAX_COLS + 1)) { psf_error(2130L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 1, (i4) sizeof(cb->pss_lineno), &cb->pss_lineno); return (E_DB_ERROR); } coldesc->att_number = cb->pss_rsdmno; status = pst_rsdm_dt_resolve(right, coldesc, cb, psq_cb); if (DB_FAILURE_MACRO(status)) return(status); } /* Copy attribute information into PST_RSDM_NODE */ resdom.pst_rsno = coldesc->att_number; /* The two fields below are initialized for a common case. ** They are context sensitive and in many cases may have to be ** modified by the caller of this routine. */ resdom.pst_ntargno = resdom.pst_rsno; resdom.pst_ttargtype = PST_USER; resdom.pst_dmuflags = 0; /* Don't bother with the conversion id for now */ /* Not for update until we know otherwise */ resdom.pst_rsupdt = FALSE; resdom.pst_rsflags = PST_RS_PRINT; MEcopy((char *) &coldesc->att_name, sizeof(DB_ATT_NAME), (char *) resdom.pst_rsname); temp_collID = coldesc->att_collID; /* If client can not handle i8 INTs downgrade to i4 */ adf_scb = (ADF_CB *) cb->pss_adfcb; if ( !(adf_scb->adf_proto_level & AD_I8_PROTO) && (abs(coldesc->att_type) == DB_INT_TYPE) ) { if(coldesc->att_type < 0) { null_adjust = 1; } if((coldesc->att_width - null_adjust) == sizeof(i8)) { coldesc->att_width -= sizeof(i4); } } /* Now allocate the node */ status = pst_node(cb, &cb->pss_ostream, left, right, PST_RESDOM, (char *) &resdom, sizeof(PST_RSDM_NODE), (DB_DT_ID) coldesc->att_type, (i2) coldesc->att_prec, (i4) coldesc->att_width, (DB_ANYTYPE *) NULL, newnode, &psq_cb->psq_error, (i4) 0); if (status != E_DB_OK) { return (status); } (*newnode)->pst_sym.pst_dataval.db_collID = temp_collID; /* Remember the last result domain produced */ cb->pss_tlist = *newnode; return (E_DB_OK); }
/*{ ** Name: psy_subsvars - Scan query tree and replace VAR nodes ** ** Description: ** Scans a tree and finds all VAR nodes for this variable. ** These nodes are looked up in the translation tree and ** replaced by the value found there. If this is for a ** view, the corresponding node must exist in the translation ** tree. If not for a view, a 'zero' node (of a type appropriate based ** on the context) is created and inserted, or, if for a "replace ** cursor" command, a CURVAL node is substituted for the VAR ** node. ** ** This routine is one half of the guts of the whole view ** algorithm. ** ** VAR nodes are detached and replaced with the replacement ** as defined by the view. Note that there can never be any ** problems here with update anomalies, since VAR nodes are ** only used in retrieve contexts. ** ** It does some extra processing with RESDOM nodes with ** resno = 0. These nodes specify a 'tid' domain, and are ** included by the parser on REPLACE and DELETE commands ** Subsvars will allow this construct iff the right hand pointer is a ** VAR node with attno = 0. In pre-Jupiter versions, this used ** to update the variable number in these tid RESDOMs; in Jupiter, ** however, it was changed to keep the same result variable number ** throughout the qrymod process, so this should be unnecessary. ** This is because the resvar is the variable number of the one and ** only underlying base relation of the view on an update ** (which is presumably the only case where this can come ** up). Psy_vrscan has already insured that there can only be ** a single base relation in this case. ** ** This whole messy thing is only done with view substitutions. ** NOTE: THIS IS NOT TRUE! IT IS ALSO DONE FOR INTEGRITY SUBSTITUTIONS! ** I DON'T KNOW WHY THIS COMMENT IS HERE. ** ** In order to fix the handling of aggregates over views, subsvars ** calls psy_apql at the appropriate place to append a ** view qualification (if any). It is done here to handle nested ** aggregates correctly since after psy_subsvars we no longer know ** which nested aggregates actually contained the view variable. ** The view qual will be appended if and only if the view var ** appears explicitly within the immediate scope of a root node ** (NOT in a nested aggregate.) ** ** If at any scope we encounter a var node, we add the qualification ** to that scope. Once a var node has been found in a scope (and ** the qualifaction added), for example, a nested aggregate, the ** qualification is not added to an outer scope unless a var node ** in the view or integ has been found in that outer scope. ** ** ** Inputs: ** proot Pointer to pointer to root of tree ** to be updated ** rngvar view variable range table entry ** transtree Pointer to the target list of the ** translation tree ** vmode PSQ_VIEW if called from view processor, ** PSQ_APPEND is called from the integrity ** processor with an APPEND command, else ** something else. Mostly, changes ** handling of tid nodes, and forces an ** error on a view if the VAR node in the ** scanned tree does not exist in the ** vtree. ** vqual View qualification to be appended, ** if any. ** resvar Range table entry for result variable ** in query being modified. ** from_list from_list from view getting added. ** qmode Query mode of user query. ** cursid Cursor id of current cursor, if any ** result Pointer to indicator for result. ** dup_rb Ptr to dup. request block ** pss_op_mask -- 0 ** pss_num_joins -- PST_NOJOIN ** pss_tree_info -- NULL ** pss_mstream -- ptr to memory stream to be used ** pss_err_blk -- ptr to error block ** ** Outputs: ** proot User query tree can be updated ** result Filled in with TRUE if view variable ** was found, FALSE if not. Valid only ** if E_DB_OK returned. ** dup_rb ** pss_err_blk Filled in if an error happens. ** Returns: ** E_DB_OK Success ** E_DB_ERROR Failure ** Exceptions: ** none ** ** Side Effects: ** Can allocate memory ** ** History: ** 19-jun-86 (jeff) ** written ** 1-oct-86 (daved) ** set return to TRUE if a VAR node is found. ** 23-dec-86 (daved) ** copy qualification before appending it. this avoids graphs in ** the tree as well as, and more importantly, the re-use of the ** memory used by the qualification before its time. That is, ** if vqual is in temporary memory and gets deleted but the ** proot tree thinks the memory is still around, bad things happen. ** 12-aug-87 (stec) ** Removed test for special 'tid' attribute case, which, according ** to Jeff is no longer needed. ** Check for special 'tid' resdom now includes open cursor stmt. ** 15-oct-87 (stec) ** Added the removed test for special 'tid' attribute case; ** it's necessary for checking cases like "retrieve (viewname.tid)". ** 03-dec-87 (stec) ** Change psy_apql interface. ** 31-dec-87 (stec) ** Cleanup. ** 08-feb-88 (stec) ** Modify psy_subsvars to generate CURVAL nodes for replace cursor statement. ** 04-nov-88 (stec) ** Fix a view substitution bug. When visiting an AGHEAD node it may happen ** that count(*), or count(const) were defined, in which case there are ** no VAR nodes and the applicability of the view has to be determined from ** the relation bitmap in the node. This anomaly exists only in SQL. ** 14-dec-88 (stec) ** Fix correlated subqueries bug. ** 05-apr-89 (andre) ** simplify the test for when reference to the view in pst_tvrm is to ** be replaced with the tables used to define the view. We no longer ** care if there were any variables found below the root node, instead, ** we do it for every root node which has a bit corresponding to the ** view set in pst_tvrm. ** As a part of the fix, qualification of the view will be appended to ** the tree whenever the view is found in the pst_tvrm of the root ** node. ** Besides allowing us to get rid of calling recursive psy_fixmap() in ** psy_view, but it also fixes bugs such as: ** "select const from view" returning more rows than there are in the ** view. ** 04-may-89 (andre) ** for the time being, set pst_maks1 in all ROOT-type nodes to 0. ** 01-jun-89 (andre) ** The preceding fix was not perfect. ** "create view v as select * from t where <qual>; ** select <aggregate> from v\g" ** would result in as many rows containing result of applying ** <aggregate> as there are rows in v. This happens only in SQL. The ** problem is that <qual> gets appended to both AGGHEAD node and the ** ROOT node. The solution is as follows: ** For every node N s.t. N is of type ROOT or SUBSELECT, remember ** if <qual> has been applied to an AGGHEAD node in the left ** subtree of N (in SQL you can not have AGGHEADs in the ** "where-clause"). If <qual> has been applied to AGGHEAD(s) in ** the left subtree of N, do not append it to the right subtree of ** N. ** 22-jun-89 (andre) ** And yet another fix for the previous bug fix. I have incorrectly ** assumed that there may be no AGGHEADs in the right subtrre of ** ROOT/SUBSEL (select ... having agg(col)). Before setting *mask to ** indicate that an AGGHEAD has been seen, make sure that we are in the ** left subtree of the immediate ROOT/SUBSEL parent ** (mask != (i4 *) NULL). If mask is NULL, we must be in the right ** subtree, and the fact that we saw an AGGHEAD is of no importance ** (or shall I add "or so I believe"?) ** 13-sep-89 (andre) ** receive ptr to PSS_DUPRB which will point at memopry stream and ** error block + it will be used when calling pst_treedup(). The ** fields in dup_rb must be set as follows: ** pss_op_mask -- 0 ** pss_num_joins -- PST_NOJOIN ** pss_tree_info -- NULL ** pss_mstream -- ptr to memory stream to be used ** pss_err_blk -- ptr to error block ** 14-sep-92 (andre) ** do not zero out pst_mask1 in PST_ROOT and PST_SUBSEL node ** (fix for bug 45238) ** 11-feb-93 (andre) ** if a query tree involved a reference to a TID attribute of a view V, ** replace it with a reference to TID attribute of V's underlying table ** or view; this is accomplished by replacing variable number found in ** the PST_VAR node with the variable number of the view's underlying ** table/view (which can be found by looking for the first set bit in ** from_list) ** 27-nov-02 (inkdo01) ** Range table expansion (i4 changed to PSAT_J_MASK). ** 13-Jun-2006 (kschendel) ** Barf if we translate a var node to a seqop default in an INSERT. ** This only happens if we're translating an integrity where-clause ** tree, and a var in that where-clause isn't mentioned in the ** values list, so we stick the default in instead. Seqops aren't ** allowed in where clauses. (It would imply that the insert ** integrity-permission depends on the sequence value, which is ** silly at best.) ** 15-May-2007 (kiria01) b111992 ** Flatten out much of the recursion of this function to reduce ** runtime stack usage - especially bad with views containing ** massive IN clauses (>5K elements). ** 28-nov-2007 (dougi) ** Add PSQ_REPDYN to PSQ_DEFCURS test for cached dynamic qs. ** 05-Nov-2009 (kiria01) b122841 ** Use psl_mk_const_similar to cast default values directly. ** 12-Nov-2009 (kiria01) b122841 ** Corrected psl_mk_const_similar parameters with explicit ** mstream. ** 5-Feb-2010 (hanal04) Bug 123209 ** psy_integ() calls psy_subsvars() to subsitute VARs in the ** integrity tree with the corresponding nodes from the ** user query. When a VAR is replaced with a CONST cast the ** CONST to the VAR's datatype. This stops the substitution from ** breaking ADE_COMPAREN & ADE_NCOMPAREN processing if the ** VAR was part of an IN LIST. ** 18-May-2010 (kiria01) b123442 ** Force psl_mk_const_similar to generate coercion to cleanly ** enable correct datatype to be represented when substituting ** default values. */ DB_STATUS psy_subsvars( PSS_SESBLK *cb, PST_QNODE **proot, PSS_RNGTAB *rngvar, PST_QNODE *transtree, i4 vmode, PST_QNODE *vqual, PSS_RNGTAB *resvar, PST_J_MASK *from_list, i4 qmode, DB_CURSOR_ID *cursid, i4 *mask, PSS_DUPRB *dup_rb) { PSY_STK stk = {0, 0, {0, }};/* Backtrack stack */ PST_QNODE *t; /* Temporary for *proot */ i4 vn = rngvar ? rngvar->pss_rgno : -1; /* can be NULL on replace cursor statements */ i4 err_code; DB_STATUS status = E_DB_OK; while(proot && (t = *proot)) { /* These 3 mask variables are only used for ROOT, SUBSEL and AGHEAD */ i4 newmask; /* For receiving result from recursive call */ i4 *l_mask; /* For propagating state to recursive caller */ i4 *r_mask; /* .. */ /* ** The recursive nature of this function has been restructured to ** allow for most of the processing to be achieved in an iterative ** manner. Unlike with the other functions in this module, the ** flattening could not be complete due to the 'mask' output parameter ** which requires local storage for certain node types: ROOT, SUBSEL ** and AGHEAD. If we have one of these node types we recurse 1 level ** to process the left and right sub-trees with the correct scoping of ** the 'mask' variable. */ switch (t->pst_sym.pst_type) { case PST_ROOT: /* Handle any unions */ if (t->pst_sym.pst_value.pst_s_root.pst_union.pst_next) { /* ** Defer the tree representing the next subselect in the UNION ** status = psy_subsvars(cb, ** &t->pst_sym.pst_value.pst_s_root.pst_union.pst_next, rngvar, transtree, ** vmode, vqual, resvar, from_list, qmode, cursid, (i4 *) NULL, ** dup_rb); */ psy_push(&stk, (PTR)&t->pst_sym.pst_value.pst_s_root.pst_union.pst_next, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } } /*FALLTHROUGH*/ case PST_SUBSEL: /* ** The following applies when language is SQL: ** if this is a ROOT or a SUBSELECT node, we want to know if the left ** subtree contains AGGHEAD nodes to which view qualification have been ** applied; we are not concerned with AGGHEAD nodes in the ** qualification (there shouldn't be any, anyway) or in other members ** of the union. */ if (cb->pss_lang == DB_SQL) { newmask = 0; l_mask = &newmask; /* we don't care about the right subtree */ r_mask = (i4 *) NULL; } /*FALLTHROUGH*/ case PST_AGHEAD: /* ** The following applies when language is SQL: ** If this is an AGGHEAD node, set a bit in 'mask' to remember that ** we saw it. */ if (t->pst_sym.pst_type == PST_AGHEAD) { if (cb->pss_lang == DB_SQL) { if (l_mask = r_mask = mask) /* ** If we are in the right subtree of the immediate ROOT/SUBSELECT ** parent, mask will be NULL, since we are not concerned with ** AGHEADs in the right subtrees. */ *mask |= PSS_1SAW_AGG; } /* ** pst_mask1 in PST_AGHEAD node is neither used nor set; I think it ** would be a good idea to set it, but at this point there is not a heck ** of a lot that we can do. Here we will zero out PST_AGHEAD.pst_mask1 ** purely for esthetic reasons. */ t->pst_sym.pst_value.pst_s_root.pst_mask1 = 0; } /* ** Recurse 1 level to process the left & right subtrees completly ** so that we can complete the processing of this node */ status = psy_subsvars(cb, &t->pst_left, rngvar, transtree, vmode, vqual, resvar, from_list, qmode, cursid, l_mask, dup_rb); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* Process the right branch */ status = psy_subsvars(cb, &t->pst_right, rngvar, transtree, vmode, vqual, resvar, from_list, qmode, cursid, r_mask, dup_rb); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* Add `from' list to bitmap, remove entry for replaced var */ if (BTtest(vn, (char*)&t->pst_sym.pst_value.pst_s_root.pst_tvrm)) { BTclear(vn, (char*)&t->pst_sym.pst_value.pst_s_root.pst_tvrm); BTor(PST_NUMVARS, (char *)from_list, (char *)&t->pst_sym.pst_value.pst_s_root.pst_tvrm); t->pst_sym.pst_value.pst_s_root.pst_tvrc = BTcount((char*)&t->pst_sym.pst_value.pst_s_root.pst_tvrm, BITS_IN(t->pst_sym.pst_value.pst_s_root.pst_tvrm)); /* ** We will append qualification (if there is one) if the ** following holds: ** 1) This is not an SQL (must be QUEL) query OR ** 2) if node is ROOT or SUBSEL (i.e. not an AGHEAD) then there ** were no AGHEADs found in its left subtree ** ** Let QUAL <==> there is a qualification, ** SQL <==> language is SQL ** ROOT <==> node type is ROOT ** SUBSEL <==> node type is SUBSEL ** AGG <==> node type is AGHEAD ** SAW_AGG <==> mask & PSS_1SAW_AGG. Then ** ** (Do not apply qualification) <==> ** !QUAL + SQL * (ROOT + SUBSEL) * SAW_AGG --> ** (Apply qualification) <==> ** !(!QUAL + SQL * (ROOT + SUBSEL) * SAW_AGG) <==> ** QUAL * !(SQL * (ROOT + SUBSEL) * SAW_AGG) <==> ** QUAL * (!SQL + !((ROOT + SUBSEL) * SAW_AGG)) <==> ** QUAL * (!SQL + !(ROOT + SUBSEL) + !SAW_AGG) <==> ** QUAL * (!SQL + AGG + !SAW_AGG) */ if (vqual && (cb->pss_lang != DB_SQL || t->pst_sym.pst_type == PST_AGHEAD || (*l_mask & PSS_1SAW_AGG) == 0)) { PST_QNODE *vqual_copy; dup_rb->pss_tree = vqual; dup_rb->pss_dup = &vqual_copy; status = pst_treedup(cb, dup_rb); dup_rb->pss_tree = (PST_QNODE *)NULL; dup_rb->pss_dup = (PST_QNODE **)NULL; if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* append view qualification */ status = psy_apql(cb, dup_rb->pss_mstream, vqual_copy, t, dup_rb->pss_err_blk); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } } } /* left & right have been processed */ break; case PST_VAR: /* ** This is a terminal node - the expectation is that left & right are 0 */ /* ** Check for a VAR node but of a different variable than the one ** we are substituting for. REPLACE CURSOR (quel version) is an ** exception because the substitution variable (resvar) is not ** defined, in that case we do not want to execute the code ** below, but want to continue the translation process. */ if (vn != -1 && t->pst_sym.pst_value.pst_s_var.pst_vno != vn) break; /* ** if this is a reference to a TID attribute of a view (which is not ** a "real" attribute), it needs to be translated into a reference ** to the TID attribute of the view's underlying table or view */ if (t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id == 0 && vmode == PSQ_VIEW) { t->pst_sym.pst_value.pst_s_var.pst_vno = BTnext(-1, (char *) from_list, sizeof(*from_list)); } else { PST_QNODE *v; /* find var in vtree */ status = psy_vfind((u_i2)t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id, transtree, &v, dup_rb->pss_err_blk); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } if (v == (PST_QNODE *)NULL) { /* attribute not defined in view */ if (vmode == PSQ_VIEW) { psf_error(E_PS0D03_ATT_NOT_FOUND, 0L, PSF_INTERR, &err_code, dup_rb->pss_err_blk, 0); status = E_DB_SEVERE; proot = NULL; /* Exiting to return error */ break; } /* append defaults for integrity. Integrity might exist on a value ** we are appending by default. I.e., the attribute was not mentioned ** in the target list. We replace the var node in the integrity with ** a default value so that the integrity will read 'default value' ? ** value. */ else if (vmode == PSQ_APPEND) { status = psl_make_default_node(cb, dup_rb->pss_mstream, resvar, t->pst_sym.pst_value .pst_s_var.pst_atno.db_att_id, &v, dup_rb->pss_err_blk); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* Try to cast to column type */ status = psl_mk_const_similar(cb, dup_rb->pss_mstream, &t->pst_sym.pst_dataval, &v, dup_rb->pss_err_blk, NULL); if (DB_FAILURE_MACRO(status)) return(status); /* If we ended up with a sequence default, fail. This is an ** unreasonable situation, integrity where tests should not apply ** to sequence defaults. */ if (v->pst_sym.pst_type == PST_SEQOP) { psf_error(6319, 0, PSF_USERERR, &err_code, dup_rb->pss_err_blk, 0); status = E_DB_ERROR; proot = NULL; /* Exiting to return error */ break; } } /* we would like to delete the qualification for this node since the ** value is not changing and thus we don't need to worry about integrity ** constaints on it. However, we don't do that. Instead we have the ** integrity refer to the value in the current row that reflects the ** value for the attribute. In the replace statement (not replace ** cursor) we just perform the retrieve, the qualification is unneeded ** but doesn't hurt anything. We want to avoid causing a retrieve for ** each update cursor; therefore, we change the varnode to refer to the ** current value (ie the retrieve has already been done). */ else if (vmode == PSQ_REPCURS) { PST_CRVAL_NODE curval; /* Create a CURVAL node for the corresponding column */ curval.pst_curcol.db_att_id = t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id; STRUCT_ASSIGN_MACRO(*cursid, curval.pst_cursor); status = pst_node(cb, dup_rb->pss_mstream, (PST_QNODE *)NULL, (PST_QNODE *)NULL, PST_CURVAL, (PTR)&curval, sizeof(curval), t->pst_sym.pst_dataval.db_datatype, t->pst_sym.pst_dataval.db_prec, t->pst_sym.pst_dataval.db_length, (DB_ANYTYPE *)t->pst_sym.pst_dataval.db_data, &v, dup_rb->pss_err_blk, (i4) 0); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } } } else { dup_rb->pss_tree = v; dup_rb->pss_dup = &v; status = pst_treedup(cb, dup_rb); dup_rb->pss_tree = (PST_QNODE *)NULL; dup_rb->pss_dup = (PST_QNODE **)NULL; if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* When called from psy_integ() v will be found but ** may still need constants to be cast. */ if ((v != (PST_QNODE *)NULL) && (v->pst_sym.pst_type == PST_CONST)) { bool handled; /* Try to cast to column type */ status = psl_mk_const_similar(cb, dup_rb->pss_mstream, &t->pst_sym.pst_dataval, &v, dup_rb->pss_err_blk, &handled); if (DB_FAILURE_MACRO(status)) return(status); } } /* replace VAR node */ if (v != (PST_QNODE *)NULL) { *proot = v; } } /* left and right should have been null as we are on a terminal */ break; case PST_RESDOM: /* Process `TID' resdom used by DELETE, REPLACE and OPEN CURSOR */ if (t->pst_sym.pst_value.pst_s_rsdm.pst_rsno == 0 && (qmode == PSQ_DELETE || qmode == PSQ_REPLACE || qmode == PSQ_DEFCURS || qmode == PSQ_REPDYN)) { /* ** If resvar not specified, or if not resvar, ignore leaf. */ if (resvar && vn == resvar->pss_rgno) { /* t->right better be VAR node, attno 0 */ t = t->pst_right; if (t->pst_sym.pst_type != PST_VAR || t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id != 0 || t->pst_sym.pst_value.pst_s_var.pst_vno != vn) { (VOID) psf_error(E_PS0D02_BAD_TID_NODE, 0L, PSF_INTERR, &err_code, dup_rb->pss_err_blk, 0); status = E_DB_SEVERE; proot = NULL; /* Exiting to return error */ break; } } else if (t->pst_right) /* Process the right branch */ psy_push(&stk, (PTR)&t->pst_right, &status); } else if (t->pst_right) /* Process the right branch */ psy_push(&stk, (PTR)&t->pst_right, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* Process left branch */ if (t->pst_left) psy_push(&stk, (PTR)&t->pst_left, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } break; default: /* ** Just ensure that we traverse the tree */ if (t->pst_right) psy_push(&stk, (PTR)&t->pst_right, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } if (t->pst_left) psy_push(&stk, (PTR)&t->pst_left, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } break; } if (!proot) /* We're breaking out early to exit */ break; /* ** Get next deferred node */ proot = (PST_QNODE**)psy_pop(&stk); } /* Release any outstanding entries */ psy_pop_all(&stk); return status; }
/*{ ** Name: 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: 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 */ } } }