Exemplo n.º 1
0
/*{
** Name: opx_verror	- this routine will report another facility's error
**
** Description:
**      This routine will report an error from another facility
**      optimizer error.  It will check the status and see the error
**      is recoverable.  The routine will not return if the status is fatal
**      but instead generate an internal exception and
**      exit via the exception handling mechanism.  The error code will be
**      placed directly in the user's calling control block.  A ptr to this
**      block was placed in the session control block (which can be requested
**      from SCF)
**
** Inputs:
**      error                           optimizer error code to report
**      facility                        facility's error code
**      status                          facility's return status
**
** Outputs:
**	Returns:
**	    VOID
**	Exceptions:
**	    - internal exception generated
**
** Side Effects:
**	    the exception handling routines are responsible for
**          deallocating OPF resources bound to this session.
**
** History:
**	3-jul-86 (seputis)
**          initial creation
**	28-jan-91 (seputis)
**          added support for OPF ACTIVE flag
**	30-apr-92 (bryanp)
**	    Translate E_RD002A_DEADLOCK to E_OP0010_RDF_DEADLOCK.
[@history_line@]...
*/
VOID
opx_verror(
	DB_STATUS          status,
	OPX_ERROR          error,
	OPX_FACILITY       facility)
{
    OPS_CB              *opscb;			/* ptr to session control block
                                                ** for this optimization */
    opscb = ops_getcb();
    opscb->ops_retstatus = status;		/* save status code */
    if (facility == E_RD002A_DEADLOCK)
	error = E_OP0010_RDF_DEADLOCK;
    if ((facility == E_RD002B_LOCK_TIMER_EXPIRED)
	&&
	(opscb->ops_smask & OPS_MCONDITION))
    {
	error = E_OP000D_RETRY;			/* when an attempt is made to
						** report the RDF timeout error
						** then it should be translated
						** into a retry error, since an
						** undetected deadlock may have
						** occurred due to the ACTIVE flag */
	opscb->ops_server->opg_sretry++;	/* track number of retrys */
	opx_vrecover(status, E_OP000D_RETRY, facility); /* this routine will not
						** return */
    }
    else
	opx_rverror(opscb->ops_callercb, status, error, facility); 

    if (DB_SUCCESS_MACRO(status))
    {
	return;					/* return if error is not
                                                ** serious */
    }
    EXsignal( (EX)EX_JNP_LJMP, (i4)1, (long)error); /* signal an optimizer long
                                                ** jump with error code */
}
Exemplo n.º 2
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);
}
Exemplo n.º 3
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);
}