Ejemplo n.º 1
0
VOID
psy_varset(
	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)
    {
	/* check out this node */
	switch (tree->pst_sym.pst_type)
	{
	case PST_ROOT:
	    /* check union nodes */
	    if (tree->pst_sym.pst_value.pst_s_root.pst_union.pst_next)
	    {
		/* Save union tree for later traversal */
                psy_push(&stk,
                    (PTR)tree->pst_sym.pst_value.pst_s_root.pst_union.pst_next,
		    &sts);
	    }
	    /*FALLTHROUGH*/
	case PST_SUBSEL:
	case PST_AGHEAD:
	    /* add this from list */
	    BTor((i4) BITS_IN(PST_VRMAP),
		(char *)&tree->pst_sym.pst_value.pst_s_root.pst_tvrm, 
		(char *)bitmap);
            break;
	default:
	    break;
	}
	if (tree->pst_right)
	    /* Save right tree for later traversal */
	    psy_push(&stk, (PTR)tree->pst_right, &sts);
	
	if (!(tree = tree->pst_left))
	   tree = (PST_QNODE*)psy_pop(&stk);
    }
}
Ejemplo n.º 2
0
/*{
** Name: psy_subsvars	- Scan query tree and replace VAR nodes
**
** Description:
**	Scans a tree and finds all VAR nodes for this variable.
**	These nodes are looked up in the translation tree and
**	replaced by the value found there.  If this is for a
**	view, the corresponding node must exist in the translation
**	tree.  If not for a view, a 'zero' node (of a type appropriate based
**	on the context) is created and inserted, or, if for a "replace
**	cursor" command, a CURVAL node is substituted for the VAR
**	node.
**
**	This routine is one half of the guts of the whole view
**	algorithm.
**
**	VAR nodes are detached and replaced with the replacement
**	as defined by the view.  Note that there can never be any
**	problems here with update anomalies, since VAR nodes are
**	only used in retrieve contexts.
**
**	It does some extra processing with RESDOM nodes with
**	resno = 0.  These nodes specify a 'tid' domain, and are
**	included by the parser on REPLACE and DELETE commands
**	Subsvars will allow this construct iff the right hand pointer is a
**	VAR node with attno = 0.  In pre-Jupiter versions, this used
**	to update the variable number in these tid RESDOMs; in Jupiter,
**	however, it was changed to keep the same result variable number
**	throughout the qrymod process, so this should be unnecessary.
**	This is because the resvar is the variable number of the one and
**	only underlying base relation of the view on an update
**	(which is presumably the only case where this can come
**	up).  Psy_vrscan has already insured that there can only be
**	a single base relation in this case.
**
**	This whole messy thing is only done with view substitutions.
**	NOTE: THIS IS NOT TRUE!  IT IS ALSO DONE FOR INTEGRITY SUBSTITUTIONS!
**	I DON'T KNOW WHY THIS COMMENT IS HERE.
**
**	In order to fix the handling of aggregates over views, subsvars
**	calls psy_apql at the appropriate place to append a
**	view qualification (if any).  It is done here to handle nested
**	aggregates correctly since after psy_subsvars we no longer know
**	which nested aggregates actually contained the view variable.
**	The view qual will be appended if and only if the view var
**	appears explicitly within the immediate scope of a root node
**	(NOT in a nested aggregate.)
**
**	If at any scope we encounter a var node, we add the qualification
**	to that scope. Once a var node has been found in a scope (and
**	the qualifaction added), for example, a nested aggregate, the
**	qualification is not added to an outer scope unless a var node
**	in the view or integ has been found in that outer scope.
**
**  
** Inputs:
**	proot				Pointer to pointer to root of tree
**					to be updated
**	rngvar				view variable range table entry
**	transtree			Pointer to the target list of the
**					translation tree
**	vmode				PSQ_VIEW if called from view processor,
**					PSQ_APPEND is called from the integrity
**					processor with an APPEND command, else
**					something else.  Mostly, changes
**					handling of tid nodes, and forces an
**					error on a view if the VAR node in the
**					scanned tree does not exist in the
**					vtree.
**	vqual				View qualification to be appended,
**					if any.
**	resvar				Range table entry for result variable
**					in query being modified.
**	from_list			from_list from view getting added.
**	qmode				Query mode of user query.
**	cursid				Cursor id of current cursor, if any
**	result				Pointer to indicator for result.
**	dup_rb				Ptr to dup. request block
**	    pss_op_mask	    --		0
**	    pss_num_joins   --		PST_NOJOIN
**	    pss_tree_info   --		NULL
**	    pss_mstream	    --		ptr to memory stream to be used
**	    pss_err_blk	    --		ptr to error block
**
** Outputs:
**      proot                           User query tree can be updated
**      result                          Filled in with TRUE if view variable
**					was found, FALSE if not. Valid only
**					if E_DB_OK returned.
**	dup_rb
**	    pss_err_blk			Filled in if an error happens.
**	Returns:
**	    E_DB_OK			Success
**	    E_DB_ERROR			Failure
**	Exceptions:
**	    none
**
** Side Effects:
**	    Can allocate memory
**
** History:
**	19-jun-86 (jeff)
**          written
**	1-oct-86 (daved)
**	    set return to TRUE if a VAR node is found.
**	23-dec-86 (daved)
**	    copy qualification before appending it. this avoids graphs in
**	    the tree as well as, and more importantly, the re-use of the
**	    memory used by the qualification before its time. That is,
**	    if vqual is in temporary memory and gets deleted but the
**	    proot tree thinks the memory is still around, bad things happen.
**	12-aug-87 (stec)
**	    Removed test for special 'tid' attribute case, which, according
**	    to Jeff is no longer needed.
**	    Check for special 'tid' resdom now includes open cursor stmt.
**	15-oct-87 (stec)
**	    Added the removed test for special 'tid' attribute case;
**	    it's necessary for checking cases like "retrieve (viewname.tid)".
**	03-dec-87 (stec)
**	    Change psy_apql interface.
**	31-dec-87 (stec)
**	    Cleanup.
**	08-feb-88 (stec)
**	    Modify psy_subsvars to generate CURVAL nodes for replace cursor statement.
**	04-nov-88 (stec)
**	    Fix a view substitution bug. When visiting an AGHEAD node it may happen
**	    that count(*), or count(const) were defined, in which case there are
**	    no VAR nodes and the applicability of the view has to be determined from
**	    the relation bitmap in the node. This anomaly exists only in SQL.
**	14-dec-88 (stec)
**	    Fix correlated subqueries bug.
**	05-apr-89 (andre)
**	    simplify the test for when reference to the view in pst_tvrm is to
**	    be replaced with the tables used to define the view.  We no longer
**	    care if there were any variables found below the root node, instead,
**	    we do it for every root node which has a bit corresponding to the
**	    view set in pst_tvrm.
**	    As a part of the fix, qualification of the view will be appended to
**	    the tree whenever the view is found in the pst_tvrm of the root
**	    node.
**	    Besides allowing us to get rid of calling recursive psy_fixmap() in
**	    psy_view, but it also fixes bugs such as:
**	    "select const from view"  returning more rows than there are in the
**	    view.
**	04-may-89 (andre)
**	    for the time being, set pst_maks1 in all ROOT-type nodes to 0.
**	01-jun-89 (andre)
**	    The preceding fix was not perfect.
**	    "create view v as select * from t where <qual>;
**	     select <aggregate> from v\g"
**	     would result in as many rows containing result of applying
**	     <aggregate> as there are rows in v.  This happens only in SQL.  The
**	     problem is that <qual> gets appended to both AGGHEAD node and the
**	     ROOT node.  The solution is as follows:
**	         For every node N s.t. N is of type ROOT or SUBSELECT, remember
**		 if <qual> has been applied to an AGGHEAD node in the left
**		 subtree of N (in SQL you can not have AGGHEADs in the
**		 "where-clause").  If <qual> has been applied to AGGHEAD(s) in
**		 the left subtree of N, do not append it to the right subtree of
**		 N.
**	22-jun-89 (andre)
**	    And yet another fix for the previous bug fix.  I have incorrectly
**	    assumed that there may be no AGGHEADs in the right subtrre of
**	    ROOT/SUBSEL (select ... having agg(col)).  Before setting *mask to
**	    indicate that an AGGHEAD has been seen, make sure that we are in the
**	    left subtree of the immediate ROOT/SUBSEL parent
**	    (mask != (i4 *) NULL).  If mask is NULL, we must be in the right
**	    subtree, and the fact that we saw an AGGHEAD is of no importance
**	    (or shall I add "or so I believe"?)
**	13-sep-89 (andre)
**	    receive ptr to PSS_DUPRB which will point at memopry stream and
**	    error block + it will be used when calling pst_treedup().  The
**	    fields in dup_rb must be set as follows:
**	    pss_op_mask	    -- 0
**	    pss_num_joins   -- PST_NOJOIN
**	    pss_tree_info   -- NULL
**	    pss_mstream	    -- ptr to memory stream to be used
**	    pss_err_blk	    -- ptr to error block
**	14-sep-92 (andre)
**	    do not zero out pst_mask1 in PST_ROOT and PST_SUBSEL node
**	    (fix for bug 45238)
**	11-feb-93 (andre)
**	    if a query tree involved a reference to a TID attribute of a view V,
**	    replace it with a reference to TID attribute of V's underlying table
**	    or view; this is accomplished by replacing variable number found in
**	    the PST_VAR node with the variable number of the view's underlying
**	    table/view (which can be found by looking for the first set bit in
**	    from_list)
**	27-nov-02 (inkdo01)
**	    Range table expansion (i4 changed to PSAT_J_MASK).
**	13-Jun-2006 (kschendel)
**	    Barf if we translate a var node to a seqop default in an INSERT.
**	    This only happens if we're translating an integrity where-clause
**	    tree, and a var in that where-clause isn't mentioned in the
**	    values list, so we stick the default in instead.  Seqops aren't
**	    allowed in where clauses.  (It would imply that the insert
**	    integrity-permission depends on the sequence value, which is
**	    silly at best.)
**	15-May-2007 (kiria01) b111992
**	    Flatten out much of the recursion of this function to reduce
**	    runtime stack usage - especially bad with views containing
**	    massive IN clauses (>5K elements). 
**	28-nov-2007 (dougi)
**	    Add PSQ_REPDYN to PSQ_DEFCURS test for cached dynamic qs.
**	05-Nov-2009 (kiria01) b122841
**	    Use psl_mk_const_similar to cast default values directly.
**	12-Nov-2009 (kiria01) b122841
**	    Corrected psl_mk_const_similar parameters with explicit
**	    mstream.
**       5-Feb-2010 (hanal04) Bug 123209
**          psy_integ() calls psy_subsvars() to subsitute VARs in the
**          integrity tree with the corresponding nodes from the
**          user query. When a VAR is replaced with a CONST cast the
**          CONST to the VAR's datatype. This stops the substitution from
**          breaking ADE_COMPAREN & ADE_NCOMPAREN processing if the
**          VAR was part of an IN LIST. 
**	18-May-2010 (kiria01) b123442
**	    Force psl_mk_const_similar to generate coercion to cleanly 
**	    enable correct datatype to be represented when substituting
**	    default values.
*/
DB_STATUS
psy_subsvars(
	PSS_SESBLK	*cb,
	PST_QNODE	**proot,
	PSS_RNGTAB	*rngvar,
	PST_QNODE	*transtree,
	i4		vmode,
	PST_QNODE	*vqual,
	PSS_RNGTAB	*resvar,
	PST_J_MASK	*from_list,
	i4		qmode,
	DB_CURSOR_ID	*cursid,
	i4		*mask,
	PSS_DUPRB	*dup_rb)

{
    PSY_STK	stk = {0, 0, {0, }};/* Backtrack stack */
    PST_QNODE	*t;		/* Temporary for *proot */
    i4		vn = rngvar
		    ? rngvar->pss_rgno : -1; /* can be NULL on replace cursor statements */
    i4		err_code;
    DB_STATUS	status = E_DB_OK;

    while(proot && (t = *proot))
    {
	/* These 3 mask variables are only used for ROOT, SUBSEL and AGHEAD */
	i4 newmask;	/* For receiving result from recursive call */
	i4 *l_mask;	/* For propagating state to recursive caller */
	i4 *r_mask;	/*  .. */

	/*
	** The recursive nature of this function has been restructured to
	** allow for most of the processing to be achieved in an iterative
	** manner. Unlike with the other functions in this module, the
	** flattening could not be complete due to the 'mask' output parameter
	** which requires local storage for certain node types: ROOT, SUBSEL
	** and AGHEAD. If we have one of these node types we recurse 1 level
	** to process the left and right sub-trees with the correct scoping of
	** the 'mask' variable.
	*/
	switch (t->pst_sym.pst_type)
	{
	case PST_ROOT:
	    /* Handle any unions */
	    if (t->pst_sym.pst_value.pst_s_root.pst_union.pst_next)
	    {
		/*
		** Defer the tree representing the next subselect in the UNION
		** status = psy_subsvars(cb, 
		**    &t->pst_sym.pst_value.pst_s_root.pst_union.pst_next, rngvar, transtree,
		**    vmode, vqual, resvar, from_list, qmode, cursid, (i4 *) NULL,
		**    dup_rb);
		*/
		psy_push(&stk, (PTR)&t->pst_sym.pst_value.pst_s_root.pst_union.pst_next,
		      &status);
		if (DB_FAILURE_MACRO(status))
		{
		    proot = NULL; /* Exiting to return error */
		    break;
		}
	    }
	    /*FALLTHROUGH*/
	case PST_SUBSEL:
	    /*
	    ** The following applies when language is SQL:
	    **	    if this is a ROOT or a SUBSELECT node, we want to know if the left
	    **	    subtree contains AGGHEAD nodes to which view qualification have been
	    **	    applied; we are not concerned with AGGHEAD nodes in the
	    **	    qualification (there shouldn't be any, anyway) or in other members
	    **	    of the union.
	    */

	    if (cb->pss_lang == DB_SQL)
	    {
		newmask = 0;
		l_mask = &newmask;

		/* we don't care about the right subtree */
		r_mask = (i4 *) NULL;	
	    }
	    /*FALLTHROUGH*/
	case PST_AGHEAD:
	    /*
	    ** The following applies when language is SQL:
	    **	    If this is an AGGHEAD node, set a bit in 'mask' to remember that
	    **	    we saw it.
	    */
	    if (t->pst_sym.pst_type == PST_AGHEAD)
	    {
		if (cb->pss_lang == DB_SQL)
		{
		    if (l_mask = r_mask = mask)
			/*
			** If we are in the right subtree of the immediate ROOT/SUBSELECT
			** parent, mask will be NULL, since we are not concerned with
			** AGHEADs in the right subtrees.
			*/
			*mask |= PSS_1SAW_AGG;
		}
		/*
		** pst_mask1 in PST_AGHEAD node is neither used nor set; I think it
		** would be a good idea to set it, but at this point there is not a heck
		** of a lot that we can do.  Here we will zero out PST_AGHEAD.pst_mask1
		** purely for esthetic reasons.
		*/
		t->pst_sym.pst_value.pst_s_root.pst_mask1 = 0;
	    }

	    /*
	    ** Recurse 1 level to process the left & right subtrees completly
	    ** so that we can complete the processing of this node
	    */
	    status = psy_subsvars(cb, &t->pst_left, rngvar, transtree, vmode, 
		vqual, resvar, from_list, qmode, cursid, l_mask, dup_rb);
	    if (DB_FAILURE_MACRO(status))
	    {
		proot = NULL; /* Exiting to return error */
		break;
	    }

	    /* Process the right branch */
	    status = psy_subsvars(cb, &t->pst_right, rngvar, transtree, vmode, 
		vqual, resvar, from_list, qmode, cursid, r_mask, dup_rb);
	    if (DB_FAILURE_MACRO(status))
	    {
		proot = NULL; /* Exiting to return error */
		break;
	    }

	    /* Add `from' list to bitmap, remove entry for replaced var */
	    if (BTtest(vn, (char*)&t->pst_sym.pst_value.pst_s_root.pst_tvrm))
	    {
		BTclear(vn, (char*)&t->pst_sym.pst_value.pst_s_root.pst_tvrm);
		BTor(PST_NUMVARS, (char *)from_list, 
			(char *)&t->pst_sym.pst_value.pst_s_root.pst_tvrm);
		t->pst_sym.pst_value.pst_s_root.pst_tvrc = 
		    BTcount((char*)&t->pst_sym.pst_value.pst_s_root.pst_tvrm,
			    BITS_IN(t->pst_sym.pst_value.pst_s_root.pst_tvrm));

		/*
		** We will append qualification (if there is one) if the
		** following holds:
		** 1) This is not an SQL (must be QUEL) query    OR
		** 2) if node is ROOT or SUBSEL (i.e. not an AGHEAD) then there
		**    were no AGHEADs found in its left subtree
		**
		**  Let QUAL <==> there is a qualification,
		**      SQL  <==> language is SQL
		**	ROOT <==> node type is ROOT
		**	SUBSEL <==> node type is SUBSEL
		**	AGG    <==> node type is AGHEAD
		**	SAW_AGG <==> mask & PSS_1SAW_AGG.  Then
		**	
		** (Do not apply qualification) <==>
		**  !QUAL + SQL * (ROOT + SUBSEL) * SAW_AGG -->
		**  (Apply qualification) <==>
		**  !(!QUAL + SQL * (ROOT + SUBSEL) * SAW_AGG) <==>
		**  QUAL * !(SQL * (ROOT + SUBSEL) * SAW_AGG)  <==>
		**  QUAL * (!SQL + !((ROOT + SUBSEL) * SAW_AGG)) <==>
		**  QUAL * (!SQL + !(ROOT + SUBSEL) + !SAW_AGG)  <==>
		**  QUAL * (!SQL + AGG + !SAW_AGG)
		*/
		if (vqual &&
		    (cb->pss_lang != DB_SQL	||
		    t->pst_sym.pst_type == PST_AGHEAD ||
		     (*l_mask & PSS_1SAW_AGG) == 0))
		{
		    PST_QNODE *vqual_copy;
		    dup_rb->pss_tree = vqual;
		    dup_rb->pss_dup  = &vqual_copy;
		    status = pst_treedup(cb, dup_rb);
		    dup_rb->pss_tree = (PST_QNODE *)NULL;
		    dup_rb->pss_dup  = (PST_QNODE **)NULL;
        		    
		    if (DB_FAILURE_MACRO(status))
		    {
			proot = NULL; /* Exiting to return error */
			break;
		    }
		    /* append view qualification */
		    status = psy_apql(cb, dup_rb->pss_mstream, vqual_copy, t,
				      dup_rb->pss_err_blk);
		    if (DB_FAILURE_MACRO(status))
		    {
			proot = NULL; /* Exiting to return error */
			break;
		    }
		}
	    }
	    /* left & right have been processed */
	    break;

	case PST_VAR:
	    /*
	    ** This is a terminal node - the expectation is that left & right are 0
	    */
            
	    /*
	    ** Check for a VAR node but of a different variable than the one
	    ** we are substituting for. REPLACE CURSOR (quel version) is an
	    ** exception because the substitution variable (resvar) is not
	    ** defined, in that case we do not want to execute the code
	    ** below, but want to continue the translation process.
	    */
	    if (vn != -1 && t->pst_sym.pst_value.pst_s_var.pst_vno != vn)
		break;
	    /*
	    ** if this is a reference to a TID attribute of a view (which is not
	    ** a "real" attribute), it needs to be translated into a reference
	    ** to the TID attribute of the view's underlying table or view
	    */
	    if (t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id == 0 &&
		vmode == PSQ_VIEW)
	    {
		t->pst_sym.pst_value.pst_s_var.pst_vno =
		    BTnext(-1, (char *) from_list, sizeof(*from_list));
	    }
	    else
	    {
		PST_QNODE *v;

		/* find var in vtree */
		status = psy_vfind((u_i2)t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id, 
		    transtree, &v, dup_rb->pss_err_blk);
		if (DB_FAILURE_MACRO(status))
		{
		    proot = NULL; /* Exiting to return error */
		    break;
		}

		if (v == (PST_QNODE *)NULL)
		{
		    /* attribute not defined in view */
		    if (vmode == PSQ_VIEW)
		    {
			psf_error(E_PS0D03_ATT_NOT_FOUND, 0L, PSF_INTERR, &err_code,
			    dup_rb->pss_err_blk, 0);
			status = E_DB_SEVERE;
			proot = NULL; /* Exiting to return error */
			break;
		    }
		    /* append defaults for integrity. Integrity might exist on a value
		    ** we are appending by default. I.e., the attribute was not mentioned
		    ** in the target list. We replace the var node in the integrity with
		    ** a default value so that the integrity will read 'default value' ?
		    ** value.
		    */
		    else if (vmode == PSQ_APPEND)
		    {
			status = psl_make_default_node(cb, dup_rb->pss_mstream, resvar,
						       t->pst_sym.pst_value
							     .pst_s_var.pst_atno.db_att_id,
						       &v, dup_rb->pss_err_blk);
			if (DB_FAILURE_MACRO(status))
			{
			    proot = NULL; /* Exiting to return error */
			    break;
			}
			/* Try to cast to column type */
			status = psl_mk_const_similar(cb, dup_rb->pss_mstream,
						&t->pst_sym.pst_dataval,
						&v, dup_rb->pss_err_blk, NULL);
			if (DB_FAILURE_MACRO(status))
			    return(status);

			/* If we ended up with a sequence default, fail. This is an
			** unreasonable situation, integrity where tests should not apply
			** to sequence defaults.
			*/
			if (v->pst_sym.pst_type == PST_SEQOP)
			{
			    psf_error(6319, 0, PSF_USERERR, &err_code,
				    dup_rb->pss_err_blk, 0);
			    status = E_DB_ERROR;
			    proot = NULL; /* Exiting to return error */
			    break;
			}
		    }
		    /* we would like to delete the qualification for this node since the
		    ** value is not changing and thus we don't need to worry about integrity
		    ** constaints on it. However, we don't do that. Instead we have the
		    ** integrity refer to the value in the current row that reflects the
		    ** value for the attribute. In the replace statement (not replace
		    ** cursor) we just perform the retrieve, the qualification is unneeded
		    ** but doesn't hurt anything. We want to avoid causing a retrieve for
		    ** each update cursor; therefore, we change the varnode to refer to the
		    ** current value (ie the retrieve has already been done).
		    */
		    else if (vmode == PSQ_REPCURS)
		    {
			PST_CRVAL_NODE curval;
			/* Create a CURVAL node for the corresponding column */
			curval.pst_curcol.db_att_id =
			    t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id;
			STRUCT_ASSIGN_MACRO(*cursid, curval.pst_cursor);
			status = pst_node(cb, dup_rb->pss_mstream, (PST_QNODE *)NULL,
				(PST_QNODE *)NULL, PST_CURVAL, (PTR)&curval, sizeof(curval),
				t->pst_sym.pst_dataval.db_datatype,
				t->pst_sym.pst_dataval.db_prec,
				t->pst_sym.pst_dataval.db_length,
				(DB_ANYTYPE *)t->pst_sym.pst_dataval.db_data, &v,
				dup_rb->pss_err_blk, (i4) 0);
			if (DB_FAILURE_MACRO(status))
			{
			    proot = NULL; /* Exiting to return error */
			    break;
			}
		    }
		}
		else
		{
		    dup_rb->pss_tree = v;
		    dup_rb->pss_dup  = &v;
		    status = pst_treedup(cb, dup_rb);
		    dup_rb->pss_tree = (PST_QNODE *)NULL;
		    dup_rb->pss_dup  = (PST_QNODE **)NULL;
                	
		    if (DB_FAILURE_MACRO(status))
		    {
			proot = NULL; /* Exiting to return error */
			break;
		    }

                    /* When called from psy_integ() v will be found but
                    ** may still need constants to be cast.
                    */
                    if ((v != (PST_QNODE *)NULL) &&
                        (v->pst_sym.pst_type == PST_CONST))
                    {
			bool handled;

                        /* Try to cast to column type */
                        status = psl_mk_const_similar(cb, dup_rb->pss_mstream,
                                                &t->pst_sym.pst_dataval,
                                                &v, dup_rb->pss_err_blk, 
                                                &handled);
                        if (DB_FAILURE_MACRO(status))
                            return(status);
                    }
		}

		/* replace VAR node */
		if (v != (PST_QNODE *)NULL)
		{
		    *proot = v;
		}
	    }
	    /* left and right should have been null as we are on a terminal */
	    break;

	case PST_RESDOM:
	    /* Process `TID' resdom used by DELETE, REPLACE and OPEN CURSOR */
	    if (t->pst_sym.pst_value.pst_s_rsdm.pst_rsno == 0 &&
		(qmode == PSQ_DELETE || qmode == PSQ_REPLACE || 
			qmode == PSQ_DEFCURS || qmode == PSQ_REPDYN))
	    {
		/*
		** If resvar not specified, or if not resvar, ignore leaf.
		*/
		if (resvar && vn == resvar->pss_rgno)
		{
		    /* t->right better be VAR node, attno 0 */
		    t = t->pst_right;
		    if (t->pst_sym.pst_type != PST_VAR ||
			t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id != 0 ||
			t->pst_sym.pst_value.pst_s_var.pst_vno != vn)
		    {			
			(VOID) psf_error(E_PS0D02_BAD_TID_NODE, 0L, PSF_INTERR, &err_code,
			    dup_rb->pss_err_blk, 0);
			status = E_DB_SEVERE;
			proot = NULL; /* Exiting to return error */
			break;
		    }
		}
		else if (t->pst_right)
		    /* Process the right branch */
		    psy_push(&stk, (PTR)&t->pst_right, &status);
	    }
	    else if (t->pst_right)
		/* Process the right branch */
		psy_push(&stk, (PTR)&t->pst_right, &status);

	    if (DB_FAILURE_MACRO(status))
	    {
		proot = NULL; /* Exiting to return error */
		break;
	    }
	    /* Process left branch */
	    if (t->pst_left)
		psy_push(&stk, (PTR)&t->pst_left, &status);
	    if (DB_FAILURE_MACRO(status))
	    {
		proot = NULL; /* Exiting to return error */
		break;
	    }
	    break;

	default:
	    /*
	    ** Just ensure that we traverse the tree
	    */
	    if (t->pst_right)
		psy_push(&stk, (PTR)&t->pst_right, &status);
	    if (DB_FAILURE_MACRO(status))
	    {
		proot = NULL; /* Exiting to return error */
		break;
	    }

	    if (t->pst_left)
		psy_push(&stk, (PTR)&t->pst_left, &status);
	    if (DB_FAILURE_MACRO(status))
	    {
		proot = NULL; /* Exiting to return error */
		break;
	    }
	    break;
	}
	if (!proot)    /* We're breaking out early to exit */
	    break;

	/*
	** Get next deferred node
	*/
	proot = (PST_QNODE**)psy_pop(&stk);
    }

    /* Release any outstanding entries */
    psy_pop_all(&stk);

    return status;
}
Ejemplo n.º 3
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 */
	}
    }
}
Ejemplo n.º 4
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);
}
Ejemplo n.º 5
0
/*{
** Name: opv_smap	- map query tree associated with subquery
**
** Description:
**      This routine will map the query tree associated with the subquery.  A
**      subquery is typically associated with a PST_AGHEAD, or PST_ROOT node. 
**      A flag is check to see if the variable map was invalidated by any 
**      previous substitution.  For now this will be a consistency check 
**      and the tree will be mapped anyways.  FIXME later change this to 
**      avoid mapping the tree if there has been no substitutions.
**
** Inputs:
**      subquery                        ptr to subquery which will be mapped
**
** Outputs:
**      subquery->ops_root              this PST_RT_NODE will have the bitmaps
**                                      updated
**	Returns:
**	    VOID
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**	3-jul-86 (seputis)
**          initial creation
**	22-apr-90 (seputis)
**          fix rohm-haas bug (no bug number), aggregate on a select distinct view
**	24-jun-91 (seputis)
**	    turn off consistency check to avoid traversal of parse tree
**	5-dec-02 (inkdo01)
**	    Changes for range table expansion.
**	31-Aug-2006 (kschendel)
**	    Watch for HFAGG as well as RFAGG.
*/
VOID
opv_smap(
	OPS_SUBQUERY       *subquery)
{
    OPV_GBMVARS         map;		/* range var map of query tree fragment
                                        */

    if (subquery->ops_vmflag)		/* TRUE if varmap is up-to-date */
    {
#ifdef E_OP0388_VARBITMAP
	/* check if this var map is valid as claimed */
#ifdef xDEBUG
	MEfill(sizeof(map), 0, (char *)&map);
	opv_mapvar(subquery->ops_root->pst_left, &map);
	if (MEcmp((char *)&map, (char *)&subquery->ops_root->pst_sym.pst_value.
		pst_s_root.pst_lvrm, sizeof(map)) != 0)
	    opx_error( E_OP0388_VARBITMAP); /* bit map
					** inconsistent with left side */
	MEfill(sizeof(map), 0, (char *)&map);
	opv_mapvar(subquery->ops_root->pst_right, &map);
	if (MEcmp((char *)&map, (char *)&subquery->ops_root->pst_sym.pst_value.
		pst_s_root.pst_rvrm, sizeof(map)) != 0)
	    opx_error( E_OP0388_VARBITMAP); /* bit map
					** inconsistent with right side */
#endif
	return;
#endif
    }

    if ((subquery->ops_sqtype == OPS_FAGG)
	||
	(subquery->ops_sqtype == OPS_HFAGG)
	||
	(subquery->ops_sqtype == OPS_RFAGG)
	)
    {
	/* map the bylist for the function aggregate */
	MEfill(sizeof(map), 0, (char *)&map);
	opv_mapvar(subquery->ops_agg.opa_byhead->pst_left, &map); /* map
					** the bylist portion of the function
					** aggregate */
	MEcopy((char *)&map, sizeof(map), (char *)&subquery->ops_agg.opa_blmap);
	if (subquery->ops_root->pst_left == subquery->ops_agg.opa_byhead)
	{
	    OPV_GBMVARS         aopmap;	/* map of AOP operator
					    */
	    MEfill(sizeof(aopmap), 0, (char *)&aopmap);
	    opv_mapvar(subquery->ops_agg.opa_aop, &aopmap); /* map the AOP node
					    ** of the function aggregate */
	    MEcopy((char *)&map, sizeof(map),
		(char *)&subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm);
	    BTor(OPV_MAXVAR, (char *)&aopmap, 
		(char *)&subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm);
	}
	else
	{   /* non-printing resdoms exist above the byhead so 
	    ** the entire tree needs to be scanned */
	    MEfill(sizeof(PST_J_MASK), 0, 
		(char *)&subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm);
	    opv_mapvar(subquery->ops_root->pst_left, 
		&subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm ); 
					/* map the AOP node
					** of the function aggregate */
	}
    }
    else
    {
	/* map the left side of the tree */
	MEfill(sizeof(map), 0, (char *)&map);
	opv_mapvar(subquery->ops_root->pst_left, &map);
	MEcopy((char *)&map, sizeof(map),
	    (char *)&subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm);
    }

    /* map the right side of the tree */
    MEfill(sizeof(map), 0, (char *)&map);
    opv_mapvar(subquery->ops_root->pst_right, &map);
    MEcopy((char *)&map, sizeof(map),
	(char *)&subquery->ops_root->pst_sym.pst_value.pst_s_root.pst_rvrm);

    subquery->ops_vmflag = TRUE;	/* bit maps are now valid */
}
Ejemplo n.º 6
0
/*{
** Name: opa_obylist	- replace variables in outer by inner
**
** Description:
**      This procedure will attempt to replace the variables in the outer 
**      aggregate by using bylist attributes of the inner aggregate. 
**
** Inputs:
**      global                          global state variable
**      inner                           inner function aggregate subquery 
**					whose bylist will be used to attempt
**                                      to replace the outer aggregate variables
**      outer                           outer aggregate subquery whose variables
**                                      will possibly be replaced by the inner
**
** Outputs:
**	Returns:
**	    VOID
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**	15-apr-86 (seputis)
**          initial creation
**	16-may-96 (inkdo01)
**	    Change 409554 has been backed out to fix bug 74793. It claimed to
**	    eliminate obsolete code (because of change 409457), but the code
**	    appears to have still been necessary for queries involving outer
**	    joins of aggregate views.
**	3-dec-02 (inkdo01)
**	    Changes for range table expansion.
**	23-nov-05 (inkdo01)
**	    Fix a bug in one of the more complex expressions that derived from 
**	    the range table expansion.
[@history_line@]...
*/
static VOID
opa_obylist(
	OPS_STATE          *global,
	OPS_SUBQUERY       *inner,
	OPS_SUBQUERY       *outer)
{
    OPV_GBMVARS         outermap;   /* var map of outer aggregate */

    opv_smap(outer);		    /* get variable map of outer aggregate */
    MEcopy((char *)&outer->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm,
	sizeof(outermap), (char *)&outermap);
    BTor(OPV_MAXVAR, (char *)&outer->ops_root->pst_sym.pst_value.pst_s_root.pst_rvrm,
	(char *)&outermap);
    opv_smap(inner);                /* get variable map of inner aggregate */

    BTand(OPV_MAXVAR, (char *)&inner->ops_agg.opa_blmap, (char *)&outermap);
    if (BTcount((char *)&outermap, OPV_MAXVAR) == 0)
	/* if the outer aggregate and the inner aggregate do not have any
	** variables in common then there can be no replacement so return.
	*/
	return;

    BTor(OPV_MAXVAR, (char *)&inner->ops_root->pst_sym.pst_value.pst_s_root.pst_tvrm,
	(char *)&outer->ops_aggmap);
    BTor(OPV_MAXVAR, (char *)&inner->ops_root->pst_sym.pst_value.pst_s_root.pst_lvrm,
	(char *)&outer->ops_aggmap);
    BTor(OPV_MAXVAR, (char *)&inner->ops_root->pst_sym.pst_value.pst_s_root.pst_rvrm,
	(char *)&outer->ops_aggmap);
				    /* this set of variables could be substituted
                                    ** in the outer, so they are not to be
                                    ** assumed to be in the from list for
                                    ** a cartesean product as in the query
                                    ** "select r.a from r,s" */
    {
	OPV_GBMVARS	       usedmap;	/* var map of variables which were
					** replaced by substituting the
					** bylist attributes of the inner
					*/
	OPV_GBMVARS            newmap;	/* var map of the outer aggregate
                                        ** after the inner aggregate bylist
                                        ** used to substitute expressions
                                        ** in the outer
                                        */
	OPV_GBMVARS		tempmap;
	MEfill(sizeof(usedmap), 0, (char *)&usedmap);
					/* initialize var map */
	opa_checkopt(global, inner->ops_agg.opa_byhead->pst_left, outer->ops_root,
	    &usedmap, &newmap); 	/* this routine will return information
                                        ** on what the query tree would
                                        ** look like if the inner aggregate
                                        ** was substituted (without actually
                                        ** doing the substitution)
                                        */
	MEcopy((char *)&newmap, sizeof(newmap), (char *)&tempmap);
	BTand(OPV_MAXVAR, (char *)&usedmap, (char *)&tempmap);
	/* This replaces the old (32 bit varmap) test of "usedmap &&
	** !(newmap & usedmap)". */
	if (BTcount((char *)&usedmap, OPV_MAXVAR) != 0 &&
					/* non-zero implies some optimizations
                                        ** were found */
	    BTcount((char *)&tempmap, OPV_MAXVAR) == 0)
					/* the substitution would eliminate
                                        ** those variables entirely */
	{
	    /* COMMIT THE CHANGES
	    ** the usedmap is non-zero so some variable subtitutions were
	    ** found.  Moreover, the substitutions would entirely eliminate
            ** the variables since newmap is non-zero
            **
	    ** First making a copy of the bylist for the outer aggregate 
	    ** if it exists (and if it is not the main query).  This
            ** is done to avoid the problem of optimizing away the links
            ** made by the outer aggregate.  For example, in the query
            ** RETRIEVE SUPPLIERS WHO SUPPLY ALL PARTS 
            **   ret( s.sname, s.s) where any(p.p by s.s where any(sp.tid
            **	    by p.p,s.s where p.p=sp.p and s.s=sp.s)=0)=0
            ** In this query the outer aggregate references only attributes
            ** in the BY list of the inner aggregate, and won't have the
            ** aggregate result linked to the main query ... if we did
            ** not make a copy of the bylist ... remember that the outer
            ** aggregate was linked to the main query by using the by list
            ** subtrees directly!
            ** FIXME - OPA_LINK will copy the bylists anyways so this copy
            ** is not needed
            */
	    OPV_IGVARS            innervarno; /* var number of inner
                                            ** aggregate which will be
                                            ** referenced for substitution */

	    if (outer->ops_sqtype == OPS_MAIN) 
		global->ops_gmask |= OPS_TCHECK;
	    else if (outer->ops_agg.opa_byhead)   /* outer aggregate has a by list */
	    {
		PST_QNODE             *bylist; /* used to traverse the bylist */

		/* traverse the bylist and copy the subtrees */
		for ( bylist = outer->ops_agg.opa_byhead->pst_left;
		    bylist && bylist->pst_sym.pst_type != PST_TREE;
		    bylist = bylist->pst_left)

		    opv_copytree( global, &bylist->pst_right );
	    }

	    /* Traverse the tree and actually perform the substitutions instead
            ** of only checking for them
            */
	    innervarno = (*inner->ops_agg.opa_graft)->pst_sym.pst_value.
		    pst_s_var.pst_vno;
	    if (outer->ops_global->ops_qheader->pst_numjoins > 0)
	    {	/* make sure that all the outer joins semantics are
		** the same for all the relations referenced, or else
		** semantics are lost, i.e. cannot substitute if variables
		** have different maps */
		PST_J_MASK	pinner;
		PST_J_MASK	pouter;
		bool		first_time;
		OPV_IGVARS	gvar;
		PST_J_MASK	*ijmaskp;
		PST_J_MASK	*ojmaskp;
		OPL_PARSER	*pinnerp;
		OPL_PARSER	*pouterp;

		first_time = TRUE;
		pinnerp = outer->ops_oj.opl_pinner;
		pouterp = outer->ops_oj.opl_pouter;
		for (gvar = -1; (gvar = BTnext((i4)gvar, (char *)&usedmap, 
			(i4)BITS_IN(usedmap)))>=0;)
		{
		    if (first_time)
		    {
			MEcopy((PTR)&pinnerp->opl_parser[gvar],
			    sizeof(pinner), (PTR)&pinner);
			MEcopy((PTR)&pouterp->opl_parser[gvar],
			    sizeof(pouter), (PTR)&pouter);
		    }
		    else
		    {
			if (MEcmp((PTR)&pinnerp->opl_parser[gvar],
			    (PTR)&pinner, sizeof(pinner))
			    ||
			    MEcmp((PTR)&pouterp->opl_parser[gvar],
			    (PTR)&pouter, sizeof(pouter))
			    )
			    return;	    /* outer joins semantics of
					    ** variables to be substituted are
					    ** different, FIXME, try to substitute
					    ** one variable instead of 2 */
		    }
		}
		/* copy the outer join semantics to the substituted variable */
		ijmaskp = &pinnerp->opl_parser[innervarno]; 
		ojmaskp = &pouterp->opl_parser[innervarno];
		if ((BTnext((i4)-1, (char *)ijmaskp, (i4)BITS_IN(*ijmaskp)) >= 0)
		    ||
		    (BTnext((i4)-1, (char *)ojmaskp, (i4)BITS_IN(*ojmaskp)) >= 0)
		    )
		    opx_error(E_OP0288_OJAGG);	/* should not already have an
					    ** outer join defined on this aggregate
					    ** in this query */
		MEcopy((PTR)&pinner, sizeof(*ijmaskp), (PTR)ijmaskp);
		MEcopy((PTR)&pouter, sizeof(*ojmaskp), (PTR)ojmaskp);
	    }
	    outer->ops_vmflag = FALSE;  /* bitmaps need to be updated if a
                                        ** substitution on the outer is made */
	    opa_commit(global, inner->ops_agg.opa_byhead->pst_left, 
		&outer->ops_root, innervarno); /* this routine will traverse 
                                        ** the tree in the same way as 
                                        ** opa_checkopt except that 
                                        ** substitutions will actually be made
                                        */
	    global->ops_gmask &= (~OPS_TCHECK);
	}
    }
}
Ejemplo n.º 7
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;
}