/*{ ** Name: OPU_GSMEMORY_CLOSE - Get memory from the stack ULM memory stream ** ** Description: ** This routine allocates memory from the ULM memory stream that is used ** for the stack style memory usage. [@comment_line@]... ** ** Inputs: ** global - ** State info for the current query. ** global->ops_mstate.ops_ulmrcb - ** The ULM control block. ** global->ops_mstate.ops_sstreamid - ** The stream id. ** size - ** size of the piece of memory to allocate. ** ** Outputs: ** ** Returns: ** The address of the allocated memory. ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 19-July-87 (eric) ** written ** 21-feb-91 (seputis) ** make non-zero initialization of memory an xDEBUG feature ** 16-sep-93 (smc) ** Moved <cs.h> for CS_SID. ** 11-oct-2006 (hayke02) ** Send E_OP0002_NOMEMORY to errlog.log. This change fixes bug 116309. [@history_line@]... [@history_template@]... */ PTR opu_Gsmemory_get( OPS_STATE *global, i4 size) { DB_STATUS ulmstatus; /* return status from ULM */ /* store the stream id */ global->ops_mstate.ops_ulmrcb.ulm_streamid_p = &global->ops_mstate.ops_sstreamid; /* store the size to be allocate */ global->ops_mstate.ops_ulmrcb.ulm_psize = size; if ( (ulmstatus = ulm_palloc( &global->ops_mstate.ops_ulmrcb )) != E_DB_OK ) { if (global->ops_mstate.ops_ulmrcb.ulm_error.err_code == E_UL0005_NOMEM) { opx_lerror(E_OP0002_NOMEMORY, 0, 0, 0, 0, 0); opx_error( E_OP0002_NOMEMORY); /* out of memory */ } #ifdef E_OP0093_ULM_ERROR else opx_verror( ulmstatus, E_OP0093_ULM_ERROR, global->ops_mstate.ops_ulmrcb.ulm_error.err_code); /* check for error */ #endif } #ifdef xDEBUG MEfill( size, (u_char)127, (PTR)global->ops_mstate.ops_ulmrcb.ulm_pptr); /*FIXME ** remove this initialization after ** test for uninitialized memory ** is not required any more */ #endif /* return the allocated memory */ return( global->ops_mstate.ops_ulmrcb.ulm_pptr ); }
/*{ ** Name: opj_uvar - get the attribute number of the union view var node ** ** Description: ** This routine will calculate the attribute number of the union view ** given the equivalence class of this var node ** ** Inputs: ** subquery ptr to subquery containing equivalence ** class of var node ** qual ptr to var node ** ** Outputs: ** attidp ptr to attribute ID for union view ** Returns: ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 28-jun-89 (seputis) ** initial creation [@history_template@]... */ static VOID opj_uvar( OPS_SUBQUERY *subquery, PST_QNODE *qual, OPV_IVARS varno, DB_ATT_ID *attidp) { OPZ_BMATTS *attrmap; /* bit map of attributes in the ** equivalence class */ OPZ_IATTS attno; /* attribute currently being looked at */ OPZ_IATTS maxattr; /* maximum attribute */ OPZ_AT *abase; maxattr = subquery->ops_attrs.opz_av; abase = subquery->ops_attrs.opz_base; /* ptr to base of array of ptrs ** to joinop attributes */ attrmap = &subquery->ops_eclass.ope_base->ope_eqclist [subquery->ops_attrs.opz_base->opz_attnums [qual->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id ]->opz_equcls ]->ope_attrmap; for (attno = -1; (attno = BTnext((i4)attno, (char *)attrmap, maxattr) ) >= 0;) { if (abase->opz_attnums[attno]->opz_varnm == varno) { /* found the union view attribute number so copy it in */ STRUCT_ASSIGN_MACRO(abase->opz_attnums[attno]->opz_attnm, *attidp); break; } } if (attno < 0) opx_error(E_OP0384_NOATTS); /* attribute not found when ** expected */ }
/*{ ** Name: OPU_RSMEMORY_RECLAIM - Return memory to the global memory pool ** ** Description: ** This routine returns memory from the given mark to the end of the ** ULM memory stream to the global memory pool. [@comment_line@]... ** ** Inputs: ** global - ** State info for the current query. ** global->ops_mstate.ops_ulmrcb - ** The ULM control block. ** global->ops_mstate.ops_sstreamid - ** The stream id. ** mark - ** The mark stating where to start returning memory. ** ** Outputs: ** ** Returns: ** The address of the allocated memory. ** Exceptions: ** none ** ** Side Effects: ** Memory is returned to the global memory pool ** ** History: ** 19-July-87 (eric) ** written ** 11-oct-2006 (hayke02) ** Send E_OP0002_NOMEMORY to errlog.log. This change fixes bug 116309. [@history_template@]... */ VOID opu_Rsmemory_reclaim( OPS_STATE *global, ULM_SMARK *mark) { DB_STATUS ulmstatus; /* return status from ULM */ /* store the stream id */ global->ops_mstate.ops_ulmrcb.ulm_streamid_p = &global->ops_mstate.ops_sstreamid; /* store the mark to be initialized */ global->ops_mstate.ops_ulmrcb.ulm_smark = mark; if ( (ulmstatus = ulm_reclaim( &global->ops_mstate.ops_ulmrcb )) != E_DB_OK ) { if (global->ops_mstate.ops_ulmrcb.ulm_error.err_code == E_UL0005_NOMEM) { opx_lerror(E_OP0002_NOMEMORY, 0, 0, 0, 0, 0); opx_error( E_OP0002_NOMEMORY); /* out of memory */ } #ifdef E_OP0093_ULM_ERROR else opx_verror( ulmstatus, E_OP0093_ULM_ERROR, global->ops_mstate.ops_ulmrcb.ulm_error.err_code); /* check for error */ #endif } }
/*{ ** Name: ope_neweqcls - create a new equivalence class for joinop attribute ** ** Description: ** This routine will create a new equivalence class for the joinop ** attribute. It is assumed that the joinop attribute is not already ** assigned to an equivalence class. ** ** Inputs: ** subquery ptr to subquery be analyzed ** attr joinop attribute to be placed ** in equivalence class ** ** Outputs: ** Returns: ** equivalence class which was assigned ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 24-apr-86 (seputis) ** initial creation ** 24-oct-88 (seputis) ** init ope_nulljoin field ** 21-dec-88 (seputis) ** init ope_mask field, check for non-nullable attributes ** 13-may-91 (seputis) ** - fix for b37172 - added ope_sargp ** 31-jan-94 (rganski) ** Removed ope_sargp, due to change to oph_sarglist. ** 9-aug-05 (inkdo01) ** Add support of OJ & EQC bit maps. ** 24-aug-05 (inkdo01) ** Drop ope_ojs - problem was solved differently. ** 21-jul-06 (hayke02) ** Re-introcude and initialize ope_ojs. This change fixes bug 116406. [@history_line@]... */ OPE_IEQCLS ope_neweqcls( OPS_SUBQUERY *subquery, OPZ_IATTS attr) { OPE_IEQCLS eqcls; /* index into equivalence class array */ OPE_ET *ebase; /* ptr to base of equivalence class array */ ebase = subquery->ops_eclass.ope_base; eqcls = subquery->ops_eclass.ope_ev++; /* find first available unassigned ** equivalence class, allocate it */ if (eqcls >= OPE_MAXEQCLS) opx_error(E_OP0301_EQCLS_OVERFLOW); { /* eqcls now contains the unassigned equivalence class index */ OPE_EQCLIST *eqcls_ptr; eqcls_ptr = (OPE_EQCLIST *) opu_memory(subquery->ops_global, (i4) sizeof(OPE_EQCLIST)); ebase->ope_eqclist[eqcls] = eqcls_ptr; /* assign memory for the ** equivalence class element */ MEfill(sizeof(OPZ_BMATTS), (u_char) 0, (PTR) &eqcls_ptr->ope_attrmap); /* initialize equivalence class ** element attribute bit map to zero */ eqcls_ptr->ope_bfindex = OPB_NOBF; /* no constant predicates found yet*/ eqcls_ptr->ope_nbf = OPB_NOBF; /* no sargable predicates found yet */ eqcls_ptr->ope_nulljoin = TRUE; /* keep nulls in joins unless user ** explicitly joins two attributes */ eqcls_ptr->ope_mask = 0; /* mask of various booleans */ if (attr != OPZ_NOATTR) { /* update information in attribute to reference equivalence class*/ OPZ_ATTS *attr_ptr; /* ptr to attribute element to ** be placed in equivalence class */ BTset((i4) attr, (char *)&eqcls_ptr->ope_attrmap); /* set the appropriate ** bit to indicate attribute */ attr_ptr = subquery->ops_attrs.opz_base->opz_attnums[attr]; if (attr_ptr->opz_dataval.db_datatype > 0) /* NULLs will not exist, if this eventually becomes a joining eqcls */ eqcls_ptr->ope_nulljoin = FALSE; eqcls_ptr->ope_eqctype = (attr_ptr->opz_attnm.db_att_id ==DB_IMTID) ? OPE_TID : OPE_NONTID; /* set type of this eqclass to ** OPE_TID if the att is an implicit TID */ MEfill((u_i2) sizeof(OPL_BMOJ), (u_char) 0, (PTR) &eqcls_ptr->ope_ojs); /* init OJ bit map */ attr_ptr->opz_equcls = eqcls; /* remember that this att has ** been assigned to an eqclass */ BTset((i4)eqcls, (char *)&attr_ptr->opz_eqcmap); /* & set eqc in map (for multi-EQC atts) */ } else eqcls_ptr->ope_eqctype = OPE_NONTID; } return(eqcls); }
/*{ ** Name: opu_memory - get joinop memory ** ** Description: ** This routine will allocate the requested size of memory from the ** joinop memory stream. Memory in this stream is not deallocated ** until the optimization has completed. The allocated memory will ** be aligned for any datatype. ** ** Inputs: ** global ptr to global state variable ** size size of memory block requested. ** ** Outputs: ** Returns: ** PTR to aligned memory of "size" bytes ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 16-jun-86 (seputis) ** initial creation ** 4-mar-91 (seputis) ** make initialization of memory an xDEBUG feature ** 11-oct-2006 (hayke02) ** Send E_OP0002_NOMEMORY to errlog.log. This change fixes bug 116309. ** 5-oct-2007 (dougi) ** Accumulate memory acquisition stats. */ PTR opu_memory( OPS_STATE *global, i4 size) { DB_STATUS ulmstatus; /* return status from ULM */ global->ops_mstate.ops_countalloc++; if (size >= 2048) global->ops_mstate.ops_count2kalloc++; global->ops_mstate.ops_totalloc += size; global->ops_mstate.ops_ulmrcb.ulm_streamid_p = &global->ops_mstate.ops_streamid; /* allocate memory ** from the global stream */ global->ops_mstate.ops_ulmrcb.ulm_psize = size; /* size of request */ ulmstatus = ulm_palloc( &global->ops_mstate.ops_ulmrcb ); if (DB_FAILURE_MACRO(ulmstatus)) { if (global->ops_mstate.ops_ulmrcb.ulm_error.err_code == E_UL0005_NOMEM) { opx_lerror(E_OP0002_NOMEMORY, 0, 0, 0, 0, 0); opx_error( E_OP0002_NOMEMORY); /* out of memory */ } #ifdef E_OP0093_ULM_ERROR else opx_verror( ulmstatus, E_OP0093_ULM_ERROR, global->ops_mstate.ops_ulmrcb.ulm_error.err_code); /* check for error */ #endif } #ifdef xDEBUG MEfill( size, (u_char)247, (PTR)global->ops_mstate.ops_ulmrcb.ulm_pptr); /*FIXME ** remove this initialization after ** test for uninitialized memory ** is not required any more */ #endif return( global->ops_mstate.ops_ulmrcb.ulm_pptr ); /* return the allocated ** memory */ }
/*{ ** Name: OPU_OSMEMORY_OPEN - Initialize the stack ULM memory stream ** ** Description: ** This routine initializes the ULM memory stream that will be used ** for the stack style memory allocation and deallocation. [@comment_line@]... ** ** Inputs: ** global - ** State info for the current query. ** global->ops_mstate.ops_ulmrcb - ** The ULM control block. ** ** Outputs: ** global->ops_mstate.ops_sstreamid - ** The new stream id. ** ** Returns: ** Nothing ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 19-July-87 (eric) ** written ** 11-oct-2006 (hayke02) ** Send E_OP0002_NOMEMORY to errlog.log. This change fixes bug 116309. [@history_template@]... */ VOID opu_Osmemory_open( OPS_STATE *global) { DB_STATUS ulmstatus; /* return status from ULM */ /* Set the output streamid location for ULM */ global->ops_mstate.ops_ulmrcb.ulm_streamid_p = &global->ops_mstate.ops_sstreamid; if ( (ulmstatus = ulm_openstream( &global->ops_mstate.ops_ulmrcb )) != E_DB_OK ) { if (global->ops_mstate.ops_ulmrcb.ulm_error.err_code == E_UL0005_NOMEM) { opx_lerror(E_OP0002_NOMEMORY, 0, 0, 0, 0, 0); opx_error( E_OP0002_NOMEMORY); /* out of memory */ } #ifdef E_OP0093_ULM_ERROR else opx_verror( ulmstatus, E_OP0093_ULM_ERROR, global->ops_mstate.ops_ulmrcb.ulm_error.err_code); /* check for error */ #endif } }
/*{ ** Name: OPU_CSMEMORY_CLOSE - Close the stack ULM memory stream ** ** Description: ** This routine closes the ULM memory stream that was used ** for the stack style memory allocation and deallocation. [@comment_line@]... ** ** Inputs: ** global - ** State info for the current query. ** global->ops_mstate.ops_ulmrcb - ** The ULM control block. ** global->ops_mstate.ops_sstreamid - ** The stream id. ** ** Outputs: ** ** Returns: ** Nothing ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 19-July-87 (eric) ** written ** 11-oct-2006 (hayke02) ** Send E_OP0002_NOMEMORY to errlog.log. This change fixes bug 116309. [@history_template@]... */ VOID opu_Csmemory_close( OPS_STATE *global) { DB_STATUS ulmstatus; /* return status from ULM */ /* store the stream id */ global->ops_mstate.ops_ulmrcb.ulm_streamid_p = &global->ops_mstate.ops_sstreamid; /* ULM will nullify ops_sstreamid */ if ( (ulmstatus = ulm_closestream( &global->ops_mstate.ops_ulmrcb )) != E_DB_OK ) { if (global->ops_mstate.ops_ulmrcb.ulm_error.err_code == E_UL0005_NOMEM) { opx_lerror(E_OP0002_NOMEMORY, 0, 0, 0, 0, 0); opx_error( E_OP0002_NOMEMORY); /* out of memory */ } #ifdef E_OP0093_ULM_ERROR else opx_verror( ulmstatus, E_OP0093_ULM_ERROR, global->ops_mstate.ops_ulmrcb.ulm_error.err_code); /* check for error */ #endif } }
/*{ ** Name: opv_tproc - Load RDF defs for table procedure range table entry ** ** Description: ** This function allocates and formats simulated RDF structures for ** a table procedure, including result column descriptors that are ** used to resolve "column" references to the procedure. ** ** Inputs: ** sess_cb Pointer to session control block ** rngtable Pointer to the user range table ** scope scope that range variable belongs in ** -1 means don't care. ** corrname Correlation name of table procedure ** dbp Ptr to PSF procedure descriptor ** rngvar Place to put pointer to new range ** variable ** root Ptr to RESDOM list of parameter specs ** err_blk Place to put error information ** ** Outputs: ** rngvar Set to point to the range variable ** err_blk Filled in if an error happens ** Returns: ** E_DB_OK Success ** E_DB_ERROR Non-catastrophic failure ** E_DB_FATAL Catastrophic failure ** Exceptions: ** none ** ** Side Effects: ** Can allocate memory ** ** History: ** 17-april-2008 (dougi) ** Written for table procedures(semi-cloned from pst_stproc). ** 15-dec-2008 (dougi) BUG 121381 ** Fix computation of result column offsets. */ DB_STATUS opv_tproc( OPS_STATE *global, OPV_IGVARS gvar, OPV_GRV *grvp, PST_RNGENTRY *rngentry) { DMT_ATT_ENTRY **attarray, *attrp; DMT_ATT_ENTRY **parmarray, **rescarray; DMT_TBL_ENTRY *tblptr; RDR_INFO *rdrinfop; QEF_DATA *qp; RDF_CB *rdf_cb; RDR_RB *rdf_rb; DB_PROCEDURE *dbp; DB_DBP_NAME proc_name; DB_OWN_NAME proc_owner; i4 parameterCount, rescolCount, resrowWidth; u_i4 created; i4 i, j, totlen, offset; DB_STATUS status; i4 err_code; /* First retrieve iiprocedure row using id from range table entry. */ rdf_cb = &global->ops_rangetab.opv_rdfcb; rdf_rb = &rdf_cb->rdf_rb; STRUCT_ASSIGN_MACRO(rngentry->pst_rngvar, rdf_rb->rdr_tabid); rdf_rb->rdr_types_mask = RDR_PROCEDURE; rdf_rb->rdr_2types_mask = 0; rdf_rb->rdr_instr = RDF_NO_INSTR; /* ** need to set rdf_info_blk to NULL for otherwise RDF assumes that we ** already have the info_block */ rdf_cb->rdf_info_blk = (RDR_INFO *) NULL; status = rdf_call(RDF_GETINFO, rdf_cb); if (DB_FAILURE_MACRO(status)) { (VOID) opx_verror(status, E_OP0004_RDF_GETDESC, rdf_cb->rdf_error.err_code); } dbp = rdf_cb->rdf_info_blk->rdr_dbp; /* Before proceeding - assure this is the same proc that we think it is. */ if (rngentry->pst_timestamp.db_tab_high_time != dbp->db_created) { /* If not, bounce back to SCF to re-parse query. */ opx_vrecover(E_DB_ERROR, E_OP008F_RDF_MISMATCH, rdf_cb->rdf_error.err_code); } /* Save procedure stuff for later. */ parameterCount = dbp->db_parameterCount; rescolCount = dbp->db_rescolCount; resrowWidth = dbp->db_resrowWidth; created = dbp->db_created; STRUCT_ASSIGN_MACRO(dbp->db_dbpname, proc_name); STRUCT_ASSIGN_MACRO(dbp->db_owner, proc_owner); /* Allocate attr descriptors and address from ptr arrays. */ i = dbp->db_parameterCount + dbp->db_rescolCount; attarray = (DMT_ATT_ENTRY **) opu_memory(global, (sizeof(PTR) + sizeof(DMT_ATT_ENTRY)) * i + sizeof(PTR)); /* 1 extra ptr because array is 1-origin */ /* Set up attr pointer arrays for both parms and result columns. */ for (j = 1, attrp = (DMT_ATT_ENTRY *)&attarray[i+1], attarray[0] = (DMT_ATT_ENTRY *)NULL; j <= i; j++, attrp = &attrp[1]) { attarray[j] = attrp; MEfill(sizeof(DMT_ATT_ENTRY), (u_char)0, (char *)attrp); } rescarray = attarray; parmarray = &attarray[rescolCount+1]; /* Load iiprocedure_parameter rows for both parms and result cols. */ rdf_rb->rdr_types_mask = 0; rdf_rb->rdr_2types_mask = RDR2_PROCEDURE_PARAMETERS; rdf_rb->rdr_instr = RDF_NO_INSTR; rdf_rb->rdr_update_op = RDR_OPEN; rdf_rb->rdr_qrymod_id = 0; /* get all tuples */ rdf_rb->rdr_qtuple_count = 20; /* get 20 at a time */ rdf_cb->rdf_error.err_code = 0; /* ** must set rdr_rec_access_id since otherwise RDF will barf when we ** try to RDR_OPEN */ rdf_rb->rdr_rec_access_id = NULL; while (rdf_cb->rdf_error.err_code == 0) { status = rdf_call(RDF_READTUPLES, rdf_cb); rdf_rb->rdr_update_op = RDR_GETNEXT; /* Must not use DB_FAILURE_MACRO because E_RD0011 returns ** E_DB_WARN that would be missed. */ if (status != E_DB_OK) { switch(rdf_cb->rdf_error.err_code) { case E_RD0011_NO_MORE_ROWS: status = E_DB_OK; break; case E_RD0013_NO_TUPLE_FOUND: status = E_DB_OK; continue; default: opx_error(E_OP0013_READ_TUPLES); break; } /* switch */ } /* if status != E_DB_OK */ /* For each dbproc parameter tuple */ for (qp = rdf_cb->rdf_info_blk->rdr_pptuples->qrym_data, j = 0; j < rdf_cb->rdf_info_blk->rdr_pptuples->qrym_cnt; qp = qp->dt_next, j++) { DB_PROCEDURE_PARAMETER *param_tup = (DB_PROCEDURE_PARAMETER *) qp->dt_data; if (i-- == 0) { opx_error(E_OP0013_READ_TUPLES); } if (param_tup->dbpp_flags & DBPP_RESULT_COL) { attrp = rescarray[param_tup->dbpp_number]; attrp->att_number = param_tup->dbpp_number; } else { attrp = parmarray[param_tup->dbpp_number-1]; attrp->att_flags = DMT_F_TPROCPARM; attrp->att_number = param_tup->dbpp_number + dbp->db_rescolCount; } STRUCT_ASSIGN_MACRO(param_tup->dbpp_name, attrp->att_name); attrp->att_type = param_tup->dbpp_datatype; attrp->att_width = param_tup->dbpp_length; attrp->att_prec = param_tup->dbpp_precision; attrp->att_offset = param_tup->dbpp_offset; } } /* Reset result column offsets to remove effect of parms. */ offset = rescarray[1]->att_offset; for (j = 1; j <= rescolCount; j++) rescarray[j]->att_offset -= offset; if (rdf_rb->rdr_rec_access_id != NULL) { rdf_rb->rdr_update_op = RDR_CLOSE; status = rdf_call(RDF_READTUPLES, rdf_cb); if (DB_FAILURE_MACRO(status)) { opx_error(E_OP0013_READ_TUPLES); } } /* now unfix the dbproc description */ rdf_rb->rdr_types_mask = RDR_PROCEDURE | RDR_BY_NAME; rdf_rb->rdr_2types_mask = 0; rdf_rb->rdr_instr = RDF_NO_INSTR; status = rdf_call(RDF_UNFIX, rdf_cb); if (DB_FAILURE_MACRO(status)) { opx_error(E_OP008D_RDF_UNFIX); } /* Allocate table descriptor. */ tblptr = (DMT_TBL_ENTRY *) opu_memory(global, sizeof(DMT_TBL_ENTRY)); /* Allocate RDR_INFO block. */ rdrinfop = (RDR_INFO *) opu_memory(global, sizeof(RDR_INFO)+sizeof(PTR)); /* Now format them all. */ MEfill(sizeof(DMT_TBL_ENTRY), (u_char) 0, tblptr); MEcopy((char *)&proc_name.db_dbp_name, sizeof(DB_DBP_NAME), (char *)&tblptr->tbl_name.db_tab_name); STRUCT_ASSIGN_MACRO(proc_owner, tblptr->tbl_owner); MEfill(sizeof(DB_LOC_NAME), (u_char)' ', (char *)&tblptr->tbl_location); tblptr->tbl_attr_count = rescolCount + parameterCount; tblptr->tbl_width = resrowWidth; tblptr->tbl_date_modified.db_tab_high_time = created; tblptr->tbl_storage_type = DB_TPROC_STORE; /* Load cost parameters (if there). */ tblptr->tbl_record_count = (dbp->db_estRows) ? dbp->db_estRows : DBP_ROWEST; tblptr->tbl_page_count = (dbp->db_estCost) ? dbp->db_estCost : DBP_DIOEST; tblptr->tbl_pgsize = 2048; /* All the other DMT_TBL_ENTRY fields are being left 0 until ** something happens that suggests other values. */ /* Finally fill in the RDR_INFO structure. */ MEfill(sizeof(RDR_INFO), (u_char) 0, rdrinfop); rdrinfop->rdr_rel = tblptr; rdrinfop->rdr_attr = rescarray; rdrinfop->rdr_no_attr = tblptr->tbl_attr_count; rdrinfop->rdr_dbp = dbp; grvp->opv_relation = rdrinfop; BTset((i4)gvar, (char *)&global->ops_rangetab.opv_mrdf); /* indicate ** that RDF info is fixed */ grvp->opv_gmask |= OPV_TPROC; global->ops_gmask |= OPS_TPROCS; /* show query has table procs */ return (E_DB_OK); }
/*{ ** Name: opn_ceval - evaluate the cost of the join operator tree ** ** Description: ** Entry point for routines which evaluate cost of a join operator tree. ** Contains checks for timeouts within the optimizer. ** ** Inputs: ** subquery ptr to subquery being analyzed ** ->ops_estate.opn_sroot ptr to root of join operator tree ** ** Outputs: ** subquery->ops_bestco ptr to best plan found ** subquery->ops_cost cost of best plan found ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 11-jun-86 (seputis) ** initial creation from costeval ** 2-nov-88 (seputis) ** changed CSstatistics interface ** 2-nov-88 (seputis) ** add trace flag to timeout on number of plans being evaluated ** 15-aug-89 (seputis) ** add trace flag to adjust timeout factor which converts cost to time ** used to help isolate early timeout problems ** 16-may-90 (seputis) ** - b21582, move timeout checking to opn_timeout routine so it can be ** called from opn_arl ** 17-oct-90 (seputis) ** - b33386 - print error is no qep is found ** 22-apr-91 (seputis) ** - fix floating point exception handling problems ** 19-nov-99 (inkdo01) ** Changes to remove EXsignal from opn_exit processing. ** 18-oct-02 (inkdo01) ** Changes to enable new enumeration. ** 30-mar-2004 (hayke02) ** Call opn_exit() with a FALSE longjump. ** 30-Jun-2006 (kiria01) b116309 ** Capture memory exhaustion error to errlog.log. ** 11-Mar-2008 (kschendel) b122118 ** Remove unused ops_tempco. Fix up bfexception stuff so that we ** stop calling recover pointlessly at the start of every query. [@history_line@]... */ OPN_STATUS opn_ceval( OPS_SUBQUERY *subquery) { EX_CONTEXT excontext; /* context block for exception ** handler*/ OPN_SUBTREE *opn_ssubtp; /* dummy var ignored at this level ** - list of subtrees with possible cost ** orderings (including reformats) ** - all element in the list have same ** relations but in different order or ** use a different tree structure. */ OPN_RLS *opn_srlmp; /* dummy var ignored at this level ** - list of list of subtrees, with a ** different set of relations for each ** OPN_RLS element */ OPN_EQS *opn_seqp; /* dummy var ignored at this level ** - used to create a list of different ** OPN_SUBTREE (using indexes) if ** an index exists. */ OPN_STATUS sigstat = OPN_SIGOK; if ( EXdeclare(opn_mhandler, &excontext) == EX_DECLARE ) /* set ** up exception handler to ** recover from out of memory ** errors */ { /* this point will only be reached if the enumeration processing has ** run out of memory. The optimizer will try to continue by copying ** the best CO tree found so far, out of enumeration memory, and ** reinitializing the enumeration stream, and continuing. ** The out-of-memory error will be reported if a complete pass of ** cost evaluation cannot be completed. */ (VOID)EXdelete(); /* cancel exception handler prior ** to exiting routine */ if (subquery->ops_global->ops_gmask & OPS_FLINT) { subquery->ops_global->ops_gmask &= ~OPS_FLINT; /* reset exception ** indicator */ return(OPN_SIGOK); /* skip this plan if a float ** or integer exception has occurred ** during processing */ } if ( EXdeclare(opn_mhandler, &excontext) == EX_DECLARE ) /* set ** exception handler to catch case in ** which no progress is made */ { (VOID)EXdelete(); /* cancel exception handler prior ** to exiting routine */ if (subquery->ops_bestco) opx_verror(E_DB_WARN, E_OP0400_MEMORY, (OPX_FACILITY)0); /* report ** warning message to caller */ else { opx_lerror(E_OP0400_MEMORY, 0); /* Output to errlog.log as well kiria01-b116309 */ opx_error(E_OP0400_MEMORY); /* exit with a user error if the query ** cannot find at least one acceptable ** query plan */ } opn_exit(subquery, FALSE); /* exit with current query plan */ return(OPN_SIGEXIT); } opn_recover(subquery); /* routine will copy best CO tree ** and reinitialize memory stream */ } subquery->ops_global->ops_gmask &= (~OPS_FPEXCEPTION); /* reset fp exception ** indicator before evaluating ** plan */ if (subquery->ops_global->ops_gmask & OPS_BFPEXCEPTION && subquery->ops_bestco != NULL) opn_recover(subquery); /* if the current best plan occurred with ** exceptions then enumeration memory needs ** to be flushed, so that subsequent plans ** do not use subtrees which may have ** been created with exceptions, this means ** that the OPF cache of sub-trees is not ** used if one exception has occurred and ** the current best plan was created with ** exceptions */ (VOID) opn_nodecost ( subquery, subquery->ops_global->ops_estate.opn_sroot, (subquery->ops_mask & OPS_LAENUM) ? subquery->ops_laeqcmap : &subquery->ops_eclass.ope_maps.opo_eqcmap, &opn_ssubtp, &opn_seqp, &opn_srlmp, &sigstat); if (sigstat != OPN_SIGEXIT && !(subquery->ops_mask & OPS_LAENUM) && opn_timeout(subquery)) /* check for timeout (but only for ** old style enumeration) */ { (VOID)EXdelete(); /* cancel exception handler prior ** to exiting routine, call after ** opn_timeout in case out of memory ** errors occur */ 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); } (VOID)EXdelete(); /* cancel exception handler prior ** to exiting routine */ return(sigstat); }
/* And now the real thing */ static VOID opc_exnodearrcnt( OPS_STATE *global, QEN_NODE *node, i4 *arrcnts, PTR rowmap) { QEN_OJINFO *ojinfop; QEN_PART_INFO *partp; QEN_PART_QUAL *pqual; QEN_SJOIN *sjnp; QEN_KJOIN *kjnp; QEN_TJOIN *tjnp; QEN_HJOIN *hjnp; QEN_SEJOIN *sejnp; QEN_SORT *srtp; QEN_TPROC *tprocp; QEN_TSORT *tsrtp; QEN_ORIG *origp; QEN_QP *qpp; QEN_EXCH *exchp; QEF_QP_CB *qp = global->ops_cstate.opc_qp; QEF_RESOURCE *resp; QEF_VALID *vlp; QEF_AHD *act; i4 i, j, k; i4 dmrix; bool endloop; /* Loop (recurse on left, iterate on right) and switch to process ** each node in subtree. */ for ( ; ; ) { if (node == (QEN_NODE *) NULL) return; /* just in case */ opc_exnheadcnt(global, node, arrcnts, rowmap); /* count node header indexes */ ojinfop = (QEN_OJINFO *) NULL; partp = (QEN_PART_INFO *) NULL; pqual = NULL; dmrix = -1; switch (node->qen_type) { case QE_CPJOIN: case QE_FSMJOIN: case QE_ISJOIN: sjnp = &node->node_qen.qen_sjoin; ojinfop = sjnp->sjn_oj; if (sjnp->sjn_krow >= 0) BTset(sjnp->sjn_krow, rowmap); if (sjnp->sjn_hfile >= 0) arrcnts[IX_HLD]++; if (sjnp->sjn_itmat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sjnp->sjn_okmat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sjnp->sjn_okcompare != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sjnp->sjn_joinkey != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sjnp->sjn_jqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; opc_exnodearrcnt(global, sjnp->sjn_out, arrcnts, rowmap); node = sjnp->sjn_inner; break; case QE_KJOIN: kjnp = &node->node_qen.qen_kjoin; ojinfop = kjnp->kjoin_oj; partp = kjnp->kjoin_part; pqual = kjnp->kjoin_pqual; if ((dmrix = kjnp->kjoin_get) >= 0) arrcnts[IX_DMR]++; if (kjnp->kjoin_krow >= 0) BTset(kjnp->kjoin_krow, rowmap); if (kjnp->kjoin_key != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (kjnp->kjoin_kqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (kjnp->kjoin_kcompare != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (kjnp->kjoin_iqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (kjnp->kjoin_jqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; node = kjnp->kjoin_out; break; case QE_TJOIN: tjnp = &node->node_qen.qen_tjoin; ojinfop = tjnp->tjoin_oj; partp = tjnp->tjoin_part; pqual = tjnp->tjoin_pqual; if ((dmrix = tjnp->tjoin_get) >= 0) arrcnts[IX_DMR]++; if (tjnp->tjoin_orow >= 0) BTset(tjnp->tjoin_orow, rowmap); if (tjnp->tjoin_irow >= 0) BTset(tjnp->tjoin_irow, rowmap); if (tjnp->tjoin_jqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (tjnp->tjoin_isnull != (QEN_ADF *) NULL) arrcnts[IX_CX]++; node = tjnp->tjoin_out; break; case QE_HJOIN: hjnp = &node->node_qen.qen_hjoin; ojinfop = hjnp->hjn_oj; pqual = hjnp->hjn_pqual; arrcnts[IX_HSH]++; if (hjnp->hjn_brow >= 0) BTset(hjnp->hjn_brow, rowmap); /* prow is probably already counted as qen_row but make sure */ if (hjnp->hjn_prow >= 0) BTset(hjnp->hjn_prow, rowmap); if (hjnp->hjn_dmhcb >= 0) arrcnts[IX_DMH]++; if (hjnp->hjn_btmat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (hjnp->hjn_ptmat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (hjnp->hjn_jqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; opc_exnodearrcnt(global, hjnp->hjn_out, arrcnts, rowmap); node = hjnp->hjn_inner; break; case QE_SEJOIN: sejnp = &node->node_qen.qen_sejoin; ojinfop = (QEN_OJINFO *) NULL; partp = (QEN_PART_INFO *) NULL; /* if (sejnp->sejn_hget >= 0) - these aren't ref'ed in QEF arrcnts[IX_DMR]++; */ if (sejnp->sejn_hfile >= 0) arrcnts[IX_HLD]++; if (sejnp->sejn_itmat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sejnp->sejn_ccompare != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sejnp->sejn_oqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (sejnp->sejn_okmat != NULL) arrcnts[IX_CX]++; if (sejnp->sejn_kcompare != NULL) arrcnts[IX_CX]++; if (sejnp->sejn_kqual != NULL) arrcnts[IX_CX]++; opc_exnodearrcnt(global, sejnp->sejn_out, arrcnts, rowmap); node = sejnp->sejn_inner; break; case QE_TSORT: tsrtp = &node->node_qen.qen_tsort; pqual = tsrtp->tsort_pqual; if (tsrtp->tsort_get >= 0) arrcnts[IX_DMR]++; if (tsrtp->tsort_load >= 0) arrcnts[IX_DMR]++; if (tsrtp->tsort_create >= 0) arrcnts[IX_DMT]++; if (tsrtp->tsort_shd >= 0) arrcnts[IX_SHD]++; if (tsrtp->tsort_mat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; node = tsrtp->tsort_out; break; case QE_SORT: srtp = &node->node_qen.qen_sort; if (srtp->sort_load >= 0) arrcnts[IX_DMR]++; if (srtp->sort_create >= 0) arrcnts[IX_DMT]++; if (srtp->sort_shd >= 0) arrcnts[IX_SHD]++; if (srtp->sort_mat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; node = srtp->sort_out; break; case QE_ORIG: case QE_ORIGAGG: origp = &node->node_qen.qen_orig; if ((dmrix = origp->orig_get) >= 0) { arrcnts[IX_DMR]++; } partp = origp->orig_part; pqual = origp->orig_pqual; if (origp->orig_qual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; node = (QEN_NODE *) NULL; break; case QE_QP: qpp = &node->node_qen.qen_qp; if (qpp->qp_qual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; /* Process action headers anchored in QP node. */ for (act = node->node_qen.qen_qp.qp_act; act; act = act->ahd_next) opc_exactarrcnt(global, act, arrcnts, rowmap); return; case QE_EXCHANGE: exchp = &node->node_qen.qen_exch; if (exchp->exch_mat != (QEN_ADF *) NULL) arrcnts[IX_CX]++; /* Don't probe below 1:N exchanges, they'll do their own setup. ** 1:1 exchange depends on parent, so keep going. */ if (exchp->exch_ccount > 1) return; node = exchp->exch_out; break; case QE_TPROC: tprocp = &node->node_qen.qen_tproc; if (tprocp->tproc_parambuild != NULL) arrcnts[IX_CX]++; if (tprocp->tproc_qual != NULL) arrcnts[IX_CX]++; return; /* Nothing else interesting */ default: TRdisplay("Unexpected QP node type %d under exch\n",node->qen_type); opx_error(E_OP068E_NODE_TYPE); } /* end of switch */ /* Node specific bits have been set - now go over OJ and ** partition stuff (if any). */ if (ojinfop) { if (ojinfop->oj_heldTidRow >= 0) BTset(ojinfop->oj_heldTidRow, rowmap); if (ojinfop->oj_ijFlagsRow >= 0) BTset(ojinfop->oj_ijFlagsRow, rowmap); if (ojinfop->oj_resultEQCrow >= 0) BTset(ojinfop->oj_resultEQCrow, rowmap); if (ojinfop->oj_specialEQCrow >= 0) BTset(ojinfop->oj_specialEQCrow, rowmap); if (ojinfop->oj_tidHoldFile >= 0) arrcnts[IX_HLD]++; if (ojinfop->oj_ijFlagsFile >= 0) arrcnts[IX_HLD]++; if (ojinfop->oj_innerJoinedTIDs) arrcnts[IX_TTAB]++; if (ojinfop->oj_oqual != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (ojinfop->oj_equal != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (ojinfop->oj_lnull != (QEN_ADF *) NULL) arrcnts[IX_CX]++; if (ojinfop->oj_rnull != (QEN_ADF *) NULL) arrcnts[IX_CX]++; } if (partp) { if (partp->part_groupmap_ix >= 0) BTset(partp->part_groupmap_ix, rowmap); if (partp->part_ktconst_ix >= 0) BTset(partp->part_ktconst_ix, rowmap); if (partp->part_knokey_ix >= 0) BTset(partp->part_knokey_ix, rowmap); } /* Part-qual structures contain mini-programs that must be ** scanned to gather up row numbers. */ if (pqual != NULL) { QEN_PQ_EVAL *pqe; arrcnts[IX_PQUAL]++; if (pqual->part_constmap_ix >= 0) BTset(pqual->part_constmap_ix, rowmap); if (pqual->part_lresult_ix >= 0) BTset(pqual->part_lresult_ix, rowmap); if (pqual->part_work1_ix >= 0) BTset(pqual->part_work1_ix, rowmap); pqe = pqual->part_const_eval; if (pqe != NULL) opc_arrcnt_pqe(pqe, arrcnts, rowmap); pqe = pqual->part_join_eval; if (pqe != NULL) opc_arrcnt_pqe(pqe, arrcnts, rowmap); } /* If TJOIN, KJOIN or ORIG, locate DMT_CB index in valid's. */ if (dmrix >= 0) for (resp = qp->qp_resources, endloop = FALSE; resp && !endloop; resp = resp->qr_next) if (resp->qr_type == QEQR_TABLE) for (vlp = resp->qr_resource.qr_tbl.qr_lastvalid; vlp && !endloop; vlp = vlp->vl_next) if (dmrix == vlp->vl_dmr_cb) { arrcnts[IX_DMT]++; endloop = TRUE; if (vlp->vl_partition_cnt > 1) arrcnts[IX_DMR]++; /* set master DMR_CB, too */ } if (node == (QEN_NODE *) NULL) return; } /* end of for ( ; ; ) */ }
/*{ ** Name: ops_qinit - initialize structures needed for optimization ** ** Description: ** This routine will initialize the "global state" variable for one query ** within a procedure. It must be called prior to the optimization of ** every query in the procedure ** ** Inputs: ** global ptr to global state variable ** .ops_cb ptr to session control block ** .ops_caller_cb ptr to same object as opf_cb ** statementp ptr to statement containing query ** to be optimized ** ** Outputs: ** global all components initialized ** Returns: ** E_DB_OK, E_DB_ERROR ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 24-feb-86 (seputis) ** initial creation ** 8-sep-86 (seputis) ** DBP init range table only if statementp == NULL ** 6-nov-88 (seputis) ** initial opn_statistics ** 28-jan-89 (paul) ** Initialize rules processing flag to FALSE. Indicates we are ** not currently compiling a rule action. ** 22-may-89 (neil) ** Put PSQ_DELCURS through same fast path as PSQ_REPCURS. ** 22-sep-89 (seputis) ** added ops_gmask for boolean expansion ** 14-nov-89 (seputis) ** fixed 8629, to not use repeat parameters in procedures ** 6-jun-90 (seputis) ** fix b30463 - moved some large memory structure handling to ** the procedure level rather than query level ** 28-aug-90 (seputis) ** - fix b32757 - init opn_statistics in all cases ** 26-nov-90 (stec) ** Added initialization of ops_prbuf in OPS_STATE struct. ** 23-jan-91 (seputis) ** moved RDF_CB initialization here so that OPC can use ** this initialization also. ** 28-oct-92 (jhahn) ** Added initialization of ops_inStatementEndRules. ** 01-Jan-00 (sarjo01) ** Bug 99120: Added opn_initcollate() call ** 5-dec-02 (inkdo01) ** Changes for range table expansion. ** 30-jan-2003 (somsa01) ** Moved the initialization of ops_gmask before checking ** for !statementp. This fixes E_OP0791_ADE_INSTRGEN when ** creating a procedure after the fixes for bug 109194. ** 4-nov-04 (inkdo01) ** Init opn_fragcolist for greedy enumeration. ** 15-june-06 (dougi) ** Add support for "before" triggers. ** 21-Oct-2010 (kiria01) b123345 ** Do not assume PST_DV_TYPE is only present when the ** context is a DBP. It may also be present from temporaries ** created by the parser. [@history_template@]... */ VOID ops_qinit( OPS_STATE *global, PST_STATEMENT *statementp) { MEfill(sizeof(OPV_GBMVARS), 0, (char *)&global->ops_rangetab.opv_mrdf); /* used for deallocation */ global->ops_rangetab.opv_gv = 0; /* no global range variables defined */ global->ops_rangetab.opv_base = NULL; /* - NULL indicates that the ** global range table not been allocated ** - used by error handling to ** deallocate ULM and RDF resources */ /* Initially, we are not processing rules at the beginning of an */ /* optimization or the beginning of a PST_QT_TYPE */ global->ops_inAfterRules = global->ops_inBeforeRules = FALSE; global->ops_inAfterStatementEndRules = FALSE; global->ops_inBeforeStatementEndRules = FALSE; global->ops_gmask = 0; /* init boolean mask */ if (!statementp) return; /* only initialize if statement is provided ** otherwise, only init the global range ** table for resource deallocation */ global->ops_statement = statementp; global->ops_qheader = statementp->pst_specific.pst_tree; /* ** get the query tree header root from ** the statement */ global->ops_copiedco = NULL; /* no CO nodes have been allocated yet */ global->ops_subquery = NULL; /* initialize the subquery list */ /* global->ops_astate initialized by aggregate processing phase */ /* global->ops_estate initialized by joinop processing phase */ { global->ops_mstate.ops_usemain = FALSE; /* disable redirection of ** memory allocation */ global->ops_terror = FALSE; /* TRUE if tuple too wide for ** intermediate relation */ } /* the number of parameters may increase in a query if any simple ** aggregates are found, thus a separate count of query parameters ** is kept in the state variable. */ if (global->ops_procedure->pst_isdbp) { /* procedures do not have repeat query parameters, so use ** the local variable declaration to find the largest parameter ** number, there is an assumption that there is only one ** PST_DECVAR statement and this can be used to determine ** the largest allocated constant number */ PST_STATEMENT *decvarp; decvarp = global->ops_procedure->pst_stmts; if (decvarp->pst_type != PST_DV_TYPE) opx_error(E_OP0B81_VARIABLE_DEC); /* verify statement type, an ** assumption is that a declaration ** is always the first statement of ** a procedure */ global->ops_parmtotal = decvarp->pst_specific.pst_dbpvar->pst_nvars + decvarp->pst_specific.pst_dbpvar->pst_first_varno - 1; } else { global->ops_parmtotal = global->ops_qheader->pst_numparm; if (global->ops_procedure->pst_stmts->pst_type == PST_DV_TYPE) { /* Support local variables that may have been introduced ** as temporaries */ PST_DECVAR *decvarp = global->ops_procedure-> pst_stmts->pst_specific.pst_dbpvar; global->ops_parmtotal += decvarp->pst_nvars + decvarp->pst_first_varno - 1; } } { /* initialize outer join descriptors */ global->ops_goj.opl_gbase = (OPL_GOJT *) NULL; global->ops_goj.opl_view = (OPL_GOJT *) NULL; global->ops_goj.opl_fjview = (OPL_GOJT *) NULL; global->ops_goj.opl_glv = global->ops_qheader->pst_numjoins; if (global->ops_qheader->pst_numjoins > 0) global->ops_goj.opl_mask = OPL_OJFOUND; else global->ops_goj.opl_mask = 0; } global->ops_rqlist = NULL; /* list of repeat query descriptors */ if (( (global->ops_qheader->pst_mode != PSQ_REPCURS) && (global->ops_qheader->pst_mode != PSQ_DELCURS) ) || (global->ops_cb->ops_smask & OPS_MDISTRIBUTED) ) { opv_grtinit( global, TRUE); /* initialize the global range table ** ops_rangetab structure, ... replace ** & delete cursor do not need this */ } else { /* special case short cut "hack" for replace/delete cursor stmt which ** should not need to go through enumeration,... it should go ** directly to query compilation to compile the target list ** expressions */ /* for distributed the parse tree is traversed to handle the ** ~V case for multi-site so this section is not needed */ #if 0 /* looks like this was replaced by opv_grtinit call, with the extra ** boolean parameter */ MEfill(sizeof(OPV_GLOBAL_RANGE), (u_char)0, (PTR)&global->ops_rangetab); /* init global range table for ** query compilation */ #endif opv_grtinit( global, FALSE); global->ops_subquery = (OPS_SUBQUERY *)opu_memory(global, (i4) sizeof(OPS_SUBQUERY)); MEfill(sizeof(OPS_SUBQUERY), (u_char)0, (PTR)global->ops_subquery); /* init subquery structure for ** query compilation */ global->ops_subquery->ops_sqtype = OPS_MAIN; global->ops_subquery->ops_root = global->ops_qheader->pst_qtree; } /* these fields should be initialized once before the enumeration phase */ global->ops_estate.opn_cocount = 0; /* number of CO nodes available */ global->ops_estate.opn_statistics = FALSE; /* statistics has not been ** turned on yet */ global->ops_estate.opn_colist = NULL; /* list of avail perm CO nodes */ global->ops_estate.opn_fragcolist = NULL; global->ops_union = FALSE; /* TRUE if union view needs to be ** processed */ global->ops_tvar = OPV_NOGVAR; /* init var for special case update ** by TID */ { /* distributed initialization */ global->ops_gdist.opd_tcost = NULL; global->ops_gdist.opd_dv = 0; global->ops_gdist.opd_base = NULL; global->ops_gdist.opd_tbestco = NULL; global->ops_gdist.opd_copied = NULL; global->ops_gdist.opd_scopied = FALSE; global->ops_gdist.opd_repeat = NULL; global->ops_gdist.opd_gmask = 0; global->ops_gdist.opd_user_parameters = 0; } opn_initcollate(global); }
/*{ ** Name: opv_smap - map query tree associated with subquery ** ** Description: ** This routine will map the query tree associated with the subquery. A ** subquery is typically associated with a PST_AGHEAD, or PST_ROOT node. ** A flag is check to see if the variable map was invalidated by any ** previous substitution. For now this will be a consistency check ** and the tree will be mapped anyways. FIXME later change this to ** avoid mapping the tree if there has been no substitutions. ** ** Inputs: ** subquery ptr to subquery which will be mapped ** ** Outputs: ** subquery->ops_root this PST_RT_NODE will have the bitmaps ** updated ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 3-jul-86 (seputis) ** initial creation ** 22-apr-90 (seputis) ** fix rohm-haas bug (no bug number), aggregate on a select distinct view ** 24-jun-91 (seputis) ** turn off consistency check to avoid traversal of parse tree ** 5-dec-02 (inkdo01) ** Changes for range table expansion. ** 31-Aug-2006 (kschendel) ** Watch for HFAGG as well as RFAGG. */ VOID opv_smap( OPS_SUBQUERY *subquery) { OPV_GBMVARS map; /* range var map of query tree fragment */ if (subquery->ops_vmflag) /* TRUE if varmap is up-to-date */ { #ifdef E_OP0388_VARBITMAP /* check if this var map is valid as claimed */ #ifdef xDEBUG MEfill(sizeof(map), 0, (char *)&map); opv_mapvar(subquery->ops_root->pst_left, &map); if (MEcmp((char *)&map, (char *)&subquery->ops_root->pst_sym.pst_value. pst_s_root.pst_lvrm, sizeof(map)) != 0) opx_error( E_OP0388_VARBITMAP); /* bit map ** inconsistent with left side */ MEfill(sizeof(map), 0, (char *)&map); opv_mapvar(subquery->ops_root->pst_right, &map); if (MEcmp((char *)&map, (char *)&subquery->ops_root->pst_sym.pst_value. pst_s_root.pst_rvrm, sizeof(map)) != 0) opx_error( E_OP0388_VARBITMAP); /* bit map ** inconsistent with right side */ #endif return; #endif } if ((subquery->ops_sqtype == OPS_FAGG) || (subquery->ops_sqtype == OPS_HFAGG) || (subquery->ops_sqtype == OPS_RFAGG) ) { /* map the bylist for the function aggregate */ MEfill(sizeof(map), 0, (char *)&map); opv_mapvar(subquery->ops_agg.opa_byhead->pst_left, &map); /* map ** the bylist portion of the function ** aggregate */ MEcopy((char *)&map, sizeof(map), (char *)&subquery->ops_agg.opa_blmap); if (subquery->ops_root->pst_left == subquery->ops_agg.opa_byhead) { OPV_GBMVARS aopmap; /* map of AOP operator */ MEfill(sizeof(aopmap), 0, (char *)&aopmap); opv_mapvar(subquery->ops_agg.opa_aop, &aopmap); /* map the AOP node ** of the function aggregate */ MEcopy((char *)&map, sizeof(map), (char *)&subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm); BTor(OPV_MAXVAR, (char *)&aopmap, (char *)&subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm); } else { /* non-printing resdoms exist above the byhead so ** the entire tree needs to be scanned */ MEfill(sizeof(PST_J_MASK), 0, (char *)&subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm); opv_mapvar(subquery->ops_root->pst_left, &subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm ); /* map the AOP node ** of the function aggregate */ } } else { /* map the left side of the tree */ MEfill(sizeof(map), 0, (char *)&map); opv_mapvar(subquery->ops_root->pst_left, &map); MEcopy((char *)&map, sizeof(map), (char *)&subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm); } /* map the right side of the tree */ MEfill(sizeof(map), 0, (char *)&map); opv_mapvar(subquery->ops_root->pst_right, &map); MEcopy((char *)&map, sizeof(map), (char *)&subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_rvrm); subquery->ops_vmflag = TRUE; /* bit maps are now valid */ }
/*{ ** Name: opz_addatts - add joinop attributes to the array ** ** Description: ** This routine will add or find the appropriate joinop attribute ** to the joinop attributes array given the ** (range variable, range variable attribute number) pair. ** ** The exception to this rule occurs with function attributes which ** always results in a new joinop attribute being allocated. A ** function attribute uses "OPZ_FANUM" as the input attribute number. ** ** Inputs: ** subquery ptr to subquery being analyzed ** joinopvar index into joinop range table ** dmfattr dmf attribute number ** datatype ptr to ADT datatype ** ** Outputs: ** Returns: ** joinop attribute number ** Exceptions: ** none ** ** Side Effects: ** An entry may be allocated in the joinop attributes array ** (OPS_SUBQUERY->ops_attrs) ** ** History: ** 20-apr-86 (seputis) ** initial creation addatts ** 06-nov-89 (fred) ** Added support for non-histogrammable datatypes. This support ** entails providing a simple, coordinated replacement for this ** histogram. In our case, the datatype & length will be varchar, ** minimum & maximum being those for varchar, and adc_helem() ** replacement will be the histogram for "nohistos". ** ** This code is done in OPF as opposed to ADF because I think it ** desirable that OPF manage the histogram replacement. ADF will not ** know the appropriate values. Furthermore, it is more desirable to ** teach OPF to manage w/out histograms; however, that change is ** beyond the scope of this project. ** 26-dec-90 (seputis) ** init mask of booleans associated with attribute descriptor ** 18-jan-93 (rganski) ** Character Histogram Enhancements project: ** Initialize attr_ptr->opz_histogram.oph_dataval.db_length to 8 ** before call to adc_hg_dtln(), which has been changed; this gives ** old behavior (limit for char types was 8). This length may be ** changed later when RDF gets info from iistatistics. ** 24-may-93 (rganski) ** Character Histograms Enhancements project: ** Initialize attr_ptr->opz_histogram.oph_dataval.db_length to 0 ** before call to adc_hg_dtln(), which causes histogram value to be ** same length as attribute. This is necessary because otherwise the ** histogram value, which is used to determine selectivity of boolean ** factors, is truncated; this removes the benefits of having long ** histogram values. The histogram value length is adjusted in ** oph_catalog(), which reads the actual length from iistatistics (or ** truncates to 8, for old histograms). ** 6-dec-93 (ed) ** - bug 56139 - project union view before it is instantiated ** 09-oct-98 (matbe01) ** Added NO_OPTIM for NCR to eliminate runtime error that produces the ** message: E_OP0889 Eqc is not available at a CO node. ** 6-sep-04 (hayke02) ** Add OPZ_COLLATT attribute if OPS_COLLATION is on (resdom list, ** collation sequence). This change fixes problem INGSRV 2940, bug ** 112873. ** 8-nov-05 (hayke02) ** Add OPZ_VAREQVAR if OPS_VAREQVAR is on. Return if OPZ_VAREQVAR is ** not set and the attribute is found. This ensures that the fix for ** 110675 is limited to OPZ_VAREQVAR PST_BOP nodes. This change ** fixes bug 115420 problem INGSRV 3465. ** 15-aug-2007 (huazh01) ** ensure the datatype/length info of a created attribute matches ** the corresponding column type/length info on the user table. ** bug 117316. ** [@history_line@]... */ OPZ_IATTS opz_addatts( OPS_SUBQUERY *subquery, OPV_IVARS joinopvar, OPZ_DMFATTR dmfattr, DB_DATA_VALUE *datatype) { OPZ_IATTS attribute; /* used to save joinop attribute ** number if found by opz_findatt */ RDR_INFO *rel; /* FIXME - need to deal with unsigned compare problem here */ if ( (dmfattr != OPZ_FANUM) && (dmfattr != OPZ_SNUM) && (dmfattr != OPZ_CORRELATED) && (attribute = opz_findatt(subquery, joinopvar, dmfattr)) >= 0 && !(subquery->ops_attrs.opz_base->opz_attnums[attribute]->opz_mask & OPZ_VAREQVAR && subquery->ops_mask2 & OPS_COLLATION && (abs(datatype->db_datatype) == DB_CHA_TYPE || abs(datatype->db_datatype) == DB_VCH_TYPE || abs(datatype->db_datatype) == DB_CHR_TYPE || abs(datatype->db_datatype) == DB_TXT_TYPE)) ) return( attribute ); /* if this (joinopvar,dmfattr) pair ** exists and is not a function ** attribute, or subselect attribute ** then return */ { /* create new joinop attribute */ OPZ_IATTS nextattr; /* next available joinop attribute ** number */ OPS_STATE *global; /* ptr to global state variable */ global = subquery->ops_global; /* get ptr to global state variable */ if ( (nextattr = subquery->ops_attrs.opz_av++) >= OPZ_MAXATT) opx_error(E_OP0300_ATTRIBUTE_OVERFLOW); /* exit with error ** if no more room in attributes ** array */ { OPZ_ATTS *attr_ptr; /* ptr to newly created joinop ** attribute element */ OPV_VARS *var_ptr; /* ptr to respective joinop ** variable containing attribute */ var_ptr = subquery->ops_vars.opv_base->opv_rt[joinopvar]; /* allocate new joinop attribute structure and initialize */ subquery->ops_attrs.opz_base->opz_attnums[nextattr] = attr_ptr = (OPZ_ATTS *) opu_memory( global, (i4) sizeof(OPZ_ATTS)); MEfill(sizeof(*attr_ptr), (u_char)0, (PTR)attr_ptr); /* b117316: ** ** the fix to b109879 set PST_VAR node under the RESDOM to ** to nullable and increase the db_length by one, though ** the column corresponds to PST_VAR is defined as not null. ** Need to reset them to not null in order to prevent ** wrong db_type and db_length being used during query ** execution. e.g., wrong db_type and db_length could cause OPC ** to generate a set of ADF code which materialize tuples ** using incorrect offset and cause wrong result. ** */ rel = var_ptr->opv_grv->opv_relation; if (datatype->db_datatype < 0 && dmfattr > 0 && rel && rel->rdr_attr[dmfattr]->att_type * -1 == datatype->db_datatype && rel->rdr_attr[dmfattr]->att_width + 1 == datatype->db_length) { datatype->db_datatype = rel->rdr_attr[dmfattr]->att_type; datatype->db_length = rel->rdr_attr[dmfattr]->att_width; } attr_ptr->opz_varnm = joinopvar; /* index into local range table */ BTset( (i4)nextattr, (char *)&var_ptr->opv_attrmap); /* indicate ** that this joinop ** attribute belongs to this range ** variable */ attr_ptr->opz_gvar = var_ptr->opv_gvar; /* move global range ** variable number to attribute */ attr_ptr->opz_attnm.db_att_id = dmfattr;/* dmf attribute of ** range variable */ STRUCT_ASSIGN_MACRO((* datatype), attr_ptr->opz_dataval); attr_ptr->opz_equcls = OPE_NOEQCLS; /* this attribute has not been ** assigned an equivalence class yet */ attr_ptr->opz_func_att = OPZ_NOFUNCATT; /* this indicates that a ** function attribute is not ** associated with this joinop ** attribute element */ if ((dmfattr != OPZ_FANUM) && (dmfattr != OPZ_SNUM) && (dmfattr != OPZ_CORRELATED) && (attribute >= 0)) attr_ptr->opz_mask |= OPZ_COLLATT; if (subquery->ops_mask2 & OPS_VAREQVAR) attr_ptr->opz_mask |= OPZ_VAREQVAR; if ((var_ptr->opv_grv->opv_gmask & OPV_UVPROJECT) && (dmfattr >= 0)) { /* if union view exists in which a projection is possible ** (i.e. a UNION ALL view) then ** mark the attribute which are referenced in the union view*/ BTset((i4)dmfattr, (char *)var_ptr->opv_grv->opv_attrmap); } { /* initialize the histogram information associated with this ** attribute */ DB_STATUS hgstatus; /* ADT return status */ i4 dt_bits; hgstatus = adi_dtinfo(global->ops_adfcb, datatype->db_datatype, &dt_bits); if ((hgstatus == E_AD0000_OK) && (dt_bits & AD_NOHISTOGRAM)) { attr_ptr->opz_histogram.oph_dataval.db_datatype = OPH_NH_TYPE; attr_ptr->opz_histogram.oph_dataval.db_prec = OPH_NH_PREC; attr_ptr->opz_histogram.oph_dataval.db_length = OPH_NH_LENGTH; } else { attr_ptr->opz_histogram.oph_dataval.db_length = 0; hgstatus = adc_hg_dtln(global->ops_adfcb, datatype, &attr_ptr->opz_histogram.oph_dataval ); } # ifdef E_OP0780_ADF_HISTOGRAM if ((hgstatus != E_AD0000_OK) || (attr_ptr->opz_histogram.oph_dataval.db_datatype < 0) /* do not expect a nullable ** histogram datatype */ ) opx_verror( hgstatus, E_OP0780_ADF_HISTOGRAM, global->ops_adfcb->adf_errcb.ad_errcode); /* ** abort if unexpected error occurs */ # endif attr_ptr->opz_histogram.oph_dataval.db_data = NULL; /* init the ** data value ptr */ attr_ptr->opz_histogram.oph_numcells = OPH_NOCELLS; /* histogram ** has not been fetched so no cells ** exist at this point */ attr_ptr->opz_histogram.oph_histmm = NULL; /* no histogram ** exists at this point */ attr_ptr->opz_histogram.oph_mindomain = NULL; /* no minimum ** value for this histogram yet */ attr_ptr->opz_histogram.oph_maxdomain = NULL; /* no maximum ** value for this histogram yet */ } if (dmfattr == DB_IMTID) { /* implicit TID found so an equivalence class needs to be ** created ... so we know the equivalence class of the ** implicit TID for this (since the implicit TID is referenced ** by an index or explicitly in the qualification) */ var_ptr->opv_primary.opv_tid = ope_neweqcls( subquery, nextattr ); } } return( nextattr ); /* return joinop attribute which has ** been assigned */ } }
/*{ ** Name: OPC_EXNODEARRSET - set DSH ptr array indexes in exch_array1/2 ** ** Description: Analyze QP subtree saving DSH ptr array indexes to indicate ** the buffers and structures that need to be allocated for child ** thread DSH ** ** Inputs: ** ** Outputs: ** Returns: ** Exceptions: ** ** Side Effects: ** ** History: ** 1-mar-04 (inkdo01) ** Written for || query processing. ** 28-apr-04 (inkdo01) ** Forgot to set bits for QEN_STATUS and hash structures. ** 11-june-04 (inkdo01) ** Code to recurse on actions owned by QP node. ** 15-june-04 (inkdo01) ** Remove sejn_hget from bit map - not used (and not filled in) ** in QEF. ** 17-june-04 (inkdo01) ** Add logic to set QEN_PQ_RESET to allow earlier thread shutdown. ** 22-july-04 (inkdo01) ** Reworked to produce arrays of DSH ptr array indexes. ** 27-aug-04 (inkdo01) ** Forgot QEN_PART_INFOs addr'ed from ORIG nodes. ** 10-sep-04 (inkdo01) ** Remove QEN_PART_INFO entities from arrays. ** 15-May-2010 (kschendel) b123565 ** Add missing TPROC case to prevent looping; add default. ** Continue below 1:1 exch, as they depend on parents. ** Delete "resettable", done as a separate pass in opcran now. ** 19-May-2010 (kschendel) b123759 ** ijFlagsFile is a hold, not a row. ** Don't need to do rows here, done via bitmap. */ static VOID opc_exnodearrset( OPS_STATE *global, QEN_NODE *node, i2 *array1, i4 *array2, i4 *arrcnts) { QEN_OJINFO *ojinfop; QEN_PART_QUAL *pqual; QEN_SJOIN *sjnp; QEN_KJOIN *kjnp; QEN_TJOIN *tjnp; QEN_HJOIN *hjnp; QEN_SEJOIN *sejnp; QEN_SORT *srtp; QEN_TPROC *tprocp; QEN_TSORT *tsrtp; QEN_ORIG *origp; QEN_QP *qpp; QEN_EXCH *exchp; QEF_QP_CB *qp = global->ops_cstate.opc_qp; QEF_RESOURCE *resp; QEF_VALID *vlp; QEF_AHD *act; i4 i, j, k; i4 dmrix; bool endloop; /* Loop (recurse on left, iterate on right) and switch to process ** each node in subtree. */ for ( ; ; ) { if (node == (QEN_NODE *) NULL) return; /* just in case */ opc_exnheadset(global, node, array1, array2, arrcnts); /* set node header indexes */ ojinfop = (QEN_OJINFO *) NULL; pqual = NULL; dmrix = -1; switch (node->qen_type) { case QE_CPJOIN: case QE_FSMJOIN: case QE_ISJOIN: sjnp = &node->node_qen.qen_sjoin; ojinfop = sjnp->sjn_oj; if (sjnp->sjn_hfile >= 0) array1[arrcnts[IX_HLD]++] = sjnp->sjn_hfile; if (sjnp->sjn_itmat != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = sjnp->sjn_itmat->qen_pos; if (sjnp->sjn_okmat != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = sjnp->sjn_okmat->qen_pos; if (sjnp->sjn_okcompare != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = sjnp->sjn_okcompare->qen_pos; if (sjnp->sjn_joinkey != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = sjnp->sjn_joinkey->qen_pos; if (sjnp->sjn_jqual != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = sjnp->sjn_jqual->qen_pos; opc_exnodearrset(global, sjnp->sjn_out, array1, array2, arrcnts); node = sjnp->sjn_inner; break; case QE_KJOIN: kjnp = &node->node_qen.qen_kjoin; ojinfop = kjnp->kjoin_oj; pqual = kjnp->kjoin_pqual; if ((dmrix = kjnp->kjoin_get) >= 0) array2[arrcnts[IX_DMR]++] = kjnp->kjoin_get; if (kjnp->kjoin_key != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = kjnp->kjoin_key->qen_pos; if (kjnp->kjoin_kqual != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = kjnp->kjoin_kqual->qen_pos; if (kjnp->kjoin_kcompare != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = kjnp->kjoin_kcompare->qen_pos; if (kjnp->kjoin_iqual != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = kjnp->kjoin_iqual->qen_pos; if (kjnp->kjoin_jqual != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = kjnp->kjoin_jqual->qen_pos; node = kjnp->kjoin_out; break; case QE_TJOIN: tjnp = &node->node_qen.qen_tjoin; ojinfop = tjnp->tjoin_oj; pqual = tjnp->tjoin_pqual; if ((dmrix = tjnp->tjoin_get) >= 0) array2[arrcnts[IX_DMR]++] = tjnp->tjoin_get; if (tjnp->tjoin_jqual != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = tjnp->tjoin_jqual->qen_pos; if (tjnp->tjoin_isnull != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = tjnp->tjoin_isnull->qen_pos; node = tjnp->tjoin_out; break; case QE_HJOIN: hjnp = &node->node_qen.qen_hjoin; ojinfop = hjnp->hjn_oj; pqual = hjnp->hjn_pqual; array1[arrcnts[IX_HSH]++] = hjnp->hjn_hash; if (hjnp->hjn_dmhcb >= 0) array2[arrcnts[IX_DMH]++] = hjnp->hjn_dmhcb; if (hjnp->hjn_btmat != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = hjnp->hjn_btmat->qen_pos; if (hjnp->hjn_ptmat != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = hjnp->hjn_ptmat->qen_pos; if (hjnp->hjn_jqual != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = hjnp->hjn_jqual->qen_pos; opc_exnodearrset(global, hjnp->hjn_out, array1, array2, arrcnts); node = hjnp->hjn_inner; break; case QE_SEJOIN: sejnp = &node->node_qen.qen_sejoin; ojinfop = (QEN_OJINFO *) NULL; /* if (sejnp->sejn_hget >= 0) - these aren't ref'ed in QEF array2[arrcnts[IX_DMR]++] = sejnp->sejn_hget; */ if (sejnp->sejn_hfile >= 0) array1[arrcnts[IX_HLD]++] = sejnp->sejn_hfile; if (sejnp->sejn_itmat != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = sejnp->sejn_itmat->qen_pos; if (sejnp->sejn_ccompare != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = sejnp->sejn_ccompare->qen_pos; if (sejnp->sejn_oqual != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = sejnp->sejn_oqual->qen_pos; if (sejnp->sejn_okmat != NULL) array2[arrcnts[IX_CX]++] = sejnp->sejn_okmat->qen_pos; if (sejnp->sejn_kcompare != NULL) array2[arrcnts[IX_CX]++] = sejnp->sejn_kcompare->qen_pos; if (sejnp->sejn_kqual != NULL) array2[arrcnts[IX_CX]++] = sejnp->sejn_kqual->qen_pos; opc_exnodearrset(global, sejnp->sejn_out, array1, array2, arrcnts); node = sejnp->sejn_inner; break; case QE_TSORT: tsrtp = &node->node_qen.qen_tsort; pqual = tsrtp->tsort_pqual; if (tsrtp->tsort_get >= 0) array2[arrcnts[IX_DMR]++] = tsrtp->tsort_get; if (tsrtp->tsort_load >= 0) array2[arrcnts[IX_DMR]++] = tsrtp->tsort_load; if (tsrtp->tsort_create >= 0) array2[arrcnts[IX_DMT]++] = tsrtp->tsort_create; if (tsrtp->tsort_shd >= 0) array1[arrcnts[IX_SHD]++] = tsrtp->tsort_shd; if (tsrtp->tsort_mat != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = tsrtp->tsort_mat->qen_pos; node = tsrtp->tsort_out; break; case QE_SORT: srtp = &node->node_qen.qen_sort; if (srtp->sort_load >= 0) array2[arrcnts[IX_DMR]++] = srtp->sort_load; if (srtp->sort_create >= 0) array2[arrcnts[IX_DMT]++] = srtp->sort_create; if (srtp->sort_shd >= 0) array1[arrcnts[IX_SHD]++] = srtp->sort_shd; if (srtp->sort_mat != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = srtp->sort_mat->qen_pos; node = srtp->sort_out; break; case QE_ORIG: case QE_ORIGAGG: origp = &node->node_qen.qen_orig; pqual = origp->orig_pqual; if ((dmrix = origp->orig_get) >= 0) { array2[arrcnts[IX_DMR]++] = origp->orig_get; } if (origp->orig_qual != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = origp->orig_qual->qen_pos; node = (QEN_NODE *) NULL; break; case QE_QP: qpp = &node->node_qen.qen_qp; if (qpp->qp_qual != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = qpp->qp_qual->qen_pos; /* Process action headers anchored in QP node. */ for (act = node->node_qen.qen_qp.qp_act; act; act = act->ahd_next) opc_exactarrset(global, act, array1, array2, arrcnts); return; case QE_EXCHANGE: exchp = &node->node_qen.qen_exch; if (exchp->exch_mat != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = exchp->exch_mat->qen_pos; /* Don't probe below 1:N exchanges, they'll do their own setup. ** 1:1 exchange depends on parent, so keep going. */ if (exchp->exch_ccount > 1) return; node = exchp->exch_out; break; case QE_TPROC: tprocp = &node->node_qen.qen_tproc; if (tprocp->tproc_parambuild != NULL) array2[arrcnts[IX_CX]++] = tprocp->tproc_parambuild->qen_pos; if (tprocp->tproc_qual != NULL) array2[arrcnts[IX_CX]++] = tprocp->tproc_qual->qen_pos; return; /* Nothing underneath */ default: TRdisplay("Unexpected QP node type %d under exch\n",node->qen_type); opx_error(E_OP068E_NODE_TYPE); } /* end of switch */ /* Node specific bits have been set - now go over OJ and ** partition stuff (if any). */ if (ojinfop) { if (ojinfop->oj_tidHoldFile >= 0) array1[arrcnts[IX_HLD]++] = ojinfop->oj_tidHoldFile; if (ojinfop->oj_ijFlagsFile >= 0) array1[arrcnts[IX_HLD]++] = ojinfop->oj_ijFlagsFile; if (ojinfop->oj_innerJoinedTIDs) array1[arrcnts[IX_TTAB]++] = ojinfop->oj_innerJoinedTIDs-> ttb_tempTableIndex; if (ojinfop->oj_oqual != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = ojinfop->oj_oqual->qen_pos; if (ojinfop->oj_equal != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = ojinfop->oj_equal->qen_pos; if (ojinfop->oj_lnull != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = ojinfop->oj_lnull->qen_pos; if (ojinfop->oj_rnull != (QEN_ADF *) NULL) array2[arrcnts[IX_CX]++] = ojinfop->oj_rnull->qen_pos; } if (pqual != NULL) { array1[arrcnts[IX_PQUAL]++] = pqual->part_pqual_ix; if (pqual->part_const_eval != NULL) array2[arrcnts[IX_CX]++] = pqual->part_const_eval->un.hdr.pqe_cx->qen_pos; if (pqual->part_join_eval != NULL) array2[arrcnts[IX_CX]++] = pqual->part_join_eval->un.hdr.pqe_cx->qen_pos; } /* If TJOIN, KJOIN or ORIG, locate DMT_CB index in valid's. */ if (dmrix >= 0) for (resp = qp->qp_resources, endloop = FALSE; resp && !endloop; resp = resp->qr_next) if (resp->qr_type == QEQR_TABLE) for (vlp = resp->qr_resource.qr_tbl.qr_lastvalid; vlp && !endloop; vlp = vlp->vl_next) if (dmrix == vlp->vl_dmr_cb) { array2[arrcnts[IX_DMT]++] = vlp->vl_dmf_cb; endloop = TRUE; if (vlp->vl_partition_cnt > 1) array2[arrcnts[IX_DMR]++] = dmrix - 1; /* set master DMR_CB, too */ } if (node == (QEN_NODE *) NULL) return; } /* end of for ( ; ; ) */ }
/*{ ** Name: opv_parser - init global range table element given parser varno ** ** Description: ** This routine will initialize the global range table element in OPF ** corresponding to the PSF range table element. ** ** Inputs: ** global ptr to global range table ** gvar element in parser range table ** which is referenced in query ** ** Outputs: ** global->ops_rangetab.opv_base[gvar] initialize corresponding element ** in optimizer range table ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 2-jul-86 (seputis) ** initial creation ** 6-nov-88 (seputis) ** change RDF invalidation to include all indexes on base relation ** since this is only notification that OPF gets that its' time ** stamp is out of date ** 25-sep-89 (seputis) ** - made addition to timestamp check, to refresh if this is a ** multi-variable query and the tuple count is zero ** 9-nov-89 (seputis) ** - added OPA_NOVID initialization for b8160, and corrected ** sep 25 fix for timestamps ** 12-jan-90 (seputis) ** - detect table ID's which are the same for different range vars ** 26-feb-91 (seputis) ** - add improved diagnostics for b35862 ** 31-dec-91 (seputis) ** - flush cache entry if tuple count is zero and more than one ** range table entry, since aggregate queries and flattened queries ** are not getting handled. ** 12-feb-93 (jhahn) ** Added support for statement level rules. (FIPS) ** 12-apr-93 (ed) ** - fix bug 50673, relax range variable check for larger OPF table ** 7-dec-93 (ed) ** b56139 - add OPZ_TIDHISTO to mark tid attributes which ** need histograms,... needed since target list is traversed ** earlier than before ** 16-feb-95 (inkdo01) ** b66907 - check for explicit refs to inconsistent tabs/idxes ** 23-feb-95 (wolf) ** Recent addition of MEcopy call should have been accompanied by ** an include of me.h ** 10-aug-98 (stial01) ** opv_parser() Remove code to invalidate indexes. The invalidate ** table that follows will do the job. Invalidate indexes by table id ** can get E_OP008E_RDF_INVALIDATE, E_RD0045_ULH_ACCESS ** 31-oct-1998 (nanpr01) ** Reset the rdfcb->infoblk ptr before checking the error code. ** 18-june-99 (inkdo01) ** Init opv_ttmodel for temp table model histogram feature. ** 19-Jan-2000 (thaju02) ** relation descriptor may be out of date and contain a stale ** secondary index count, use rdfcb->rdf_info_block->rdr_no_index ** which reflects the number of index entries in the rdr_indx ** array. (b100060) ** 17-Jan-2004 (schka24) ** Rename RDR_BLD_KEY to RDR_BLD_PHYS. ** 3-Sep-2005 (schka24) ** Remove if-0'ed out section that included a ref to a member ** that is going away (opv_ghist). ** 17-Nov-2005 (schka24) ** Don't propagate RDF invalidates that we cause. Our RDF cache ** is out of date but that's not other servers' problem. ** 14-Mar-2007 (kschendel) SIR 122513 ** Light "partitioned" flag if partitioned table seen. ** 18-april-2008 (dougi) ** Add support for table procedures. ** 8-Jul-2010 (wanfr01) b123949 ** If rdf_gdesc failed with an error, don't use the column ** information - it may not be fully initialized. */ bool opv_parser( OPS_STATE *global, OPV_IGVARS gvar, OPS_SQTYPE sqtype, bool rdfinfo, bool psf_table, bool abort) { OPV_GRT *gbase; /* ptr to base of global range table */ # ifdef E_OP0387_VARNO if ((gvar < 0) || ((gvar >= PST_NUMVARS) && (gvar >= OPV_MAXVAR))) opx_error(E_OP0387_VARNO ); /* range var out of range - ** consistency check */ # endif gbase = global->ops_rangetab.opv_base; /* get base of global range table ** ptr to array of ptrs */ if ( !gbase->opv_grv[gvar] ) { /* if global range variable element has not been allocated */ OPV_GRV *grvp; /* ptr to new range var element */ if (global->ops_rangetab.opv_gv <= gvar) global->ops_rangetab.opv_gv = gvar+1; /* update the largest range ** table element assigned so far */ grvp = (OPV_GRV *) opu_memory(global, sizeof( OPV_GRV ) ); /* save ** and allocate ptr to global ** var */ /* Explicitly zero out the grv entry */ MEfill(sizeof(*grvp), (u_char)0, (PTR)grvp); grvp->opv_qrt = gvar; /* save index to ** parser range table element */ grvp->opv_created = sqtype; /* save global range table type */ grvp->opv_gvid = OPA_NOVID; /* if this base table was implicitly ** referenced then the view id of the ** explicitly reference view will be ** saved */ grvp->opv_siteid = OPD_NOSITE; /* initialize distributed site location */ grvp->opv_same = OPV_NOGVAR; /* used to map tables with similiar ** IDs */ grvp->opv_compare = OPV_NOGVAR; /* used to map tables with similiar ** IDs */ grvp->opv_ttmodel = NULL; /* RDR_INFO ptr for temp table model ** histograms */ gbase->opv_grv[gvar] = grvp; /* place into table */ /* get RDF information about the table */ if (rdfinfo) { RDF_CB *rdfcb; /* ptr to RDF control block which ** has proper db_id and sessionid ** info */ PST_RNGENTRY *rngentry; /* ptr to parse tree range entry */ DB_STATUS status; /* RDF return status */ i4 ituple; rdfcb = &global->ops_rangetab.opv_rdfcb; if (psf_table) { /* Snag table procedures and handle them elsewhere. */ if ((rngentry = global->ops_qheader->pst_rangetab[gvar]) ->pst_rgtype == PST_TPROC) { if (opv_tproc(global, gvar, grvp, rngentry) == E_DB_OK) return(FALSE); else return(TRUE); } STRUCT_ASSIGN_MACRO(global->ops_qheader->pst_rangetab[gvar]-> pst_rngvar, rdfcb->rdf_rb.rdr_tabid); /* need ** table id from parser's table */ #if 0 if ((BTnext((i4)-1, (char *)&rangep->pst_outer_rel, (i4)BITS_IN(rangep->pst_outer_rel)) >= 0) || (BTnext((i4)-1, (char *)&rangep->pst_inner_rel, (i4)BITS_IN(rangep->pst_outer_rel)) >= 0) ) grvp->opv_gmask |= OPV_GOJVAR; /* mark whether this ** variable is within the scope of an ** outer join */ /* OPV_GOJVAR is not reliably set since the subquery bitmap should be tested ** also for an aggregate temp, multi-to-one mappings may exist for global range ** variables */ #endif if (global->ops_qheader->pst_rangetab[gvar]-> pst_rgtype == PST_SETINPUT) grvp->opv_gmask |= OPV_SETINPUT; /* is this the set input ** parameter for a procedure */ rdfcb->rdf_rb.rdr_types_mask = RDR_RELATION | RDR_INDEXES | RDR_ATTRIBUTES | RDR_BLD_PHYS; /* get relation info ** - The optimizer uses attribute ** info in query tree directly ** but it is needed to be requested ** since the RDF uses attr info to ** build RDR_BLK_KEY info. The ** attribute info does not need to ** requested if RDF is changed. ** - ask for indexes so that ** invalidating the cache can also ** invalidate any indexes in the ** cache */ /* When we're ready to enable column comparison stats, uncomment the following statement. ** rdfcb->rdf_rb.rdr_2types_mask = RDR2_COLCOMPARE; /* column ** comparison stats, too */ } rdfcb->rdf_info_blk = NULL; /* get new ptr to info ** associated with global var */ status = rdf_call( RDF_GETDESC, (PTR)rdfcb); grvp->opv_relation = rdfcb->rdf_info_blk; /* save ptr to ** new info block */ if ((status != E_RD0000_OK) && (rdfcb->rdf_error.err_code != E_RD026A_OBJ_INDEXCOUNT) /* this ** is a check for distributed ** index information, which could ** be inconsistent but should not ** cause the query to be aborted ** it will cause indexes to be ** avoided */ ) { gbase->opv_grv[gvar] = NULL; if (abort) opx_verror( status, E_OP0004_RDF_GETDESC, rdfcb->rdf_error.err_code); else { return (TRUE); /* indicate failure to get RDF ** descriptor */ } } BTset( (i4)gvar, (char *)&global->ops_rangetab.opv_mrdf); /* indicate ** that RDF information is fixed */ /* Check table and all associated indexes to see if there is a ** statement level index associated with this variable */ if (grvp->opv_relation->rdr_rel->tbl_2_status_mask & DMT_STATEMENT_LEVEL_UNIQUE) grvp->opv_gmask |= OPV_STATEMENT_LEVEL_UNIQUE; for( ituple = grvp->opv_relation->rdr_no_index; ituple-- ;) { if (grvp->opv_relation->rdr_indx[ituple]->idx_status & DMT_I_STATEMENT_LEVEL_UNIQUE) grvp->opv_gmask |= OPV_STATEMENT_LEVEL_UNIQUE; } if (psf_table) { /* check if timestamp matches and invalidate RDF cache ** date of last modify do not match, this is only done ** for tables passed by PSF, the other tables should ** be dependent on the PSF time stamp */ DB_TAB_TIMESTAMP *timeptr; /* ptr to last modify ** date that RDF has cached */ DB_TAB_TIMESTAMP *psftimeptr; /* ptr to last modify ** date which parser used for the ** table */ psftimeptr = &global->ops_qheader->pst_rangetab[gvar]-> pst_timestamp; timeptr = &grvp->opv_relation->rdr_rel->tbl_date_modified; if (timeptr->db_tab_high_time != psftimeptr->db_tab_high_time || timeptr->db_tab_low_time != psftimeptr->db_tab_low_time || ( !grvp->opv_relation->rdr_rel->tbl_record_count && (global->ops_qheader->pst_rngvar_count > 1) ) /* special zero tuple count ** case check to see if tuple count ** is way off, i.e. a table create ** followed by a number of appends ** will cause a 0 tuple count, check ** for more than one variable since ** refresh is only useful when joins occur */ ) { PTR save_fcb = rdfcb->rdf_rb.rdr_fcb; /* Don't propagate this invalidate to other DBMS servers. ** There's no reason to think that they are as out of ** date as we are. (plus this might be a session temp ** which is strictly local!) */ rdfcb->rdf_rb.rdr_fcb = NULL; status = rdf_call( RDF_INVALIDATE, (PTR)rdfcb); rdfcb->rdf_rb.rdr_fcb = save_fcb; # ifdef E_OP008E_RDF_INVALIDATE if (status != E_RD0000_OK) opx_verror( E_DB_ERROR, E_OP008E_RDF_INVALIDATE, rdfcb->rdf_error.err_code); # endif status = rdf_call( RDF_GETDESC, (PTR)rdfcb); grvp->opv_relation = rdfcb->rdf_info_blk; /* save ptr to ** new info block */ if (status != E_RD0000_OK) { gbase->opv_grv[gvar] = NULL; opx_verror( E_DB_ERROR, E_OP0004_RDF_GETDESC, rdfcb->rdf_error.err_code); } timeptr = &grvp->opv_relation->rdr_rel->tbl_date_modified; if (timeptr->db_tab_high_time != psftimeptr->db_tab_high_time || timeptr->db_tab_low_time != psftimeptr->db_tab_low_time ) opx_vrecover( E_DB_ERROR, E_OP008F_RDF_MISMATCH, rdfcb->rdf_error.err_code); /* PSF timestamp is ** still out of date, so tell ** SCF to reparse the query */ } { /* search thru existing tables to discover if any ** tables have the same table ID */ OPV_IGVARS gvno; DB_TAB_ID *tabidp; OPV_IGVARS target_vno; tabidp = &global->ops_qheader->pst_rangetab[gvar]->pst_rngvar; target_vno = OPV_NOGVAR; for (gvno = 0; gvno < OPV_MAXVAR; gvno++) { OPV_GRV *grv1p; grv1p = gbase->opv_grv[gvno]; if (grv1p && grv1p->opv_relation) { DB_TAB_ID *gtabidp; if (gvno == gvar) continue; gtabidp = &grv1p->opv_relation->rdr_rel->tbl_id; if ((tabidp->db_tab_base == gtabidp->db_tab_base) && (tabidp->db_tab_index == gtabidp->db_tab_index) ) { /* found 2 table ID's which are identical */ global->ops_gmask |= OPS_IDSAME; if (target_vno == OPV_NOGVAR) { /* map all table id's vars to the lowest ** global range number of the group */ if (gvno > gvar) target_vno = gvar; else target_vno = gvno; } grv1p->opv_same = target_vno; grvp->opv_same = target_vno; if (target_vno != gvar) break; } } } } } if (global->ops_cb->ops_smask & OPS_MDISTRIBUTED) opd_addsite(global, grvp); /* add site information if a ** distributed thread */ /* Check for partitioned table, turns on additional ** analysis after enumeration. */ if (grvp->opv_relation != NULL && grvp->opv_relation->rdr_parts != NULL) global->ops_gmask |= OPS_PARTITION; if (grvp->opv_relation && grvp->opv_relation->rdr_rel-> tbl_2_status_mask & DMT_INCONSIST) /* check for inconsistent table ** (due to partial back/recov) */ { OPT_NAME tabname; i2 i; MEcopy(&grvp->opv_relation->rdr_rel->tbl_name, sizeof(grvp->opv_relation->rdr_rel->tbl_name), &tabname); /* table name is msg token */ for (i = sizeof(grvp->opv_relation->rdr_rel->tbl_name); i > 1 && tabname.buf[i-1] == ' '; i--); tabname.buf[i] = 0; /* lop blanks off name */ opx_1perror(E_OP009A_INCONSISTENT_TAB,(PTR)&tabname); } } else grvp->opv_relation = NULL; /* set to NULL if not used */ } return(FALSE); }
/*{ ** Name: opa_byproject - project bydoms if necessary ** ** Description: ** Traverse the subqueries and determine if the by domains need to be ** projected for any function aggregates. ** ** Inputs: ** global ptr to global state variable ** .ops_subquery beginning of a list of subqueries ** to be analyzed ** ** Outputs: ** global.ops_subquery...ops_next.ops_agg.opa_projection - this flag is ** set TRUE for each subquery which will ** require by domains to be projected. ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 28-jun-86 (seputis) ** initial creation ** 25-aug-89 (seputis) ** fix OPF access violation when parse tree gets normalized ** 31-Aug-2006 (kschendel) ** Watch for HFAGG as well as RFAGG, even though they shouldn't ** appear yet this early in opa. */ VOID opa_byproject( OPS_STATE *global) { OPS_SUBQUERY *subquery; /* used to traverse the list of ** subqueries */ if (global->ops_cb->ops_alter.ops_noproject) return; /* no projections requested */ for (subquery = global->ops_subquery; subquery->ops_sqtype != OPS_MAIN; subquery = subquery->ops_next) { #if 0 if (subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_project && ( subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_qlang == DB_SQL ) ) { /* this should only occur if the flattening code is enabled ** so double check this */ if (subquery->ops_agg.opa_mask & OPA_APROJECT) { if (!(global->ops_cb->ops_smask & OPS_MDISTRIBUTED)) /* distributed ** should be able to deal with ** SQL */ subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_qlang = DB_QUEL; /* FIXME - for now make this quel ** since server does not support ** projection of SQL aggregate, ** in particular the empty set ** returns a different value */ } else opx_error(E_OP0283_PROJECT); /* this is unexpected since ** SQL never requires a projection */ } #endif if (( (subquery->ops_sqtype == OPS_FAGG) || (subquery->ops_sqtype == OPS_HFAGG) || (subquery->ops_sqtype == OPS_RFAGG) ) && ( (subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_qlang == DB_QUEL) || (subquery->ops_agg.opa_mask & OPA_APROJECT) ) && subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_project ) { /* only valid for function aggregates */ if (subquery->ops_agg.opa_projection = opa_bydomproject(subquery)) { subquery->ops_sqtype = OPS_FAGG; /* if projection is required ** then a temporary is needed */ subquery->ops_result = subquery->ops_gentry; /* assign a result ** relation */ global->ops_rangetab.opv_base->opv_grv[subquery->ops_result]-> opv_gsubselect = NULL; /* remove subselect descriptor*/ global->ops_rangetab.opv_base->opv_grv[subquery->ops_result]-> opv_created = OPS_FAGG; /* assign new creation type to ** global range variable */ } } } }
/*{ ** Name: opa_obylist - replace variables in outer by inner ** ** Description: ** This procedure will attempt to replace the variables in the outer ** aggregate by using bylist attributes of the inner aggregate. ** ** Inputs: ** global global state variable ** inner inner function aggregate subquery ** whose bylist will be used to attempt ** to replace the outer aggregate variables ** outer outer aggregate subquery whose variables ** will possibly be replaced by the inner ** ** Outputs: ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 15-apr-86 (seputis) ** initial creation ** 16-may-96 (inkdo01) ** Change 409554 has been backed out to fix bug 74793. It claimed to ** eliminate obsolete code (because of change 409457), but the code ** appears to have still been necessary for queries involving outer ** joins of aggregate views. ** 3-dec-02 (inkdo01) ** Changes for range table expansion. ** 23-nov-05 (inkdo01) ** Fix a bug in one of the more complex expressions that derived from ** the range table expansion. [@history_line@]... */ static VOID opa_obylist( OPS_STATE *global, OPS_SUBQUERY *inner, OPS_SUBQUERY *outer) { OPV_GBMVARS outermap; /* var map of outer aggregate */ opv_smap(outer); /* get variable map of outer aggregate */ MEcopy((char *)&outer->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm, sizeof(outermap), (char *)&outermap); BTor(OPV_MAXVAR, (char *)&outer->ops_root->pst_sym.pst_value.pst_s_root.pst_rvrm, (char *)&outermap); opv_smap(inner); /* get variable map of inner aggregate */ BTand(OPV_MAXVAR, (char *)&inner->ops_agg.opa_blmap, (char *)&outermap); if (BTcount((char *)&outermap, OPV_MAXVAR) == 0) /* if the outer aggregate and the inner aggregate do not have any ** variables in common then there can be no replacement so return. */ return; BTor(OPV_MAXVAR, (char *)&inner->ops_root->pst_sym.pst_value.pst_s_root.pst_tvrm, (char *)&outer->ops_aggmap); BTor(OPV_MAXVAR, (char *)&inner->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm, (char *)&outer->ops_aggmap); BTor(OPV_MAXVAR, (char *)&inner->ops_root->pst_sym.pst_value.pst_s_root.pst_rvrm, (char *)&outer->ops_aggmap); /* this set of variables could be substituted ** in the outer, so they are not to be ** assumed to be in the from list for ** a cartesean product as in the query ** "select r.a from r,s" */ { OPV_GBMVARS usedmap; /* var map of variables which were ** replaced by substituting the ** bylist attributes of the inner */ OPV_GBMVARS newmap; /* var map of the outer aggregate ** after the inner aggregate bylist ** used to substitute expressions ** in the outer */ OPV_GBMVARS tempmap; MEfill(sizeof(usedmap), 0, (char *)&usedmap); /* initialize var map */ opa_checkopt(global, inner->ops_agg.opa_byhead->pst_left, outer->ops_root, &usedmap, &newmap); /* this routine will return information ** on what the query tree would ** look like if the inner aggregate ** was substituted (without actually ** doing the substitution) */ MEcopy((char *)&newmap, sizeof(newmap), (char *)&tempmap); BTand(OPV_MAXVAR, (char *)&usedmap, (char *)&tempmap); /* This replaces the old (32 bit varmap) test of "usedmap && ** !(newmap & usedmap)". */ if (BTcount((char *)&usedmap, OPV_MAXVAR) != 0 && /* non-zero implies some optimizations ** were found */ BTcount((char *)&tempmap, OPV_MAXVAR) == 0) /* the substitution would eliminate ** those variables entirely */ { /* COMMIT THE CHANGES ** the usedmap is non-zero so some variable subtitutions were ** found. Moreover, the substitutions would entirely eliminate ** the variables since newmap is non-zero ** ** First making a copy of the bylist for the outer aggregate ** if it exists (and if it is not the main query). This ** is done to avoid the problem of optimizing away the links ** made by the outer aggregate. For example, in the query ** RETRIEVE SUPPLIERS WHO SUPPLY ALL PARTS ** ret( s.sname, s.s) where any(p.p by s.s where any(sp.tid ** by p.p,s.s where p.p=sp.p and s.s=sp.s)=0)=0 ** In this query the outer aggregate references only attributes ** in the BY list of the inner aggregate, and won't have the ** aggregate result linked to the main query ... if we did ** not make a copy of the bylist ... remember that the outer ** aggregate was linked to the main query by using the by list ** subtrees directly! ** FIXME - OPA_LINK will copy the bylists anyways so this copy ** is not needed */ OPV_IGVARS innervarno; /* var number of inner ** aggregate which will be ** referenced for substitution */ if (outer->ops_sqtype == OPS_MAIN) global->ops_gmask |= OPS_TCHECK; else if (outer->ops_agg.opa_byhead) /* outer aggregate has a by list */ { PST_QNODE *bylist; /* used to traverse the bylist */ /* traverse the bylist and copy the subtrees */ for ( bylist = outer->ops_agg.opa_byhead->pst_left; bylist && bylist->pst_sym.pst_type != PST_TREE; bylist = bylist->pst_left) opv_copytree( global, &bylist->pst_right ); } /* Traverse the tree and actually perform the substitutions instead ** of only checking for them */ innervarno = (*inner->ops_agg.opa_graft)->pst_sym.pst_value. pst_s_var.pst_vno; if (outer->ops_global->ops_qheader->pst_numjoins > 0) { /* make sure that all the outer joins semantics are ** the same for all the relations referenced, or else ** semantics are lost, i.e. cannot substitute if variables ** have different maps */ PST_J_MASK pinner; PST_J_MASK pouter; bool first_time; OPV_IGVARS gvar; PST_J_MASK *ijmaskp; PST_J_MASK *ojmaskp; OPL_PARSER *pinnerp; OPL_PARSER *pouterp; first_time = TRUE; pinnerp = outer->ops_oj.opl_pinner; pouterp = outer->ops_oj.opl_pouter; for (gvar = -1; (gvar = BTnext((i4)gvar, (char *)&usedmap, (i4)BITS_IN(usedmap)))>=0;) { if (first_time) { MEcopy((PTR)&pinnerp->opl_parser[gvar], sizeof(pinner), (PTR)&pinner); MEcopy((PTR)&pouterp->opl_parser[gvar], sizeof(pouter), (PTR)&pouter); } else { if (MEcmp((PTR)&pinnerp->opl_parser[gvar], (PTR)&pinner, sizeof(pinner)) || MEcmp((PTR)&pouterp->opl_parser[gvar], (PTR)&pouter, sizeof(pouter)) ) return; /* outer joins semantics of ** variables to be substituted are ** different, FIXME, try to substitute ** one variable instead of 2 */ } } /* copy the outer join semantics to the substituted variable */ ijmaskp = &pinnerp->opl_parser[innervarno]; ojmaskp = &pouterp->opl_parser[innervarno]; if ((BTnext((i4)-1, (char *)ijmaskp, (i4)BITS_IN(*ijmaskp)) >= 0) || (BTnext((i4)-1, (char *)ojmaskp, (i4)BITS_IN(*ojmaskp)) >= 0) ) opx_error(E_OP0288_OJAGG); /* should not already have an ** outer join defined on this aggregate ** in this query */ MEcopy((PTR)&pinner, sizeof(*ijmaskp), (PTR)ijmaskp); MEcopy((PTR)&pouter, sizeof(*ojmaskp), (PTR)ojmaskp); } outer->ops_vmflag = FALSE; /* bitmaps need to be updated if a ** substitution on the outer is made */ opa_commit(global, inner->ops_agg.opa_byhead->pst_left, &outer->ops_root, innervarno); /* this routine will traverse ** the tree in the same way as ** opa_checkopt except that ** substitutions will actually be made */ global->ops_gmask &= (~OPS_TCHECK); } } }
/*{ ** Name: opv_agrv - allocate new global range variable ** ** Description: ** Find a free slot in the global range table for an ** aggregate function, or implicitly referenced index. ** If there are no free slots for the aggregate function, then the ** optimization is aborted and an error reported. There will be ** one global range table per optimization and there will be no ** overlapping of range table assignments i.e. it is conceivable ** the two temporary relations could use the same range table ** entry since they do not exist at the same... this will not be ** done. ** ** Inputs: ** global ptr to global state variable ** name ptr to table name ** NULL- indicates a temporary table ** owner ptr to owner name ** abort TRUE if optimization should be aborted ** in case of error ** ** Outputs: ** Returns: ** - index into global range table representing the allocated ** variable ** Exceptions: ** Will generate an internal exception if the global range table ** is full. This will abort the query and report an error. ** ** Side Effects: ** none ** ** History: ** 7-apr-86 (seputis) ** initial creation ** 11-apr-91 (seputis) ** ask for RDF info if name, or table ID is given so ** that explicit secondary index substitution can get ** histograms ** 18-sep-92 (ed) ** bug 44850 - added parameter to allow multi-to-one mapping ** so a common aggregate temp can be used ** 17-Jan-2004 (schka24) ** Rename RDR_BLD_KEY to RDR_BLD_PHYS, gives us partition info too. [@history_line@]... */ OPV_IGVARS opv_agrv( OPS_STATE *global, DB_TAB_NAME *name, DB_OWN_NAME *owner, DB_TAB_ID *table_id, OPS_SQTYPE sqtype, bool abort, OPV_GBMVARS *gbmap, OPV_IGVARS gvarno) { OPV_IGVARS grv_index; /* index into global range table */ OPV_IGVARS empty_index; /* index into global range table of ** free element */ OPV_GRT *gbase; /* ptr to base of array of ptrs to global ** range table elements */ bool lookup; /* look for existing definition if ** names are available */ lookup = name && owner; /* TRUE - if RDF table ID given */ empty_index = OPV_NOGVAR; gbase = global->ops_rangetab.opv_base; for ( grv_index = 0; grv_index < OPV_MAXVAR; grv_index++) { OPV_GRV *existing_var; if (!(existing_var = gbase->opv_grv[grv_index])) { if (empty_index == OPV_NOGVAR) { empty_index = grv_index; if (!lookup) break; /* empty slot found and we do not need ** to continue searching for an existing ** table entry of the same name */ } } else { if (lookup && existing_var->opv_relation && existing_var->opv_relation->rdr_rel && ( existing_var->opv_relation->rdr_rel->tbl_name.db_tab_name[0] == name->db_tab_name[0] ) && ( existing_var->opv_relation->rdr_rel->tbl_owner.db_own_name[0] == owner->db_own_name[0] ) && !MEcmp((PTR)&existing_var->opv_relation->rdr_rel->tbl_name, (PTR)name, sizeof(*name)) && !MEcmp((PTR)&existing_var->opv_relation->rdr_rel->tbl_owner, (PTR)owner, sizeof(*owner)) && ( !gbmap || !BTtest((i4)grv_index, (char *)gbmap) /* do not use the ** same global range variable ** in the same subquery twice ** or OPC will complain */ ) ) { /* a match has been found */ if (gbmap) BTset((i4)grv_index, (char *)gbmap); /* set the global ** bit map so that this ** range variable is not ** reused in the same subquery */ return(grv_index); } } } if (empty_index != OPV_NOGVAR) { if (gvarno == OPV_NOGVAR) { /* empty slot found - allocate and initialize slot and return */ OPV_GRV *grv; /* pointer to global range table element */ RDF_CB *rdfcb; rdfcb = &global->ops_rangetab.opv_rdfcb; if (name || table_id) { /* if name is available then table ID might be available */ if (table_id) { /* use table ID if available */ STRUCT_ASSIGN_MACRO((*table_id), rdfcb->rdf_rb.rdr_tabid); /* ** need table name */ rdfcb->rdf_rb.rdr_types_mask = RDR_RELATION | RDR_ATTRIBUTES | RDR_BLD_PHYS; /*get relation info ** - The optimizer uses attribute ** info in query tree directly ** but it is needed to be requested ** since the RDF uses attr info to ** build RDR_BLK_PHYS info. The ** attribute info does not need to ** be requested if RDF is changed.*/ } else { /* table ID not available so use name */ MEfill( (i4)sizeof(DB_TAB_ID), (u_char)0, (PTR)&rdfcb->rdf_rb.rdr_tabid); rdfcb->rdf_rb.rdr_types_mask = RDR_RELATION | RDR_ATTRIBUTES | RDR_BLD_PHYS | RDR_BY_NAME; STRUCT_ASSIGN_MACRO((*name), rdfcb->rdf_rb.rdr_name.rdr_tabname);/* need ** table name */ STRUCT_ASSIGN_MACRO((*owner), rdfcb->rdf_rb.rdr_owner);/* ** need table owner */ } } else rdfcb->rdf_info_blk = NULL; /* get new ptr to info ** associated with global var */ /* allocate and initialize global range table element */ if (opv_parser(global, empty_index, sqtype, (name != NULL) || (table_id != NULL), /* TRUE - if rdf info needs to be retrieved */ FALSE, /* TRUE - if this is a parser range table element */ abort) /* TRUE - if error occurs then otherwise FALSE ** means return varinit == TRUE */ ) return(OPV_NOGVAR); /* ignore variable if error occurs */ grv = gbase->opv_grv[empty_index]; /* get ptr to element */ grv->opv_qrt = OPV_NOGVAR; /* indicates that this table was not ** explicitly referenced in the query */ grv->opv_relation = rdfcb->rdf_info_blk; /* save ptr to RDF info */ } else { gbase->opv_grv[empty_index] = gbase->opv_grv[gvarno]; /* used ** for aggregate temporaries which ** require "2 cursors" */ } if (gbmap) BTset((i4)empty_index, (char *)gbmap); /* set the global ** bit map so that this ** range variable is not ** reused in the same subquery */ } else if (abort) /* the entire table is full so report and error */ opx_error(E_OP0005_GRANGETABLE); return (empty_index); /* return with no range table entry */ }
/*{ ** Name: 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); }
/*{ ** 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: ope_type - get datatype of equivalence class ** ** Description: ** Get attribute from the eqclass with the greatest length if ** the types are the same or if FLOATs and INTs, pick the ** FLOAT. ** ** this only works for interior nodes, not original nodes ** ** Inputs: ** subquery ptr to subquery being analyzed ** eqcls equivalence class ** ** Outputs: ** Returns: ** ptr to DB_DATA_VALUE datatype of the equivalence class ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 13-jun-86 (seputis) ** initial creation from newatt ** 4-nov-97 (inkdo01) ** Don't get mad at the dopey OPE_SPATJ eqclass (it can be ** heterogeneous). [@history_line@]... */ DB_DATA_VALUE * ope_type( OPS_SUBQUERY *subquery, OPE_IEQCLS eqcls) { OPZ_BMATTS *attrmap; /* ptr to bit map of attributes in the ** equilvalence class */ OPZ_IATTS attr; /* current attribute of equivalence ** class being analyzed */ OPZ_AT *abase; /* ptr to base of array of ptrs to ** joinop attributes */ DB_DATA_VALUE *bestdtp; /* ptr to data type of attribute with ** largest width in equivalence class ** map */ DB_DT_ID besttype; /* best type found so far */ OPZ_IATTS maxattr; /* number of attributes defined in ** subquery */ OPE_EQCLIST *eqcp; /* ptr to eqcls structure */ bestdtp = NULL; eqcp = subquery->ops_eclass.ope_base->ope_eqclist[eqcls]; attrmap = &eqcp->ope_attrmap; maxattr = subquery->ops_attrs.opz_av; /* number of joinop attributes defined */ abase = subquery->ops_attrs.opz_base; /* ptr to base of array of ptrs to ** joinop attributes */ for (attr = -1; (attr = BTnext(attr, (char *)attrmap, (i4)maxattr)) >= 0;) { DB_DATA_VALUE *datatypep; /* ptr to datatype of current ** attribute element being tested */ DB_DT_ID type; /* type of next attribute being tested */ datatypep = &abase->opz_attnums[attr]->opz_dataval; /* datatype of ** this attribute */ type = abs(datatypep->db_datatype); /* save type of new attribute */ if (!bestdtp) { /* if this is the first then use it */ bestdtp = datatypep; besttype = type; } else { if (( (type == DB_FLT_TYPE) && (besttype == DB_INT_TYPE) ) || ( (type == besttype || eqcp->ope_mask & OPE_SPATJ) && (datatypep->db_length > bestdtp->db_length) )) { /* new attribute has higher priority type */ bestdtp = datatypep; besttype = type; } #ifdef E_OP0383_TYPEMISMATCH if ((abs(datatypep->db_datatype) != abs(bestdtp->db_datatype) ) && !(eqcp->ope_mask & OPE_SPATJ) && (!opn_numeric(type) || !opn_numeric(besttype))) /* newatt: bad types:%d, %d */ opx_error(E_OP0383_TYPEMISMATCH); #endif } } #ifdef E_OP0384_NOATTS if (!bestdtp) /* newatt: nothing in att map, %d */ opx_error(E_OP0384_NOATTS); #endif return(bestdtp); }