Example #1
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;
}
Example #2
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);
}
Example #3
0
/*{
** Name: pst_2adparm	- Add a parameter node to a query tree.
**
** Description:
**      This function adds a parameterized constant node to a query tree.
**
** Inputs:
**	sess_cb				session control block
**	psq_cb				query control block
**      stream                          The memory stream for allocating the
**					node
**	parmno				The parameter number
**	format				The format db data value 
**	newnode				Place to put pointer to new node
**	highparm			Pointer to the highest parm number found
**					so far.
**
** Outputs:
**      newnode                         Filled in with pointer to new node
**	highparm			Filled in with new parm number, if
**					higher than previous value.
**	psq_cb				query control block
**	    .psq_error			filled in if an error happens
**	Returns:
**	    E_DB_OK			Success
**	    E_DB_ERROR			Non-catastrophic error
**	    E_DB_FATAL			Catastrophic error
**	Exceptions:
**	    none
**
** Side Effects:
**	    Allocates memory
**
** History:
**	25-nov-1986 (daved)
**          written
**	18-feb-88 (stec)
**	    Change initialization of pst_pmspec (must not be PST_PMMAYBE).
**	07-jun-88 (stec)
**	    Initialize pst_tparmtype in the constant node.
**	30-mar-89 (jrb)
**	    Changed declaration of datalen to an i4 (to match pst_node); added
**	    dataprec to pst_node parms for decimal project.
**	19-sep-89 (andre)
**	    Do NOT call adi_pm_encode() for SQL queries.
**	15-jun-92 (barbara)
**	    Sybil merge.  Added Star comments:
**	    28-feb-89 (andre)
**		Do not set db_length of VLUPs to ADE_LEN_UNKNOWN in STAR.
**	28-aug-92 (barbara)
**	    parmno is now the actual number that appears after the '$'
**	    ($#=...); it used to be #+1.  Consequently, pst_parm_no will
**	    be set to parmno+1.  'highparm' is still set to parmno, which is
**	    correct.  In pst_header, header->pst_numparm will be set to
**	    highparm+1.  Therefore if there are 3 repeat query parameters,
**	    highparm=2 (0 based), pst_numparm=3.
**      15-Jul-1993 (fred)
**          Add DB_VBYTE_TYPE as a VLUP type.  This mechanism needs to
**          be improved for FE support of OME.
**	04-Nov-2004 (gupsh01)
**	    Added support for nvarchar types.
**	3-Aug-2010 (kschendel)
**	    Let pst-node figure out join id.
*/
DB_STATUS
pst_2adparm(
	PSS_SESBLK	   *sess_cb,
	PSQ_CB		   *psq_cb,
	PSF_MSTREAM        *stream,
	i2		   parmno,
	DB_DATA_VALUE	   *format,
	PST_QNODE	   **newnode,
	i4		   *highparm)
{
    DB_STATUS		status;
    DB_DT_ID		datatype;
    i2			dataprec;
    i4			datalen;
    PST_CNST_NODE	const_node;
    i4		err_code;

    datatype = format->db_datatype;
    dataprec = format->db_prec;
    datalen  = format->db_length;

    /* Allocate the constant node */
    const_node.pst_tparmtype = PST_RQPARAMNO;
    const_node.pst_parm_no = parmno +1;

    /* will not look for QUEL pattern-matching characters for SQL queries */
    if (sess_cb->pss_lang == DB_SQL)
    {
        const_node.pst_pmspec = PST_PMNOTUSED;
    }
    else
    {
        switch (abs(datatype))
        {
	    case DB_CHR_TYPE:
	    case DB_TXT_TYPE:
	    case DB_CHA_TYPE:
	    case DB_VCH_TYPE:
	    {
	        i4	reqs = 0;
	        i4	lines = 0;
	        bool	pmchars;
	        ADF_CB	*adf_scb = (ADF_CB*) sess_cb->pss_adfcb;

	        reqs |= (ADI_DO_BACKSLASH | ADI_DO_MOD_LEN);
	        if (sess_cb->pss_qualdepth)
	        {
		    reqs |= ADI_DO_PM;
	        }

	        status = adi_pm_encode(adf_scb, reqs, format, &lines, &pmchars);
	        sess_cb->pss_lineno += lines;

	        if (DB_FAILURE_MACRO(status))
	        {
		    if (   adf_scb->adf_errcb.ad_errcode == E_AD1015_BAD_RANGE
			|| adf_scb->adf_errcb.ad_errcode == 
			       E_AD3050_BAD_CHAR_IN_STRING
		       )
		    {
			psf_adf_error(&adf_scb->adf_errcb, 
				      &psq_cb->psq_error, sess_cb);
		    }
		    else
		    {
			(VOID) psf_error(E_PS0377_ADI_PM_ERR, 
			    adf_scb->adf_errcb.ad_errcode, PSF_INTERR,
			    &err_code, &psq_cb->psq_error, 0);
		    }

		    return (status);
	        }

		const_node.pst_pmspec = (pmchars == TRUE) ? PST_PMUSED
							  : PST_PMNOTUSED;

	        break;
	    }
	    default:
	    {
	        const_node.pst_pmspec = PST_PMNOTUSED;
	        break;
	    }		
        }
    }
	
    const_node.pst_cqlang = sess_cb->pss_lang;
    const_node.pst_origtxt = (char *) NULL;

    status = pst_node(sess_cb, stream, (PST_QNODE *) NULL,
        (PST_QNODE *) NULL, PST_CONST, (PTR) &const_node,
        sizeof(const_node), datatype, dataprec, datalen,
        (DB_ANYTYPE *) format->db_data, newnode, &psq_cb->psq_error,
        (i4) 0);
	    
    if (DB_FAILURE_MACRO(status))
        return (status);

    /* Remember the highest parameter number */
    if (*highparm < parmno)
        *highparm = parmno;

    /*
    ** Catch VLUP (variable length user param). The value of
    ** db_length for these cases needs to be set as follows.
    */
    if ((~sess_cb->pss_distrib & DB_3_DDB_SESS) &&
        (abs(datatype) == DB_VCH_TYPE ||
	 abs(datatype) == DB_NVCHR_TYPE ||
	 abs(datatype) == DB_TXT_TYPE ||
	 abs(datatype) == DB_VBYTE_TYPE)
       )
    {
        (*newnode)->pst_sym.pst_dataval.db_length = ADE_LEN_UNKNOWN;
    }

    if (add_unorm(*newnode, sess_cb))
    {
	PST_OP_NODE opnode;

	opnode.pst_opno = ADI_UNORM_OP;
	opnode.pst_opmeta = PST_NOMETA;
	opnode.pst_pat_flags = AD_PAT_DOESNT_APPLY;

	status = pst_node( sess_cb, &sess_cb->pss_ostream, *newnode, NULL,
	    PST_UOP, (char *)&opnode, sizeof(opnode), 
	    DB_NODT, (i2)0, (i4)0, (DB_ANYTYPE *)NULL, 
	    newnode, &psq_cb->psq_error, PSS_JOINID_STD);
    }

    return (status);
}
Example #4
0
/*
** NOTE: in SQL grammar target_list of a subselect is processed BEFORE the
**	 from_list; consequently, data types of target list elements are not
**	 known when we build RESDOM nodes for the target list elements of form
**	 [<corr_name>.]<col_name>.  In psl_p_tlist(), we revisit the prototype
**	 tree and fill in the newly available information (type, length,
**	 precision, etc.)
**
**	 When making changes to pst_adresdom(), please take time to understand
**	 the effect these changes may have on the processing of prototype trees.
*/
DB_STATUS
pst_adresdom(
	char               *attname,
	PST_QNODE	   *left,
	PST_QNODE	   *right,
	PSS_SESBLK	   *cb,
	PSQ_CB		   *psq_cb,
	PST_QNODE	   **newnode)
{
    DB_STATUS           status;
    DMT_ATT_ENTRY	*coldesc;
    DMT_ATT_ENTRY	column;
    PSS_RNGTAB		*resrange;
    char		colname[sizeof(DB_ATT_NAME) + 1];   /* null term. */
    PST_RSDM_NODE	resdom;
    i4		err_code;
    PSC_RESCOL		*rescol;
    ADF_CB	*adf_scb;
    i2          null_adjust = 0;
    i4			temp_collID;

    /* Convert column name to a null-terminated string. */
    (VOID) MEcopy((PTR) attname, sizeof(DB_ATT_NAME), (PTR) colname);
    colname[sizeof(DB_ATT_NAME)] = '\0';
    (VOID) STtrmwhite(colname);

    /* For these operations, the result domain comes from the result table */
    if (psq_cb->psq_mode == PSQ_APPEND || psq_cb->psq_mode == PSQ_PROT)
    {
	/* Get the result range variable */
	if (psq_cb->psq_qlang == DB_SQL)
	{
	    resrange = &cb->pss_auxrng.pss_rsrng;
	}
	else
	{
	    resrange = &cb->pss_usrrange.pss_rsrng;
	}

	/* "tid" result column not allowed with these operations */
	if (!STcasecmp(((*cb->pss_dbxlate & CUI_ID_REG_U) ? "TID" : "tid"),
			colname ))
	{
	    psf_error(2100L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 4,
		(i4) sizeof(cb->pss_lineno), &cb->pss_lineno, 
		psf_trmwhite(sizeof(DB_TAB_NAME), 
		    (char *) &resrange->pss_tabname),
		&resrange->pss_tabname,
		psf_trmwhite(sizeof(DB_OWN_NAME), 
		    (char *) &resrange->pss_ownname),
		&resrange->pss_ownname,
		psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname);
	    return (E_DB_ERROR);
	}

	/* Get the column description */
	coldesc = pst_coldesc(resrange, (DB_ATT_NAME *) attname);
	if (coldesc == (DMT_ATT_ENTRY *) NULL)
	{
	    psf_error(2100L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 4,
		(i4) sizeof(cb->pss_lineno), &cb->pss_lineno,
		psf_trmwhite(sizeof(DB_TAB_NAME), 
		    (char *) &resrange->pss_tabname),
		&resrange->pss_tabname,
		psf_trmwhite(sizeof(DB_OWN_NAME), 
		    (char *) &resrange->pss_ownname),
		&resrange->pss_ownname,
		psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname);
	    return (E_DB_ERROR);
	}
	if (coldesc->att_flags & DMU_F_SYS_MAINTAINED)
	{
	    psf_error(E_US1900_6400_UPD_LOGKEY, 0L, PSF_USERERR, 
		&err_code, &psq_cb->psq_error, 4,
		(i4) sizeof(cb->pss_lineno), &cb->pss_lineno,
		psf_trmwhite(sizeof(DB_TAB_NAME), 
		    (char *) &resrange->pss_tabname),
		&resrange->pss_tabname,
		psf_trmwhite(sizeof(DB_OWN_NAME), 
		    (char *) &resrange->pss_ownname),
		&resrange->pss_ownname,
		psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname);

	    return (E_DB_ERROR);
	}
    }
    else if (psq_cb->psq_mode == PSQ_REPLACE)
    {
	/*
	** For the "replace" command, use the result range variable that's
	** in the normal user range table, not the special slot that's
	** reserved for the result table in the append command.
	*/
	/* Get the result range variable */
	resrange = cb->pss_resrng;

	/* "tid" result column not allowed with these operations */
	if (!STcasecmp(((*cb->pss_dbxlate & CUI_ID_REG_U) ? "TID" : "tid"),
			colname))
	{
	    psf_error(2100L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 4,
		(i4) sizeof(cb->pss_lineno), &cb->pss_lineno,
		psf_trmwhite(sizeof(DB_TAB_NAME), 
		    (char *) &resrange->pss_tabname),
		&resrange->pss_tabname,
		psf_trmwhite(sizeof(DB_OWN_NAME), 
		    (char *) &resrange->pss_ownname),
		&resrange->pss_ownname,
		psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname);
	    return (E_DB_ERROR);
	}

	/* Get the column description */
	coldesc = pst_coldesc(resrange, (DB_ATT_NAME *) attname);
	if (coldesc == (DMT_ATT_ENTRY *) NULL)
	{
	    psf_error(2100L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 4,
		(i4) sizeof(cb->pss_lineno), &cb->pss_lineno,
		psf_trmwhite(sizeof(DB_TAB_NAME), 
		    (char *) &resrange->pss_tabname),
		&resrange->pss_tabname,
		psf_trmwhite(sizeof(DB_OWN_NAME), 
		    (char *) &resrange->pss_ownname),
		&resrange->pss_ownname,
		psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname);
	    return (E_DB_ERROR);
	}

	if (coldesc->att_flags & DMU_F_SYS_MAINTAINED)
	{
	    psf_error(E_US1900_6400_UPD_LOGKEY, 0L, PSF_USERERR, 
		&err_code, &psq_cb->psq_error, 4,
		(i4) sizeof(cb->pss_lineno), &cb->pss_lineno,
		psf_trmwhite(sizeof(DB_TAB_NAME), 
		    (char *) &resrange->pss_tabname),
		&resrange->pss_tabname,
		psf_trmwhite(sizeof(DB_OWN_NAME), 
		    (char *) &resrange->pss_ownname),
		&resrange->pss_ownname,
		psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname);

	    return (E_DB_ERROR);
	}
    }
    else if (psq_cb->psq_mode == PSQ_REPCURS)
    {
	/*
	** For the "replace cursor" command, the info comes from the cursor 
	** control block. Cursor column list and update map should always 
	** specify same column set, so the second if statemnt (BTtest) could,
	** perhaps, be removed.
	*/
	rescol = psq_ccol(cb->pss_crsr, (DB_ATT_NAME *) attname);
	if (rescol == (PSC_RESCOL *) NULL)
	{
	    psf_error(2207L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 3,
		sizeof(cb->pss_lineno), &cb->pss_lineno,
		psf_trmwhite(DB_CURSOR_MAXNAME,
			cb->pss_crsr->psc_blkid.db_cur_name),
		cb->pss_crsr->psc_blkid.db_cur_name,
		psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname);
	    return (E_DB_ERROR);
	}

	/* Make sure the column was declared "for update" */
	if (!BTtest((i4) rescol->psc_attid.db_att_id,
	    (char *) &cb->pss_crsr->psc_updmap))
	{
	    psf_error(2207L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 3,
		sizeof(cb->pss_lineno), &cb->pss_lineno,
		psf_trmwhite(DB_CURSOR_MAXNAME, 
			cb->pss_crsr->psc_blkid.db_cur_name),
		cb->pss_crsr->psc_blkid.db_cur_name,
		psf_trmwhite(sizeof(DB_ATT_NAME), attname), attname);
	    return (E_DB_ERROR);
	}

	/* Set up column descriptor */
	coldesc = &column;
	MEcopy((char *) attname, sizeof(DB_ATT_NAME),
	    (char *) &coldesc->att_name);

#ifdef NO
	/*
	** Count columns.  Give error if too many.  One extra for tid.
	*/
	cb->pss_rsdmno++;
	if (cb->pss_rsdmno > (DB_MAX_COLS + 1))
	{
	    psf_error(2130L, 0L, PSF_USERERR, &err_code,
		&psq_cb->psq_error, 1, (i4) sizeof(cb->pss_lineno),
		&cb->pss_lineno);
	    return (E_DB_ERROR);
	}
	coldesc->att_number = cb->pss_rsdmno;
#endif

	coldesc->att_number	= rescol->psc_attid.db_att_id;
	coldesc->att_type	= rescol->psc_type;
	coldesc->att_width	= rescol->psc_len;
	coldesc->att_prec	= rescol->psc_prec;
	coldesc->att_collID	= -1;
	coldesc->att_geomtype = -1;
	coldesc->att_srid = -1;
	coldesc->att_encflags = 0;
	coldesc->att_encwid = 0;
    }
    else
    {
	/*
	** In all other cases, just take the datatype info
	** from the right child.
	*/
	coldesc = &column;
	MEcopy((char *) attname, sizeof(DB_ATT_NAME),
	    (char *) &coldesc->att_name);

	/*
	** Count columns.  Give error if too many.  One extra for tid.
	*/
	cb->pss_rsdmno++;
	if (cb->pss_rsdmno > (DB_MAX_COLS + 1))
	{
	    psf_error(2130L, 0L, PSF_USERERR, &err_code,
		&psq_cb->psq_error, 1, (i4) sizeof(cb->pss_lineno),
		&cb->pss_lineno);
	    return (E_DB_ERROR);
	}
	coldesc->att_number = cb->pss_rsdmno;

	status = pst_rsdm_dt_resolve(right, coldesc, cb, psq_cb);
	if (DB_FAILURE_MACRO(status))
	    return(status);
    }

    /* Copy attribute information into PST_RSDM_NODE */
    resdom.pst_rsno = coldesc->att_number;
    /* The two fields below are initialized for a common case.
    ** They are context sensitive and in many cases may have to be
    ** modified by the caller of this routine.
    */
    resdom.pst_ntargno = resdom.pst_rsno;
    resdom.pst_ttargtype = PST_USER;
    resdom.pst_dmuflags  = 0;
    /* Don't bother with the conversion id for now */
    /* Not for update until we know otherwise */
    resdom.pst_rsupdt = FALSE;
    resdom.pst_rsflags = PST_RS_PRINT;
    MEcopy((char *) &coldesc->att_name, sizeof(DB_ATT_NAME),
	(char *) resdom.pst_rsname);
    temp_collID = coldesc->att_collID;

    /* If client can not handle i8 INTs downgrade to i4 */
    adf_scb = (ADF_CB *) cb->pss_adfcb;
    if ( !(adf_scb->adf_proto_level & AD_I8_PROTO) && (abs(coldesc->att_type) == DB_INT_TYPE) )
    {
        if(coldesc->att_type < 0)
        {
            null_adjust = 1;
        }
        if((coldesc->att_width - null_adjust) == sizeof(i8))
        {
            coldesc->att_width -= sizeof(i4);
        }
    }

    /* Now allocate the node */
    status = pst_node(cb, &cb->pss_ostream, left, right, PST_RESDOM,
	(char *) &resdom, sizeof(PST_RSDM_NODE), (DB_DT_ID) coldesc->att_type,
	(i2) coldesc->att_prec, (i4) coldesc->att_width, (DB_ANYTYPE *) NULL,
	newnode, &psq_cb->psq_error, (i4) 0);
    if (status != E_DB_OK)
    {
	return (status);
    }

    (*newnode)->pst_sym.pst_dataval.db_collID = temp_collID;
    /* Remember the last result domain produced */
    cb->pss_tlist = *newnode;
    return (E_DB_OK);
}
Example #5
0
/*{
** Name: pst_adparm	- Add a parameter node to a query tree.
**
** Description:
**      This function adds a parameterized constant node to a query tree.
**	It takes a parameter number and a format, translates the format to
**	a datatype and length, and creates a constant node containing the
**	datatype, length, and parameter number.
**
**	Valid formats are:
**	    c
**	    i1
**	    i2
**	    i4
**	    f4
**	    f8
**
** Inputs:
**	sess_cb				Session control block
**	psq_cb				Query control block
**      stream                          The memory stream for allocating the
**					node
**	parmno				The parameter number
**	format				The format string
**	newnode				Place to put pointer to new node
**	highparm			Pointer to the highest parm number found
**					so far.
**
** Outputs:
**      newnode                         Filled in with pointer to new node
**	highparm			Filled in with new parm number, if
**					higher than previous value.
**	psq_cb				Query control block
**	    .psq_error			filled in, if an error happens
**	Returns:
**	    E_DB_OK			Success
**	    E_DB_ERROR			Non-catastrophic error
**	    E_DB_FATAL			Catastrophic error
**	Exceptions:
**	    none
**
** Side Effects:
**	    Allocates memory
**
** History:
**	01-may-86 (jeff)
**          written
**	29-jul-87 (daved)
**	    set VLUP (strings) to have length of ADE_LEN_UNKNOWN
**	18-feb-88 (stec)
**	    Change initialization of pst_pmspec (must not be PST_PMMAYBE).
**	07-jun-88 (stec)
**	    Initialize pst_tparmtype in the constant node.
**	03-nov-88 (stec)
**	    Correct a bug found by lint.
**	30-mar-89 (jrb)
**	    Changed datalen to an i4 (to match pst_node's input type) and added
**	    dataprec for decimal project.
**	12-jan-93 (andre)
**	    pass 0 as precision to pst_node()
**	10-aug-93 (andre)
**	    fixed cause of compiler warning
*/
DB_STATUS
pst_adparm(
	PSS_SESBLK	   *sess_cb,
	PSQ_CB		   *psq_cb,
	PSF_MSTREAM        *stream,
	i2		   parmno,
	char		   *format,
	PST_QNODE	   **newnode,
	i4		   *highparm)
{
    DB_STATUS		status;
    register i4         first_char = format[0];
    register i4	second_char = format[1];
    register i4	third_char = format[2];
    DB_DT_ID		datatype;
    i4			datalen;
    PST_CNST_NODE	const_node;
    bool		legal_format = TRUE;
    i4		err_code;

    if (first_char == 'c')
    {
        if (!second_char)
        {
            const_node.pst_pmspec = PST_PMMAYBE;
            datatype = DB_TXT_TYPE;
            datalen = ADE_LEN_UNKNOWN;
	}
	else
	{
	    legal_format = FALSE;
        }
    }
    else if (first_char == 'i' && third_char == 0)
    {
        const_node.pst_pmspec  = PST_PMNOTUSED;
        datatype = DB_INT_TYPE;
        if (second_char == '1')
	    datalen = 1;
        else if (second_char == '2')
	    datalen = 2;
        else if (second_char == '4')
	    datalen = 4;
        else
	    legal_format = FALSE;
    }
    else if (first_char == 'f' && third_char == 0)
    {
        const_node.pst_pmspec  = PST_PMNOTUSED;
        datatype = DB_FLT_TYPE;
        if (second_char == '4')
	    datalen = 4;
        else if (second_char == '8')
            datalen = 8;
        else
	    legal_format = FALSE;
    }
    else
    {
        legal_format = FALSE;
    }

    if (legal_format)
    {
	/* Allocate the constant node */
	const_node.pst_tparmtype = PST_RQPARAMNO;
	const_node.pst_parm_no = parmno;
	const_node.pst_cqlang = sess_cb->pss_lang;
	const_node.pst_origtxt = (char *) NULL;

	status = pst_node(sess_cb, stream, (PST_QNODE *) NULL,
	    (PST_QNODE *) NULL, PST_CONST, (PTR) &const_node,
	    sizeof(const_node), datatype, (i2) 0, datalen,
	    (DB_ANYTYPE *) NULL, newnode, &psq_cb->psq_error, (i4) 0);

	/* Remember the highest parameter number */
	if (*highparm < parmno)
	    *highparm = parmno;
	return (status);
    }
    else
    {
        (VOID) psf_error(200L, 0L, PSF_USERERR,
	    &err_code, &psq_cb->psq_error, 1, STtrmwhite(format), format);
        return (E_DB_ERROR);
    }
}