Ejemplo n.º 1
0
/*{
** 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 );
}
Ejemplo n.º 2
0
/*{
** 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 */
}
Ejemplo n.º 3
0
/*{
** 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
    }	
}
Ejemplo n.º 4
0
/*{
** 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);
}
Ejemplo n.º 5
0
/*{
** 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 */
}
Ejemplo n.º 6
0
/*{
** 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
    }	
}
Ejemplo n.º 7
0
/*{
** 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
    }	
}
Ejemplo n.º 8
0
/*{
** 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);
}
Ejemplo n.º 9
0
/*{
** 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);
}
Ejemplo n.º 10
0
/* 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 ( ; ; ) */

}
Ejemplo n.º 11
0
/*{
** 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);

}
Ejemplo n.º 12
0
/*{
** 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 */
}
Ejemplo n.º 13
0
/*{
** 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
                                        */
    }
}
Ejemplo n.º 14
0
/*{
** 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 ( ; ; ) */

}
Ejemplo n.º 15
0
/*{
** 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);
}
Ejemplo n.º 16
0
/*{
** 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 */
            }
        }
    }
}
Ejemplo n.º 17
0
/*{
** 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);
	}
    }
}
Ejemplo n.º 18
0
/*{
** 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 */
}
Ejemplo n.º 19
0
/*{
** 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);
}
Ejemplo n.º 20
0
/*{
** 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);
}
Ejemplo n.º 21
0
/*{
** 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);
}