Beispiel #1
0
/*{
** Name: OPC_EXNHEADCNT	- count array entriesfor node headers
**
** Description: Counts array entries for node headers - allows code to be 
**	called from several places in module
**
** Inputs:
**
** Outputs:
**	Returns:
**	Exceptions:
**
** Side Effects:
**
** History:
**      22-july-04 (inkdo01)
**	    Written for || query processing.
*/
static VOID
opc_exnheadcnt(
	OPS_STATE	*global,
	QEN_NODE	*node,
	i4		*arrcnts,
	PTR		rowmap)

{

    /* Simply analyze possible array entries for node header and
    ** add (as necessary) to counts. */

    if (node == (QEN_NODE *) NULL)
	return;		/* just in case */

    arrcnts[IX_STAT]++;
    if (node->qen_row >= 0)
	BTset(node->qen_row, rowmap);
    if (node->qen_frow >= 0)
	BTset(node->qen_frow, rowmap);
    if (node->qen_fatts != (QEN_ADF *) NULL)
	arrcnts[IX_CX]++;
    if (node->qen_prow != (QEN_ADF *) NULL)
	arrcnts[IX_CX]++;

}
Beispiel #2
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);
}
Beispiel #3
0
/* First, a tiny helper routine to deal with the mini-program pointed
** to by a QEN_PART_QUAL.
*/
static void
opc_arrcnt_pqe(QEN_PQ_EVAL *pqe, i4 *arrcnts, PTR rowmap)
{
    i4 ninstrs;

    arrcnts[IX_CX]++;
    BTset(pqe->un.hdr.pqe_value_row, rowmap);
    BTset(pqe->pqe_result_row, rowmap);
    ninstrs = pqe->un.hdr.pqe_ninstrs;
    while (--ninstrs > 0)
    {
	pqe = (QEN_PQ_EVAL *) ((char *)pqe + pqe->pqe_size_bytes);
	BTset(pqe->pqe_result_row, rowmap);
	if (pqe->pqe_eval_op == PQE_EVAL_ANDMAP
	  || pqe->pqe_eval_op == PQE_EVAL_ORMAP)
	    BTset(pqe->un.andor.pqe_source_row, rowmap);
    }
} /* opc_arrcnt_pqe */
Beispiel #4
0
/*{
** Name: opv_mapvar	- update varmap of a query tree fragment
**
** Description:
**      This routine will recursively descent the query tree and update the 
**      varmap i.e. bitmap of all range variables referenced in this query tree
**      given to this routine.
**
** Inputs:
**      nodep                           ptr to current query tree node being
**                                      analyzed
**       map                            ptr to bitmap of vars to be updated
**
** Outputs:
**	Returns:
**	    VOID
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**	3-jul-86 (seputis)
**          initial creation
**     28-jan-04 (wanfr01)
**        Bug 36414, INGSRV582
**        Constant node doesn't need it's children recursed;
**        all children will also be constants.
[@history_line@]...
*/
VOID
opv_mapvar(
	PST_QNODE          *nodep,
	OPV_GBMVARS        *map)
{
    while (nodep)
    {
	if (nodep->pst_sym.pst_type == PST_VAR)
	{
	    /* var node found so set bit map */
	    BTset( (i4)nodep->pst_sym.pst_value.pst_s_var.pst_vno,
		   (char *)map );
	    return;
	}
	if ((nodep->pst_left) && (nodep->pst_sym.pst_type != PST_CONST))
	    opv_mapvar( nodep->pst_left, map); /* recurse down left */
	nodep = nodep->pst_right;	/* iterate down right side */
    };
}
Beispiel #5
0
VOID
psy_vcount(
	PST_QNODE	*tree,
	PST_VRMAP	*bitmap)
{
    PSY_STK stk = {0, 0, {0, }};
    STATUS sts;

    (VOID)MEfill(sizeof(PST_VRMAP), (u_char)0, (PTR)bitmap);

    while (tree)
    {
	/* If this is a VAR node supplement the bitmap */
	switch (tree->pst_sym.pst_type)
	{
	case PST_VAR:
	    (VOID)BTset((i4)tree->pst_sym.pst_value.pst_s_var.pst_vno,
		(char *)bitmap);
            break;

        case PST_ROOT:
	    /* check union nodes */
	    if (tree->pst_sym.pst_value.pst_s_root.pst_union.pst_next)
		psy_push(&stk,
			(PTR)tree->pst_sym.pst_value.pst_s_root.pst_union.pst_next,
			&sts);
	    break;
	default:
	    break;
	}
	/* scan left and right branches if any */
	if (tree->pst_right)
	    psy_push(&stk, (PTR)tree->pst_right, &sts);
	if (!(tree = tree->pst_left))
	    tree = (PST_QNODE*)psy_pop(&stk);
    }
}
Beispiel #6
0
/*{
** Name: opj_union	- optimize unions
**
** Description:
**      The routine determines which qualifications of a parent query 
**      can be copied to the union, so that the union can be more restrictive 
**      or partitions of the union can be eliminated entirely in some cases. 
**
** Inputs:
**      subquery                        ptr to subquery be analyzed
**
** Outputs:
**	Returns:
**	    VOID
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**      21-jun-89 (seputis)
**          initial creation
**	01-Oct-2001 (hanal04) Bug 105464 INGSRV 1517
**	    After calling opv_copytree() mark the target subquery
**	    as non-CNF if the source suquery was non-CNF.
**	11-may-05 (inkdo01)
**	    Don't copy ON clause BFs to union subqueries - fixes 114493.
**	20-apr-07 (hayke02)
**	    Limit the fix for bug 114493 (test for a OPL_NOOUTER pst_joinid)
**	    to those subqueries containing outer joins, otherwise pst_joinid
**	    will be PST_NOJOIN (0). This change fixes bug 118088.
**      27-mar-2009 (huazh01)
**          don't copy OPZ_FANUM, OPZ_SNUM, OPZ_CORRELATED attribute into 
**          'unionbm'. Doing so will cause opj_utree() to convert the  
**          union_subquery->ops_sunion.opu_qual qualification into a 
**          PST_QNODE node containing negative attribute id, and causes 
**          E_OP0384 error. (bug 121602)
[@history_template@]...
*/
VOID
opj_union(
	OPS_SUBQUERY       *subquery)
{
    OPV_IVARS	    lvar;
    for (lvar = subquery->ops_vars.opv_prv; --lvar>=0;)
    {	/* look at the union views referenced in this subquery */
	OPV_VARS    *unionp;
	unionp = subquery->ops_vars.opv_base->opv_rt[lvar];
	if (unionp->opv_grv 
	    && 
	    (unionp->opv_grv->opv_created == OPS_VIEW)
	    &&
	    unionp->opv_grv->opv_gquery
	    &&
	    unionp->opv_grv->opv_gquery->ops_union
	    )
	{   /* a union view has been found, so search the list of boolean
	    ** factors to determine which terms can be placed into the qualifications
	    ** of the union */
	    OPE_BMEQCLS	    unionbm;	/* bitmap of the equivalence classes which
					** are available in the union view */
            OPS_SUBQUERY    *union_subquery;
	    OPB_BMBF	    bfbm;
	    OPB_IBF	    maxbf;	/* current boolean factor number */
	    		    
	    maxbf = BITS_IN(bfbm);
	    union_subquery = unionp->opv_grv->opv_gquery;

	    if (union_subquery->ops_sunion.opu_mask & OPU_QUAL)
	    {
		if (union_subquery->ops_sunion.opu_qual == (PST_QNODE *)NULL)
		    continue;		/* since entire union is required by
					** another subquery, no need to proceed */
		MEfill(sizeof(bfbm), (u_char)0, (PTR)&bfbm);
		BTnot((i4)BITS_IN(bfbm), (char *)&bfbm); /* bits will get
					** reset as identical qualifications are 
					** found */
	    }
	    MEfill(sizeof(unionbm), (u_char)0, (PTR)&unionbm);
	    {	/* search thru list of attributes and mark 
		** appropriate equivalence classes*/
		/* opv_eqcmp not initialized yet */
		OPZ_IATTS	attr;
		OPZ_AT		*abase;
		abase = subquery->ops_attrs.opz_base;   /* ptr to base of array of ptrs
                                            ** to joinop attributes */
		for (attr = subquery->ops_attrs.opz_av; --attr>=0;)
		{   /* opv_eqcmp not initialized yet */
		    OPZ_ATTS	    *attrp;
		    attrp = abase->opz_attnums[attr];
		    if ((attrp->opz_varnm == lvar)
			&&
			(attrp->opz_equcls >= 0)
                        &&
                        attrp->opz_attnm.db_att_id >= 0
                       )
			BTset ((i4)attrp->opz_equcls, (char *)&unionbm);
		}
	    }
	    {	/* search list of conjuncts, boolean factor structure has
		** not been created at this point */
		OPB_IBF	    total_bfs;	/* number of qualifications placed into
					** union view list */
		PST_QNODE   *qual;

		total_bfs = 0;
		for (qual = subquery->ops_root->pst_right;
		    qual->pst_sym.pst_type != PST_QLEND;
		    qual = qual->pst_right)
		{
		    OPE_BMEQCLS	    qual_eqcmp;
		    MEfill(sizeof(qual_eqcmp), (u_char)0, (PTR)&qual_eqcmp);
		    ope_aseqcls(subquery, &qual_eqcmp, qual->pst_left);
					/* assign
                                        ** equivalence classes for vars in
                                        ** the qualification which have not
                                        ** been assigned yet
                                        */
		    if (BTsubset((char *)&qual_eqcmp, (char *)&unionbm, (i4)BITS_IN(unionbm))
			&&
			!(subquery->ops_oj.opl_base &&
			(qual->pst_left->pst_sym.pst_value.pst_s_op.pst_joinid
			!= OPL_NOOUTER)))
		    {	/* this boolean factor contains equivalence classes which
			** are all in the union view, so it can be evaluated inside
			** the union view */
			if (union_subquery->ops_sunion.opu_mask & OPU_QUAL)
			{   /* qualification must already be in the list
			    ** so compare to existing list of entries */
			    PST_QNODE	    *old_qual;
			    OPB_IBF	    bfcount;
			    bfcount = 0;
			    for (old_qual = union_subquery->ops_sunion.opu_qual;
				old_qual; old_qual = old_qual->pst_right)
			    {
				if (BTtest((i4)bfcount, (char *)&bfbm) /* if this
						    ** boolean factor has not been
						    ** matched */
				    &&
				    opj_ctree(subquery, qual->pst_left, lvar,
					union_subquery, old_qual->pst_left) /*
						    ** and if the qualification
						    ** matches the current one */
				    )
				{   /* found a match so mark the boolean
				    ** factor as being in common */
				    BTclear((i4)bfcount, (char *)&bfbm);
				    break;
				}
				bfcount++;
			    }
			}
			else
			{   /* if this is the first time then just copy the
			    ** qualification and place into the list */
			    PST_QNODE	    *and_node;
			    total_bfs++;
			    if (total_bfs >= BITS_IN(unionbm))
				break;		    /* cannot add anymore qualifications
						    ** since the max boolean factor
						    ** count has been reached */
			    and_node = opv_opnode(subquery->ops_global, PST_AND, (ADI_OP_ID)0,
				(OPL_IOUTER)OPL_NOOUTER);
			    and_node->pst_left = qual->pst_left;
			    opv_copytree(subquery->ops_global, &and_node->pst_left);
			    and_node->pst_right = union_subquery->ops_sunion.opu_qual;
			    union_subquery->ops_sunion.opu_qual = and_node;
                            if(subquery->ops_mask & OPS_NONCNF)
                            {
				union_subquery->ops_mask |= OPS_NONCNF;
			    }

			}
		    }
		}
	    }
	    if (union_subquery->ops_sunion.opu_mask & OPU_QUAL)
	    {	/* previous qualification exists so need to get common qualifications,
		** by using boolean factor bitmap of those qualifications which are
		** to be eliminated */
		OPB_IBF		bfno;
		PST_QNODE	**qualpp;
		OPB_IBF		current;

		current = 0;
		qualpp = &union_subquery->ops_sunion.opu_qual;
		for (bfno = -1; (bfno = BTnext((i4)bfno, (char *)&bfbm, (i4)maxbf)) >= 0;)
		{
		    while ((current < bfno) && *qualpp)
		    {	/* find next qualification to eliminate */
			current++;
			qualpp = &(*qualpp)->pst_right;
		    }
		    if (*qualpp)
			*qualpp = (*qualpp)->pst_right; /* remove this qual since
					    ** no match was found for this subquery */
		    else
			break;		    /* end of qual list reached */
		}
	    }
	    else
	    {	/* this is the first qualification in the list, so it can be applied */
		union_subquery->ops_sunion.opu_mask |= OPU_QUAL;
		if (union_subquery->ops_sunion.opu_qual)
		    opj_utree(subquery, union_subquery->ops_sunion.opu_qual, lvar); /* 
					    ** convert the 
					    ** qualification to reference var nodes
					    ** of the union view, so that comparisons
					    ** can be made later */
	    }
	}
    }
}
Beispiel #7
0
/*{
** Name: psy_integ	- Apply integrity constraints
**
** Description:
**      This function applies integrity constraints.  It gets the constraints
**	from RDF and puts them in the query where appropriate.
**
** Inputs:
**      mstream                         QSF memory stream to allocate from
**	root				Root of query tree to constrain
**	rngtab				Pointer to the range table
**	resvar				Pointer to the result range variable
**	qmode				Query mode of user's query
**	sess_cb				session control block
**	result				Place to put pointer to constrained tree
**	err_blk				Filled in if an error happens
**
** Outputs:
**      root                            Integrity constraints may be appended
**	result				Filled in with pointer to constrained
**					tree
**	err_blk				Filled in if an error happened
**	Returns:
**	    E_DB_OK			Success
**	    E_DB_ERROR			Failure
**	Exceptions:
**	    none
**
** Side Effects:
**	    Allocates memory
**
** History:
**	19-jun-86 (jeff)
**          Adapted from integrity.c in 4.0.
**      02-sep-86 (seputis)
**          changes for new RDF interface
**          fixed bug for no integrity case
**	04-dec-86 (daved)
**	    process define and replace cursor. Use copy of saved qual for
**	    replace cursor.
**	03-dec-87 (stec)
**	    Change psy_apql interface.
**	11-may-88 (stec)
**	    Make changes for db procs.
**	06-feb-89 (ralph)
**	    Modified to use DB_COL_WORDS*2 as extent of dset array
**	23-Feb-89 (andre)
**	    Changed the way the tree consisting of qualifications obtained from
**	    the integrity trees is constructed and merged with the
**	    qualifications found in the original tree.
**	18-may-89 (neil)
**	    Use session memory for cursors with integrities (bug fix).
**	12-mar-90 (andre)
**	    set rdr_2types_mask to 0.
**      22-may-90 (teg)
**          init rdr_instr to RDF_NO_INSTR
**	04-sep-90 (andre)
**	    fixed bug 32976: for OPEN CURSOR (qmode==PSQ_DEFCURS) we need to
**			     compare attribute number(s) found in the integrity
**			     tuple with attribute number found in the VAR
**			     node(s) found in the portion of the target list
**			     which was built to represent the FOR UPDATE list
**			     (such node(s) are right children of RESDOM nodes
**			     with pst_rsupdt set to TRUE; note that for
**			     RESDOM nodes built to represent the real target
**			     list of SELECT, this field is set to FALSE.)
**	29-sep-92 (andre)
**	    RDF may choose to allocate a new info block and return its address
**	    in rdf_info_blk - we need to copy it over to pss_rdrinfo to avoid
**	    memory leak and other assorted unpleasantries
**	05-dec-92 (rblumer)
**	    ignore FIPS constraints during INGRES integrity processing
**	10-jan-93 (andre)
**	    after calling rdf_call() for IIINTEGRITY tuples, compare status to
**	    E_DB_OK instead of using DB_FAILURE_MACRO() since if there are fewer
**	    than 20 rows, rdf_call() sets err_code to E_RD0011 and status to
**	    E_DB_WARN
**	10-Feb-93 (Teresa)
**	    Changed RDF_GETINFO to RDF_READTUPLES for new RDF I/F
**	23-May-1997 (shero03)
**	    Save the rdr_info_blk after an UNFIX
**	22-Jul-2004 (schka24)
**	    Delete old ifdef'ed out normalization call
**	28-nov-2007 (dougi)
**	    Add PSQ_REPDYN to PSQ_DEFCURS test (cached dynamic).
*/
DB_STATUS
psy_integ(
	PSF_MSTREAM	*mstream,
	PST_QNODE	*root,
	PSS_USRRANGE	*rngtab,
	PSS_RNGTAB	*resvar,
	i4		qmode,
	PSS_SESBLK	*sess_cb,
	PST_QNODE	**result,
	DB_ERROR	*err_blk)
{
    register PST_QNODE	*r;
    PSC_CURBLK		*curblk = sess_cb->pss_crsr;
    CS_SID		sessid  = sess_cb->pss_sessid;
    PTR			db_id   = sess_cb->pss_dbid;
    i2			dset[DB_COL_WORDS*2];
    i2			doms;
    i2			*domset;
    bool		subset;
    PST_QNODE		*p;
    register i4	i;
    PST_QNODE		*iqual;
    PST_QNODE		**tmp1;
    DB_INTEGRITY	*inttup;
    i4		err_code;
    PST_QTREE		*qtree;
    PST_PROCEDURE	*pnode;
    DB_STATUS		status = E_DB_OK;
    i4			found;
    RDF_CB		rdf_cb;
    QEF_DATA		*qp;
    RDF_CB		rdf_tree_cb;
    PSS_DUPRB		dup_rb;
    i4			tupcount;
    i4			map[PST_NUMVARS];   /* Map for reassigning varnos */

    r = root;

    /* Initialize fields in dup_rb */
    dup_rb.pss_op_mask = 0;
    dup_rb.pss_num_joins = PST_NOJOIN;
    dup_rb.pss_tree_info = (i4 *) NULL;
    dup_rb.pss_mstream = mstream;
    dup_rb.pss_err_blk = err_blk;

    if (qmode == PSQ_REPCURS)
    {
	if (!curblk->psc_integ)
	{
	    *result = r;
	    return (E_DB_OK);
	}
	/*
	** On a replace cursor, get the query tree that was stored in the
	** cursor control block.
	*/
	/* copy the qual fragment so we can change below. */
	dup_rb.pss_tree = curblk->psc_integ;
	dup_rb.pss_dup  = &iqual;
	status = pst_treedup(sess_cb, &dup_rb);
	
	if (DB_FAILURE_MACRO(status))
	{
	    return (status);
	}
    }
    else
    {
	/*
	**  Check to see if we should apply the integrity
	**  algorithm.
	**
	**  This means checking to insure that we have an update
	**  and seeing if any integrity constraints apply.
	*/
	if 
	(
	    resvar == 0 || 
	    (resvar->pss_tabdesc->tbl_status_mask & DMT_INTEGRITIES) == 0
	)
	{
	    *result = r;
	    return (E_DB_OK);
	}
	/*
	**  Create a set of the domains updated in this query.
	*/

	for (i = 0; i < DB_COL_WORDS*2; i++)
	    dset[i] = 0;

	for (p = r->pst_left, doms = 0;
	    p != (PST_QNODE *) NULL && p->pst_sym.pst_type != PST_TREE;
	    p = p->pst_left)
	{
	    if (p->pst_sym.pst_type != PST_RESDOM)
	    {
		psf_error(E_PS0D0C_NOT_RESDOM, 0L, PSF_INTERR, &err_code,
		    err_blk, 0);
		return (E_DB_SEVERE);
	    }

	    /*
	    ** if we are defining a cursor, RESDOM numbers are meaningless as
	    ** at best they reflect position of an attribute in the target list
	    ** of a subselect or, at worst, they are set to 1 for all columns
	    ** mentioned in the FOR UPDATE LIST.  We really are interested only
	    ** in the VAR nodes which are children of RESDOM nodes built to
	    ** repersent columns appearing in the FOR UPDATE list, so we will
	    ** skip over RESDOM nodes which represent a true subselect
	    ** (i.e. pst_rsupdt == FALSE)
	    */
	    if (qmode == PSQ_DEFCURS)
	    {
		if (p->pst_sym.pst_value.pst_s_rsdm.pst_rsupdt)
		{
		    PST_QNODE	    *r_child = p->pst_right;

		    /*
		    ** this RESDOM was built to represent the column in the FOR
		    ** UPDATE list
		    */

		    /*
		    ** make sure the right child is a VAR node; otherwise flag
		    ** an error
		    */
		    if (r_child == (PST_QNODE *) NULL ||
		        r_child->pst_sym.pst_type != PST_VAR)
		    {
			psf_error(E_PS0C04_BAD_TREE, 0L, PSF_INTERR, &err_code,
			    err_blk, 0);
			return (E_DB_SEVERE);
		    }
		    
		    BTset((i4) r_child->
				 pst_sym.pst_value.pst_s_var.pst_atno.db_att_id,
			  (char *) dset);
		    ++doms;
		}
	    }
	    else
	    {
		BTset((i4) p->pst_sym.pst_value.pst_s_rsdm.pst_rsno,
		    (char *) dset);
		++doms;
	    }
	}

	/*
	** Note if we are appending a subset of the relation's domains.
	** If we are, we'll need to be extra careful to avoid violating
	** constraints on those attributes not being explicitly appended:
	*/
	subset = ((doms < resvar->pss_tabdesc->tbl_attr_count)
	    && (qmode == PSQ_APPEND));

	
	/*
	**  Scan integrity catalog for possible tuples.  If found,
	**  include them in the integrity qualification.
	*/

	iqual = (PST_QNODE *) NULL;

        /* Set up constant part of rdf query tree control block */
	pst_rdfcb_init(&rdf_tree_cb, sess_cb);
        STRUCT_ASSIGN_MACRO(resvar->pss_tabid, rdf_tree_cb.rdf_rb.rdr_tabid);
        rdf_tree_cb.rdf_rb.rdr_types_mask = RDR_INTEGRITIES | RDR_QTREE;
	rdf_tree_cb.rdf_rb.rdr_qtuple_count = 1;  
		/* Get 1 integ tree at a time */
	rdf_tree_cb.rdf_info_blk = resvar->pss_rdrinfo;

	/* Get all integrity tuples */
	pst_rdfcb_init(&rdf_cb, sess_cb);
	STRUCT_ASSIGN_MACRO(resvar->pss_tabid, rdf_cb.rdf_rb.rdr_tabid);
	rdf_cb.rdf_rb.rdr_types_mask = RDR_INTEGRITIES;
	rdf_cb.rdf_rb.rdr_update_op = RDR_OPEN;
	rdf_cb.rdf_rb.rdr_rec_access_id = NULL;
	rdf_cb.rdf_rb.rdr_qtuple_count = 20;  /* Get 20 integrities at a time */
	rdf_cb.rdf_info_blk = resvar->pss_rdrinfo;
	/* For each group of 20 integrities */
	while (rdf_cb.rdf_error.err_code == 0)
	{
	  status = rdf_call(RDF_READTUPLES, (PTR) &rdf_cb);

	  /*
          ** RDF may choose to allocate a new info block and return its address
	  ** in rdf_info_blk - we need to copy it over to pss_rdrinfo to avoid
	  ** memory leak and other assorted unpleasantries
          */
	  if (rdf_cb.rdf_info_blk != resvar->pss_rdrinfo)
	  {
	    resvar->pss_rdrinfo =
		rdf_tree_cb.rdf_info_blk = rdf_cb.rdf_info_blk;
	  }
	  
	  /*
	  ** Must not use DB_FAILURE_MACRO because E_RD0011 returns E_DB_WARN
	  ** that would be missed.
	  */
	  if (status != E_DB_OK)
	  {
	    if (   rdf_cb.rdf_error.err_code == E_RD0011_NO_MORE_ROWS
		|| rdf_cb.rdf_error.err_code == E_RD0013_NO_TUPLE_FOUND)
	    {
		status = E_DB_OK;
		if (rdf_cb.rdf_error.err_code == E_RD0013_NO_TUPLE_FOUND)
		    continue;
	    }
	    else if (rdf_cb.rdf_error.err_code == E_RD0002_UNKNOWN_TBL)
	    {
		(VOID) psf_error(2117L, 0L, PSF_USERERR,
		    &err_code, err_blk, 1,
		    psf_trmwhite(sizeof(DB_TAB_NAME), 
			(char *) &resvar->pss_tabname),
		    &resvar->pss_tabname);
		status = E_DB_ERROR;
		goto exit;
	    }
	    else
	    {
		(VOID) psf_rdf_error(RDF_READTUPLES, &rdf_cb.rdf_error, err_blk);
		goto exit;
	    }
	  }

	  rdf_cb.rdf_rb.rdr_update_op = RDR_GETNEXT;
    
	  /* FOR EACH INTEGRITY */
	  for 
	  (
	    qp = rdf_cb.rdf_info_blk->rdr_ituples->qrym_data,
	    tupcount = 0;
	    tupcount < rdf_cb.rdf_info_blk->rdr_ituples->qrym_cnt;
	    qp = qp->dt_next,
	    tupcount++
	  )
	  {
	    inttup = (DB_INTEGRITY*) qp->dt_data;

	    /*
	    ** Ignore FIPS constraints (i.e. SQL92 constraints)
	    ** (they are implemented via rules instead)
	    */
	    if (inttup->dbi_consflags != 0)
	    {
		continue;
	    }

	    /* check for some domain set overlap */

	    domset = (i2*) inttup->dbi_columns.db_domset;

	    for (i = 0; i < DB_COL_WORDS*2; i++)
	    {
		if ((dset[i] & domset[i]) != 0)
		    break;
	    }

	    /*
	    ** Check for appends where defaults don't satisfy integrity.
	    */

	    if ((i >= DB_COL_WORDS*2) && !subset)
	    {
		continue;
	    }

	    /* Get integrity tree and make qtree point to it */
	STRUCT_ASSIGN_MACRO(inttup->dbi_tree, rdf_tree_cb.rdf_rb.rdr_tree_id);
	    rdf_tree_cb.rdf_rb.rdr_qrymod_id = inttup->dbi_number;
	    rdf_tree_cb.rdf_rb.rdr_update_op = 0;
	    rdf_tree_cb.rdf_rb.rdr_rec_access_id = NULL;
	    rdf_tree_cb.rdf_info_blk = resvar->pss_rdrinfo;
	    rdf_tree_cb.rdf_rb.rdr_integrity = NULL;
	    STRUCT_ASSIGN_MACRO(inttup->dbi_tabid,
				rdf_tree_cb.rdf_rb.rdr_tabid);
	    rdf_tree_cb.rdf_rb.rdr_sequence = inttup->dbi_number;

	    status = rdf_call(RDF_GETINFO, (PTR) &rdf_tree_cb);
	    /*
	    ** RDF may choose to allocate a new info block and return its
	    ** address in rdf_info_blk - we need to copy it over to pss_rdrinfo
	    ** to avoid memory leak and other assorted unpleasantries
	    */
	    if (rdf_tree_cb.rdf_info_blk != resvar->pss_rdrinfo)
	    {
		resvar->pss_rdrinfo =
		    rdf_cb.rdf_info_blk = rdf_tree_cb.rdf_info_blk;
	    }

	    if (DB_FAILURE_MACRO(status))
	    {
		if (rdf_tree_cb.rdf_error.err_code == E_RD0002_UNKNOWN_TBL)
		{
		    (VOID) psf_error(E_PS0903_TAB_NOTFOUND, 0L, PSF_USERERR,
			&err_code, err_blk, 1,
			psf_trmwhite(sizeof(DB_TAB_NAME), 
			    (char *) &resvar->pss_tabname),
			&resvar->pss_tabname);
		}
		else
		{
		    (VOID) psf_rdf_error(RDF_GETINFO, &rdf_tree_cb.rdf_error,
			err_blk);
		}
		goto exit;
	    }
	    pnode   = 
	    (PST_PROCEDURE *) rdf_tree_cb.rdf_rb.rdr_integrity->qry_root_node;
	    qtree = pnode->pst_stmts->pst_specific.pst_tree;
		
	    /* trim off (null) target list */
	    p = qtree->pst_qtree->pst_right;
	    /* use a copy of the qtree because the qtree is in RDF memory */
	    dup_rb.pss_tree = qtree->pst_qtree->pst_right;
	    dup_rb.pss_dup  = &p;
	    status = pst_treedup(sess_cb, &dup_rb);

	    {	/* unfix the query tree no matter what the above status is */
		DB_STATUS	temp_status;

		temp_status = rdf_call(RDF_UNFIX, (PTR) &rdf_tree_cb);
	        resvar->pss_rdrinfo = 
			rdf_cb.rdf_info_blk = rdf_tree_cb.rdf_info_blk;
		if (DB_FAILURE_MACRO(temp_status))
		{
		    (VOID) psf_rdf_error(RDF_UNFIX, &rdf_tree_cb.rdf_error,
			err_blk);
		    status = temp_status;
		}
	    }
	    if (DB_FAILURE_MACRO(status))
		goto exit;		/* close integrity file */

	    /*
	    ** Make the result variable for the integrity the same as the result
	    ** variable for the user query.  
	    ** I AM NOT SURE THE FOLLOWING COMMENT APPLIES SO I AM MERGING
	    ** THE RANGE VAR FOR APPENDS.
	    ** This is not done for append because
	    ** append doesn't have a result range variable.
	    */
	    for (i = 0; i < PST_NUMVARS; i++)
		map[i] = i;
	    i = inttup->dbi_resvar;

	    map[i] = resvar->pss_rgno;
	    status = psy_mapvars(p, map, err_blk);
	    if (DB_FAILURE_MACRO(status))
		goto exit;

	    /* add to integrity qual */

	    if (iqual == NULL)
	    {
		status = pst_node(sess_cb, mstream, p, (PST_QNODE *) NULL,
		    PST_AND, (PTR) NULL, sizeof(PST_OP_NODE), DB_NODT, (i2) 0,
		    (i4) 0, (DB_ANYTYPE *) NULL, &iqual, err_blk, (i4) 0);

		if (DB_FAILURE_MACRO(status))
		    goto exit;		/* close integrity file */

		/*
		** Note that tmp1 will contain address of the pst_right ptr
		** of the bottom AND node in the tree constructed from the
		** qualifications found in the integrities
		*/

		tmp1 = &iqual->pst_right;
	    }
	    else
	    {
		PST_QNODE	*newnode;
		
		status = pst_node(sess_cb, mstream, p, iqual, PST_AND,
		    (PTR) NULL, sizeof(PST_OP_NODE), DB_NODT, (i2) 0, (i4) 0,
		    (DB_ANYTYPE *) NULL, &newnode, err_blk, (i4) 0);

		if (DB_FAILURE_MACRO(status))
		    goto exit;		/* close integrity file */

		iqual = newnode;
	    }
	  }
	}
	if (rdf_cb.rdf_rb.rdr_rec_access_id != NULL)
	{
	    DB_STATUS	temp_status;
	    /* unfix integrity tuples */
	    rdf_cb.rdf_rb.rdr_update_op = RDR_CLOSE;
	    temp_status = rdf_call(RDF_READTUPLES, (PTR) &rdf_cb);
	    resvar->pss_rdrinfo =
		rdf_tree_cb.rdf_info_blk = rdf_cb.rdf_info_blk;
	    if (DB_FAILURE_MACRO(temp_status))
	    {
		(VOID) psf_rdf_error(RDF_READTUPLES, &rdf_cb.rdf_error, err_blk);
		status = temp_status;
	    }
	}
    }

    if (qmode == PSQ_DEFCURS || qmode == PSQ_REPDYN)
    {
        /*
        ** On a "define cursor", keep a copy of the integrity qualification
        ** for later use.
        */
        if (iqual == NULL)
        {
	    curblk->psc_integ = (PST_QNODE *) NULL;
        }
        else
        {
	    status = pst_trdup(curblk->psc_stream, iqual,
		&curblk->psc_integ, &sess_cb->pss_memleft, err_blk);
	    if (DB_FAILURE_MACRO(status))
	    {
		return (status);
	    }
	}
    }
    else
    {   
	/*
	**  Clean up the integrity qualification so that it will merge
	**  nicely into the tree, and then append it to the user's
	**  qualification.
	*/

	if (iqual != NULL)
	{
	    /* replace VAR nodes by corresponding user afcn from user's
	    ** query. For example, if the integ says r.a > 1 and the user's
	    ** query says resdom 4 = 4 + 3, the integ is modified to 4 + 3 > 1.
	    **
	    ** The following paragraph refers to the case where an integrity
	    ** refers to a variable not updated in the target list.
	    **
	    ** If there is no resdom for r.a, if the user didn't specify r.a
	    ** in the target list of the query, and the query is an append,
	    ** r.a > 1 is replaced with 'default val for col a' > 1. If the
	    ** query is a replace statement, we do nothing because r.a will
	    ** be retrieved but is not in the targ list. We will be verifying
	    ** what we know should already hold (ie the integrity constraint).
	    ** If we have replace cursor, we wan't to replace r.a with a value
	    ** so a retrieve can be avoided on the replace command. We can't
	    ** have col a = 5 where r.a > 1. We do, however, have the value r.a
	    ** in the row to be updated. QEF has this value. We replace r.a with
	    ** a curval node that refers to the value r.a.
	    */

	    /* will pass dup_rb, but first null out pss_tree and pss_dup */
	    dup_rb.pss_tree = (PST_QNODE *) NULL;
	    dup_rb.pss_dup  = (PST_QNODE **) NULL;
	    
	    status = psy_subsvars(sess_cb, &iqual, resvar, r->pst_left, qmode,
		(PST_QNODE *) NULL, resvar, (i4) 0, qmode,
		&curblk->psc_blkid, &found, &dup_rb);

	    if (DB_FAILURE_MACRO(status))
	    {
		return (status);
	    }

	    /*
	    ** for REPLACE CURSOR, we need to traverse down the pst_right's
	    ** until we encounter NULL in place of which we will append the
	    ** qualification from the ROOT
	    ** Note that iqual is guaranteed to be an AND node with BOP for a
	    ** left child and either AND or NULL for the right child.
	    */

	    if (qmode == PSQ_REPCURS)
	    {
		for (tmp1 = &iqual->pst_right; (*tmp1) != (PST_QNODE *) NULL;
		     tmp1 = &(*tmp1)->pst_right)
		;
	    }
	    
	    /*
	    ** append qualification from the tree to the integrities and
	    ** make the result the new qualification for the tree
	    */
	    (*tmp1) = r->pst_right;
	    r->pst_right = iqual; 
	}
    }

exit:
    if ((qmode != PSQ_REPCURS)
	&&
	(rdf_cb.rdf_rb.rdr_rec_access_id != NULL))
    {
	DB_STATUS	temp_status;
	/* unfix integrity tuples */
	rdf_cb.rdf_rb.rdr_update_op = RDR_CLOSE;
	temp_status = rdf_call(RDF_READTUPLES, (PTR) &rdf_cb);
	resvar->pss_rdrinfo =
	    rdf_tree_cb.rdf_info_blk = rdf_cb.rdf_info_blk;
	if (DB_FAILURE_MACRO(temp_status))
	{
	    (VOID) psf_rdf_error(RDF_READTUPLES, &rdf_cb.rdf_error, err_blk);
	    status = temp_status;
	}
    }
    if (status == E_DB_OK)
    {
	*result = r;
    }
    return (status);
}
Beispiel #8
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 */
}
Beispiel #9
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);
}
Beispiel #10
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);
}
Beispiel #11
0
/*{
** Name: opn_jmaps	- set various join operator tree maps
**
** Description:
{@comment_line@}...
**
** Inputs:
**      subquery                        ptr to subquery being analyzed
**      nodep                           ptr to current operator node which
**                                      will have maps initialized
**	ojmap				NULL if no outer joins exist
**					- map of outer joins which are
**					evaluated in parent nodes.
**
** Outputs:
**      nodep->opn_eqm                  set of equivalence class available
**                                      from subtree
**      nodep->opn_rlmap                bitmap of relations in the subtree
**      nodep->opn_rlasg                order in which the relations in
**                                      opn_rlmap are assigned to the leaves
**	Returns:
**	    TRUE if there is a valid placement of subselect nodes with
**          all required equivalence classes available for execution of the
**          boolean factor used for the subselect
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**	11-jun-86 (seputis)
**          initial creation from setjmaps
**      29-mar-90 (seputis)
**          fix byte alignment problems
**	2-apr-91 (seputis)
**	    only partially copied relation assignment causing run_all diffs
**	    fix for b35461, this would cause the OPF cache of query plans
**	    to not be effectively used, and could cause performance problems
**	    on non-VMS systems.  This fix should be used whenever a poor
**	    query plan bug is reported which cannot be reproduced on VMS.
**      15-feb-94 (ed)
**          - bug 59598 - correct mechanism in which boolean factors are
**          evaluated at several join nodes
**      11-apr-94 (ed)
**          - bug 59937 - E_OP0489 consistency check due to inner join ojid
**          not being visible in boolean factor placement maps
**	31-jul-97 (inkdo01)
**	    Fix to force SVAR func atts ref'ed in ON clauses to materialize
**	    just before needed (to incorporate proper null semantics).
**	 9-jul-99 (hayke02 for inkdo01)
**	    Set opn_eqm bits in lower level nodes (e.g. leaf nodes) when
**	    func attrs are found in higher level OJ nodes. This change
**	    fixes bug 92749.
**	20-Mar-02 (wanfr01)
**	    Bug 106678, INGSRV 1633
**	    Confirm function attribute as an ojid before associating it
**	    with an outer join node.
**	16-sep-03 (hayke02)
**	    Check for OPN_OJINNERIDX in nodep->opn_jmask to indicate that
**	    placement of a coverqual inner index outer join needs to be
**	    checked.
**	28-apr-04 (hayke02)
**	    Modify previous change so that we return FALSE and reject the QEP
**	    if the index outer join node (node->LEFT) has a non-zero opn_nchild
**	    for node->LEFT->RIGHT. This will allow only QEPs where the OJ
**	    inner is the index only and not the index joined to another
**	    relation. This change fixes problem INGSRV 2808, bug 112211.
**      05-Oct-2004 (huazh01)
**          Remove the above fix. The fix for 111627 handles b106678
**          as well. This fixes b113160, INGSRV2984. 
**	14-mar-05 (hayke02)
**	    Modify the fix for problem INGSRV 2808, bug 112211 so that we now
**	    check for more than 1 var in the opl_ivmap. This now allows the
**	    correct rejection of plans that have had their opn_child's set up
**	    with left joins 'reversed' into right joins. This change fixes
**	    problems INGSRV 3049 and 3094, bugs 113457 and 113990.
**	23-sep-05 (hayke02)
**	    Check for a WHERE clause (OPL_NOOOUTER opz_ojid) OJSVAR func att,
**	    and make sure that all OJs that this func att is inner to are
**	    executed before the join involving this func att. This change fixes
**	    bug 114912.
**	30-jan-07 (hayke02)
**	    Disable the fix for bug 114912 for cart prod (OPL_BOJCARTPROD) OJs.
**	    This change fixes bug 117513.
**	27-Oct-2009 (kiria01) SIR 121883
**	    Scalar sub-selects - protect subp->opv_eqcrequired from bad de-ref.
[@history_line@]...
*/
bool
opn_jmaps(
	OPS_SUBQUERY       *subquery,
	OPN_JTREE          *nodep,
	OPL_BMOJ	   *ojmap)
{
	OPL_OUTER	    *outerp;

    if (nodep->opn_nleaves == 1)
    {	/* leaf node */
	OPV_IVARS              varno;		/* joinop range variable number
                                                ** of leaf */

	varno = nodep->opn_prb[0];		/* by definition of leaf - only
                                                ** one variable in partition */
	nodep->opn_rlasg[0] = varno;            /* trivial ordering for leaf */
	MEfill( sizeof(nodep->opn_rlmap), (u_char)0, (PTR)&nodep->opn_rlmap );
	if (subquery->ops_oj.opl_lv > 0)
	{
	    MEfill( sizeof(nodep->opn_ojinnermap),
		(u_char)0, (PTR)&nodep->opn_ojinnermap);
	    MEfill( sizeof(nodep->opn_ojevalmap),
		(u_char)0, (PTR)&nodep->opn_ojevalmap);
	    opl_sjij(subquery, varno, &nodep->opn_ojinnermap, ojmap);
	}
	BTset((i4)varno, (char *)&nodep->opn_rlmap); /* only one bit set for leaf*/
	MEcopy ((PTR)&subquery->ops_vars.opv_base->opv_rt[varno]->opv_maps.opo_eqcmap,
	    sizeof(nodep->opn_eqm), (PTR)&nodep->opn_eqm ); /* copy map of
                                                ** equivalence classes 
						** associated with varno of this
                                                ** leaf */

	return(TRUE);
    }
    else
    {	/* non-leaf node */
	OPN_JTREE              *leftchildp;	/* ptr to left child node */
	OPN_JTREE              *rightchildp;	/* ptr to right child node */

	leftchildp = nodep->opn_child[OPN_LEFT];
	rightchildp = nodep->opn_child[OPN_RIGHT];
	if (ojmap)
	{
	    MEfill(sizeof(nodep->opn_ojevalmap), (u_char)0,
		(PTR)&nodep->opn_ojevalmap);
	    if( (nodep->opn_ojid >= 0)
		&&
		!BTtest((i4)nodep->opn_ojid, (char *)ojmap))
	    {   /* setup the outer join map which contains all outer joins
		** which are completely evaluated within this subtree */
		BTset((i4)nodep->opn_ojid, (char *)&nodep->opn_ojevalmap);
		BTset((i4)nodep->opn_ojid, (char *)ojmap);
	    }
	}
	if (!opn_jmaps (subquery, leftchildp, ojmap))	/* get info on left child */
	    return(FALSE);
	if (!opn_jmaps (subquery, rightchildp, ojmap))	/* get info on right child */
	    return(FALSE);

	MEcopy ((PTR)&leftchildp->opn_rlmap,
		sizeof(nodep->opn_rlmap),
		(PTR)&nodep->opn_rlmap );	/* get var bitmap from left
                                                ** child */
	BTor(	(i4)BITS_IN(nodep->opn_rlmap),
		(char *)&rightchildp->opn_rlmap,
		(char *)&nodep->opn_rlmap );	/* "OR" rightchildp->opn_rlmap
						** into nodep->opn_rlmap */
	if (ojmap)
	{   /* setup ojinnermap which contains all outer joins which are
	    ** partially or totally evaluated within this subtree, but 
	    ** not at this node unless it is in the subtree */
	    MEcopy((PTR)&leftchildp->opn_ojinnermap,
		sizeof(leftchildp->opn_ojinnermap),
		(PTR)&nodep->opn_ojinnermap);
	    BTor((i4)BITS_IN(rightchildp->opn_ojinnermap),
                (char *)&rightchildp->opn_ojinnermap,
                (char *)&nodep->opn_ojinnermap);
	    if (leftchildp->opn_ojid >= 0)
		BTset((i4)leftchildp->opn_ojid, (char *)&nodep->opn_ojinnermap);
	    if (rightchildp->opn_ojid >= 0)
		BTset((i4)rightchildp->opn_ojid, (char *)&nodep->opn_ojinnermap);
	    if (subquery->ops_mask & OPS_IJCHECK)
		opl_ijcheck(subquery, &leftchildp->opn_ojinnermap,
		    &rightchildp->opn_ojinnermap, &nodep->opn_ojinnermap,
		    &leftchildp->opn_ojevalmap, &rightchildp->opn_ojevalmap,
		    &nodep->opn_rlmap);
	    /* map of outerjoins which are entirely evaluated within
	    ** this subtree */
	    BTor((i4)BITS_IN(leftchildp->opn_ojevalmap),
                (char *)&leftchildp->opn_ojevalmap,
                (char *)&nodep->opn_ojevalmap);
	    BTor((i4)BITS_IN(rightchildp->opn_ojevalmap),
                (char *)&rightchildp->opn_ojevalmap,
                (char *)&nodep->opn_ojevalmap);
	    if ((nodep->opn_jmask & OPN_OJINNERIDX) &&
		((nodep->opn_ojid != nodep->opn_child[OPN_LEFT]->opn_ojid) ||
		((nodep->opn_ojid == nodep->opn_child[OPN_LEFT]->opn_ojid) &&
		(nodep->opn_ojid >= 0) &&
		(BTcount((char *)subquery->ops_oj.opl_base->opl_ojt
		[nodep->opn_ojid]->opl_ivmap, subquery->ops_vars.opv_rv) > 1))))
		return(FALSE);
	}
	MEcopy ((PTR)leftchildp->opn_rlasg,
		leftchildp->opn_nleaves * sizeof(nodep->opn_rlasg[0]),
		(PTR)nodep->opn_rlasg);		/* get relations from left
                                                ** child */
	MEcopy ((PTR)rightchildp->opn_rlasg,
		rightchildp->opn_nleaves * sizeof(nodep->opn_rlasg[0]),
		(PTR)(nodep->opn_rlasg + leftchildp->opn_nleaves)); /* get
                                                ** relations from right child
                                                ** and place them beside the
                                                ** ones from the left child */

	MEcopy ((PTR)&leftchildp->opn_eqm,
		sizeof(nodep->opn_eqm),
		(PTR)&nodep->opn_eqm );		/* copy equivalence class map
                                                ** from left child to this node 
                                                */
	BTor(	(i4)BITS_IN(nodep->opn_eqm),
		(char *)&rightchildp->opn_eqm,
		(char *)&nodep->opn_eqm );	/* "OR" right child equivalence
                                                ** map into this to produce 
                                                ** map for this node */
	if (subquery->ops_joinop.opj_virtual)
	{   /* there is a subselect in this query so check for availability
            ** of equivalence classes used for evaluation of boolean factors
	    ** containing the subselect
            */
	    OPV_SUBSELECT	*subp;		/* ptr to subselect descriptor
                                                ** for range variable */
	    if  (   (rightchildp->opn_nleaves == 1)
		    &&
		    (
			subp = subquery->ops_vars.opv_base->
			    opv_rt[rightchildp->opn_prb[0]]->opv_subselect
		    )
		)
	    {	/* right child is a subselect so test for correct leaf
                ** placement */
		if (!subp->opv_eqcrequired ||
			!BTsubset(	(char *)subp->opv_eqcrequired,
				(char *)&nodep->opn_eqm,
				(i4)BITS_IN(OPE_BMEQCLS)
			    )
		    )
		    return (FALSE);		/* cannot evaluate all boolean
                                                ** factors with SEJOIN nodes
                                                ** in this configuration */
		else
		{   /* check if all corelated variables are available in the
		    ** outer - FIXME create another pointer field which
                    ** contains this information so this loop is avoided */
		    for (;subp; subp = subp->opv_nsubselect)
		    {
			if (!BTsubset(	(char *)&subp->opv_eqcmp,
					(char *)&leftchildp->opn_eqm,
					(i4)BITS_IN(OPE_BMEQCLS)
				    )
			    )
			    return (FALSE);	/* not all correlated
                                                ** equivalence classes
						** are available from the outer
						** so return */
		    }
		}
	    }
	    if  (   (leftchildp->opn_nleaves == 1)
		    &&
		    (subp = subquery->ops_vars.opv_base->
			opv_rt[leftchildp->opn_prb[0]]->opv_subselect)
		)
	    {
		if (!subp->opv_eqcrequired ||
			!BTsubset(	(char *)subp->opv_eqcrequired,
				(char *)&nodep->opn_eqm,
				(i4)BITS_IN(OPE_BMEQCLS)
			    )
		    )
		    return (FALSE);		/* cannot evaluate all boolean
                                                ** factors with SEJOIN nodes
                                                ** in this configuration */
		else
		{   /* check if all corelated variables are available in the
		    ** outer - FIXME create another pointer field which
                    ** contains this information so this loop is avoided */
		    for (;subp; subp = subp->opv_nsubselect)
		    {
			if (!BTsubset(	(char *)&subp->opv_eqcmp,
					(char *)&rightchildp->opn_eqm,
					(i4)BITS_IN(OPE_BMEQCLS)
				    )
			    )
			    return (FALSE);	/* not all correlated
                                                ** equivalence classes
						** are available from the outer
						** so return */
		    }
		}
	    }
	}
	if ((subquery->ops_oj.opl_lv > 0)
	    &&
	    (nodep->opn_ojid != OPL_NOOUTER))
	{
	    OPV_IVARS	    maxvar;

	    /* this is an outer join function attribute which should
	    ** only appear at the point that the outer join is
	    ** actually performed */
	    maxvar = subquery->ops_vars.opv_rv;
	    outerp = subquery->ops_oj.opl_base->opl_ojt[nodep->opn_ojid];
	    if ((outerp->opl_type == OPL_LEFTJOIN)
		||
		(outerp->opl_type == OPL_FULLJOIN))
	    {
		OPV_BMVARS	tempvmap;
		OPV_IVARS	innervar;

		MEcopy((PTR)outerp->opl_maxojmap, sizeof(tempvmap),
		    (PTR)&tempvmap);
		BTand((i4)BITS_IN(tempvmap), (char *)&nodep->opn_rlmap,
		    (char *)&tempvmap);
		for (innervar = -1; (innervar = BTnext((i4)innervar,
		    (char *)&tempvmap, (i4)maxvar))>=0;)
		{
		    OPE_IEQCLS	    ojeqcls;
		    ojeqcls = subquery->ops_vars.opv_base->opv_rt
			[innervar]->opv_ojeqc;
		    if (ojeqcls != OPE_NOEQCLS)
			BTset((i4)ojeqcls, (char *)&nodep->opn_eqm);
		}
	    }
	}
	{   /* determine multi-variable functions that can first be calculated
            ** at this node 
	    */
	    OPZ_IFATTS         fattr;		/* current function attribute
                                                ** being analyzed */
	    OPZ_IFATTS	       maxfattr;        /* maximum number of function
                                                ** attributes defined */
	    OPZ_FT             *fbase;          /* ptr to base of array of ptrs
                                                ** to function attribute
                                                ** elements */
	    OPZ_AT	       *abase;          /* ptr to base of array of ptrs
                                                ** to joinop attribute elements
                                                */
	    maxfattr = subquery->ops_funcs.opz_fv; /* number of function
                                                ** attributes defined */
	    fbase = subquery->ops_funcs.opz_fbase; /* ptr to base of array
                                                ** of function attributes */
	    abase = subquery->ops_attrs.opz_base; /* ptr to base of array
						** of ptrs to joinop attribute
                                                ** elements */
	    for (fattr = 0; fattr < maxfattr ; fattr++)
	    {
		OPZ_FATTS             *fattrp;	/* ptr to current function
                                                ** attribute being analyzed */
		OPE_IEQCLS            eqcls;    /* equivalence class of the
                                                ** multi-variable function
                                                ** attribute */

		fattrp = fbase->opz_fatts[fattr];
		eqcls = abase->opz_attnums[fattrp->opz_attno]->opz_equcls; /*
						** equivalence class associated
                                                ** with the multi-variable
                                                ** function attribute */
		/* check for WHERE clause (OPL_NOOUTER opz_ojid) OJSVAR func att
		** and then make sure that all OJs that this func att is inner
		** to are executed before the join involving this func att
		*/
		if (fattrp->opz_type == OPZ_SVAR
		    &&
		    (fattrp->opz_mask & OPZ_OJSVAR)
		    &&
		    nodep->opn_ojid != OPL_NOOUTER
		    &&
		    fattrp->opz_ojid == OPL_NOOUTER
		    &&
		    (fattrp->opz_ijmap
		    &&
		    BTtest((i4)nodep->opn_ojid, (char *)fattrp->opz_ijmap)
		    &&
		    !(outerp->opl_mask & OPL_BOJCARTPROD)))
		{
		    OPZ_IATTS		attno;
		    bool		allinrlmap = TRUE;
		    OPE_EQCLIST		*eqclsp;

		    eqclsp = subquery->ops_eclass.ope_base->ope_eqclist[eqcls];
		    for (attno = -1;  (attno = BTnext((i4)attno,
			(PTR)&eqclsp->ope_attrmap,
			(i4)subquery->ops_attrs.opz_av)) != -1; )
		    {
			if (!BTtest((i4)abase->opz_attnums[attno]->opz_varnm,
						    (char *)&nodep->opn_rlmap))
			{
			    allinrlmap = FALSE;
			    break;
			}
		    }
		    if (allinrlmap)
			return(FALSE);
		}
		if ((fattrp->opz_type != OPZ_MVAR)
		    && !(fattrp->opz_type == OPZ_SVAR &&
			 fattrp->opz_mask & OPZ_OJSVAR &&
			 fattrp->opz_ojid == nodep->opn_ojid)
		    ||
		    (fattrp->opz_mask & OPZ_OJFA))
		    continue;			/* only multi-variable 
						** functions are assigned here 
						** (and SVARs in ON clauses
						** of OJs eval'd at this node)
						** since others where assigned
                                                ** earlier, ... also outer join
						** special eqc were assigned 
						** prior to this loop */
		if (fattrp->opz_type == OPZ_SVAR)
		{
		    /* Must be ON clause ref'ed SVAR. Set bit in whichever
		    ** child node covers the SVAR eqcmap. */
		    if (BTsubset((char *)&fattrp->opz_eqcm,
			(char *)&nodep->opn_child[1]->opn_eqm,
			(i4)BITS_IN(nodep->opn_eqm)))
		     BTset((i4)eqcls, (char *)&nodep->opn_child[1]->opn_eqm);
		    else if (BTsubset((char *)&fattrp->opz_eqcm,
			(char *)&nodep->opn_child[0]->opn_eqm,
			(i4)BITS_IN(nodep->opn_eqm)))
		     BTset((i4)eqcls, (char *)&nodep->opn_child[0]->opn_eqm);
						/* set fattr eqcls bit in 
						** proper child opn_eqm */
		    continue;
		}
		if (BTtest( (i4)eqcls, (char *)&nodep->opn_eqm))
			continue;		/* if function attribute has
                                                ** been added then
                                                ** continue */
		if (BTsubset((char *)&fattrp->opz_eqcm,
			     (char *)&nodep->opn_eqm,
			     (i4)BITS_IN(nodep->opn_eqm))
		    )
		    BTset((i4)eqcls, (char *)&nodep->opn_eqm); /* set bit if all 
                                                ** the required equivalence 
                                                ** classes are available 
                                                ** for the function attribute
                                                */
	    }
	}
    }
    return(TRUE);
}
Beispiel #12
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
                                        */
    }
}
Beispiel #13
0
/*{
** Name: opa_checkopt	- check for possibility of optimization
**
** Description:
**      This routine will create variable map of what the outer aggregate
**      would appear like if all possible substitutions of the inner aggregate
**      bylist attributes were made.  The map of the all the inner aggregate
**      bylist elements used in the substitution is also created.  Thus, if
**      no substitutions are made then this map would be empty.
**
** Inputs:
**      global                          global state variable
**      root                            root of query tree which will
**                                      be analyzed for possible substitutions
**                                      - this root is a subtree of the outer
**                                      aggregate.
**      bylist                          base of bylist which will be
**                                      used for substitutions
**
** Outputs:
**      usedmap                         ptr to map of all variables found
**                                      in the "hit list" i.e. map of variables
**                                      which will be replaced if the
**                                      substitution were actually made
**	newmap				ptr to varmap of root, filled in if 
**					the substitutions were actually made
**	Returns:
**	    varmap of root if the substitutions were actually made
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**	15-apr-86 (seputis)
**          initial creation
**	4-dec-02 (inkdo01)
**	    Return variable changed to a call-by-ref parm to support
**	    range table expansion.
[@history_line@]...
*/
static VOID
opa_checkopt(
	OPS_STATE          *global,
	PST_QNODE          *bylist,
	PST_QNODE          *root,
	OPV_GBMVARS	   *usedmap,
	OPV_GBMVARS	   *newmap)
{
    PST_QNODE           *node;	    /* ptr to current by list element being
                                    ** substituted
                                    */

    MEfill(sizeof(*newmap), 0, (char *)newmap); /* init the bit map */

    if ( root )
    {
	for(node = bylist;	    /* get first element of by list */
	    node && node->pst_sym.pst_type != PST_TREE; /* at the end of 
                                    ** the bylist ? */
	    node = node->pst_left)  /* get next bydom attribute */
	{
	    if ( opv_ctrees( global, root, node->pst_right ) )
	    {
		opv_mapvar( root, usedmap); /* update map of global range 
                                    ** variables used in subtree */
		return;             /* substitution is made so no vars will
                                    ** be contributed to the map of the tree
                                    ** after substitution i.e. return 0
                                    */
	    }
	}
	/* try the subtrees if none of the bylist elements matched */
	{
	    OPV_GBMVARS	    tempmap; /* used to create bit map for var node*/

	    if (root->pst_sym.pst_type == PST_VAR)
	    {
		OPV_GRV	    *gvarp;	    /* global range var associated
					    ** with this node */
		BTset( (i4)root->pst_sym.pst_value.pst_s_var.pst_vno, 
		       (char *)newmap);
		gvarp = global->ops_rangetab.opv_base->
		    opv_grv[root->pst_sym.pst_value.pst_s_var.pst_vno];
		if (gvarp->opv_gsubselect
		    &&
		    gvarp->opv_gsubselect->opv_subquery)
		{
		    MEcopy((char *)&gvarp->opv_gsubselect->opv_subquery->
			ops_correlated, sizeof(tempmap), (char *)&tempmap);
		    BTor(OPV_MAXVAR, (char *)&gvarp->opv_gsubselect->opv_subquery->
			ops_fcorelated, (char *)&tempmap);
		    if (BTcount((char *)&tempmap, OPV_MAXVAR))
		    {   /* if a correlated subquery is referenced then the
			** correlated variables cannot be substituted 
                	** - FIXME need to find all correlated vars, and see if
                	** enough attributes exist to supply correlated values
                	** this can be done by running opa_subselect in OPAFINAL.C
                	** prior to this optimization
                	** ... also need to make sure the correlated subquery
                	** is not used in another context as determined by
                	** opa_compat
                	** - for now do not substitute correlated variables
                	*/
			BTor( (i4)BITS_IN(OPV_GBMVARS), (char *)
			    &gvarp->opv_gsubselect->opv_subquery->ops_correlated,
			    (char *)newmap);
			BTor( (i4)BITS_IN(OPV_GBMVARS), (char *)
			    &gvarp->opv_gsubselect->opv_subquery->ops_fcorelated,
			    (char *)newmap);
		    }
		}
		return;		    /* return map with bit set for this var
                                    ** since a substition will not be made, so
                                    ** the var will appear in the optimized tree
                                    ** if it is created */
	    }
	    else
	    {
		opa_checkopt( global, bylist, root->pst_left, usedmap, newmap);
		MEcopy((char *)newmap, sizeof(tempmap), (char *)&tempmap);
		BTand(OPV_MAXVAR, (char *)usedmap, (char *)&tempmap);
		if (BTcount((char *)&tempmap, OPV_MAXVAR))
		    /* if the maps have an intersection then abort the search
                    ** since there will not be a commit made
                    */
		    return;

		/* traverse the right side of the tree */
		opa_checkopt( global, bylist, root->pst_right,usedmap, &tempmap);
		BTor(OPV_MAXVAR, (char *)&tempmap, (char *)newmap);
		return;
		    
	    }
	}
    }
    MEfill(sizeof(*newmap), 0, (char *)newmap); /* return empty map */
    return;
}
Beispiel #14
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 ( ; ; ) */

}
Beispiel #15
0
/*{
** Name: OPC_EXCH_BUILD	- Build a QEN_EXCH node.
**
** Description:
{@comment_line@}...
**
** Inputs:
[@PARAM_DESCR@]...
**
** Outputs:
[@PARAM_DESCR@]...
**	Returns:
**	    {@return_description@}
**	Exceptions:
**	    [@description_or_none@]
**
** Side Effects:
**	    [@description_or_none@]
**
** History:
**      13-nov-03 (inkdo01)
**	    Written for parallel query processing (cloned from opcsorts.c).
**	1-mar-04 (inkdo01)
**	    Allocate and format bit map to identify DSH-based structures
**	    to allocate with child DSHs.
**	21-july-04 (inkdo01)
**	    Change to use index arrays, rather than bit map.
**	6-aug-04 (inkdo01)
**	    Change setting of || union to opo_punion.
**	31-dec-04 (inkdo01)
**	    Added parm to opc_materialize to assure there's always exch_mat 
**	    code (fixes 113694).
**	27-Aug-2007 (kschendel) SIR 122512
**	    Count subplan headers, info used by hashop reservation.
**	14-May-2010 (kschendel) b123565
**	    Node nthreads now defaults to zero, push counts even for 1:1
**	    exchange so that QEF knows whether a given node is under an
**	    exchange or not.
**	19-May-2010 (kschendel) b123759
**	    Include (new style) partition qualification data in exch counts.
**	    DSH rows can pop up multiple times, especially in pquals, use
**	    a bitmap to avoid listing a row more than once (would leak
**	    memory in QEF, at least for 1:N exchanges).
*/
VOID
opc_exch_build(
		OPS_STATE   *global,
		OPC_NODE    *cnode)
{
    OPS_SUBQUERY	*subqry = global->ops_cstate.opc_subqry;
    OPO_CO		*co = cnode->opc_cco;
    OPC_EQ		*ceq = cnode->opc_ceq;
    QEN_NODE		*qn = cnode->opc_qennode;
    QEN_EXCH		*ex = &cnode->opc_qennode->node_qen.qen_exch;
    QEN_QP		*qpp;
    QEF_QP_CB		*qp = global->ops_cstate.opc_qp;
    OPC_NODE		outer_cnode;
    QEF_AHD		*union_action = NULL;
    PTR			rowmap;		/* DSH row tracker */
    i4			arr1size, arr2size, i, j, ucount;
    i4			arrcnts[IX_MAX];
    i4			mapbits, mapbytes;
    bool		weset_exchn = FALSE;

    /* compile the outer tree */
    STRUCT_ASSIGN_MACRO(*cnode, outer_cnode);

    if (!(global->ops_cstate.opc_flags & OPC_EXCH_N) &&
	co->opo_ccount > 1)
    {
	weset_exchn = TRUE;
	global->ops_cstate.opc_flags |= OPC_EXCH_N;
    }
    qp->qp_status |= QEQP_PARALLEL_PLAN;
    outer_cnode.opc_cco = co->opo_outer;
    outer_cnode.opc_invent_sort = FALSE;
    opc_qentree_build(global, &outer_cnode);
    ex->exch_out = outer_cnode.opc_qennode;
    ex->exch_ccount = co->opo_ccount;
    ex->exch_flag = 0;
    cnode->opc_below_rows += outer_cnode.opc_below_rows;
    if (global->ops_cstate.opc_flags & OPC_EXCH_N)
	ex->exch_flag |= EXCH_NCHILD;
    if (weset_exchn)
	global->ops_cstate.opc_flags &= ~OPC_EXCH_N;
    if (co->opo_punion)
    {
	ex->exch_flag |= EXCH_UNION;
	ex->exch_out->node_qen.qen_qp.qp_flag |= QP_PARALLEL_UNION;
	ucount = ex->exch_ccount;
    }
    else ucount = 1;

    /* Count up subplan headers below here, normally 1, but N if this is
    ** a parallel union.  This is purely to help out join reservation,
    ** so don't bother if child is just an orig.
    */
    if (ex->exch_out->qen_type != QE_ORIG)
	qp->qp_pqhead_cnt += ucount;

    /* Post exch thread-counts to the sub-plan underneath this exch.
    ** (even if it's a 1:1 exch).  This tells everyone that the sub-plan
    ** will be executed in a child thread.  In addition, for the 1:n
    ** case, the ORIG and partition-compatible joins need to know N.
    */
    opc_pushcount(ex->exch_out, ex->exch_ccount);

    /* materialize the inner tuple */
    opc_materialize(global, cnode, &ex->exch_mat, 
	&co->opo_maps->opo_eqcmap, ceq,
	&qn->qen_row, &qn->qen_rsz, (i4)FALSE, (bool)TRUE, (bool)TRUE);

    /* only drop dups if explicitly told to do so. This also disables the 
    ** "only sort on key columns" change (because of odd cases where it seems
    ** to rely on the additional sort keys in its dups dropping strategies),
    ** so it's to our advantage to limit the dups dropping cases. */
    if ((subqry->ops_duplicates == OPS_DKEEP || subqry->ops_duplicates == 
		OPS_DUNDEFINED) && co->opo_odups == OPO_DDEFAULT
	)
    {
	ex->exch_dups = 0;
    }
    else if (co->opo_odups == OPO_DREMOVE ||
		subqry->ops_duplicates == OPS_DREMOVE
	    )
    {
	ex->exch_dups = DMR_NODUPLICATES;
    }
    else
    {
	/* error */
    }

    /* CB number for exchange context CB */
    opc_ptcb(global, &ex->exch_exchcb, sizeof(EXCH_CB));

    /* Fill in the attributes to place in the exchange buffer. */
    opc_eexatts(global, &co->opo_maps->opo_eqcmap, ceq, 
	&ex->exch_atts, &ex->exch_acount);

    /* Fill in the atts to merge on. */
    ex->exch_macount = 0;
    ex->exch_matts = (DMT_KEY_ENTRY **) NULL;

    /* To avoid double-counting a row, which is especially easy if PQUAL's
    ** are around, use a bitmap to track rows.  There usually aren't a
    ** huge number of rows, even in complex query plans.
    ** Double-counting a row is nonfatal, but it leaks memory for unused
    ** row buffers in QEF for 1:N plans.
    */
    mapbits = global->ops_cstate.opc_qp->qp_row_cnt;
    mapbytes = (mapbits + 7) / 8;
    rowmap = opu_Gsmemory_get(global, mapbytes);

    /* Build buffer/structure index arrays for each child (if || union,
    ** one set of arrays per child, else just one set of arrays). */
    for (i = 0; i < ucount; i++)
    {
	/* Allocate arrays to identify buffers and structures to attach
	** to child DSHs resulting from this EXCH node. First build vector
	** of subarray counts for each class of buffers/structs. */
	for (j = 0; j < IX_MAX; j++)
	    arrcnts[j] = 0;		/* init count array */
	MEfill(mapbytes, 0, rowmap);	/* and row tracker */

	/* Set subarray counts. */
	opc_exnheadcnt(global, qn, arrcnts, rowmap); /* count for EXCH node hdr */
	arrcnts[IX_CX]++;		/* + 1 for the exch_mat CX */
	BTset(OPC_CTEMP, rowmap);	/* + the temp row under exch */

	if (ex->exch_flag & EXCH_UNION)
	    opc_exunion_arrcnt(global, qn, &union_action, arrcnts, rowmap);
					/* counts for curr union select */
	else
	    opc_exnodearrcnt(global, qn->node_qen.qen_exch.exch_out, arrcnts, rowmap);
					/* counts everything under exch */

	/* Determine total size of i2 and i4 arrays and alloc memory. */
	arrcnts[IX_ROW] = BTcount(rowmap, mapbits);
	for (j = 0, arr1size = 0; j < IX_CX; j++)
	    arr1size += arrcnts[j];
	for (j = IX_CX, arr2size = 0; j < IX_MAX; j++)
	    arr2size += arrcnts[j];
	ex->exch_ixes[i].exch_array1 = (i2 *)opu_qsfmem(global, arr1size *
				sizeof(i2));
	ex->exch_ixes[i].exch_array2 = (i4 *)opu_qsfmem(global, arr2size *
				sizeof(i4));

	/* Set counts into EXCH. */
	ex->exch_ixes[i].exch_row_cnt = arrcnts[IX_ROW];
	ex->exch_ixes[i].exch_hsh_cnt = arrcnts[IX_HSH];
	ex->exch_ixes[i].exch_hsha_cnt = arrcnts[IX_HSHA];
	ex->exch_ixes[i].exch_stat_cnt = arrcnts[IX_STAT];
	ex->exch_ixes[i].exch_ttab_cnt = arrcnts[IX_TTAB];
	ex->exch_ixes[i].exch_hld_cnt = arrcnts[IX_HLD];
	ex->exch_ixes[i].exch_shd_cnt = arrcnts[IX_SHD];
	ex->exch_ixes[i].exch_pqual_cnt = arrcnts[IX_PQUAL];
	ex->exch_ixes[i].exch_cx_cnt = arrcnts[IX_CX];
	ex->exch_ixes[i].exch_dmr_cnt = arrcnts[IX_DMR];
	ex->exch_ixes[i].exch_dmt_cnt = arrcnts[IX_DMT];
	ex->exch_ixes[i].exch_dmh_cnt = arrcnts[IX_DMH];

	/* Determine start of each subarray. */
	for (j = IX_MAX; j > IX_CX; j--)
	{
	    arr2size -= arrcnts[j-1];
	    arrcnts[j-1] = arr2size;
	}
	for (j = IX_CX; j > 0; j--)
	{
	    arr1size -= arrcnts[j-1];
	    arrcnts[j-1] = arr1size;
	}

	/* Load subarrays - note EXCH nodes stop array loading, so this
	** EXCH node is loaded here, not in opc_ex...arrset(). Likewise,
	** the QP node on top of a || union is loaded here. The subtrees
	** below the EXCH and QP nodes are handled by the opc_ex...arrset()
	** functions.  The row indexes are listed via the row bitmap.
	*/

	j = BTnext(-1, rowmap, mapbits);
	while (j != -1)
	{
	    ex->exch_ixes[i].exch_array1[arrcnts[IX_ROW]++] = j;
	    j = BTnext(j, rowmap, mapbits);
	}
	opc_exnheadset(global, qn, ex->exch_ixes[i].exch_array1,
		ex->exch_ixes[i].exch_array2, arrcnts);
					/* for EXCH node header */
	ex->exch_ixes[i].exch_array2[arrcnts[IX_CX]++] = ex->exch_mat->
			qen_pos;	/* exch_mat CX */
	
	if (ex->exch_flag & EXCH_UNION)
	{
	    qpp = &ex->exch_out->node_qen.qen_qp;
	    opc_exnheadset(global, ex->exch_out, ex->exch_ixes[i].exch_array1,
		ex->exch_ixes[i].exch_array2, arrcnts);
					/* for QP node header */
	    if (qpp->qp_qual != (QEN_ADF *) NULL)
		ex->exch_ixes[i].exch_array2[arrcnts[IX_CX]++] = 
			qpp->qp_qual->qen_pos;
	    opc_exactarrset(global, union_action, 
		ex->exch_ixes[i].exch_array1,
		ex->exch_ixes[i].exch_array2, arrcnts);
	}
	else opc_exnodearrset(global, qn->node_qen.qen_exch.exch_out,
		ex->exch_ixes[i].exch_array1,
		ex->exch_ixes[i].exch_array2, arrcnts);
    }

}
Beispiel #16
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);
}
Beispiel #17
0
/*{
** Name: psy_dpermit	- Define a permit.
**
**  INTERNAL PSF call format: status = psy_dpermit(&psy_cb, sess_cb);
**
**  EXTERNAL call format:     status = psy_call(PSY_DPERMIT, &psy_cb, sess_cb);
**
** Description:
**	Given all of the parameters necessary to CREATE/DEFINE a permit on a
**	table or view, this function will store the permission in the system
**	catalogs.  This will include storing the query tree in the tree table,
**	storing the text of the query in the iiqrytext table (really done by
**	QEF), storing a row in the protect table, and issuing an "alter table"
**	operation to DMF to indicate that there are permissions on the given
**	table.
**
** Inputs:
**      psy_cb
**	    .psy_qrytext		Id of query text as stored in QSF.
**	    .psy_cols[]			Array of columns on which to grant
**					permission
**	    .psy_numcols		Number of columns listed above; 0 means
**					give permission on all columns
**          .psy_intree                 QSF id of query tree representing the
**					where clause in the permit
**          .psy_opctl                  Bit map of defined   operations
**          .psy_opmap                  Bit map of permitted operations
**          .psy_user                   Name of user who will get permission
**          .psy_terminal               Terminal at which permission is given
**					(blank if none specified)
**          .psy_timbgn                 Time of day at which the permission
**					begins (minutes since 00:00)
**          .psy_timend                 Time of day at which the permission ends
**					(minutes since 00:00)
**          .psy_daybgn                 Day of week at which the permission
**					begins (0 = Sunday)
**          .psy_dayend                 Day of week at which the permission ends
**					(0 = Sunday)
**	    .psy_grant
**		PSY_CPERM		CREATE/DEFINE PERMIT
**	    .psy_tblq			head of table queue
**	    .psy_colq			head of column queue
**	    .psy_usrq			head of user queue
**	    .psy_qlen			length of first iiqrytext
**	    .psy_flags			useful info
**		PSY_EXCLUDE_COLUMNS	user specified a list of columns to
**					which privilege should not apply
**	sess_cb				Pointer to session control block
**					(Can be NULL)
**
** Outputs:
**      psy_cb
**          .psy_txtid                  Id of query text as stored in the
**					iiqrytext system relation.
**	    .psy_error			Filled in if error happens
**	Returns:
**	    E_DB_OK			Function completed normally.
**	    E_DB_WARN			Function completed with warning(s);
**	    E_DB_ERROR			Function failed; non-catastrophic error
**	    E_DB_FATAL			Function failed; catastrophic error
**	Exceptions:
**	    none
**
** Side Effects:
**	    Stores text of query in iiqrytext relation, query tree in tree
**	    relation, row in protect relation identifying the permit.  Does
**	    an alter table DMF operation to indicate that there are permissions
**	    on the table.
**
** History:
**	02-oct-85 (jeff)
**          written
**      03-sep-86 (seputis)
**          changed some psy_cb. to psy_cb->
**          added .db_att_id reference
**          changed rdr_cb. rdr_cb->
**	02-dec-86 (daved)
**	    bug fixing. check for permit on tables owned by user and not
**	    view.
**	29-apr-87 (stec)
**	    Implemented changes for GRANT statement.
**	10-may-88 (stec)
**	    Make changes for db procs.
**	03-oct-88 (andre)
**	    Modified call to pst_rgent to pass 0 as a query mode since it is
**	    clearly not PSQ_DESTROY
**	06-feb-89 (ralph)
**	    Added support for 300 attributes:
**		Use DB_COL_BITS in place of DB_MAX_COLS
**		Loop over domset array using DB_COL_WORDS
**	06-mar-89 (ralph)
**	    GRANT Enhancements, Phase 1:
**	    Initialize new DB_PROTECTION fields, dbp_seq and dbp_gtype
**	03-apr-89 (ralph)
**	    GRANT Enhancements, Phase 2:
**	    Use DBGR_USER when initializing dbp_gtype
**	08-may-89 (ralph)
**	    Initialize reserved field to blanks (was \0)
**	04-jun-89 (ralph)
**	    Initialize dbp_fill1 to zero
**	    Fix unix portability problems
**	02-nov-89 (neil)
**	    Alerters: Allowed privileges for events.
**	1-mar-90 (andre)
**	    If processing a GRANT on tables, check if 
**	    ALL-TO-ALL or RETRIEVE-TO-ALL has already been granted, and if so,
**	    mark psy_mask appropriately.
**	    If user tried to CREATE ALL/RETRIEVE-TO-ALL, and one already exists,
**	    skip to the exit.
**	12-mar-90 (andre)
**	    set rdr_2types_mask to 0.
**      22-may-90 (teg)
**          init rdr_instr to RDF_NO_INSTR
**	08-aug-90 (ralph)
**	    Initialize new fields in iiprotect tuple
**	14-dec-90 (ralph)
**	    Disallow use of GRANT by non-DBA if xORANGE
**	11-jan-90 (ralph)
**	    Allow user "$ingres" to use GRANT if xORANGE.
**	    This was done for CREATEDB (UPGRADEFE).
**	20-feb-91 (andre)
**	    For CREATE/DEFINE PERMIT, grantee type was stored in
**	    psy_cb->psy_gtype.
**	24-jun-91 (andre)
**	    IIPROTECT tuples for table permits will contain exactly one
**	    privilege.  IIQRYTEXT template built for table-wide privileges
**	    contains a placeholder for a privilege name which will be filled in
**	    with each of the table-wide privileges being granted, one at a time.
**	    PSY_CB.psy_opmap will be set to correspond with privilege name
**	    stored in the IIQRYTEXT permit.
**	16-jul-91 (andre)
**	    responsibility for splitting permit tuples will passed on to
**	    qeu_cprot().  If a permit specified only one privilege, we will
**	    substitute the appropriate privilege name here and will not ask
**	    qeu_cprot() to split tuples.
**	06-aug-91 (andre)
**	    before proceeding to CREATE a permit on a view owned by the current
**	    user, we will call psy_tbl_grant_check() to ensure that this user
**	    may create a permit on his view.  If the object is not owned by the
**	    current user, we will not try to verify that the user may
**	    CREATE/DEFINE a permit since (until the relevant FE changes are
**	    made) we intend to continue allowing any user with CATUPD to
**	    CREATE/DEFINE permits on catalogs and the dba will be allowed to
**	    CREATE/DEFINE permits on extended catalogs
**	11-nov-91 (rblumer)
**	  merged from 6.4:  26-feb-91 (andre)
**	    PST_QTREE was changed to store the range table as an array of
**	    pointers to PST_RNGENTRY structure.
**	14-feb-92 (andre)
**	    we will no longer have to fill in privilege name for permits
**	    specifying one privilege - it will be handled in respective
**	    grammars.
**	15-jun-92 (barbara)
**	    For Sybil, change interface to pst_rgent(), Star returns from
**	    psy_dpermit before permits get stored.
**	07-jul-92 (andre)
**	    DB_PROTECTION tuple will contain an indicator of how the permit was
**	    created, i.e. whether it was created using SQL or QUEL and if the
**	    former, then whether it was created using GRANT statement.  Having
**	    this information will facilitate merging similar and identical
**	    permit tuples.
**	14-jul-92 (andre)
**	    semantics of GRANT ALL [PRIVILEGES] is different from that of
**	    CREATE PERMIT ALL in that the former (as dictated by SQL92) means
**	    "grant all privileges which the current auth id posesses WGO"
**	    whereas the latter (as is presently interpreted) means "grant all
**	    privileges that can be defined on the object" which in case of
**	    tables and views means SELECT, INSERT, DELETE, UPDATE.
**	    psy_tbl_grant_check() (function responsible for determining whether
**	    a user may grant specified privilege on a specified table or view)
**	    will have to be notified whether we are processing GRANT ALL.  Its
**	    behaviour will change as follows:
**	      - if processing GRANT ALL and psy_tbl_grant_check() determines
**	        that the user does not possess some (but not all) of the
**		privileges passed to it by the caller it will not treat it as an
**		error, but will instead inform the caller of privileges that the
**		user does not posess,
**	      - if processing GRANT ALL and psy_tbl_grant_check() determines
**	        that the user does not possess any of the privileges passed to
**		it by the caller it will treat it as an error
**	      - if processing a statement other than GRANT ALL and
**	        psy_tbl_grant_check() determines that the user does not possess
**		some of the privileges passed to it by the caller it will treat
**		it as an error
**	16-jul-92 (andre)
**	    if a permit being created depends on some privileges, build a
**	    structure describing these privileges and store its address in
**	    rdf_cb->rdr_indep.
**	18-jul-92 (andre)
**	    we will no longer be telling QEF to turn off DMT_ALL_PROT or
**	    DMT_RETRIEVE_PRO when a user creates ALL/RETRIEVE TO ALL permit.
**	    QEF will figure out on its own whether PUBLIC now has RETRIEVE or
**	    ALL on a table/view
**	20-jul-92 (andre)
**	    if user specified a list of columns to which privilege(s) should
**	    not apply, set dbp_domset correctly
**	03-aug-92 (barbara)
**	    Invalidate base table infoblk from RDF cache for CREATE PERMIT
**	    and CREATE SEC_ALARM.
**	16-sep-92 (andre)
**	    privilege maps are build using bitwise ops, so care should be
**	    exercised when accessing it using BT*() functions
**	17-jun-93 (andre)
**	    changed interface of psy_secaudit() to accept PSS_SESBLK
**	5-jul-93 (robf)
**	    changed interface of  psy_secaudit() to accept security label
**	 7-jan-94 (swm)
**	    Bug #58635
**	    Added PTR cast for qsf_owner which has changed type to PTR.
**	06-mar-96 (nanpr01)
**	    Move the QSF request block initialization up. because if  
**	    pst_rgnent returns a failure status code, subsequent QSF
**	    calls get bad control block error.
*/
DB_STATUS
psy_dpermit(
	PSY_CB             *psy_cb,
	PSS_SESBLK	   *sess_cb)
{
    RDF_CB              rdf_cb;
    register RDR_RB	*rdf_rb = &rdf_cb.rdf_rb;
    QSF_RCB		qsf_rb;
    DB_STATUS		status;
    DB_STATUS		stat;
    DB_PROTECTION	ptuple;
    register DB_PROTECTION *protup = &ptuple;
    i4			*domset	= ptuple.dbp_domset;
    register i4	i, j;
    i4		err_code;
    PSS_RNGTAB		*rngvar;
    PSS_USRRANGE	*rngtab;
    PST_PROCEDURE	*pnode;
    PST_QTREE		*qtree;
    DB_ERROR		*err_blk = &psy_cb->psy_error;
    i4			textlen;
    i4			tree_lock   = 0;
    i4			text_lock   = 0;
    DB_TAB_ID		tabids[PST_NUMVARS];
    PSQ_INDEP_OBJECTS   indep_objs;
    PSQ_OBJPRIV         obj_priv;       /* space for independent DELETE */
    PSQ_COLPRIV         col_privs[2];   /*
                                        ** space for independent INSERT and
					** UPDATE
					*/
    PST_VRMAP		varmap;
    PSY_TBL		*psy_tbl;
    DB_TIME_ID		timeid;
    DB_NAME             *objname;

    /*
    ** For CREATE/DEFINE PERMIT execute code below.
    */

    /* initialize the QSF control block */
    qsf_rb.qsf_type	= QSFRB_CB;
    qsf_rb.qsf_ascii_id = QSFRB_ASCII_ID;
    qsf_rb.qsf_length	= sizeof(qsf_rb);
    qsf_rb.qsf_owner	= (PTR)DB_PSF_ID;
    qsf_rb.qsf_sid	= sess_cb->pss_sessid;

    rngtab = &sess_cb->pss_auxrng;

    /* table info is stored in the only entry in the table queue */
    psy_tbl = (PSY_TBL *) psy_cb->psy_tblq.q_next;

    status = pst_rgent(sess_cb, rngtab, -1, "", PST_SHWID,
	(DB_TAB_NAME *) NULL, (DB_TAB_OWN *) NULL,
	&psy_tbl->psy_tabid, TRUE, &rngvar, (i4) 0, err_blk);
	
    if (DB_FAILURE_MACRO(status))
	goto exit;

    /* In STAR, we do not actually store permits */
    if (sess_cb->pss_distrib & DB_3_DDB_SESS)
    {
	qsf_rb.qsf_lk_state = QSO_EXLOCK;
	goto exit;
    }

    /* Fill in the RDF request block */
    pst_rdfcb_init(&rdf_cb, sess_cb);

    /* The table which is receiving the permit */
    STRUCT_ASSIGN_MACRO(psy_tbl->psy_tabid, rdf_rb->rdr_tabid);
    
    /* Tell RDF we're doing a permit definition */
    rdf_rb->rdr_update_op   = RDR_APPEND;

    rdf_rb->rdr_types_mask = RDR_PROTECT;
    rdf_rb->rdr_qrytuple = (PTR) protup;

    /* initialize independent object structure */
    indep_objs.psq_objs	= (PSQ_OBJ *) NULL;
    indep_objs.psq_objprivs = (PSQ_OBJPRIV *) NULL;
    indep_objs.psq_colprivs = (PSQ_COLPRIV *) NULL;
    indep_objs.psq_grantee  = &sess_cb->pss_user;

    rdf_rb->rdr_indep	    = (PTR) &indep_objs;

    /*
    ** populate the IIPROTECT tuple
    */

    /* Zero out the template */
    (VOID)MEfill(sizeof(ptuple), (u_char) 0, (PTR) protup);

    /* store grantee type */
    protup->dbp_gtype = psy_cb->psy_gtype;

    /* Init reserved block */
    (VOID)MEfill(sizeof(protup->dbp_reserve),
	(u_char) ' ', (PTR) protup->dbp_reserve);

    /* Init obj name */
    STRUCT_ASSIGN_MACRO(psy_tbl->psy_tabnm, protup->dbp_obname);

    /*@FIX_ME@ Where does this come from? */
    protup->dbp_obstat = ' ';

    /* store the object type indicator */
    if (psy_tbl->psy_mask & PSY_OBJ_IS_TABLE)
    {
	protup->dbp_obtype = DBOB_TABLE;
    }
    else if (psy_tbl->psy_mask & PSY_OBJ_IS_VIEW)
    {
	protup->dbp_obtype = DBOB_VIEW;
    }
    else
    {
	protup->dbp_obtype = DBOB_INDEX;
    }

    STRUCT_ASSIGN_MACRO(psy_tbl->psy_owner, protup->dbp_obown);

    STRUCT_ASSIGN_MACRO(sess_cb->pss_user, protup->dbp_grantor);

    TMnow((SYSTIME *)&timeid);
    protup->dbp_timestamp.db_tim_high_time = timeid.db_tim_high_time;
    protup->dbp_timestamp.db_tim_low_time  = timeid.db_tim_low_time;

    /* The table on which we're giving permission */
    STRUCT_ASSIGN_MACRO(psy_tbl->psy_tabid, protup->dbp_tabid);

    /* Beginning and ending times of day */
    protup->dbp_pdbgn = psy_cb->psy_timbgn;
    protup->dbp_pdend = psy_cb->psy_timend;

    /* Beginning and ending days of week */
    protup->dbp_pwbgn = psy_cb->psy_daybgn;
    protup->dbp_pwend = psy_cb->psy_dayend;

    if (psy_cb->psy_numcols != 0 && ~psy_cb->psy_flags & PSY_EXCLUDE_COLUMNS)
    {
	/* user specified a list of columns to which privilege(s) will apply */
	
	/* Bit map of permitted columns */
	psy_fill_attmap(domset, ((i4) 0));

	for (i = 0; i < psy_cb->psy_numcols; i++)
	{
	    BTset((i4)psy_cb->psy_cols[i].db_att_id, (char *) domset);
	}	
    }
    else
    {
	/*
	** user specified table-wide privilege(s) or a list of columns L s.t.
	** privilege(s) will apply to the entire table except for columns in L
	*/

	psy_fill_attmap(domset, ~((i4) 0));

	if (psy_cb->psy_flags & PSY_EXCLUDE_COLUMNS)
	{
	    /*
	    ** exclude specified columns from the list of columns to which
	    ** privilege(s) will apply
	    */
	    for (i = 0; i < psy_cb->psy_numcols; i++)
	    {
		BTclear((i4) psy_cb->psy_cols[i].db_att_id, (char *) domset);
	    }
	}
    }

    if (rngvar->pss_tabdesc->tbl_status_mask & DMT_VIEW)
    {
	/*
	** if view is owned by the current user, psy_tbl_grant_check() will
	** determine if the permit can, indeed, be created;  as long as we are
	** preserving the kludge that allows users with CATUPD create permits on
	** catalogs and DBAs to create permits on extended catalogs, we shall
	** not call psy_tbl_grant_check() on view not owned by the current user,
	** since it is likely to result in psy_tbl_grant_check() complaining
	** about inadequate permissions
	*/
	if (!MEcmp((PTR) &rngvar->pss_ownname, (PTR) &sess_cb->pss_user,
	    sizeof(sess_cb->pss_user)))
	{
	    i4			    tbl_wide_privs;
	    PSY_COL_PRIVS	    col_specific_privs, *csp,
				    indep_col_specific_privs;
	    DB_TAB_ID		    indep_id;
	    i4			    indep_tbl_wide_privs;
	    bool		    insuf_privs, quel_view;
	    i4		    val1, val2;

	    /*
	    ** build maps of table-wide and column-specific privileges for
	    ** psy_tbl_grant_check()
	    ** if a column list was specified with CREATE PERMIT and
	    ** privileges specified in the statement include a set of
	    ** privileges S s.t. for all P in S, P can only be specified as
	    ** table-wide with GRANT statement (currently this includes
	    ** SELECT, INSERT, DELETE), we will make 
	    ** psy_tbl_grant_check() think that privileges in S are
	    ** table-wide.
	    ** This will work correctly since if the view was defined over
	    ** some objects owned by other user(s), for every P in S we
	    ** would need table-wide privilege WGO on the underlying object.
	    **
	    ** For the purposes of providing more descriptive output for
	    ** trace point ps131, if column-list was specified, we will pass
	    ** the map of attributes even if column-specific UPDATE was not
	    ** specified
	    */

	    if (psy_cb->psy_numcols != 0 &&
		(psy_cb->psy_opmap & DB_REPLACE ||
		 ult_check_macro(&sess_cb->pss_trace, 3, &val1, &val2)
		)
	       )
	    {
		i4	    *ip;

		csp = &col_specific_privs;

		/*
		** column-specific UPDATE privilege will not be translated into
		** a table-wide privilege since GRANT allows for specification
		** of column-specific UPDATE privilege
		*/
		csp->psy_col_privs = psy_cb->psy_opmap & DB_REPLACE;
		tbl_wide_privs = psy_cb->psy_opmap & ~DB_REPLACE;

		/*
		** if creating a permit on a set of columns and UPDATE is not
		** one of the privileges named in the statement, store the
		** attribute map in the first element of the attribute map list
		*/
		ip = (csp->psy_col_privs)
		    ? csp->psy_attmap[PSY_UPDATE_ATTRMAP].map
		    : csp->psy_attmap->map;

		/* copy the attribute map */
		for (i = 0; i < DB_COL_WORDS; i++, ip++)
		{
		    *ip = domset[i];
		}
	    }
	    else
	    {
		tbl_wide_privs = psy_cb->psy_opmap;
		csp = (PSY_COL_PRIVS *) NULL;
	    }

	    status = psy_tbl_grant_check(sess_cb, (i4) PSQ_PROT,
		&rngvar->pss_tabid, &tbl_wide_privs, csp, &indep_id,
		&indep_tbl_wide_privs, &indep_col_specific_privs,
		psy_cb->psy_flags, &insuf_privs, &quel_view,
		&psy_cb->psy_error);
	    if (DB_FAILURE_MACRO(status))
	    {
		goto exit;
	    }

	    if (insuf_privs)
	    {
		/* must audit failure to create a permit */
		if ( Psf_srvblk->psf_capabilities & PSF_C_C2SECURE )
		{
		    DB_ERROR	e_error;

		    /* Must audit CREATE PERMIT failure. */
		    status = psy_secaudit(FALSE, sess_cb,
			    (char *)&rngvar->pss_tabdesc->tbl_name,
			    &rngvar->pss_tabdesc->tbl_owner,
			    sizeof(DB_TAB_NAME), SXF_E_TABLE,
			    I_SX2016_PROT_TAB_CREATE, SXF_A_FAIL | SXF_A_CREATE,
			    &e_error);
		    
		    status = (status > E_DB_ERROR) ? status : E_DB_ERROR;
		}
		goto exit;
	    }
	    else if (quel_view)
	    {
		goto exit;
	    }
	    
	    /*
	    ** If user is trying to grant one or more of
	    ** INSERT/DELETE/UPDATE on his/her view whose underlying table
	    ** or view is owned by another user, psy_tbl_grant_check() will
	    ** return id of the underlying object along with map of
	    ** privileges.  We will convert maps of independent privileges
	    ** into elements of independent privilege list and pass them
	    ** along to QEF
	    */
	    if (   indep_id.db_tab_base != (i4) 0
		&& (   indep_id.db_tab_base != rngvar->pss_tabid.db_tab_base
		    || indep_id.db_tab_index !=
			   rngvar->pss_tabid.db_tab_index
		   )
	       )
	    {
		if (indep_tbl_wide_privs & DB_DELETE)
		{
		    /*
		    ** the only expected independent table-wide privilege
		    ** is DELETE
		    */
		    obj_priv.psq_next		= (PSQ_OBJPRIV *) NULL;
		    obj_priv.psq_objtype		= PSQ_OBJTYPE_IS_TABLE;
		    obj_priv.psq_privmap		= (i4) DB_DELETE;
		    obj_priv.psq_objid.db_tab_base	= indep_id.db_tab_base;
		    obj_priv.psq_objid.db_tab_index = indep_id.db_tab_index;
		    indep_objs.psq_objprivs		= &obj_priv;
		}

		if (indep_col_specific_privs.psy_col_privs)
		{
		    i4		i, j;
		    PSQ_COLPRIV	*csp;
		    i4		*att_map, *p;
		    i4		priv_map = 0;

		    /*
		    ** privilege map is built using bitwise operators, but
		    ** here using BTnext() makes code much more palatable,
		    ** so convert a privilege map
		    */
		    if (indep_col_specific_privs.psy_col_privs & DB_APPEND)
			BTset(DB_APPP, (char *) &priv_map);
		    if (indep_col_specific_privs.psy_col_privs & DB_REPLACE)
			BTset(DB_REPP, (char *) &priv_map);

		    for (i = -1, csp = col_privs;
			 (i = BTnext(i, (char *) &priv_map, BITS_IN(priv_map)))
			      != -1;
			  csp++
			)
		    {
			csp->psq_next = indep_objs.psq_colprivs;
			indep_objs.psq_colprivs = csp;
			csp->psq_objtype = PSQ_OBJTYPE_IS_TABLE;
			csp->psq_tabid.db_tab_base = indep_id.db_tab_base;
			csp->psq_tabid.db_tab_index = indep_id.db_tab_index;
			switch (i)
			{
			    case DB_APPP:	    /* INSERT privilege */
			    {
				csp->psq_privmap = (i4) DB_APPEND;
				att_map = indep_col_specific_privs.
				    psy_attmap[PSY_INSERT_ATTRMAP].map;
				break;
			    }
			    case DB_REPP:
			    {
				csp->psq_privmap = (i4) DB_REPLACE;
				att_map = indep_col_specific_privs.
				    psy_attmap[PSY_UPDATE_ATTRMAP].map;
				break;
			    }
			}

			for (p = csp->psq_attrmap, j = 0;
			     j < DB_COL_WORDS;
			     j++)
			{
			    *p++ = *att_map++;
			}
		    }
		}
	    }
	}
	else
	{
	    /*
	    ** either this is a catalog and the user has CATUPD or
	    ** this is an extended catalog and the user is the DBA;
	    ** since we may be allowing a user to create a permit by
	    ** circumventing the permit system, we only need to ascertain that
	    ** this is an SQL view
	    */
	    i4	    issql = 0;

	    status = psy_sqlview(rngvar, sess_cb, err_blk, &issql);
	    if (status)
	    {
		goto exit;
	    }
	    if (!issql)
	    {
		/* can only have permits on SQL views */
		psf_error(3598L, 0L, PSF_USERERR, &err_code, err_blk, 1, 
		    psf_trmwhite(sizeof(rngvar->pss_tabname),
			(char *) &rngvar->pss_tabname),
		    &rngvar->pss_tabname);
		status = E_DB_ERROR;
		goto exit;
	    }
	}
    }
    /* Name of user getting permission */
    STRUCT_ASSIGN_MACRO(psy_cb->psy_user, protup->dbp_owner);

    /* Terminal at which permission given */
    STRUCT_ASSIGN_MACRO(psy_cb->psy_terminal, protup->dbp_term);

    /* Give RDF pointer to query tree, if any */
    if (!psy_cb->psy_istree)
    {
	rdf_rb->rdr_qry_root_node = (PTR) NULL;
    }
    else
    {
	PST_VRMAP   varset;
	i4	    j;

	STRUCT_ASSIGN_MACRO(psy_cb->psy_intree, qsf_rb.qsf_obj_id);
	qsf_rb.qsf_lk_state = QSO_EXLOCK;
	status = qsf_call(QSO_LOCK, &qsf_rb);
	if (DB_FAILURE_MACRO(status))
	{
	    (VOID) psf_error(E_PS0D19_QSF_INFO, qsf_rb.qsf_error.err_code,
		PSF_INTERR, &err_code, err_blk, 0);
	    goto exit;
	}

	tree_lock		= qsf_rb.qsf_lk_id;
	pnode = (PST_PROCEDURE *) qsf_rb.qsf_root;
	qtree = (PST_QTREE *) pnode->pst_stmts->pst_specific.pst_tree;
	rdf_rb->rdr_qry_root_node = (PTR) pnode;
	/* check for no views in the qualification.
	*/
	(VOID)psy_varset(qtree->pst_qtree, &varset);	
	j = BTnext(-1, (char *) &varset, BITS_IN(varset));
	for ( ; j >= 0; j = BTnext(j, (char *) &varset, BITS_IN(varset)))
	{
	    status = pst_rgent(sess_cb, rngtab, -1, "", PST_SHWID,
		(DB_TAB_NAME *) NULL, (DB_TAB_OWN *) NULL,
		&qtree->pst_rangetab[j]->pst_rngvar, TRUE,
		&rngvar, (i4) 0, err_blk);
	    if (status)
		goto exit;		

	    if (rngvar->pss_tabdesc->tbl_status_mask & DMT_VIEW)
	    {
		psf_error(3597L, 0L, PSF_USERERR, &err_code, err_blk, 1,
		    psf_trmwhite(sizeof(rngvar->pss_tabname),
			(char *) &rngvar->pss_tabname),
		    &rngvar->pss_tabname);
		    status = E_DB_ERROR;
		    goto exit;
	    }
	}
    }
    
    /* Give RDF a pointer to the query text to be stored in iiqrytext */
    STRUCT_ASSIGN_MACRO(psy_cb->psy_qrytext, qsf_rb.qsf_obj_id);
    qsf_rb.qsf_lk_state = QSO_EXLOCK;
    status = qsf_call(QSO_LOCK, &qsf_rb);
    if (DB_FAILURE_MACRO(status))
    {
	(VOID) psf_error(E_PS0D19_QSF_INFO, qsf_rb.qsf_error.err_code,
	    PSF_INTERR, &err_code, err_blk, 0);
	goto exit;
    }

    text_lock = qsf_rb.qsf_lk_id;

    MEcopy((char *) qsf_rb.qsf_root, sizeof(i4), (char *) &textlen);
    rdf_rb->rdr_l_querytext = textlen;
    rdf_rb->rdr_querytext = ((char *) qsf_rb.qsf_root) + sizeof(i4);
    rdf_rb->rdr_status = (sess_cb->pss_lang == DB_SQL) ? DB_SQL : 0;

    /* determine if the permit specifies exactly one privilege */
    if (BTcount((char *) &psy_cb->psy_opmap, BITS_IN(psy_cb->psy_opmap)) > 1)
    {
	/*
	** if permit specified more than one privilege, notify QEF that it will
	** have to split the permit into multiple IIPROTECT tuples
	*/
	rdf_rb->rdr_instr |= RDF_SPLIT_PERM;
    }
    else if (psy_cb->psy_opmap & DB_RETRIEVE)
    {
	/*
	** if qeu_cprot() will not be splitting the permit into multiple tuples
	** and RETRIEVE is the privilege mentioned in it, set the two bits
	** associated with DB_RETRIEVE
	*/
	psy_cb->psy_opmap |= DB_TEST | DB_AGGREGATE;
	psy_cb->psy_opctl |= DB_TEST | DB_AGGREGATE;
    }

    /* Null out the DMU control block pointer, just in case */
    rdf_rb->rdr_dmu_cb = (PTR) NULL;

    /* produce list of dependent tables */
    rdf_rb->rdr_cnt_base_id = 0;
    if (psy_cb->psy_istree && qtree->pst_qtree) 
    {
	j = 0;
	(VOID)psy_varset(qtree->pst_qtree, &varmap);
	for (i = -1; (i = BTnext(i, (char*) &varmap, PST_NUMVARS)) > -1;)
	{
	    /* if this is the table that is getting the permit, ignore */
	    if (qtree->pst_rangetab[i]->pst_rngvar.db_tab_base != 
		    psy_tbl->psy_tabid.db_tab_base
		||
		qtree->pst_rangetab[i]->pst_rngvar.db_tab_index !=
		    psy_tbl->psy_tabid.db_tab_index
	    )
	    {
		rdf_rb->rdr_cnt_base_id++;
		STRUCT_ASSIGN_MACRO(qtree->pst_rangetab[i]->pst_rngvar,
		    tabids[j++]);
	    }
	}
	rdf_rb->rdr_base_id = tabids;
    }

    protup->dbp_popctl = psy_cb->psy_opctl;
    protup->dbp_popset = psy_cb->psy_opmap;
    
    /*
    ** store an indication of whether this permit is being created using SQL or
    ** QUEL
    */
    protup->dbp_flags = (sess_cb->pss_lang == DB_SQL) ? DBP_SQL_PERM : (i2) 0;
    protup->dbp_flags |= DBP_65_PLUS_PERM;

    /* Now let RDF do all the work of the permit definition */
    status = rdf_call(RDF_UPDATE, (PTR) &rdf_cb);
    if (DB_FAILURE_MACRO(status))
    {
	if (rdf_cb.rdf_error.err_code == E_RD0002_UNKNOWN_TBL)
	{
	    (VOID) psf_error(E_PS0903_TAB_NOTFOUND, 0L, PSF_USERERR,
		&err_code, err_blk, 1,
		psf_trmwhite(sizeof(psy_tbl->psy_tabnm),
		    (char *) &psy_tbl->psy_tabnm),
		&psy_tbl->psy_tabnm);
	}
	else
	{
	    (VOID) psf_rdf_error(RDF_UPDATE, &rdf_cb.rdf_error,
		&psy_cb->psy_error);
	}
	goto exit;
    }

    /*
    ** Invalidate base object's infoblk from RDF cache.
    */
    pst_rdfcb_init(&rdf_cb, sess_cb);
    STRUCT_ASSIGN_MACRO(psy_cb->psy_tables[0], rdf_rb->rdr_tabid);
    status = rdf_call(RDF_INVALIDATE, (PTR) &rdf_cb);
    if (DB_FAILURE_MACRO(status))
    {
	(VOID) psf_rdf_error(RDF_INVALIDATE, &rdf_cb.rdf_error,
				&psy_cb->psy_error);
    }

exit:

    qsf_rb.qsf_lk_state = QSO_EXLOCK;

    if (psy_cb->psy_istree)
    {
	/* Destroy query tree */
	STRUCT_ASSIGN_MACRO(psy_cb->psy_intree, qsf_rb.qsf_obj_id);

	if ((qsf_rb.qsf_lk_id = tree_lock) == 0)
	{
	    stat = qsf_call(QSO_LOCK, &qsf_rb);
	    if (DB_FAILURE_MACRO(stat))
	    {
		(VOID) psf_error(E_PS0D18_QSF_LOCK,
		    qsf_rb.qsf_error.err_code, PSF_INTERR,
		    &err_code, &psy_cb->psy_error, 0);
		if (!status || stat == E_DB_FATAL)
		    status = stat;
	    }
	    tree_lock = qsf_rb.qsf_lk_id;
	}

	stat = qsf_call(QSO_DESTROY, &qsf_rb);
	if (DB_FAILURE_MACRO(stat))
	{
	    (VOID) psf_error(E_PS0D1A_QSF_DESTROY,
		qsf_rb.qsf_error.err_code, PSF_INTERR,
		&err_code, &psy_cb->psy_error, 0);
	    if (!status || stat == E_DB_FATAL)
		status = stat;
	}

	tree_lock = 0;
    }

    /* Destroy query text */
    STRUCT_ASSIGN_MACRO(psy_cb->psy_qrytext, qsf_rb.qsf_obj_id);

    if ((qsf_rb.qsf_lk_id = text_lock) == 0)
    {
	stat = qsf_call(QSO_LOCK, &qsf_rb);
	if (DB_FAILURE_MACRO(stat))
	{
	    (VOID) psf_error(E_PS0D18_QSF_LOCK, qsf_rb.qsf_error.err_code,
		PSF_INTERR, &err_code, &psy_cb->psy_error, 0);
	    if (!status || stat == E_DB_FATAL)
		status = stat;
	}
	text_lock = qsf_rb.qsf_lk_id;
    }

    stat = qsf_call(QSO_DESTROY, &qsf_rb);
    if (DB_FAILURE_MACRO(stat))
    {
	(VOID) psf_error(E_PS0D1A_QSF_DESTROY, qsf_rb.qsf_error.err_code,
	    PSF_INTERR, &err_code, &psy_cb->psy_error, 0);
	if (!status || stat == E_DB_FATAL)
	    status = stat;
    }

    return (status);
}
Beispiel #18
0
/*{
** Name: opn_gnperm	- get next valid permutation of relations
**
** Description:
**	A valid permutation of relations is a group of relations
**	that provide the necessary data (eqc classes) to process
**	the query. The equivalence classes provided by the base
**	relations must be provided by this resulting permutiation
**	either through the use of the base relations themselves
**	or through their own secondary indexes.
**
**      Relations move back and forth between the 2 partitions.
**      There must be exactly 2 partitions where partsz[0] is the number of 
**	relations to be considered for enumeration and partsz[1] contain the
**	remainder.  Each call will produce
**      a "new" set (where order does not matter) of relations
**      in the first partition which satisfy the constraints mentioned
**      earlier.
**      
**      FIXME - can make this routine go much faster by calculating which
**      relations can be replaced by indexes and iterating through all
**      combinations of those only i.e. there will be 3 partitions, 1)the
**      primaries, 2)the set of indexes which cannot ever be 
**      used to replace the base
**      relation, and 3)the set of indexes which can.  Iterate through
**      all combinations of (1&3), for each subset of 2
**
**      Also, can decide that if an index was included to replace the
**      base relation, then any indexes on that relation would not be
**      useful if the relation was not included, and eliminate that case here
**      since a scan is required anyways.  An improvement would be to detect
**      that a keyed lookup would be useful on these indexes 
**
** Inputs:
**      subquery                        ptr to current subquery being analyzed
**      permutation                     current permutation of relations
**      numleaves                       number of relations in permutation
**      partsz                          array of partition sizes where partsz[0]
**                                      is the size of the first partition
**                                      "partsz[0]+partsz[1] == numleaves"
**      pr_n_included                   map of base relations not included in
**                                      valid partition
**	firstcomb			TRUE - combination was set by opn_process
**					so we're just checking heuristics
**
** Outputs:
**	Returns:
**	    TRUE if another permutation exists
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**	11-jun-86 (seputis)
**          initial creation
**	29-mar-89 (seputis)
**          gateway additions
**	29-mar-90 (seputis)
**          fix byte alignment problems
**	1-apr-94 (ed)
**	    b60125 - cart prod variable incorrectly eliminated
**	27-aug-02 (inkdo01)
**	    Apply heuristics to initial combination generated by opn_process
**	    so some index heuristics aren't applied to all permutations of a 
**	    bad combination in opn_jintersect.
**	21-mar-06 (dougi)
**	    Verify that current combination satisfies index hints (if any).
[@history_line@]...
*/
bool
opn_gnperm(
	OPS_SUBQUERY       *subquery,
	OPN_STLEAVES	   permutation,
	OPN_LEAVES	   numleaves,
	OPN_PARTSZ         partsz,
	OPV_BMVARS         *pr_n_included,
	bool		   firstcomb)
{
    OPN_CHILD           numpartitions;		/* number of partitions */
    OPV_IVARS           maxprimary;		/* number of primary joinop 
                                                ** range variables in the
                                                ** query */
    OPV_RT              *vbase;			/* ptr to base of array of
                                                ** ptrs to joinop range
                                                ** variables */
    OPN_LEAVES          firstpartsz;		/* number of elements in the
                                                ** first partition */
    OPE_IEQCLS          maxeqcls;		/* number of equivalence classes
                                                ** defined */
    bool		indexes_gateway;	/* TRUE if subquery contains secondaries
						** on restricted gateway tables */

    maxeqcls = subquery->ops_eclass.ope_ev;	/* get number of equivalence
                                                ** classes in subquery */

    maxprimary = subquery->ops_vars.opv_prv;    /* number of primary relations
                                                ** defined */
    numpartitions = 2;                          /* number of partitions of the
                                                ** set of relations is always 2
                                                ** - first partition contains
                                                ** the set of relations which
                                                ** will be considered for 
                                                ** the next enumeration
                                                ** - second partition contains
                                                ** relations which are not being
                                                ** considered for this
                                                ** enumeration */
    vbase = subquery->ops_vars.opv_base;        /* ptr to base of array of ptrs
                                                ** to joinop range variables */
    firstpartsz = partsz[0];                    /* number of elements in first
                                                ** partition */
    indexes_gateway = (subquery->ops_gateway.opg_smask
	& OPG_INDEX) != 0;			/* gateway mask indicates whether
						** indexes need to be checked */
    /* Return when a valid partition has been found */
    for (;;)
    {
	if (!firstcomb && numleaves == firstpartsz)
	    return(FALSE);			/* no combinations and it failed
						** heuristics */
	/* Get next partition */
	if (!firstcomb && !opn_partition(subquery, permutation, numleaves, 
		partsz, numpartitions, FALSE))
	    return(FALSE);			/* no more partitions */

	firstcomb = FALSE;			/* reset for loop */
	MEfill(sizeof(*pr_n_included), (u_char)0, (PTR)pr_n_included);
        BTnot((i4)maxprimary, (char *)pr_n_included); /* all base relations are
                                                ** not included */
	
	{   /* For each element in the partition, find out if it is a 
	    ** base relation */
	    OPV_IVARS          partvarno;	/* joinop range variable in the
                                                ** first partition */
	    OPV_BMVARS          replaced;	/* bitmap of vars that should
                                                ** be replaced */
	    OPV_BMVARS		table_gateway;	/* for gateways, there are tables
						** which cannot accessed by TID joins
						** but secondaries can be used to
						** replace the base relation
						** entirely */
	    bool		skip_gateway;	/* set TRUE if this particular set of
						** relations references both a base table
						** and a secondary index , which is
						** not supported for gateway tables */
	    
	    if (indexes_gateway)
		MEfill(sizeof(table_gateway), (u_char)0, (PTR)&table_gateway);
	    skip_gateway = FALSE;
	    MEfill(sizeof(replaced), (u_char)0, (PTR)&replaced);
	    for (partvarno = 0; partvarno < firstpartsz; partvarno++)
	    {
		OPV_IVARS	vno;		/* variable being analzyed */
		OPV_VARS	*ivarp;		/* ptr to index range variable*/


		vno = permutation[partvarno];
		ivarp = vbase->opv_rt[vno];
		/* if its included, don't worry about it */
		if (vno < maxprimary)
		    BTclear((i4) vno, (char *)pr_n_included);
		else 
		{
		    if ( ivarp->opv_index.opv_eqclass == OPE_NOEQCLS ||
			ivarp->opv_mask & OPV_CINDEX )
			/* set bitmap if index was added only to replace
                        ** base relation */
			BTset ( (i4) ivarp->opv_index.opv_poffset,
			        (char *) &replaced );
		}
		if (indexes_gateway
		    &&
		    ivarp->opv_grv->opv_relation    /* if this is an RDF relation */
		    &&
		    (ivarp->opv_grv->opv_relation->rdr_rel->tbl_status_mask & DMT_GATEWAY) /* is this
						** a gateway table */
		    &&
		    (subquery->ops_global->ops_cb->ops_server->opg_smask & OPF_INDEXSUB)
		    				/* and the gateway relation has index
						** constraints on it */
		    &&
		    (vno != ivarp->opv_index.opv_poffset) /* do not consider the primary
						** relation for this test */
		    )
		{   /* check constraint on secondary index access for gateway */
		    if (BTtest((i4)ivarp->opv_index.opv_poffset, (char *)&table_gateway))
		    {
			skip_gateway = TRUE;	/* this base relation was referenced
						** previously by this set, so this set
						** of relations needs to be skipped */
			break;
		    }
		    BTset((i4)ivarp->opv_index.opv_poffset, (char *)&table_gateway); /*
						** if only one index for this table in this
						** partition then it is legal, so mark
						** base table bit so that 2+ index search
						** space is eliminated */
		}
	    }
            if ((!BTsubset((char *)&replaced, (char *)pr_n_included, (i4)maxprimary))
		||
		skip_gateway)
		continue;			/* this is a useless partition
                                                ** since an index and the 
                                                ** respective base relation it
                                                ** was intended to replace are
                                                ** both included 
						** OR a restricted gateway table
						** was accessed */

	    if ((subquery->ops_mask2 & OPS_IXHINTS) &&
		!opn_index_hint(subquery, permutation, partsz[0]))
		continue;			/* if there are index hints in
						** this subquery and the 
						** current combination doesn't
						** satisfy them, get the next */
	}

	{
	    OPV_IVARS          varno;		/* varno of primary which was
                                                ** not included */
	    bool	       noprimary;       /* TRUE if no primaries were
                                                ** replaced */
	    noprimary = TRUE;
	    for (varno = -1; 
		 (varno = BTnext((i4)varno, 
				 (char *)pr_n_included, 
				 (i4)maxprimary))
		 >= 0;)
	    {	/* initialize temp equivalence class map associated with
                ** primary relations - the map will be used to gather
                ** all equivalence classes which the indexes provide */

		noprimary = FALSE;
		MEfill( sizeof(vbase->opv_rt[varno]->opv_primary.opv_teqcmp),
			(u_char)0,
			(PTR)&vbase->opv_rt[varno]->opv_primary.opv_teqcmp);/* 
						** init temporary work 
                                                ** area of joinop range 
						** variable element */
	    }
	    if (noprimary)
		return(TRUE);			/* no need to check indexes
                                                ** since no primaries were
                                                ** replaced */
	}
	

	{
	    /* For each index being included in the partition, add the available
	    ** equivalence classes to the primary's map
	    */
	    OPV_IVARS          indexvarno;	/* joinop range var number of
                                                ** index being analyzed */

	    for (indexvarno = 0; indexvarno < firstpartsz; indexvarno++)
	    {
		OPV_VARS           *indexp;     /* ptr to joinop range var 
                                                ** element of current index 
                                                ** being analyzed */
		if(permutation[indexvarno] < maxprimary)
		    continue;			/* not an index so continue*/
		indexp = vbase->opv_rt[permutation[indexvarno]]; /* get ptr 
                                                ** to index element */
		BTor( (i4)maxeqcls,
		      (char *)&indexp->opv_maps.opo_eqcmap,
		      (char *)&vbase->opv_rt[indexp->opv_index.opv_poffset]->
			    opv_primary.opv_teqcmp); /* accumulate all
                                                ** all equivalence classes
                                                ** available from indexes in
                                                ** the temp associated with
                                                ** the primary */
	    }
	}

	{
	    /* check if each primary which is replaced has all the necessary
            ** equivalence classes available from the indexes */
	    OPV_IVARS          primvarno;	/* joinop range variable
                                                ** number of primary which
                                                ** is not included */
	    for (primvarno = -1; 
		(primvarno = BTnext((i4)primvarno, 
				    (char *)pr_n_included, 
				    (i4) maxprimary))
		 >= 0;)
	    {
	        OPV_VARS           *primvarp;   /* ptr to primary joinop range
                                                ** var element to be
                                                ** replaced */
		primvarp = vbase->opv_rt[primvarno]; /* get ptr to primary to
                                                ** be replaced */
		if (!BTsubset( (char *)&primvarp->opv_maps.opo_eqcmap,
			       (char *)&primvarp->opv_primary.opv_teqcmp,
			       (i4)maxeqcls)
		    ||
		    (primvarp->opv_mask & OPV_NOATTRIBUTES)) /* check if this
						** is a no attribute cart prod
						** in which case the base relation
						** is not removed */
		    break;			/* if there are equivalence
                                                ** classes in the primary which
                                                ** are not in the set provided
                                                ** by the indexes then exit
                                                ** with primvarno >= 0 */
	    }
	    if (primvarno < 0)
		return (TRUE);			/* all the primaries have been
                                                ** successfully replaced */
	}
    }
}