Пример #1
0
/*{
** Name: psq_jrel_dmp	- Dump inner and outer relation mask
**
** Description:
**      This function dumps inner and outer relation mask in a somewhat
**	readable form.
**
** Inputs:
**	inner_rel		    ptr to the inner relation mask
**      outer_rel		    ptr to the outer relation mask
**
** Outputs:
**      None
**	Returns:
**	    E_DB_OK			Success
**	    E_DB_ERROR			Non-catastrophic failure
**	    E_DB_FATAL			Catastrophic failure
**	Exceptions:
**	    none
**
** Side Effects:
**	    Sends output to terminal and/or log file.
**
** History:
**	19-sep-89 (andre)
**          written
*/
DB_STATUS
psq_jrel_dmp(
	PST_J_MASK	*inner_rel,
	PST_J_MASK	*outer_rel)
{
    char	    inner_buf[sizeof(PST_J_MASK) * BITSPERBYTE + 1];
    char	    outer_buf[sizeof(PST_J_MASK) * BITSPERBYTE + 1];
    register i4     i;
    register char   *cur_inner = inner_buf;
    register char   *cur_outer = outer_buf;

    for (i = sizeof(PST_J_MASK) * BITSPERBYTE - 1;
         i >= 0;
	 i--, cur_inner++, cur_outer++)
    {
	*cur_inner = (BTtest(i, (char *) inner_rel)) ? '1' : '0';
	*cur_outer = (BTtest(i, (char *) outer_rel)) ? '1' : '0';
    }
    *cur_inner = *cur_outer = '\0';

    if (TRdisplay("\n\t\t\tInner relation mask: %s", inner_buf) != TR_OK)
    {
	return (E_DB_ERROR);
    }

    if (TRdisplay("\n\t\t\tOuter relation mask: %s", outer_buf) != TR_OK)
    {
	return (E_DB_ERROR);
    }

    return (E_DB_OK);
}
Пример #2
0
/*{
** Name: opn_impliedtid	- check for implied TID join between two relations
**
** Description:
**      Check if there is an implied TID join between the two subtrees
**
** Inputs:
**      subquery                        ptr to subquery being analyzed
**      lp                              ptr to left join subtree
**      rp                              ptr to right join subtree
**      eqcls                           joining equivalence class to test
**
** Outputs:
**      lfflag                          TRUE if left subtree "rp" contains
**                                      the implicit TID
**	Returns:
**	    TRUE - if there is an implied TID join between the two relations
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**	30-may-86 (seputis)
**          initial creation
[@history_line@]...
*/
bool
opn_imtidjoin(
	OPS_SUBQUERY       *subquery,
	OPN_JTREE          *lp,
	OPN_JTREE          *rp,
	OPE_IEQCLS         eqcls,
	bool               *lfflag)
{
    OPE_EQCLIST         *eqclsp;	    /* ptr to joining equivalence class
                                            */
    OPZ_BMATTS          *amap;              /* ptr to attribute map for
                                            ** this equivalence class */
    OPZ_IATTS           attr;               /* current attribute of equivalence
                                            ** class being analyzed */
    OPZ_IATTS           maxattr;            /* number of joinop attributes
                                            ** defined */
    OPZ_AT              *abase;             /* ptr to base of array of ptrs
                                            ** to joinop attributes */
    bool                rightleaf;          /* TRUE if right subtree is a leaf
                                            */
    bool                leftleaf;           /* TRUE if left subtree is a leaf
                                            */

    eqclsp = subquery->ops_eclass.ope_base->ope_eqclist[eqcls]; /* get ptr to
                                            ** joining equivalence class */
    if (eqclsp->ope_eqctype != OPE_TID)	    /* is this a TID equivalence class*/
	return (FALSE);

    amap = &eqclsp->ope_attrmap;	    /* get equivalence class attribute
                                            ** map */
    maxattr = subquery->ops_attrs.opz_av;   /* maximum number of joinop
                                            ** attributes defined */
    abase = subquery->ops_attrs.opz_base;   /* ptr to base of array of ptrs
                                            ** to joinop attributes */
    leftleaf = (lp->opn_nleaves == 1);      /* TRUE if the left subtree is a 
                                            ** leaf */
    rightleaf = (rp->opn_nleaves == 1);     /* TRUE if the right subtree is a
                                            ** leaf */
    for (attr = -1; (attr = BTnext((i4)attr, (char *)amap, (i4)maxattr)) >= 0;)
    {
        OPZ_ATTS        *attrp;             /* ptr to joinop attribute element*/

	attrp = abase->opz_attnums[attr];
	/* if on one side the TID is implied, then on the other side
	** the TID must be explicit 
        */
	if ((*lfflag = (   (leftleaf) 
			&& BTtest((i4)attrp->opz_varnm, (char *)&lp->opn_rlmap)
			&& attrp->opz_attnm.db_att_id == DB_IMTID))
		||     (   (rightleaf)
			&& BTtest((i4)attrp->opz_varnm, (char *)&rp->opn_rlmap)
			&& attrp->opz_attnm.db_att_id == DB_IMTID))
	    return (TRUE);
    }
    return (FALSE);
}
Пример #3
0
DB_STATUS
adi_ficoerce(
ADF_CB              *adf_scb,
DB_DT_ID            adi_from_did,
DB_DT_ID            adi_into_did,
ADI_FI_ID           *adi_fid)
{
    DB_DT_ID		from_bdt = abs(adi_from_did);
    DB_DT_ID		into_bdt = abs(adi_into_did);
    DB_DT_ID		minto_bdt;
    DB_DT_ID		mfrom_bdt;
    ADI_DATATYPE	*dt;
    ADI_COERC_ENT	*cent;
    i4		        found = FALSE;

    mfrom_bdt = ADI_DT_MAP_MACRO(from_bdt);
    minto_bdt = ADI_DT_MAP_MACRO(into_bdt);

    for (;;)
    {
	/* Check the validity of the datatype ids */

        if (   (mfrom_bdt <= 0 || mfrom_bdt > ADI_MXDTS)
	    || (minto_bdt <= 0 || minto_bdt > ADI_MXDTS)
	    || Adf_globs->Adi_dtptrs[minto_bdt] == NULL
	    || (dt = Adf_globs->Adi_dtptrs[mfrom_bdt]) == NULL
	   )
	{
	    return (adu_error(adf_scb, E_AD2004_BAD_DTID, 0));
	}

	/* First check the datatypes table to see if a coercion exists */
	if (!BTtest((i4) minto_bdt, adf_scb->adf_qlang == DB_SQL
				? (char *) &dt->adi_dtcoerce_sql
				: (char *) &dt->adi_dtcoerce_quel))
	    return (adu_error(adf_scb, E_AD2009_NOCOERCION, 0));


	/* Search for the appropriate function instance id */
	for (cent = dt->adi_coerc_ents; cent->adi_from_dt == from_bdt; cent++)
	{
	    if (cent->adi_into_dt == into_bdt)
	    {
		*adi_fid = cent->adi_fid_coerc;
		found = TRUE;
		break;
	    }
	}
	break;

    }	/* end of for(;;) stmt */

    if (!found)
	return (adu_error(adf_scb, E_AD2009_NOCOERCION, 0));
    else
        return (E_DB_OK);    
}
Пример #4
0
/*{
** Name: opj_uboolfact	- copy boolean factors into all parts of the union
**
** Description:
**      A previous pass of all the subqueries determined the equivalence classes 
**      and that some conjuncts could be evaluated in the union view instead of 
**      the parent query.  This routine will attach this qualification list to
**	all parts of the union. 
**
** Inputs:
**      subquery                        ptr to beginning of union list
**
** Outputs:
**	Returns:
**	    VOID
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**      28-jun-89 (seputis)
**          initial creation
**      7-dec-93 (ed)
**          b56139 - remove unused attributes from union views, i.e.
**		create a projection
**	01-Oct-2001 (hanal04) Bug 105464 INGSRV 1517
**	    After calling opv_copytree() or opj_subboolfact() mark the
**	    target subquery as non-CNF if the source subquery was non-CNF.
[@history_template@]...
*/
VOID
opj_uboolfact(
	OPS_SUBQUERY       *subquery)
{
    OPS_SUBQUERY	*unionp;
    OPV_GRV		*grvp;
    for (unionp = subquery->ops_next; 
	(unionp->ops_sqtype == OPS_UNION)
	&&
	unionp->ops_union; 
	unionp = unionp->ops_next)
	;				/* search for the first union
					** element of the list */
    subquery = unionp;
    grvp = subquery->ops_global->ops_rangetab.opv_base->opv_grv[subquery->ops_gentry];
    if ((   !subquery->ops_sunion.opu_qual 
	    &&
	    !(grvp->opv_gmask & OPV_UVPROJECT)
	)
	|| 
	(subquery->ops_sunion.opu_mask & OPU_PROCESSED))
	return;				/* return if no qualifications
					** exist to propagate */
    subquery->ops_sunion.opu_mask |= OPU_PROCESSED;

    for (; unionp; unionp=unionp->ops_union)
    {
	PST_QNODE	*qual;

	qual = subquery->ops_sunion.opu_qual;
	if (qual)
	{
	    PST_QNODE	**qualpp;
	    if (unionp->ops_union)
		/* if there is still another union then make a copy
		** of the query tree, so that the original can be used
		** for subsequent unions */
		opv_copytree(subquery->ops_global, &qual);
	    qualpp = opj_subboolfact(unionp, &qual);
	    unionp->ops_sunion.opu_mask |= OPU_EQCLS;
	    if(subquery->ops_mask & OPS_NONCNF)
	    {
		unionp->ops_mask |= OPS_NONCNF;
	    }
#if 0
	    /* cannot assign equivalence classes here since the ope_ebase array
	    ** does not have any more room */
	    ope_aseqcls(unionp, (OPE_BMEQCLS *) NULL, qual); /* assign
					** equivalence classes for vars in
					** the qualification which have not
					** been assigned yet
					*/
#endif
	    *qualpp = unionp->ops_root->pst_right;
	    unionp->ops_root->pst_right = qual;
	}
	unionp->ops_gentry = subquery->ops_gentry;
	if (grvp->opv_gmask & OPV_UVPROJECT)
	{   /* remove attributes from target list which are not needed
	    ** by parent query */
	    PST_QNODE	**qnodepp;	/* used to traverse resdom list
					** to create projection */
	    PST_QNODE	*savep;		/* save at least one resdom in
					** case entire list is eliminated */
	    bool	insert_resdom;	/* TRUE if all resdoms are eliminated */
	    savep = NULL;
	    insert_resdom = TRUE;
	    for (qnodepp = &unionp->ops_root->pst_left; 
		(*qnodepp) && ((*qnodepp)->pst_sym.pst_type == PST_RESDOM);)
	    {
		if ((*qnodepp)->pst_sym.pst_value.pst_s_rsdm.pst_rsflags&PST_RS_PRINT
		    &&
		    !BTtest((i4)(*qnodepp)->pst_sym.pst_value.pst_s_rsdm.pst_rsno,
			(char *)grvp->opv_attrmap))
		{
		    savep = *qnodepp;
		    *qnodepp = (*qnodepp)->pst_left;	/* eliminate unneeded
					    ** resdom */
		}
		else
		{
		    qnodepp = &(*qnodepp)->pst_left; /* resdom needed by parent */
		    insert_resdom = FALSE;
		}
	    }
	    if (insert_resdom && savep)
	    {	/* make sure at least one printing resdom remains */
		savep->pst_left = unionp->ops_root->pst_left;
		unionp->ops_root->pst_left = savep;
		savep->pst_right = opv_i1const(subquery->ops_global, 0);
		STRUCT_ASSIGN_MACRO(savep->pst_right->pst_sym.pst_dataval, 
		    savep->pst_sym.pst_dataval); /* create i1 constant since only
					    ** a place holder is needed and the
					    ** parent query does not reference
					    ** this attribute so change it to a
					    ** one byte integer constant */
		savep->pst_sym.pst_dataval.db_data = NULL;
	    }
	}
    }
}
Пример #5
0
/*{
** Name: opj_union	- optimize unions
**
** Description:
**      The routine determines which qualifications of a parent query 
**      can be copied to the union, so that the union can be more restrictive 
**      or partitions of the union can be eliminated entirely in some cases. 
**
** Inputs:
**      subquery                        ptr to subquery be analyzed
**
** Outputs:
**	Returns:
**	    VOID
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**      21-jun-89 (seputis)
**          initial creation
**	01-Oct-2001 (hanal04) Bug 105464 INGSRV 1517
**	    After calling opv_copytree() mark the target subquery
**	    as non-CNF if the source suquery was non-CNF.
**	11-may-05 (inkdo01)
**	    Don't copy ON clause BFs to union subqueries - fixes 114493.
**	20-apr-07 (hayke02)
**	    Limit the fix for bug 114493 (test for a OPL_NOOUTER pst_joinid)
**	    to those subqueries containing outer joins, otherwise pst_joinid
**	    will be PST_NOJOIN (0). This change fixes bug 118088.
**      27-mar-2009 (huazh01)
**          don't copy OPZ_FANUM, OPZ_SNUM, OPZ_CORRELATED attribute into 
**          'unionbm'. Doing so will cause opj_utree() to convert the  
**          union_subquery->ops_sunion.opu_qual qualification into a 
**          PST_QNODE node containing negative attribute id, and causes 
**          E_OP0384 error. (bug 121602)
[@history_template@]...
*/
VOID
opj_union(
	OPS_SUBQUERY       *subquery)
{
    OPV_IVARS	    lvar;
    for (lvar = subquery->ops_vars.opv_prv; --lvar>=0;)
    {	/* look at the union views referenced in this subquery */
	OPV_VARS    *unionp;
	unionp = subquery->ops_vars.opv_base->opv_rt[lvar];
	if (unionp->opv_grv 
	    && 
	    (unionp->opv_grv->opv_created == OPS_VIEW)
	    &&
	    unionp->opv_grv->opv_gquery
	    &&
	    unionp->opv_grv->opv_gquery->ops_union
	    )
	{   /* a union view has been found, so search the list of boolean
	    ** factors to determine which terms can be placed into the qualifications
	    ** of the union */
	    OPE_BMEQCLS	    unionbm;	/* bitmap of the equivalence classes which
					** are available in the union view */
            OPS_SUBQUERY    *union_subquery;
	    OPB_BMBF	    bfbm;
	    OPB_IBF	    maxbf;	/* current boolean factor number */
	    		    
	    maxbf = BITS_IN(bfbm);
	    union_subquery = unionp->opv_grv->opv_gquery;

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

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

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

		current = 0;
		qualpp = &union_subquery->ops_sunion.opu_qual;
		for (bfno = -1; (bfno = BTnext((i4)bfno, (char *)&bfbm, (i4)maxbf)) >= 0;)
		{
		    while ((current < bfno) && *qualpp)
		    {	/* find next qualification to eliminate */
			current++;
			qualpp = &(*qualpp)->pst_right;
		    }
		    if (*qualpp)
			*qualpp = (*qualpp)->pst_right; /* remove this qual since
					    ** no match was found for this subquery */
		    else
			break;		    /* end of qual list reached */
		}
	    }
	    else
	    {	/* this is the first qualification in the list, so it can be applied */
		union_subquery->ops_sunion.opu_mask |= OPU_QUAL;
		if (union_subquery->ops_sunion.opu_qual)
		    opj_utree(subquery, union_subquery->ops_sunion.opu_qual, lvar); /* 
					    ** convert the 
					    ** qualification to reference var nodes
					    ** of the union view, so that comparisons
					    ** can be made later */
	    }
	}
    }
}
Пример #6
0
/*{
** Name: opv_agrv	- allocate new global range variable
**
** Description:
**	Find a free slot in the global range table for an 
**      aggregate function, or implicitly referenced index.  
**      If there are no free slots for the aggregate function, then the 
**      optimization is aborted and an error reported.  There will be
**      one global range table per optimization and there will be no
**      overlapping of range table assignments i.e. it is conceivable
**      the two temporary relations could use the same range table
**      entry since they do not exist at the same... this will not be
**      done.
**
** Inputs:
**      global				ptr to global state variable
**      name                            ptr to table name
**                                      NULL- indicates a temporary table
**      owner                           ptr to owner name
**      abort                           TRUE if optimization should be aborted
**                                      in case of error
**
** Outputs:
**	Returns:
**	    - index into global range table representing the allocated
**          variable
**	Exceptions:
**	    Will generate an internal exception if the global range table
**          is full.  This will abort the query and report an error.
**
** Side Effects:
**	    none
**
** History:
**	7-apr-86 (seputis)
**          initial creation
**	11-apr-91 (seputis)
**	    ask for RDF info if name, or table ID is given so
**	    that explicit secondary index substitution can get
**	    histograms
**	18-sep-92 (ed)
**	    bug 44850 - added parameter to allow multi-to-one mapping
**	    so a common aggregate temp can be used
**	17-Jan-2004 (schka24)
**	    Rename RDR_BLD_KEY to RDR_BLD_PHYS, gives us partition info too.
[@history_line@]...
*/
OPV_IGVARS
opv_agrv(
	OPS_STATE          *global,
	DB_TAB_NAME        *name,
	DB_OWN_NAME        *owner,
	DB_TAB_ID          *table_id,
	OPS_SQTYPE         sqtype,
	bool               abort,
	OPV_GBMVARS        *gbmap,
	OPV_IGVARS	   gvarno)
{
    OPV_IGVARS		grv_index; /* index into global range table */
    OPV_IGVARS		empty_index; /* index into global range table of
				    ** free element */
    OPV_GRT             *gbase;    /* ptr to base of array of ptrs to global
				   ** range table elements */
    bool		lookup;    /* look for existing definition if
                                   ** names are available */

    lookup = name && owner;	   /* TRUE - if RDF table ID given */
    empty_index = OPV_NOGVAR;
    gbase = global->ops_rangetab.opv_base;
    for ( grv_index = 0; grv_index < OPV_MAXVAR; grv_index++)
    {
	OPV_GRV		*existing_var;
	if (!(existing_var = gbase->opv_grv[grv_index]))
	{
	    if (empty_index == OPV_NOGVAR)
	    {
		empty_index = grv_index;
		if (!lookup)
		    break;	    /* empty slot found and we do not need
				    ** to continue searching for an existing
                                    ** table entry of the same name */
	    }
	}
	else
	{
	    if (lookup
		&&
		existing_var->opv_relation
		&&
		existing_var->opv_relation->rdr_rel
		&&
		(   existing_var->opv_relation->rdr_rel->tbl_name.db_tab_name[0]
		    ==
		    name->db_tab_name[0]
		)
		&&
		(  existing_var->opv_relation->rdr_rel->tbl_owner.db_own_name[0]
		    ==
		    owner->db_own_name[0]
		)
		&&
		!MEcmp((PTR)&existing_var->opv_relation->rdr_rel->tbl_name,
		    (PTR)name, sizeof(*name))
		&&
		!MEcmp((PTR)&existing_var->opv_relation->rdr_rel->tbl_owner,
		    (PTR)owner, sizeof(*owner))
		&&
		(   !gbmap
		    ||
		    !BTtest((i4)grv_index, (char *)gbmap)  /* do not use the
						** same global range variable
                                                ** in the same subquery twice
                                                ** or OPC will complain */
		)
	       )
	    {	/* a match has been found */
		if (gbmap)
		    BTset((i4)grv_index, (char *)gbmap); /* set the global
						** bit map so that this
                                                ** range variable is not
                                                ** reused in the same subquery
                                                */
		return(grv_index);
	    }
	}
    }

    if (empty_index != OPV_NOGVAR)
    {
	if (gvarno == OPV_NOGVAR)
	{
	    /* empty slot found - allocate and initialize slot and return     */
	    OPV_GRV       *grv;     /* pointer to global range table element  */
	    RDF_CB	      *rdfcb;

	    rdfcb = &global->ops_rangetab.opv_rdfcb;
	    if (name || table_id)
	    {   /* if name is available then table ID might be available */
		if (table_id)
		{	/* use table ID if available */
		    STRUCT_ASSIGN_MACRO((*table_id), rdfcb->rdf_rb.rdr_tabid); /*
					** need table name */
		    rdfcb->rdf_rb.rdr_types_mask = RDR_RELATION | 
			RDR_ATTRIBUTES | RDR_BLD_PHYS; /*get relation info 
					    ** - The optimizer uses attribute
					    ** info in query tree directly 
					    ** but it is needed to be requested
					    ** since the RDF uses attr info to
					    ** build RDR_BLK_PHYS info.  The
					    ** attribute info does not need to
					    ** be requested if RDF is changed.*/
		}
		else
		{	/* table ID not available so use name */
		    MEfill( (i4)sizeof(DB_TAB_ID), (u_char)0, 
			(PTR)&rdfcb->rdf_rb.rdr_tabid);
		    rdfcb->rdf_rb.rdr_types_mask = RDR_RELATION | 
			RDR_ATTRIBUTES | RDR_BLD_PHYS | RDR_BY_NAME;
		    STRUCT_ASSIGN_MACRO((*name), rdfcb->rdf_rb.rdr_name.rdr_tabname);/* need
					    ** table name */
		    STRUCT_ASSIGN_MACRO((*owner), rdfcb->rdf_rb.rdr_owner);/* 
					    ** need table owner */
		}
	    }
	    else
		rdfcb->rdf_info_blk = NULL; /* get new ptr to info
					    ** associated with global var */

	    /* allocate and initialize global range table element */
	    if (opv_parser(global, empty_index, sqtype, 
		(name != NULL) || (table_id != NULL), /* TRUE - if rdf info needs to be retrieved */ 
		FALSE,		/* TRUE - if this is a parser range table element */
		abort)		/* TRUE - if error occurs then otherwise FALSE
				    ** means return varinit == TRUE */
		)
		return(OPV_NOGVAR);	/* ignore variable if error occurs */

	    grv = gbase->opv_grv[empty_index]; /* get ptr to element */
	    grv->opv_qrt = OPV_NOGVAR; /* indicates that this table was not
				    ** explicitly referenced in the query */
	    grv->opv_relation = rdfcb->rdf_info_blk; /* save ptr to RDF info */
	}
	else
	{
	    gbase->opv_grv[empty_index] = gbase->opv_grv[gvarno]; /* used
					** for aggregate temporaries which
					** require "2 cursors" */
	}
	if (gbmap)
	    BTset((i4)empty_index, (char *)gbmap); /* set the global
				    ** bit map so that this
				    ** range variable is not
				    ** reused in the same subquery
				    */
    }
    else if (abort)
	/* the entire table is full so report and error */
	opx_error(E_OP0005_GRANGETABLE);
    return (empty_index);	    /* return with no range table entry */
}
Пример #7
0
VOID
opc_querycomp(
		OPS_STATE          *global)
{
    DB_STATUS	    	ret;

    global->ops_gmask |= OPS_OPCEXCEPTION;  /* mark facility as being in OPC */
#ifdef OPT_F033_OPF_TO_OPC
    if (opt_strace(global->ops_cb, OPT_F033_OPF_TO_OPC) == TRUE)
    {
	char	temp[OPT_PBLEN + 1];
	bool	init = 0;

	if (global->ops_cstate.opc_prbuf == NULL)
	{
	    global->ops_cstate.opc_prbuf = temp;
	    init++;
	}

	/* Trace all of 'global' */
        if (global->ops_statement != NULL)
        {
	    opt_state(global);
	}
	    
	if (init)
	{
	    global->ops_cstate.opc_prbuf = NULL;
	}
    }
#endif

    if ( opt_strace(global->ops_cb, OPT_F071_QEP_WITHOUT_COST ) == TRUE && global->ops_subquery)
    {
	opt_cotree_without_stats( global );
    }

    /* If this is CREATE TABLE, check for primary, unique, foreign key
    ** constraints to use for default base table structure. */
    if (global->ops_statement &&
	global->ops_statement->pst_type == PST_CREATE_TABLE_TYPE &&
	global->ops_statement->pst_specific.pst_createTable.
			pst_createTableFlags == PST_CRT_TABLE)
    {
	QEU_CB *qeucb = global->ops_statement->pst_specific.pst_createTable.pst_createTableQEUCB;
	DMU_CB *dmucb = (DMU_CB *) qeucb->qeu_d_cb;
	bool checkit = FALSE;

	if (BTtest(DMU_AUTOSTRUCT, dmucb->dmu_chars.dmu_indicators))
	    checkit = (dmucb->dmu_chars.dmu_flags & DMU_FLAG_AUTOSTRUCT) != 0;
	else
	    checkit = opt_strace(global->ops_cb, OPT_F084_TBLAUTOSTRUCT ) ||
			global->ops_cb->ops_alter.ops_autostruct != 0;
	if (checkit)
	    opc_checkcons(global->ops_statement, dmucb);
    }

    /* On entry for rule processing, assume ops_qpinit == TRUE. There	    */
    /* is no need to allocate a memory stream since we are contuing	    */
    /* processing on the QP that was started by the triggering statement. */
    if (global->ops_qpinit == FALSE)
    {
	/* First, lets open the stack ULM memory stream that OPC uses */
	opu_Osmemory_open(global);

	/* Tell QSF that we want to store an object; */
	global->ops_qsfcb.qsf_obj_id.qso_type = QSO_QP_OBJ;
	if (global->ops_procedure->pst_flags & PST_REPEAT_DYNAMIC)
	{
	    char	*p;

	    global->ops_qsfcb.qsf_obj_id.qso_lname = sizeof(DB_CURSOR_ID) + sizeof(i4);
	    MEfill(sizeof(global->ops_qsfcb.qsf_obj_id.qso_name), 0,
		   global->ops_qsfcb.qsf_obj_id.qso_name);
	    MEcopy((PTR)&global->ops_procedure->pst_dbpid.db_cursor_id[0],
		   sizeof (global->ops_procedure->pst_dbpid.db_cursor_id[0]),
		   (PTR)global->ops_qsfcb.qsf_obj_id.qso_name);
	    p = (char *) global->ops_qsfcb.qsf_obj_id.qso_name + 2*sizeof(i4);
	    if (global->ops_caller_cb->opf_locator)
		MEcopy((PTR)"ql", sizeof("ql"), p);
	    else MEcopy((PTR)"qp", sizeof("qp"), p);
	    p = (char *) global->ops_qsfcb.qsf_obj_id.qso_name + sizeof(DB_CURSOR_ID);
	    I4ASSIGN_MACRO(global->ops_caller_cb->opf_udbid, *(i4 *) p); 


	}
	else if (   global->ops_procedure->pst_isdbp == TRUE
	    || (   global->ops_qheader != NULL
		&& (global->ops_qheader->pst_mask1 & PST_RPTQRY)
	       )
	   )
	{
	    global->ops_qsfcb.qsf_obj_id.qso_lname = 
		sizeof (global->ops_procedure->pst_dbpid);
	    MEcopy((PTR)&global->ops_procedure->pst_dbpid, 
		   sizeof (global->ops_procedure->pst_dbpid),
		   (PTR)global->ops_qsfcb.qsf_obj_id.qso_name);
	}
	else
	{
	    global->ops_qsfcb.qsf_obj_id.qso_lname = 0;
	}

	/* Also allow for the case where a concurrent clash causes a new
	** object at an awkward point */
	if ((ret = qsf_call(QSO_CREATE, &global->ops_qsfcb)) != E_DB_OK &&
	    !(ret == E_DB_ERROR &&
		global->ops_qsfcb.qsf_error.err_code == E_QS001C_EXTRA_OBJECT) &&
	    !((global->ops_procedure->pst_flags & PST_SET_INPUT_PARAM)  &&
	     global->ops_qsfcb.qsf_error.err_code == E_QS001C_EXTRA_OBJECT))
	{
	    /* if object exists and we have a named query plan. */
	    if (global->ops_qsfcb.qsf_error.err_code
		    == E_QS000A_OBJ_ALREADY_EXISTS
	       )
	    {
		/* Log query info */
		QSO_OBID    *obj = &global->ops_qsfcb.qsf_obj_id;
		char	    *qrytype;
		char	    *objtype;
		char	    *objname;
		char	    *qrytext;
		char	    tmp[(DB_OWN_MAXNAME + DB_CURSOR_MAXNAME)  + 3 + 1];
		DB_STATUS   status;
		QSF_RCB	    qsf_rb;
		PSQ_QDESC   *qdesc;

		if (global->ops_procedure->pst_isdbp == TRUE)
		    qrytype = "database procedure";
		else if (global->ops_qheader != NULL
			 && (global->ops_qheader->pst_mask1 & PST_RPTQRY)
			)
		    qrytype = "repeat query";
		else
		    qrytype = "non-repeat query";

		objtype = "QSO_QP_OBJ";

		if (obj->qso_lname == 0)
		{
		    objname = "QSF object has no name";
		}
		else
		{
		    char	    fmt[30];
		    DB_CURSOR_ID    *curid;
		    char	    *user;
		    i4		    *dbid;

		    curid = (DB_CURSOR_ID *)obj->qso_name;
		    user = curid->db_cur_name + DB_CURSOR_MAXNAME;
		    dbid = (i4 *)(user + DB_OWN_MAXNAME);

		    STprintf(fmt, ":%%lx:%%lx:%%.%ds:%%.%ds:%%lx:",
			DB_CURSOR_MAXNAME, DB_OWN_MAXNAME);

		    STprintf(tmp, fmt, (i4)curid->db_cursor_id[0], 
			(i4)curid->db_cursor_id[1],
			curid->db_cur_name, user, (i4)(*dbid));

		    objname = tmp;
		}
 
		qsf_rb.qsf_type = QSFRB_CB;
		qsf_rb.qsf_ascii_id = QSFRB_ASCII_ID;
		qsf_rb.qsf_length = sizeof(qsf_rb);
		qsf_rb.qsf_owner = (PTR)DB_OPF_ID;
		qsf_rb.qsf_obj_id.qso_handle = global->ops_caller_cb->opf_thandle;

		qrytext = "Query text was not available.";

		if (qsf_rb.qsf_obj_id.qso_handle != NULL)
		{
		    status = qsf_call(QSO_INFO, &qsf_rb);

		    if (DB_SUCCESS_MACRO(status))
		    {
			qdesc = (PSQ_QDESC*) qsf_rb.qsf_root;

			qrytext = qdesc->psq_qrytext;
		    }
		}

		/* log an error */
		opx_lerror((OPX_ERROR)E_OP089F_QSF_FAILCREATE, (i4)4,
		    (PTR)qrytype, (PTR)objtype, (PTR)objname, (PTR)qrytext);
	    }

	    opx_verror(ret, E_OP0882_QSF_CREATE, 
		global->ops_qsfcb.qsf_error.err_code);
	}

	/* Put the handle for the QEP into the callers CB; 
	** - will be used for deallocation in case of an error
	** - both the object id and the lock id are needed in order to destroy
	** the object
	*/
	STRUCT_ASSIGN_MACRO(global->ops_qsfcb.qsf_obj_id,
	    global->ops_caller_cb->opf_qep);
	global->ops_qplk_id = global->ops_qsfcb.qsf_lk_id;
	global->ops_qpinit = TRUE;

	/* Allocate and initialize the QP. */
	opc_iqp_init(global);
    }

    /* Continue the QP compilation by adding the current statement */
    if (global->ops_statement != NULL)
    {
	opc_cqp_continue(global);
    }

    /* if it's time to stop compiling the query, then lets close stuff. */
    /* The caller is responsible for making one additional call to OPC	    */
    /* with ops_statement == NULL after all statements in the QP we are	    */
    /* currently building have been compiled. Note that this is a change    */
    /* from the previous version of this routine which required the extra   */
    /* call only if a db procedure was being compiled. Such a call must	    */
    /* also be made after the last statement in each rule list. This allows */
    /* OPC to link all conditionals statements in the rule list together    */
    /* before continuing with the next user statement to be compiled. */
    if (global->ops_statement == NULL)
    {
	/* We're finished compiling all of the statements, so lets finish
	** the QP
	*/
	opc_fqp_finish(global);

	/* The QP is only associated with the outer query, not a rule list  */
	if (!global->ops_inAfterRules && !global->ops_inBeforeRules)
	{
	    /* Tell QSF what the root of the QEP is; */
	    global->ops_qsfcb.qsf_root = (PTR) global->ops_cstate.opc_qp;
	    if ((ret = qsf_call(QSO_SETROOT, &global->ops_qsfcb)) != E_DB_OK)
	    {
		opx_verror(ret, E_OP0883_QSF_SETROOT, 
					global->ops_qsfcb.qsf_error.err_code);
	    }

	    if ((ret = qsf_call(QSO_UNLOCK, &global->ops_qsfcb)) != E_DB_OK)
	    {
		opx_verror(ret, E_OP089E_QSF_UNLOCK, 
		    global->ops_qsfcb.qsf_error.err_code);
	    }

	    /* Now lets close the stack ULM memory stream that OPC used */
	    opu_Csmemory_close(global);
	}
    }
    global->ops_gmask &= (~OPS_OPCEXCEPTION);  /* mark facility as leaving OPC */
}
Пример #8
0
/*{
** Name: psq_crdump	- Dump cursor control block given cursor and session ids
**
**  INTERNAL PSF call format: status = psq_crdump(&psq_cb, &sess_cb);
**
**  EXTERNAL call format:    status = psq_call(PSQ_CURDUMP, &psq_cb, &sess_cb);
**
** Description:
**      The psq_crdump function will format and print a cursor control block
**	given the cursor id and the session id that identify it.  The output
**	will go to the output terminal and/or file named by the user in the
**	"SET TRACE TERMINAL" and "SET TRACE OUTPUT" commands.
**
** Inputs:
**      psq_cb
**          .psq_cursid                 Cursor id
**	sess_cb				Pointer to session control block
**					(Can be NULL)
**
** Outputs:
**	psq_cb
**	    .psq_error			Error information
**		.err_code		    What error occurred
**		    E_PS0000_OK			Success
**		    E_PS0002_INTERNAL_ERROR	Internal inconsistency in PSF
**		    E_PS0205_SRV_NOT_INIT	Server not initialized
**		    E_PS0401_CUR_NOT_FOUND	Cursor not found
**	Returns:
**	    E_DB_OK			Function completed normally.
**	    E_DB_WARN			Function completed with warning(s)
**	    E_DB_ERROR			Function failed; non-catastrophic error
**	    E_DB_FATAL			Function failed; catastrophic error
**	Exceptions:
**	    none
**
** Side Effects:
**	    Writes to output file and/or terminal as specified in the "set trace
**	    output" and "set trace terminal" commands.
**
** History:
**	02-oct-85 (jeff)
**          written
**	27-oct-88 (stec)
**	    Dump psc_iupdmap.
**	10-may-89 (neil)
**	    Tracing of new rule-related objects.
**	22-dec-92 (rblumer)
**	    Added tracing of new statement-level rules.
**	10-mar-93 (andre)
**	    dump psc_expmap
**	07-apr-93 (andre)
**	    psc_tbl_mask, psc_rchecked, psc_rules, and psc_stmt_rules have all
**	    been moved from PSC_CURBLK into PSC_TBL_DESCR.  A list of one or
**	    more PSC_TBL_DESCR structures will hang off PSC_CURBLK for 
**		updatable cursors
**	11-oct-1993 (tad)
**	    Bug #56449
**	    Changed %x to %p for pointer values.
**	15-june-06 (dougi)
**	    Add support for "before" triggers.
*/
DB_STATUS
psq_crdump(
	PSQ_CB             *psq_cb,
	PSS_SESBLK	   *sess_cb)
{
    PSC_CURBLK		*cursor;
    DB_STATUS		status;
    i4			i;
    i4			thisline;
    PSC_RESCOL		*column;
    extern PSF_SERVBLK	*Psf_srvblk;

    /*
    ** Make sure server is initialized.
    */
    if (!Psf_srvblk->psf_srvinit)
    {
	psq_cb->psq_error.err_code = E_PS0205_SRV_NOT_INIT;
	return (E_DB_ERROR);
    }

    /*
    ** Get pointer to cursor control block.
    */
    status = psq_crfind(sess_cb, &psq_cb->psq_cursid, &cursor,
	&psq_cb->psq_error);
    if (status != E_DB_OK)
	return (status);

    /*
    ** NULL means no such cursor.
    */
    if (cursor == (PSC_CURBLK *) NULL)
    {
	psq_cb->psq_error.err_code = E_PS0401_CUR_NOT_FOUND;
	return (E_DB_ERROR);
    }

    /*
    ** Now print out everything in the cursor control block.
    */

    TRdisplay("Cursor Control Block for Cursor:");
    status = psq_ciddmp(&psq_cb->psq_cursid);
    if (status != E_DB_OK)
    {
	psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR;
	return (status);
    }
    TRdisplay("\n\n");

    /* First, the control block header */
    if ((status = psq_headdmp((PSQ_CBHEAD *) cursor)) != E_DB_OK)
    {
	psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR;
	return (status);
    }

    /* The cursor id */
    TRdisplay("\tpsc_blkid:\n");
    if ((status = psq_ciddmp(&cursor->psc_blkid)) != E_DB_OK)
    {
	psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR;
	return (status);
    }

    /* Used / Not Used flag */
    TRdisplay("\tpsc_used:\t");
    if ((status = psq_booldmp(cursor->psc_used)) != E_DB_OK)
    {
	psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR;
	return (status);
    }

    TRdisplay("\n");

    /* Stream pointer */
    TRdisplay("\tpsc_stream:\t%p\n", cursor->psc_stream);

    /* Query language */
    TRdisplay("\tpsc_lang:\t(%d) ", cursor->psc_lang);
    if ((status = psq_lngdmp(cursor->psc_lang)) != E_DB_OK)
    {
	psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR;
	return (status);
    }
    TRdisplay("\n");

    /* Delete permission flag */
    TRdisplay("\tpsc_delall:\t");
    if ((status = psq_booldmp(cursor->psc_delall)) != E_DB_OK)
    {
	psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR;
	return (status);
    }
    TRdisplay("\n");

    /* For update flag */
    TRdisplay("\tpsc_forupd:\t");
    if ((status = psq_booldmp(cursor->psc_forupd)) != E_DB_OK)
    {
	psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR;
	return (status);
    }
    TRdisplay("\n");

    /* Readonly flag */
    TRdisplay("\tpsc_readonly:\t");
    if ((status = psq_booldmp(cursor->psc_readonly)) != E_DB_OK)
    {
	psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR;
	return (status);
    }
    TRdisplay("\n");

    /* Repeat cursor flag */
    TRdisplay("\tpsc_repeat:\t");
    if ((status = psq_booldmp(cursor->psc_repeat)) != E_DB_OK)
    {
	psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR;
	return (status);
    }
    TRdisplay("\n");

    /* Open flag */
    TRdisplay("\tpsc_open:\t");
    if ((status = psq_booldmp(cursor->psc_open)) != E_DB_OK)
    {
	psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR;
	return (status);
    }
    TRdisplay("\n");

    TRdisplay("\tList of descriptions of cursor's underlying table/views:\n");
    if (cursor->psc_tbl_descr_queue.q_next == &cursor->psc_tbl_descr_queue)
    {
	TRdisplay("\t\tNONE\n");
    }
    else
    {
	PSC_TBL_DESCR	    *descr, *last_descr;
	i4		    i = 0;

	descr = (PSC_TBL_DESCR *) cursor->psc_tbl_descr_queue.q_next;
	last_descr = (PSC_TBL_DESCR *) cursor->psc_tbl_descr_queue.q_prev;
	
	do
	{
	    if (i++)
		descr = (PSC_TBL_DESCR *) descr->psc_queue.q_next;

	    /* element number (starting at 1) */
	    TRdisplay("\t\telement %d:\n", i);

	    /* table/view id */
	    TRdisplay("\t\t\t\tpsc_tabid:\t(%d,%d)\n",
		descr->psc_tabid.db_tab_base, descr->psc_tabid.db_tab_index);

	    /* table mask */
	    TRdisplay("\t\t\tpsc_tbl_mask:\t0x%x\n", descr->psc_tbl_mask);

	    /* Row-level after user-defined rules */
	    TRdisplay("\t\t\tpsc_row_lvl_usr_rules:\t(address) 0x%p\n",
		descr->psc_row_lvl_usr_rules);

	    /* Row-level after system-generated rules */
	    TRdisplay("\t\t\tpsc_row_lvl_sys_rules:\t(address) 0x%p\n",
		descr->psc_row_lvl_sys_rules);

	    /* statement-level after user-defined rules */
	    TRdisplay("\t\t\tpsc_stmt_lvl_usr_rules:\t(address) 0x%p\n",
		descr->psc_stmt_lvl_usr_rules);

	    /* statement-level after system-generated rules */
	    TRdisplay("\t\t\tpsc_stmt_lvl_sys_rules:\t(address) 0x%p\n",
		descr->psc_stmt_lvl_sys_rules);

	    /* Row-level before user-defined rules */
	    TRdisplay("\t\t\tpsc_row_lvl_usr_before_rules:\t(address) 0x%p\n",
		descr->psc_row_lvl_usr_before_rules);

	    /* Row-level before system-generated rules */
	    TRdisplay("\t\t\tpsc_row_lvl_sys_before_rules:\t(address) 0x%p\n",
		descr->psc_row_lvl_sys_before_rules);

	    /* statement-level before user-defined rules */
	    TRdisplay("\t\t\tpsc_stmt_lvl_usr_before_rules:\t(address) 0x%p\n",
		descr->psc_stmt_lvl_usr_before_rules);

	    /* statement-level before system-generated rules */
	    TRdisplay("\t\t\tpsc_stmt_lvl_sys_before_rules:\t(address) 0x%p\n",
		descr->psc_stmt_lvl_sys_before_rules);

	    /* psc_flags */
	    TRdisplay("\t\t\tpsc_flags:\t");

	    TRdisplay("\t\t\t\tPSC_RULES_CHECKED:\t");
	    if ((status =
		psq_booldmp(descr->psc_flags & PSC_RULES_CHECKED)) != E_DB_OK)
	    {
		psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR;
		return (status);
	    }
	} while (descr != last_descr);
    }

    TRdisplay("\n");

    /* Now do the set of columns for update.  psc_updmap is a bit map. */
    TRdisplay("\tpsc_updmap:\t");
    thisline = 0;
    for (i = 0; i < (i4)sizeof(cursor->psc_updmap) * BITSPERBYTE; i++)
    {
	if (BTtest(i, (char *) &cursor->psc_updmap))
	{
	    TRdisplay("%d ", i);
	    thisline++;
	    /* Limit to 10 numbers per row */
	    if (thisline >= 10)
	    {
		TRdisplay("\n\t\t");
		thisline = 0;
	    }
	}
    }
    TRdisplay("\n");

    /* Now do the column set */
    TRdisplay("\tpsc_restab:\n");
    TRdisplay("\t\tpsc_tabsize: %d\n", cursor->psc_restab.psc_tabsize);
    TRdisplay("\t\tpsc_coltab:\n");
    for (i = 0; i < cursor->psc_restab.psc_tabsize; i++)
    {
	for (column = cursor->psc_restab.psc_coltab[i];
	    column != (PSC_RESCOL *) NULL;
	    column = column->psc_colnext)
	{
	    TRdisplay("\n");
	    TRdisplay("\t\t\tpsc_attname:\t%#s\n", 
		sizeof (column->psc_attname), &column->psc_attname);
	    TRdisplay("\t\t\tpsc_type:\t");
	    if ((status = psq_dtdump(column->psc_type)) != E_DB_OK)
	    {
		psq_cb->psq_error.err_code = E_PS0002_INTERNAL_ERROR;
		return (status);
	    }
	    TRdisplay("\n\t\t\tpsc_len:\t%d\n", column->psc_len);
	    TRdisplay("\t\t\tpsc_prec:\t%d\n", column->psc_prec);
	    TRdisplay("\t\t\tpsc_attid:\t%d\n", column->psc_attid.db_att_id);
	    TRdisplay("\n");
	}
    }
	
    /* Now do the internal set of columns for update,
    ** psc_iupdmap is a bit map.
    */
    TRdisplay("\tpsc_iupdmap:\t");
    thisline = 0;
    for (i = 0; i < (i4)sizeof(cursor->psc_iupdmap) * BITSPERBYTE; i++)
    {
	if (BTtest(i, (char *) &cursor->psc_iupdmap))
	{
	    TRdisplay("%d ", i);
	    thisline++;
	    /* Limit to 10 numbers per row */
	    if (thisline >= 10)
	    {
		TRdisplay("\n\t\t");
		thisline = 0;
	    }
	}
    }
    TRdisplay("\n");

    /*
    ** Now dump a map of attributes of a view on which a cursor is declared
    ** which (attributes that is) are based on an expression
    ** psc_expmap is a bit map.
    */
    TRdisplay("\tpsc_expmap:\t");
    thisline = 0;
    for (i = 0; i < (i4)sizeof(cursor->psc_expmap) * BITSPERBYTE; i++)
    {
	if (BTtest(i, (char *) &cursor->psc_expmap))
	{
	    TRdisplay("%d ", i);
	    thisline++;
	    /* Limit to 10 numbers per row */
	    if (thisline >= 10)
	    {
		TRdisplay("\n\t\t");
		thisline = 0;
	    }
	}
    }
    TRdisplay("\n");

    return    (E_DB_OK);
}
Пример #9
0
DB_STATUS
dmu_modify(DMU_CB    *dmu_cb)
{
    DM_SVCB		*svcb = dmf_svcb;
    DMU_CB		*dmu = dmu_cb;
    DML_XCB		*xcb;
    DML_ODCB		*odcb;
    DM2U_MOD_CB		local_mcb, *mcb = &local_mcb;

    i4			recovery;
    i4			truncate;
    i4			duplicates;
    i4			i,j;
    i4			indicator;
    i4			error, local_error;
    DB_STATUS		status;
    bool                bad_loc;
    i4			blob_add_extend = 0;
    bool                used_default_page_size = TRUE;
    i4			page_size;
    i4			verify_options;
    i4			mask;
    i4			has_extensions = 0;
    DB_OWN_NAME		table_owner;
    DB_TAB_NAME		table_name;
    bool		got_action;
    bool		is_table_debug;
    bool		reorg;

    CLRDBERR(&dmu->error);

    /* Any modify should make table recovery disallowed except for the
    ** alter_status options which change logical, physical consistency
    ** and table recovery bit itself
    */
    mcb->mcb_mod_options2 = DM2U_2_TBL_RECOVERY_DEFAULT;

    do
    {
	/*  Check for bad flags. */

	mask = ~(DMU_VGRANT_OK | DMU_INTERNAL_REQ | DMU_RETINTO
		| DMU_PARTITION | DMU_MASTER_OP | DMU_ONLINE_START | DMU_ONLINE_END
		| DMU_NO_PAR_INDEX | DMU_PIND_CHAINED | DMU_NODEPENDENCY_CHECK);
        if ( (dmu->dmu_flags_mask & mask) != 0)
	{
	    SETDBERR(&dmu->error, 0, E_DM001A_BAD_FLAG);
	    break;
	}

	/*  Validate the transaction id. */

	xcb = (DML_XCB *)dmu->dmu_tran_id;
	if (dm0m_check((DM_OBJECT *)xcb, (i4)XCB_CB) != E_DB_OK)
	{
	    SETDBERR(&dmu->error, 0, E_DM003B_BAD_TRAN_ID);
	    break;
	}

	/* Check for external interrupts */
	if ( xcb->xcb_scb_ptr->scb_ui_state )
	    dmxCheckForInterrupt(xcb, &error);

	if ( xcb->xcb_state )
	{
	    if (xcb->xcb_state & XCB_USER_INTR)
	    {
		SETDBERR(&dmu->error, 0, E_DM0065_USER_INTR);
		break;
	    }
	    if (xcb->xcb_state & XCB_FORCE_ABORT)
	    {
		SETDBERR(&dmu->error, 0, E_DM010C_TRAN_ABORTED);
		break;
	    }
	    if (xcb->xcb_state & XCB_ABORT)
	    {
		SETDBERR(&dmu->error, 0, E_DM0064_USER_ABORT);
		break;
	    }	    
	}

	/*  Check the database identifier. */

	odcb = (DML_ODCB *)dmu->dmu_db_id;
	if (dm0m_check((DM_OBJECT *)odcb, (i4)ODCB_CB) != E_DB_OK)
	{
	    SETDBERR(&dmu->error, 0, E_DM0010_BAD_DB_ID);
	    break;
	}

	mcb->mcb_db_lockmode = DM2T_X;

	/*  Check that this is a update transaction on the database 
        **  that can be updated. */
	if (odcb != xcb->xcb_odcb_ptr)
	{
	    SETDBERR(&dmu->error, 0, E_DM005D_TABLE_ACCESS_CONFLICT);
	    break;
	}

	/* Prime the MCB */
	mcb->mcb_dcb = odcb->odcb_dcb_ptr;
	mcb->mcb_xcb = xcb;
	mcb->mcb_tbl_id = &dmu->dmu_tbl_id;
	mcb->mcb_omcb = (DM2U_OMCB*)NULL;
	mcb->mcb_dmu = dmu;
	mcb->mcb_structure = 0;
	mcb->mcb_i_fill = 0;
	mcb->mcb_l_fill = 0;
	mcb->mcb_d_fill = 0;
	mcb->mcb_unique = FALSE;
	mcb->mcb_compressed = TCB_C_NONE;
        mcb->mcb_index_compressed = FALSE;
	mcb->mcb_temporary = FALSE;
	mcb->mcb_merge = FALSE;
	mcb->mcb_clustered = FALSE;
	mcb->mcb_modoptions = 0;
	mcb->mcb_min_pages = 0;
	mcb->mcb_max_pages = 0;
	mcb->mcb_allocation = 0;
	mcb->mcb_extend = 0;
	mcb->mcb_page_type = TCB_PG_INVALID;
	mcb->mcb_page_size = svcb->svcb_page_size;
	mcb->mcb_tup_info = &dmu->dmu_tup_cnt;
	mcb->mcb_reltups = 0;
	mcb->mcb_tab_name = &table_name;
	mcb->mcb_tab_owner = &table_owner;
	mcb->mcb_has_extensions = &has_extensions;
	mcb->mcb_relstat2 = 0;
	mcb->mcb_flagsmask = dmu->dmu_flags_mask;
	mcb->mcb_tbl_pri = 0;
	mcb->mcb_rfp_entry = (DM2U_RFP_ENTRY*)NULL;
	mcb->mcb_new_part_def = (DB_PART_DEF*)dmu->dmu_part_def;
	mcb->mcb_new_partdef_size = dmu->dmu_partdef_size;
	mcb->mcb_verify = 0;

	dmu->dmu_tup_cnt = 0;
        truncate = 0;
	reorg = FALSE;
	duplicates = -1;
	verify_options = 0;
	got_action = FALSE;

	/* FIXME better messages (in general) */
	/* If there's a partdef it has to be one-piece, else bad param */
	if (dmu->dmu_part_def != NULL
	  && dmu->dmu_part_def->ndims > 0
	  && (dmu->dmu_part_def->part_flags & DB_PARTF_ONEPIECE) == 0)
	{
	    SETDBERR(&dmu->error, 0, E_DM002A_BAD_PARAMETER);
	    break;
	}

	/* Disassemble the modify action.
	** FIXME this used to be buried in the characteristics array.
	** It would make much more sense to just carry the action
	** code through, but that will have to wait for another day.
	*/
	got_action = FALSE;
	switch (dmu->dmu_action)
	{
	case DMU_ACT_STORAGE:
	    if (BTtest(DMU_STRUCTURE, dmu->dmu_chars.dmu_indicators))
	    {
		got_action = TRUE;
		mcb->mcb_structure = dmu->dmu_chars.dmu_struct;
	    }
	    break;

	case DMU_ACT_ADDEXTEND:
	    got_action = TRUE;
	    mcb->mcb_mod_options2 |= DM2U_2_ADD_EXTEND;
	    break;

	case DMU_ACT_ENCRYPT:
	    got_action = TRUE;
	    mcb->mcb_mod_options2 |= DM2U_2_ENCRYPT;
	    break;

	case DMU_ACT_LOG_CONSISTENT:
	    if (BTtest(DMU_ACTION_ONOFF, dmu->dmu_chars.dmu_indicators))
	    {
		got_action = TRUE;
		mcb->mcb_mod_options2 &= ~DM2U_2_TBL_RECOVERY_DEFAULT;
		if ( dmu->dmu_chars.dmu_flags & DMU_FLAG_ACTON )
		    mcb->mcb_mod_options2 |= DM2U_2_LOG_CONSISTENT;
		else
		    mcb->mcb_mod_options2 |= DM2U_2_LOG_INCONSISTENT;
	    }
	    break;

	case DMU_ACT_MERGE:
	    got_action = TRUE;
	    mcb->mcb_merge = TRUE;
	    break;

	case DMU_ACT_PERSISTENCE:
	    if (BTtest(DMU_PERSISTS_OVER_MODIFIES, dmu->dmu_chars.dmu_indicators))
	    {
		got_action = TRUE;
		mcb->mcb_mod_options2 |= (dmu->dmu_chars.dmu_flags & DMU_FLAG_PERSISTENCE) ?
			DM2U_2_PERSISTS_OVER_MODIFIES :
			DM2U_2_NOPERSIST_OVER_MODIFIES;
	    }
	    break;

	case DMU_ACT_PHYS_CONSISTENT:
	    if (BTtest(DMU_ACTION_ONOFF, dmu->dmu_chars.dmu_indicators))
	    {
		got_action = TRUE;
		mcb->mcb_mod_options2 &= ~DM2U_2_TBL_RECOVERY_DEFAULT;
		if ( dmu->dmu_chars.dmu_flags & DMU_FLAG_ACTON )
		    mcb->mcb_mod_options2 |= DM2U_2_PHYS_CONSISTENT;
		else
		    mcb->mcb_mod_options2 |= DM2U_2_PHYS_INCONSISTENT;
	    }
	    break;

	case DMU_ACT_PRIORITY:
	    if (BTtest(DMU_TABLE_PRIORITY, dmu->dmu_chars.dmu_indicators))
		got_action = TRUE;
	    /* flag setting when we hit the priority char */
	    break;

	case DMU_ACT_READONLY:
	    if (BTtest(DMU_ACTION_ONOFF, dmu->dmu_chars.dmu_indicators))
	    {
		got_action = TRUE;
		if ( dmu->dmu_chars.dmu_flags & DMU_FLAG_ACTON )
		    mcb->mcb_mod_options2 |= DM2U_2_READONLY;
		else
		    mcb->mcb_mod_options2 |= DM2U_2_NOREADONLY;
	    }
	    break;

	case DMU_ACT_REORG:
	    got_action = TRUE;
	    reorg = TRUE;
	    break;

	case DMU_ACT_TABLE_RECOVERY:
	    if (BTtest(DMU_ACTION_ONOFF, dmu->dmu_chars.dmu_indicators))
	    {
		got_action = TRUE;
		mcb->mcb_mod_options2 &= ~DM2U_2_TBL_RECOVERY_DEFAULT;
		if ( dmu->dmu_chars.dmu_flags & DMU_FLAG_ACTON )
		    mcb->mcb_mod_options2 |= DM2U_2_TBL_RECOVERY_ALLOWED;
		else
		    mcb->mcb_mod_options2 |= DM2U_2_TBL_RECOVERY_DISALLOWED;
	    }
	    break;

	case DMU_ACT_TRUNC:
	    got_action = TRUE;
	    truncate++;
	    break;

	case DMU_ACT_USCOPE:
	    if (BTtest(DMU_STATEMENT_LEVEL_UNIQUE, dmu->dmu_chars.dmu_indicators))
	    {
		got_action = TRUE;
		mcb->mcb_mod_options2 |= DM2U_2_STATEMENT_LEVEL_UNIQUE;
	    }
	    break;

	case DMU_ACT_VERIFY:
	    if (BTtest(DMU_VACTION, dmu->dmu_chars.dmu_indicators))
	    {
		got_action = TRUE;
		mcb->mcb_verify = dmu->dmu_chars.dmu_vaction;
	    }
	    break;
	} /* switch */

	if (! got_action)
	{
	    SETDBERR(&dmu->error, 0, E_DM000E_BAD_CHAR_VALUE);
	    break;
	}

	/* Disassemble the characteristics.
	** FIXME probably better to just carry it through, but one step
	** at a time!
	*/
	indicator = -1;
	while ((indicator = BTnext(indicator, dmu->dmu_chars.dmu_indicators, DMU_CHARIND_LAST)) != -1)
	{
	    switch (indicator)
	    {
	    case DMU_ACTION_ONOFF:
	    case DMU_STRUCTURE:
		/* Already picked it up, just skip on */
		continue;

	    case DMU_IFILL:
		mcb->mcb_i_fill = dmu->dmu_chars.dmu_nonleaff;
		if (mcb->mcb_i_fill > 100)
		    mcb->mcb_i_fill = 100;
		continue;

	    case DMU_LEAFFILL:
		mcb->mcb_l_fill = dmu->dmu_chars.dmu_leaff;
		if (mcb->mcb_l_fill > 100)
		    mcb->mcb_l_fill = 100;
		continue;

	    case DMU_DATAFILL:
		mcb->mcb_d_fill = dmu->dmu_chars.dmu_fillfac;
		if (mcb->mcb_d_fill > 100)
		    mcb->mcb_d_fill = 100;
		continue;

	    case DMU_PAGE_SIZE:
		used_default_page_size = FALSE;
		mcb->mcb_page_size = dmu->dmu_chars.dmu_page_size;
		if (mcb->mcb_page_size != 2048   && mcb->mcb_page_size != 4096  &&
		    mcb->mcb_page_size != 8192   && mcb->mcb_page_size != 16384 &&
		    mcb->mcb_page_size != 32768  && mcb->mcb_page_size != 65536)
		{
		    SETDBERR(&dmu->error, indicator, E_DM000E_BAD_CHAR_VALUE);
		    break;
		}
		else if (!dm0p_has_buffers(mcb->mcb_page_size))
		{
		    SETDBERR(&dmu->error, 0, E_DM0157_NO_BMCACHE_BUFFERS);
		    break;
		}		    
		else
		{
		    continue;
		}

	    case DMU_MINPAGES:
		mcb->mcb_min_pages = dmu->dmu_chars.dmu_minpgs;
		continue;

	    case DMU_MAXPAGES:
		mcb->mcb_max_pages = dmu->dmu_chars.dmu_maxpgs;
		continue;

	    case DMU_UNIQUE:
		mcb->mcb_unique = TRUE;
		continue;

	    case DMU_DCOMPRESSION:
		/* Translate DMU_xxx to TCB compression types */
		if (dmu->dmu_chars.dmu_dcompress == DMU_COMP_ON)
		    mcb->mcb_compressed = TCB_C_DEFAULT;
		else if (dmu->dmu_chars.dmu_dcompress == DMU_COMP_HI)
		    mcb->mcb_compressed = TCB_C_HICOMPRESS;
		continue;

            case DMU_KCOMPRESSION:
                mcb->mcb_index_compressed =
			(dmu->dmu_chars.dmu_kcompress != DMU_COMP_OFF);
                continue;

	    case DMU_TEMP_TABLE:
		mcb->mcb_temporary = TRUE;
		continue;

	    case DMU_RECOVERY:
		recovery = (dmu->dmu_chars.dmu_flags & DMU_FLAG_RECOVERY) != 0;
		if (recovery)
		{
		    /* recovery isn't currently supported */
		    SETDBERR(&dmu->error, indicator, E_DM000D_BAD_CHAR_ID);
		    break;
		}
		continue;

	    case DMU_DUPLICATES:
		duplicates = 0;
		if (dmu->dmu_chars.dmu_flags & DMU_FLAG_DUPS)
		    duplicates = 1;
		continue;

	    case DMU_ALLOCATION:
		mcb->mcb_allocation = dmu->dmu_chars.dmu_alloc;
		continue;

	    case DMU_EXTEND:
		mcb->mcb_extend = dmu->dmu_chars.dmu_extend;
		continue;

	    case DMU_VACTION:
		/* Already got it, just skip on */
		continue;

	    case DMU_VOPTION:
		verify_options = dmu->dmu_chars.dmu_voption;
		continue;

	    case DMU_STATEMENT_LEVEL_UNIQUE:
		if (dmu->dmu_chars.dmu_flags & DMU_FLAG_UNIQUE_STMT)
		    mcb->mcb_relstat2 |= TCB_STATEMENT_LEVEL_UNIQUE;
		continue;

	    case DMU_PERSISTS_OVER_MODIFIES:
		if (dmu->dmu_chars.dmu_flags & DMU_FLAG_PERSISTENCE)
		    mcb->mcb_relstat2 |= TCB_PERSISTS_OVER_MODIFIES;
		continue;

	    case DMU_SYSTEM_GENERATED:
		mcb->mcb_relstat2 |= TCB_SYSTEM_GENERATED;
		continue;

	    case DMU_SUPPORTS_CONSTRAINT:
		mcb->mcb_relstat2 |= TCB_SUPPORTS_CONSTRAINT;
		continue;

	    case DMU_NOT_UNIQUE:
		mcb->mcb_relstat2 |= TCB_NOT_UNIQUE;
		continue;

	    case DMU_NOT_DROPPABLE:
		mcb->mcb_relstat2 |= TCB_NOT_DROPPABLE;
		continue;

	    case DMU_ROW_SEC_AUDIT:
		mcb->mcb_relstat2 |= TCB_ROW_AUDIT;
		continue;

	    case DMU_TABLE_PRIORITY:
		mcb->mcb_tbl_pri = dmu->dmu_chars.dmu_cache_priority;
		if (mcb->mcb_tbl_pri < 0 || mcb->mcb_tbl_pri > DB_MAX_TABLEPRI)
		{
		    SETDBERR(&dmu->error, indicator, E_DM000E_BAD_CHAR_VALUE);
		    break;
		}
		/*
		** DMU_TABLE_PRIORITY    is set if priority came from WITH clause.
		** DMU_TO_TABLE_PRIORITY is set if priority came from MODIFY TO clause.
		*/
		if (dmu->dmu_action != DMU_ACT_PRIORITY)
		    mcb->mcb_mod_options2 |= DM2U_2_TABLE_PRIORITY;
		else
		    mcb->mcb_mod_options2 |= DM2U_2_TO_TABLE_PRIORITY;
		continue;

	    case DMU_BLOBEXTEND:
		blob_add_extend = dmu->dmu_chars.dmu_blobextend;
		continue;

	    case DMU_CLUSTERED:
		mcb->mcb_clustered = (dmu->dmu_chars.dmu_flags & DMU_FLAG_CLUSTERED) != 0;
		continue;

	    case DMU_CONCURRENT_UPDATES:
		/* Translate from PSF flag to DMU internal flag */
		if (dmu->dmu_chars.dmu_flags & DMU_FLAG_CONCUR_U)
		    mcb->mcb_flagsmask |= DMU_ONLINE_START;
		continue;

	    default:
		/* Ignore anything else, might be for CREATE, who knows */
		continue;
	    }
	    break;
	}

	/*
	** If no page size specified, set page_size to zero
	** In this case the current page size will be used
	*/
	if (used_default_page_size)
	    mcb->mcb_page_size = 0;

	/* Save a local copy for dmpe_modify, since dm2u_modify can alter mcb */
	page_size = mcb->mcb_page_size;

	if (mcb->mcb_structure == TCB_HEAP)
	{
	    if (mcb->mcb_d_fill == 0)
		mcb->mcb_d_fill = DM_F_HEAP;
	}
	else if (mcb->mcb_structure == TCB_ISAM)
	{
	    if (mcb->mcb_i_fill == 0)
		mcb->mcb_i_fill = DM_FI_ISAM;
	    if (mcb->mcb_d_fill == 0)
	    {
		if (mcb->mcb_compressed != TCB_C_NONE)
		    mcb->mcb_d_fill = DM_F_CISAM;
		else
		    mcb->mcb_d_fill = DM_F_ISAM;
	    }
	}
	else if (mcb->mcb_structure == TCB_HASH)
	{
	    if (mcb->mcb_d_fill == 0)
	    {
		if (mcb->mcb_compressed != TCB_C_NONE)
		    mcb->mcb_d_fill = DM_F_CHASH;
		else
		    mcb->mcb_d_fill = DM_F_HASH;
	    }
	    if (mcb->mcb_min_pages == 0)
	    {
		if (mcb->mcb_compressed != TCB_C_NONE)
		    mcb->mcb_min_pages = 1;
		else
		    mcb->mcb_min_pages = 10;

		/* If user specified max pages, don't set minpages higher */
		if (mcb->mcb_min_pages > mcb->mcb_max_pages && mcb->mcb_max_pages != 0)
		    mcb->mcb_min_pages = mcb->mcb_max_pages;
	    }
	    if (mcb->mcb_max_pages == 0)
		mcb->mcb_max_pages = 8388607;
	}
	else if (mcb->mcb_structure == TCB_BTREE || mcb->mcb_merge)
	{
            if (DMZ_AM_MACRO(16) && !mcb->mcb_temporary)
            {
                /* DM616 -- forces index compression to be used: */
                mcb->mcb_index_compressed = TRUE;
            }

	    if (mcb->mcb_i_fill == 0)
		mcb->mcb_i_fill = DM_FI_BTREE;
	    if (mcb->mcb_l_fill == 0)
		mcb->mcb_l_fill = DM_FL_BTREE;
	    if (mcb->mcb_d_fill == 0)
	    {
		if (mcb->mcb_compressed != TCB_C_NONE)
		    mcb->mcb_d_fill = DM_F_CBTREE;
		else
		    mcb->mcb_d_fill = DM_F_BTREE;
	    }
	}
	else if (truncate)
	{
	    if (mcb->mcb_d_fill == 0)
		mcb->mcb_d_fill = DM_F_HEAP;
	}

	if (mcb->mcb_structure == TCB_HASH && mcb->mcb_min_pages > mcb->mcb_max_pages)
	{
	    SETDBERR(&dmu->error, 0, E_DM000D_BAD_CHAR_ID);
	    break;
	}

	mcb->mcb_kcount = dmu->dmu_key_array.ptr_in_count;
	mcb->mcb_key = (DMU_KEY_ENTRY**) dmu->dmu_key_array.ptr_address;
	if (mcb->mcb_kcount && (mcb->mcb_key == (DMU_KEY_ENTRY**)NULL ||
              dmu->dmu_key_array.ptr_size != sizeof(DMU_KEY_ENTRY)))
	{
	    SETDBERR(&dmu->error, 0, E_DM002A_BAD_PARAMETER);
	    break;
	}

	if (truncate)
	{
	    mcb->mcb_kcount = 0;
	    mcb->mcb_modoptions |= DM2U_TRUNCATE;
	}
	if (duplicates == 1)
	    mcb->mcb_modoptions |= DM2U_DUPLICATES;
	else if (duplicates == 0)
	    mcb->mcb_modoptions |= DM2U_NODUPLICATES;
	/* else duplicates == -1, set neither flag */
	if (reorg)
	    mcb->mcb_modoptions |= DM2U_REORG;

	/* CLUSTERED implies and requires Unique */
	if ( mcb->mcb_clustered && mcb->mcb_structure == TCB_BTREE )
	    mcb->mcb_unique = TRUE;
	else
	    mcb->mcb_clustered = FALSE;


	if (mcb->mcb_verify)
	{
	    if (verify_options == 0)
	    {
		/*  Apply defaults. */

		switch (mcb->mcb_verify)
		{
		case DMU_V_VERIFY:
		    verify_options = DMU_T_LINK | DMU_T_RECORD | DMU_T_ATTRIBUTE;
		    break;

		case DMU_V_REPAIR:
		case DMU_V_DEBUG:
		    verify_options = DMU_T_BITMAP;
		    break;

		case DMU_V_PATCH:
		case DMU_V_FPATCH:
		    break;
		}
	    }
	    /* Shift modifiers into place */
	    mcb->mcb_verify |= (verify_options << DM1U_MODSHIFT);
	}
	is_table_debug = ((mcb->mcb_verify & DM1U_OPMASK) == DM1U_DEBUG);

	/* Check the location names for duplicates, too many. */

	mcb->mcb_location = (DB_LOC_NAME*)NULL;
	mcb->mcb_l_count = 0;
	if (dmu->dmu_location.data_address && 
	    (dmu->dmu_location.data_in_size >= sizeof(DB_LOC_NAME)) &&
	    mcb->mcb_temporary == FALSE)
	{
	    mcb->mcb_location = (DB_LOC_NAME *) dmu->dmu_location.data_address;
	    mcb->mcb_l_count = dmu->dmu_location.data_in_size/sizeof(DB_LOC_NAME);
	    if (mcb->mcb_l_count > DM_LOC_MAX)
	    {
		SETDBERR(&dmu->error, 0, E_DM0071_LOCATIONS_TOO_MANY);
		break;
	    }
	    bad_loc = FALSE;
	    for (i = 0; i < mcb->mcb_l_count; i++)
	    {
		for (j = 0; j < i; j++)
		{
		    /* 
                    ** Compare this location name against other 
                    ** already given, they cannot be the same.
                    */

		    if (MEcmp(mcb->mcb_location[j].db_loc_name,
                              mcb->mcb_location[i].db_loc_name,
			      sizeof(DB_LOC_NAME)) == 0 )
		    {
			SETDBERR(&dmu->error, i, E_DM001E_DUP_LOCATION_NAME);
			bad_loc = TRUE;
			break;
		    }	    	    
		}
		if (bad_loc == TRUE)
		    break;		    
	    }

	    if (bad_loc == TRUE)
		break;
	}
	else
	{
	    /* There must a location list if you are reorganizing
            ** to a different number of locations.
            */
	    if (reorg)
	    {
		if (dmu->dmu_location.data_address &&
					    dmu->dmu_location.data_in_size)
		    SETDBERR(&dmu->error, 0, E_DM001F_LOCATION_LIST_ERROR);
		else
		    SETDBERR(&dmu->error, 0, E_DM0072_NO_LOCATION);
		break;	    
	    }
	}

	mcb->mcb_partitions = (DMU_PHYPART_CHAR*)NULL;
	mcb->mcb_nparts = 0;
	if ( dmu->dmu_ppchar_array.data_address && 
	     dmu->dmu_ppchar_array.data_in_size >= sizeof(DMU_PHYPART_CHAR) )
	{
	    mcb->mcb_partitions = (DMU_PHYPART_CHAR*)dmu->dmu_ppchar_array.data_address;
	    mcb->mcb_nparts = dmu->dmu_ppchar_array.data_in_size
			/ sizeof(DMU_PHYPART_CHAR);
	}
    
	if ((xcb->xcb_x_type & XCB_RONLY) && !is_table_debug)
	{
	    SETDBERR(&dmu->error, 0, E_DM006A_TRAN_ACCESS_CONFLICT);
	    break;
	}

	/*
	** If this is the first write operation for this transaction,
	** then we need to write the begin transaction record.
	*/
	if ((xcb->xcb_flags & XCB_DELAYBT) != 0 && mcb->mcb_temporary == FALSE
	  && !is_table_debug)
	{
	    status = dmxe_writebt(xcb, TRUE, &dmu->error);
	    if (status != E_DB_OK)
	    {
		xcb->xcb_state |= XCB_TRANABORT;
		break;
	    }
	}

        /* Calls the physical layer to process the rest of the modify */

	status = dm2u_modify(mcb, &dmu->error);

	if (status == E_DB_OK && has_extensions)
	{
	    if ((mcb->mcb_mod_options2 & DM2U_2_ADD_EXTEND) && blob_add_extend == 0)
		status = E_DB_OK;
	    else
	    {
		/* FIX ME make modify etabs optional !! */
		/* Add flag to modify top make modify etabs optional */
		/* Add sysmod dbname tablename blob-column-name */
#ifdef xDEBUG
		TRdisplay("Modify etabs for %~t %~t\n",
		    sizeof(DB_TAB_NAME), table_name.db_tab_name,
		    sizeof(DB_OWN_NAME), table_owner.db_own_name);
#endif
		status = dmpe_modify(dmu, odcb->odcb_dcb_ptr, xcb,
			    &dmu->dmu_tbl_id, mcb->mcb_db_lockmode, mcb->mcb_temporary,
			    truncate, (i4)0, blob_add_extend, &dmu->error);
	    }
	}
	  
	    
	/*	Audit successful MODIFY/PATCH of TABLE. */

	if ( status == E_DB_OK && dmf_svcb->svcb_status & SVCB_C2SECURE )
	{
	    i4 msgid;
	    i4 access = SXF_A_SUCCESS;

	    if ((mcb->mcb_verify & DM1U_OPMASK) == DM1U_PATCH ||
		(mcb->mcb_verify & DM1U_OPMASK) == DM1U_FPATCH)
	    {
		access |= SXF_A_ALTER;
		msgid = I_SX271A_TABLE_PATCH;
	    }
	    else 
	    {
		access |= SXF_A_MODIFY;
		msgid = I_SX270F_TABLE_MODIFY;
	    }
	    /*
	    **	Audit success
	    */
	    status = dma_write_audit(
			SXF_E_TABLE,
			access,
			table_name.db_tab_name,	/* Table/view name */
			sizeof(table_name.db_tab_name),	/* Table/view name */
			&table_owner,	/* Table/view owner */
			msgid, 
			FALSE, /* Not force */
			&dmu->error, NULL);
	}

	if (status == E_DB_OK)
	{
	    /* If modify to reorg or merge then return no tuple count info. */
	    if (reorg || (mcb->mcb_merge) || (mcb->mcb_verify != 0))
	    {
		dmu->dmu_tup_cnt = DM_NO_TUPINFO;
	    }
	    return (E_DB_OK);
	}
	else
	{
            if (dmu->error.err_code > E_DM_INTERNAL)
            {
                uleFormat(&dmu->error, 0, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, 
		    (char * )NULL, (i4)0, (i4 *)NULL, &local_error, 0);
		SETDBERR(&dmu->error, 0, E_DM0091_ERROR_MODIFYING_TABLE);
            }
	    switch (dmu->error.err_code)
	    {
		case E_DM004B_LOCK_QUOTA_EXCEEDED:
		case E_DM0112_RESOURCE_QUOTA_EXCEED:
		case E_DM0091_ERROR_MODIFYING_TABLE:
		case E_DM009B_ERROR_CHK_PATCH_TABLE:
		case E_DM0045_DUPLICATE_KEY:
		case E_DM0137_GATEWAY_ACCESS_ERROR:
	        case E_DM006A_TRAN_ACCESS_CONFLICT:
		    xcb->xcb_state |= XCB_STMTABORT;
		    break;

		case E_DM0042_DEADLOCK:
		case E_DM004A_INTERNAL_ERROR:
		case E_DM0100_DB_INCONSISTENT:
		    xcb->xcb_state |= XCB_TRANABORT;
		    break;
		case E_DM0065_USER_INTR:
		    xcb->xcb_state |= XCB_USER_INTR;
		    break;
		case E_DM010C_TRAN_ABORTED:
		    xcb->xcb_state |= XCB_FORCE_ABORT;
		    break;
		case E_DM007D_BTREE_BAD_KEY_LENGTH:
		    dmu->dmu_tup_cnt = dmu->dmu_tup_cnt; /* same for now */
		default:
                    break;
	    }
	}
    } while (FALSE);

    if (dmu->error.err_code > E_DM_INTERNAL)
    {
	uleFormat(&dmu->error, 0, (CL_ERR_DESC *)NULL, ULE_LOG, NULL, 
	    (char * )NULL, (i4)0, (i4 *)NULL, &local_error, 0);
	SETDBERR(&dmu->error, 0, E_DM0091_ERROR_MODIFYING_TABLE);
    }

    return (E_DB_ERROR);
}
Пример #10
0
/*{
** Name: opn_ukey	- useable key - can inner node be probed
**
** Description:
**
** 	Returns TRUE if there are enough attributes
**	to use the key of the relation.
**
**	The jeqc must be the first element in the ordering that is not 
**	satisfied by a boolfact.  This is because all in order for this 
**	routine to return true, keys 0..N were present in the join node.
**	In opn_sjeqc, we will select the join eqc after reviewing all 
**      possibilities. One of the joined eqc's will be the first in the 
**      order list. If we only return true for this one, it will be joined.
**	This only matters in the BTREE case.  The prnode, in this case is 
**	also sorted on the first equiv class.
**	If we picked an arbitrary eqc as keyed, we may do a needless sort.
**
** Inputs:
**      subquery                        ptr to subquery being analyzed
**      oeqcmp                          available equivalence class map of 
**                                      outer node
**      jeqc                            primary attribute's eqc of key
**      keyedvar		        joinop range variable number of 
**                                      possibly keyed relation
**      primkey                         TRUE - if jeqc must be first joined 
**                                      element of orderlist
**
** Outputs:
**	Returns:
**	    TRUE - if keyed access can be used for join
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**	31-may-86 (seputis)
**          initial creation
**	10-jun-96 (inkdo01)
**	    Support for Rtree inners of Kjoins.
[@history_line@]...
*/
bool
opn_ukey(
	OPS_SUBQUERY       *subquery,
	OPE_BMEQCLS        *oeqcmp,
	OPO_ISORT	   jeqc,
	OPV_IVARS          keyedvar,
	bool               primkey)
{
    OPO_STORAGE         storage;	/* storage structure of relation */
    OPV_VARS            *varp;		/* ptr to range variable element to
                                        ** be checked */
    i4                  ordercount;     /* number of attributes that the
                                        ** relation is ordered on */
    i4                  orderindex;     /* index into the OPB_MBF structure
                                        ** of the range variable element */
    OPZ_AT              *abase;         /* ptr to base of array of ptrs to
                                        ** joinop attribute elements */
    bool                ret_val;        /* TRUE is we can do keyed lookup */

    /* Relation must be disk resident */
    if (keyedvar < 0)
	return (FALSE);

    ret_val = FALSE;
    varp = subquery->ops_vars.opv_base->opv_rt[keyedvar]; /* get ptr to range
					** variable element */

    storage = varp->opv_tcop->opo_storage; /* get storage structure 
					** of relation */
    abase = subquery->ops_attrs.opz_base; /* ptr to base of array of ptrs to
                                        ** joinop attribute elements */
#if 0
/* FIXME - why is this check here */    
    if ((storage) < 0)
	storage = -storage;
#endif
# ifdef xNTR1
    if (tTf (1, 0))
    {
	TRdisplay("Checking for possible keyed lookup into %s.\n", Jn.Range[keyedvar]->xrelid);
	TRdisplay("We %s looking for primary join key eqc %d.\n", (primkey ? "are" : "are not"), jeqc);
	TRdisplay("The joinop order eqcs are:");
	for (i = 0; (att = order[i]) > -1; i++)
		TRdisplay("%d ", (i4) Jn.Attnums[att]->equcls);
	TRdisplay("\n");
	uflush();
    }
# endif

    /* Each attribute in the ordering must be available from the outer
    ** or from the boolfacts
    */
    ordercount = varp->opv_mbf.opb_count; /* number of attributes that the
					** relation is ordered on */
    for (orderindex = 0; orderindex < ordercount; orderindex++)
    {
	OPZ_IATTS	       attr;	/* current ordering attribute being
                                        ** analyzed */
	OPE_IEQCLS             eqcls;   /* equivalence class of 
                                        ** of ordering attribute */

	attr = varp->opv_mbf.opb_kbase->opb_keyorder[orderindex].opb_attno;
	eqcls = abase->opz_attnums[attr]->opz_equcls;

	if (storage == DB_RTRE_STORE)	/* special Rtree inner logic */
	{
	    OPB_IBF	bfi;
	    OPB_BOOLFACT *bfp;
	    for (bfi = 0; bfi < subquery->ops_bfs.opb_bv; bfi++)
	     if ((bfp = subquery->ops_bfs.opb_base->opb_boolfact[bfi])->
		opb_mask & OPB_SPATJ && BTtest((i4)eqcls, 
		(char *)&bfp->opb_eqcmap))
	    {
		OPE_IEQCLS	eqc1;
		if ((eqc1 = BTnext((i4)-1, (char *)&bfp->opb_eqcmap,
		    (i4)subquery->ops_eclass.ope_ev)) == eqcls) 
		 eqc1 = BTnext((i4)eqc1, (char *)&bfp->opb_eqcmap, 
		    (i4)subquery->ops_eclass.ope_ev);
		if (BTtest((i4)eqc1, (char *)oeqcmp))
		{
		    eqcls = eqc1;
		    break;	/* kinda silly to let this drop into 
				** same test again - any alternative will
				** be even uglier! */
		}
	    }
	}

	if (BTtest((i4)eqcls, (char *)oeqcmp) == TRUE)
	{
	    /* Look no further if the first eqcls in the outer
	    ** that exists in the order for the relation is not the 
	    ** joining eqcls (if primkey)
	    */
	    if (primkey && (jeqc != eqcls))
	    {
#ifdef			xNTR1
		if (tTf(1, 0))
		{
		    TRdisplay("We tried to join on eqcls %d but realized that it\n", eqcls);
		    TRdisplay("was not the the primary attribute in the ordering and\n");
		    TRdisplay("that the primary attribute is available from the outer.\n");
		    uflush();
		}
#endif
		    break;
	    }
	    /* Condition satisfied. If non-hash we are keyed */
	    ret_val = TRUE;
	    primkey = FALSE;
	    if (storage != DB_HASH_STORE)
		break;
	    /* we are hashed and need all attrs in the key */
	    continue;
	}
	if (subquery->ops_bfs.opb_bfeqc
	    &&
	    BTtest((i4)eqcls, (char *)subquery->ops_bfs.opb_bfeqc))
	    continue;			/* check if constant key is
                                        ** available for this attribute*/
	/* eqcls is not available. If we haven't gotten all
	** of the hashed key, we can't use the key.
	*/
	if (storage == DB_HASH_STORE)
	{
#	ifdef		xNTR1
	    if (tTf(1, 0))
	    {
		TRdisplay("Not all keys for hashed lookup were found.\n");
		uflush();
	    }
#	endif
	    ret_val = FALSE;
	}
	break;
    }
# ifdef	xNTR1
    if (tTf(1, 0))
    {
	if (ret_val == TRUE)
		TRdisplay("We can do keyed lookup.\n");
	else
		TRdisplay("We can NOT do keyed lookup.\n");
	uflush();
    }
# endif
    return (ret_val);
}
Пример #11
0
/*{
** Name: opn_jmaps	- set various join operator tree maps
**
** Description:
{@comment_line@}...
**
** Inputs:
**      subquery                        ptr to subquery being analyzed
**      nodep                           ptr to current operator node which
**                                      will have maps initialized
**	ojmap				NULL if no outer joins exist
**					- map of outer joins which are
**					evaluated in parent nodes.
**
** Outputs:
**      nodep->opn_eqm                  set of equivalence class available
**                                      from subtree
**      nodep->opn_rlmap                bitmap of relations in the subtree
**      nodep->opn_rlasg                order in which the relations in
**                                      opn_rlmap are assigned to the leaves
**	Returns:
**	    TRUE if there is a valid placement of subselect nodes with
**          all required equivalence classes available for execution of the
**          boolean factor used for the subselect
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**	11-jun-86 (seputis)
**          initial creation from setjmaps
**      29-mar-90 (seputis)
**          fix byte alignment problems
**	2-apr-91 (seputis)
**	    only partially copied relation assignment causing run_all diffs
**	    fix for b35461, this would cause the OPF cache of query plans
**	    to not be effectively used, and could cause performance problems
**	    on non-VMS systems.  This fix should be used whenever a poor
**	    query plan bug is reported which cannot be reproduced on VMS.
**      15-feb-94 (ed)
**          - bug 59598 - correct mechanism in which boolean factors are
**          evaluated at several join nodes
**      11-apr-94 (ed)
**          - bug 59937 - E_OP0489 consistency check due to inner join ojid
**          not being visible in boolean factor placement maps
**	31-jul-97 (inkdo01)
**	    Fix to force SVAR func atts ref'ed in ON clauses to materialize
**	    just before needed (to incorporate proper null semantics).
**	 9-jul-99 (hayke02 for inkdo01)
**	    Set opn_eqm bits in lower level nodes (e.g. leaf nodes) when
**	    func attrs are found in higher level OJ nodes. This change
**	    fixes bug 92749.
**	20-Mar-02 (wanfr01)
**	    Bug 106678, INGSRV 1633
**	    Confirm function attribute as an ojid before associating it
**	    with an outer join node.
**	16-sep-03 (hayke02)
**	    Check for OPN_OJINNERIDX in nodep->opn_jmask to indicate that
**	    placement of a coverqual inner index outer join needs to be
**	    checked.
**	28-apr-04 (hayke02)
**	    Modify previous change so that we return FALSE and reject the QEP
**	    if the index outer join node (node->LEFT) has a non-zero opn_nchild
**	    for node->LEFT->RIGHT. This will allow only QEPs where the OJ
**	    inner is the index only and not the index joined to another
**	    relation. This change fixes problem INGSRV 2808, bug 112211.
**      05-Oct-2004 (huazh01)
**          Remove the above fix. The fix for 111627 handles b106678
**          as well. This fixes b113160, INGSRV2984. 
**	14-mar-05 (hayke02)
**	    Modify the fix for problem INGSRV 2808, bug 112211 so that we now
**	    check for more than 1 var in the opl_ivmap. This now allows the
**	    correct rejection of plans that have had their opn_child's set up
**	    with left joins 'reversed' into right joins. This change fixes
**	    problems INGSRV 3049 and 3094, bugs 113457 and 113990.
**	23-sep-05 (hayke02)
**	    Check for a WHERE clause (OPL_NOOOUTER opz_ojid) OJSVAR func att,
**	    and make sure that all OJs that this func att is inner to are
**	    executed before the join involving this func att. This change fixes
**	    bug 114912.
**	30-jan-07 (hayke02)
**	    Disable the fix for bug 114912 for cart prod (OPL_BOJCARTPROD) OJs.
**	    This change fixes bug 117513.
**	27-Oct-2009 (kiria01) SIR 121883
**	    Scalar sub-selects - protect subp->opv_eqcrequired from bad de-ref.
[@history_line@]...
*/
bool
opn_jmaps(
	OPS_SUBQUERY       *subquery,
	OPN_JTREE          *nodep,
	OPL_BMOJ	   *ojmap)
{
	OPL_OUTER	    *outerp;

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

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

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

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

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

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

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

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

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

		    eqclsp = subquery->ops_eclass.ope_base->ope_eqclist[eqcls];
		    for (attno = -1;  (attno = BTnext((i4)attno,
			(PTR)&eqclsp->ope_attrmap,
			(i4)subquery->ops_attrs.opz_av)) != -1; )
		    {
			if (!BTtest((i4)abase->opz_attnums[attno]->opz_varnm,
						    (char *)&nodep->opn_rlmap))
			{
			    allinrlmap = FALSE;
			    break;
			}
		    }
		    if (allinrlmap)
			return(FALSE);
		}
		if ((fattrp->opz_type != OPZ_MVAR)
		    && !(fattrp->opz_type == OPZ_SVAR &&
			 fattrp->opz_mask & OPZ_OJSVAR &&
			 fattrp->opz_ojid == nodep->opn_ojid)
		    ||
		    (fattrp->opz_mask & OPZ_OJFA))
		    continue;			/* only multi-variable 
						** functions are assigned here 
						** (and SVARs in ON clauses
						** of OJs eval'd at this node)
						** since others where assigned
                                                ** earlier, ... also outer join
						** special eqc were assigned 
						** prior to this loop */
		if (fattrp->opz_type == OPZ_SVAR)
		{
		    /* Must be ON clause ref'ed SVAR. Set bit in whichever
		    ** child node covers the SVAR eqcmap. */
		    if (BTsubset((char *)&fattrp->opz_eqcm,
			(char *)&nodep->opn_child[1]->opn_eqm,
			(i4)BITS_IN(nodep->opn_eqm)))
		     BTset((i4)eqcls, (char *)&nodep->opn_child[1]->opn_eqm);
		    else if (BTsubset((char *)&fattrp->opz_eqcm,
			(char *)&nodep->opn_child[0]->opn_eqm,
			(i4)BITS_IN(nodep->opn_eqm)))
		     BTset((i4)eqcls, (char *)&nodep->opn_child[0]->opn_eqm);
						/* set fattr eqcls bit in 
						** proper child opn_eqm */
		    continue;
		}
		if (BTtest( (i4)eqcls, (char *)&nodep->opn_eqm))
			continue;		/* if function attribute has
                                                ** been added then
                                                ** continue */
		if (BTsubset((char *)&fattrp->opz_eqcm,
			     (char *)&nodep->opn_eqm,
			     (i4)BITS_IN(nodep->opn_eqm))
		    )
		    BTset((i4)eqcls, (char *)&nodep->opn_eqm); /* set bit if all 
                                                ** the required equivalence 
                                                ** classes are available 
                                                ** for the function attribute
                                                */
	    }
	}
    }
    return(TRUE);
}
Пример #12
0
/*{
** Name: opn_deltrl	- delete a OPN_RLS struct and its associated histograms
**
** Description:
**      This routine will delete an OPN_RLS structure and its' associated
**      histograms, including the "cell count" array of any histogram on
**      the joining equivalence class.
**
** Inputs:
**      subquery                        ptr to subquery being analyzed
**      trl                             OPN_RLS struct to delete
**      jeqc                            joining equivalence class which
**                                      will have a histogram to delete
**
** Outputs:
**	Returns:
**	    VOID
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**	14-jun-86 (seputis)
**          initial creation from deltrl
**	20-dec-90 (seputis)
**	    add support for deleting multi-attribute join histograms
**	19-nov-01 (inkdo01)
**	    Add logic to resolve linkage around OPN_RLS deleted from chain.
[@history_line@]...
*/
VOID
opn_deltrl(
	OPS_SUBQUERY       *subquery,
	OPN_RLS            *trl,
	OPE_IEQCLS         jeqc,
	OPN_LEAVES	   nleaves)
{
    OPZ_AT              *abase;			/* ptr to base of array of ptrs
                                                ** to joinop attributes */
    OPH_HISTOGRAM       *histp;			/* histogram element to
                                                ** deallocate */
    OPH_HISTOGRAM       *nexthistp;             /* need to save next histogram
                                                ** ptr in case memory manager
                                                ** munches the deallocated
                                                ** previous histogram */
    OPE_BMEQCLS		*eqcmap;		/* map of joining equivalence classes */
    OPN_RLS		*prevrls;

    abase = subquery->ops_attrs.opz_base;	/* ptr to base of array of ptrs
                                                ** to joinop attributes */
    if (jeqc >= subquery->ops_eclass.ope_ev)
    {	/* multi-attribute join found */
	eqcmap = subquery->ops_msort.opo_base->opo_stable[jeqc-subquery->ops_eclass.ope_ev]->opo_bmeqcls;
    }
    else
	eqcmap = (OPE_BMEQCLS *)NULL;
    for (histp = trl->opn_histogram; histp; histp = nexthistp)
    {
	OPZ_ATTS               *attrp;		/* ptr to joinop attribute
                                                ** associated with histogram */

	nexthistp = histp->oph_next;		/* save next histogram prior
                                                ** to deallocation */
	attrp = abase->opz_attnums[histp->oph_attribute];
	if ((attrp->opz_equcls == jeqc)
	    ||
	    (	eqcmap
		&&
		BTtest((i4)attrp->opz_equcls, (char *)eqcmap)
	    ))
	{
	    oph_dccmemory( subquery, &attrp->opz_histogram, histp->oph_fcnt);
	    /* can only free the fcnt array if it is on the joining
	    ** eqclass because then we know it was created brand new by
	    ** opn_h2 and does not point to another hist's oph_fcnt. (see
	    ** line "newhp->oph_fcnt=lnp->oph_fcnt" in opn_h2.c 
	    */
	}
	oph_dmemory(subquery, histp);
    }
    /* Locate trl in chain and remove it before deleting. */
    prevrls = subquery->ops_global->ops_estate.opn_sbase->opn_savework[nleaves];
    if (prevrls && prevrls == trl)
	subquery->ops_global->ops_estate.opn_sbase->opn_savework[nleaves] =
			trl->opn_rlnext;
    else if (prevrls)
    {
	for (; prevrls && prevrls->opn_rlnext != trl; prevrls = prevrls->opn_rlnext);
	if (prevrls)
	    prevrls->opn_rlnext = trl->opn_rlnext;	/* skip dropped OPN_RLS */
    }
    opn_drmemory(subquery, trl);
}
Пример #13
0
/*{
** Name: opn_prleaf	- project restrict for leaf
**
** Description:
**
**      This routine generates a CO-tree forest for a single leaf.  The
**      forest will contain a tree with just an ORIG in it, and another
**      tree with PR + ORIG.  (unless the caller requests that the latter
**      be omitted, i.e. wants keyed strategies only.)  The bare ORIG
**      will typically be used for K-join or T-join plans, while the
**      PR/ORIG subtree can be used to cost out simple-join plans.
**
**      [the below apparently dates from the Jurassic age, and it's not
**      entirely clear to me how much of it is accurate or useful...]
**      
**       General:
**      
**       an index is useful if it is hash (and single attribute) or isam and
**	       a) it matches a joining clause
**          or b) it matches a boolean factor
**      
**       To match a joining clause-
**      	attribute of index must equal join attribute. if isam, the
**      	attribute must be the most significant. if hash, the rel must
**      	be keyed on only this attribute.
**      
**       To match a boolean factor-
**      	there must exist a boolean factor such that
**      	a) for hash, all preds are equality sargs on the attribute or
**      	b) for isam, all preds are sargs on the most significant 
**      	   attribute.  We do not worry about sort because it can 
**                 only exist as an intermediate relation and hence will 
**                 never need a project-restrict (since it would have 
**		   been done when the tuple was created).
**      
**       Operation of project-restrict when relation is isam or hash:
**      
**       a) hash: find a matching boolean factor (all preds are equality sargs)
**      	   then for each sarg hash it's value and for all elements 
**                 in the resulting linked list apply the whole predicate
**      
**       b) isam: if there is a boolean factor where all preds are equality 
**      	   sargs then process similar to hash. the only difference 
**      	   is that a directory search is performed instead of 
**      	   hashing to determine the beginning and ending of the 
**                 linked list.
**      
**      	otherwise, for all matching boolean factors find a max and 
**      		a min value as follows:
**      	. if all preds in this boolean factor are <, <= or = then set
**      	  mx to max of the constants. if mx is greater then Gmax, set
**      	  Gmax to mx.
**      	. if all preds in this bf are >, >= or = then set mn to min of 
**      	  the constants. if mn is greater than Gmin set Gmin to mn.
**      	. do a directory search with Gmin and Gmax.
**      	. for all elements in this linked list apply the whole 
**                predicate.
**      
**      
**       COST
**      
**       cost of project-restrict-reformat is composed of the
**      	project-restrict cost and
**      	reformat cost.
**      
**       for the reformat cost see procedure "costreformat.c"
**      
**       project restrict cost is:
**      	hash and matching boolean factor
**		    ((#_pgs)/(#_primary) + hashcost) * (#_sargs_in_bf)
**      			hashcost is usually 0.
**      	isam and matching boolean factor
**		    ((#_pgs)/(#_primary) + dirsearchcost) * (#_sargs_in_bf)
**      	isam and matching boolean factor with range (at least one
**      	inequality sarg)
**		    2 * (dirsearchcost) + integrate that part of hist to find
**      			that portion of relation which must be accessed
**      
**      
**       SPECIFIC
**      
**       operation of prr-
**      
**       We are given a list of co's. this list contains one element if we are
**       working on a leaf. if we are working on the result of a join then
**       there may be zero or more co's. (there could be zero if for every co 
**       generated by calcost, there was a cheaper one in an associated co
**       list with the same opo_storage and ordeqc.)
**      
**       let stp refer to this node in the tree
**       if stp is a leaf then there is only one co. it represents the original
**       relation as it is stored on disk. there can be no associated
**       co lists because for a leaf there is only one structure and relation
**       assignment possible. so for this stp's co, do simple pr and
**       reformat to isam, hash and sort on all interesting orders.
**       the reformat co's point to the pr co which points to the original co
**       (the couter pointer does the pointing).
**      
**       If stp is the output of a join then the procedure is more complex.
**       If there are no co's then nothing is done, so return
**       otherwise find the minimum cost co in stp's co list.
**       Find the minimum cost from all the other associated co's, if any.
**       if there are other co's and a minimum cost is found which is less
**       than stp's minimum cost then do nothing, return
**       if there are other co's and a they have a higher mincost, then
**       delete all reformat co's from them cause they can be done cheaper here
**      	specifically:
**       using the minimum cost co as a base, do reformats to isam, hash
**       and sort on all interesting orders. for each structure/ordering 
**       check this stp and all associated co lists.  If this co is the best use
**       it and delete the other with same struct/ord. if this is not the
**       cheapest then do not use it, try next struct/ord combo.
**
**	 Default case will be to not do reformat to isam or hash unless the 
**       trace flags are turned on.  Also, do not reformat to isam or hash 
**       if the tuple is too wide (>MAXTUP) cause modify cannot handle it.
** 
**	 ctups is multiplied by sel so QEP will print correctly and 
**       set_qep_lock will know how many result tuples there are so it can 
**       set result relation locking appropriately.
**
**	 Always create PR nodes so that the cost of their use can be evaluated.
**       This fixes a bug where cross products would become excessively large 
**       because the restriction on the outer node would only happen
**	 after the join.  In the query "ret (r.a)where s.a =5"
**	 s.a will be the outer. We should do the restriction before the 
**       cross product.
**
**       There will be no eqclasses in eqsp->eqcmp at this point if
**       this query is from a single variable integrity constraint (right
**       now all integrity constraints are single variable). This is because
**       integrity constraints have no target list because they just want to
**       see if there exists at least one element the query and do not want
**       to return any attributes. We could do a
**       special case in the optimizer to find the fastest way to get the
**       first tuple (this could also be useful for QBF) but this is not done
**       now.
**
** Note on trace points op188/op215/op216.
**	Trace points op188/op215/op216 and op145 (set qep) both use opt_cotree().
**	op188/op215/op216 expect opt_conode to point to the fragment being
**	traced, and op145 expects it to be NULL (so that ops_bestco is used).
**	The NULL/non-NULL value of opt_conode also results in different
**	display behaviour. For these reasons opt_conode is also NULLed
**	after the call to opt_cotree().
**
** Inputs:
**      subquery                        ptr to subquery being analyzed
**      jsbtp                           set of cost orderings already calculated
**          .opn_coforw                 co tree representing leaf
**      eqsp                            equivalence class list header structure
**      rlsp                            represents the relation being restricted
**                                      and projected
**      blocks                          estimated number of blocks in relation
**                                      represented by this leaf after restrict
**                                      project applied
**      selectivity                     selectivity of predicates on relation
**
** Outputs:
**	Returns:
**	    VOID
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**	28-may-86 (seputis)
**          initial creation
**	19-sep-88 (seputis)
**          fix double application of selectivity
**	17-nov-88 (seputis)
**          non-zero PR cpu cost required
**	29-mar-90 (seputis)
**	    fix metaphor performance problem - be smarter about search space
**	20-dec-90 (seputis)
**	    add top sort node removal checks
**	22-apr-91 (seputis)
**	    - fix float exception handling problem b36920
**      15-dec-91 (seputis)
**          fix b33551 make better guess for initial set of relations to enumerate
**	26-dec-91 (seputis)
**	    - 41526 - initialize sortrequired since opn_checksort now needs this
**	    initialized as part of bug fix
**	28-jan-91 (seputis)
**	    - fix 40923 - remove parameter to opn_prcost
**	1-apr-92 (seputis)
**	    - b40687 - part of tid join estimate fix.
**      8-may-92 (seputis)
**          - b44212, check for overflow in opo_pointercnt
**      5-jan-93 (ed)
**          - bug 48049 - move definition of ordering to make it more localized
**	30-mar-94 (ed)
**	    - bug 59461 - correct estimate of index pages in fanout causing
**	    overestimates and potential overflows
**	06-mar-96 (nanpr01)
**          - Changes for variable page size. Do not use DB_MAXTUP constant
**	      rather use the opn_maxreclen function to calculate the 
**	      max tuple size given the page size.
**	06-may-96 (nanpr01)
**	    Changes for New Page Format project - account for tuple header.
**	17-jun-96 (inkdo01)
**	    Treat Rtree like Btree for relprim computation.
**	21-jan-1997 (nanpr01)
**	    opo_pagesize was not getting set and consequently in opn_prcost/
**	    opn_readahead was getting floating point exception in VMS.
**	 1-apr-99 (hayke02)
**	    Use tbl_id.db_tab_index and tbl_storage_type rather than
**	    tbl_dpage_count <= 1 to check for a btree or rtree index. As of
**	    1.x, dmt_show() returns a tbl_dpage_count of 1 for single page
**	    tables, rather than 2 in 6.x. This change fixes bug 93436.
**	17-aug-99 (inkdo01)
**	    Minor, but ugly, change to detect descending top sort where 
**	    first column of multi-attr key struct is also in constant BF. 
**	    read backwards doesn't work in this case.
**	28-sep-00 (inkdo01)
**	    Code for composite histograms (avoid using them when attr no 
**	    is expected in histogram)
**	13-nov-00 (inkdo01)
**	    Added CO-tree addr to opn_checksort call. Seems no reason to
**	    omit it and new unique index top sort removal heuristic (100482)
**	    requires it.
**      12-aug-2002 (huazh01)
**          Do not mark ncop->opo_storage as sorted on a hash structure base
**          table. This fixes bug 107351, INGSRV 1727 
**      26-aug-2003 (wanfr01)
**          Bug 111062, INGSRV 2545
**          Do not mark storage sorted unless it is a btree.
**	5-july-05 (inkdo01)
**	    Call opn_modcost() to alter row count for agg queries.
**	30-Jun-2006 (kschendel)
**	    Remove modcost, done now after subquery enumerated so that we
**	    don't affect the agg-input co tree.
**      30-Aug-2006 (kschendel)
**          Eliminate redundant opo_topsort bool.
**	31-Aug-2006 (kschendel)
**	    Watch for HFAGG as well as RFAGG.
**	22-Oct-2007 (kibro01) b119313
**	    Ensure that pr_tuples does not fall below 1.
**      7-Mar-2008 (kschendel)
**          Always generate PR/ORIG co tree if new enumeration, regardless
**          of plan ops_cost considerations.  Failure to do this can cause
**          a new enum pass to not find any valid plans until it clears the
**          subtree cache with opn-recover.  See inline.
**	04-nov-08 (hayke02 for kschendel)
**	    Modify the previous change to prevent single var substitution index
**	    QEPs always being chosen, despite the base table QEP being cheaper.
**	    This change fixes bug 121051.
**	3-Jun-2009 (kschendel) b122118
**	    OJ filter cleanup, fix here.
**	26-feb-10 (smeke01) b123349 b123350
**	    Copy over the op255 and op188 trace point code from opn_corput().
**	    Amend opn_prleaf() to return OPN_STATUS in order to make trace point
**	    op255 work with set joinop notimeout.
**	01-mar-10 (smeke01) b123333 b123373
**	    Improve op188. Replace call to uld_prtree() with call to
**	    opt_cotree() so that the same formatting code is used in op188 as
**	    in op145 (set qep). Save away the current fragment number and
**	    the best fragment number so far for use in opt_cotree(). Also
**	    set ops_trace.opt_subquery to make sure we are tracing the current
**	    subquery.
**	13-may-10 (smeke01) b123727
**	    Add new trace point op215 to output trace for all query plan
**	    fragments and best plans considered by the optimizer. Add a new
**	    trace point op216 that prints out only the best plans as they are
**	    Also cleaned up some confused variable usage: replaced dsortcop
**	    with sortcop; and removed usage of ncop after it has been assigned
**	    to cop, replacing it with cop.
**	01-jun-10 (smeke01) b123838
**	    Prevent oph_origtuples/oph_odefined from being set for the
**	    histograms of a multi-attribute key when there are no boolean
**	    factors for the key.  This in turn prevents the
**	    'delayed multi-attribute' code in oph_jtuples() from being skewed
**	    by an unreasonably high number for oph_origtuples (up to 50% of
**	    the number of rows in the base table).
*/
OPN_STATUS
opn_prleaf(
	OPS_SUBQUERY       *subquery,
	OPN_SUBTREE        *jsbtp,
	OPN_EQS            *eqsp,
	OPN_RLS            *rlsp,
	OPO_BLOCKS	   blocks,
	bool		   selapplied,
	OPN_PERCENT        selectivity,
	OPV_VARS           *varp,
	bool               keyed_only)
{
    OPS_STATE		*global;
    OPO_BLOCKS          dio;        /* estimated number of disk block
                                    ** reads/writes to produce tuples
                                    ** represented by this cost ordering
                                    ** - after the project restrict */
    OPO_CPU             cpu;        /* estimate of cpu cost to produce
                                    ** this cost ordering */
    OPO_TUPLES		pr_tuples;  /* number of tuples in the project
				    ** restrict */
    OPB_SARG            sargtype;   /* type of keyed access which can be
                                    ** used on relation */
    OPO_BLOCKS		pblk;       /* estimated number of blocks read including
                                    ** blocks in primary index */
    OPH_HISTOGRAM       *hp;        /* histogram returned only if TID access */
    OPO_CO              *cop;       /* ptr to cost-ordering representing
                                    ** the leaf node */
    OPO_CO              *ncop;      /* ptr to new cost-ordering of a project
                                    ** - restrict of "*cop" */
    bool		pt_valid;   /* TRUE if pblk can be used as an estimate
				    ** for pages touched, which is used for
				    ** lock mode calculations */
    bool		imtid;      /* TRUE if implicit TID instead of
                                    ** keyed attributes were used */
    bool		distributed; /* TRUE is this is a distributed
				    ** thread */
    OPO_COST		prcost;	    /* project restrict cost of node */
    i4 		pagesize;   /* estimate the page size     */ 
    OPO_CO		*sortcop = NULL;	/* set to not NULL if a sort node is required */
    bool 		op188 = FALSE;
    bool 		op215 = FALSE;
    bool 		op216 = FALSE;

    global = subquery->ops_global;

    if (global->ops_cb->ops_check)
    {
	if (opt_strace( global->ops_cb, OPT_F060_QEP))
	    op188 = TRUE;
	if (opt_strace( global->ops_cb, OPT_F087_ALLFRAGS))
	    op215 = TRUE;
	if (opt_strace( global->ops_cb, OPT_F088_BESTPLANS))
	    op216 = TRUE;
    }

    distributed = global->ops_cb->ops_smask & OPS_MDISTRIBUTED;
    cop = jsbtp->opn_coback = jsbtp->opn_coforw = opn_cmemory( subquery ); /* 
				    ** get a new CO node */
    STRUCT_ASSIGN_MACRO(*(varp->opv_tcop), *(jsbtp->opn_coback));
				    /* copy the node since it may be 
				    ** changed */
    jsbtp->opn_coback->opo_coforw = jsbtp->opn_coback->opo_coback = 
	(OPO_CO *) jsbtp;		    /* ptr to jsbtp used to terminate list */
    jsbtp->opn_coback->opo_maps = &eqsp->opn_maps; /* save a ptr to the 
				    ** equivalence classes supplied by this
				    ** CO subtree (i.e. node) */
    if (varp->opv_grv->opv_relation)
    {
	DMT_TBL_ENTRY          *dmfptr;	    /* ptr to DMF table info */

	dmfptr = varp->opv_grv->opv_relation->rdr_rel; /* get ptr to RDF 
					** relation description info */
	pagesize = dmfptr->tbl_pgsize;

	/*
	** Note if distributed, the DMT_TBL_ENTRY is not totally initialized
	** (it was filled in by RDF rdd_alterdate -- which did 
	** 'select ... from iitables...' against the local database
	** However the information available by querying iitables does
	** not include all information put in the DMT_TBL_ENTRY by DMT_SHOW
	*/
	if (distributed &&
		((dmfptr->tbl_storage_type == DB_BTRE_STORE) ||
		 (dmfptr->tbl_storage_type == DB_RTRE_STORE) ||
		 (dmfptr->tbl_storage_type == DB_ISAM_STORE)) )
	{
	    i4  pgtype;

	    varp->opv_dircost = 1.0;

	    /*
	    ** Make assumptions about page type -> page/row overheads
	    ** The overheads used are data page, not btree index page overheads
	    ** That's okay - we're just looking for rough estimates
	    */
	    pgtype = (dmfptr->tbl_pgsize == DB_COMPAT_PGSIZE) ?
			DB_PG_V1: DB_PG_V3;
	    varp->opv_kpb = 
		(dmfptr->tbl_pgsize - DB_VPT_PAGE_OVERHEAD_MACRO(pgtype))
	       / (varp->opv_mbf.opb_keywidth + DB_VPT_SIZEOF_LINEID(pgtype));

	    if (varp->opv_kpb < 2.0)
		varp->opv_kpb = 2.0;
	    if (dmfptr->tbl_storage_type == DB_ISAM_STORE)
	    {
		varp->opv_kpleaf = 0;
		varp->opv_leafcount = 0;
	    }
	    else
	    {
		/* kpleaf calculation assumes no non-key columns */
		varp->opv_kpleaf = varp->opv_kpb;
		if (dmfptr->tbl_record_count)
		{
		    varp->opv_leafcount = 
				dmfptr->tbl_record_count/varp->opv_kpleaf;
		    if (varp->opv_leafcount < 1.0)
			varp->opv_leafcount = 1.0;
		}
		else
		    varp->opv_leafcount = 1.0;
	    }
	}
	else if ((dmfptr->tbl_storage_type == DB_BTRE_STORE) ||
	    (dmfptr->tbl_storage_type == DB_RTRE_STORE))
	{
	    /* 
	    ** Let dmf work this out, since it knows about page and row 
	    ** overhead and also takes into consideration potentially
	    ** different entry lengths for leaf and index pages
	    */
	    varp->opv_kpb = dmfptr->tbl_kperpage;
	    varp->opv_kpleaf = dmfptr->tbl_kperleaf;
	    varp->opv_dircost = dmfptr->tbl_ixlevels;
	    varp->opv_leafcount = dmfptr->tbl_lpage_count;
	}
	else if (dmfptr->tbl_storage_type == DB_ISAM_STORE)
	{
	    OPO_TUPLES	    tupleperblock; /* estimated number of tuples in an index
					** block where size of one tuple is the
					** length of the attribute being ordered
					** on ... plus the size a block ptr */
	    OPO_BLOCKS      blocks;     /* number of blocks accessed in index */
	    OPO_BLOCKS      previous;   /* total blocks accessed in the index */
	    i4	    dirsearch;
	    OPO_BLOCKS	    indexpages;
	    OPO_BLOCKS	    root_blocks;  /* number of index pages which are not
					** on the final fanout level */
	    OPO_BLOCKS	    index_blocks; /* number of index pages for btree */
	    OPO_BLOCKS	    leaf_blocks; /* number of index and leaf pages for
					** btree */

	    indexpages = dmfptr->tbl_ipage_count;
	    if (indexpages < 1.0)
		indexpages = 1.0;

	    /* 
	    ** Let dmf work out keys-per-page since it know about
	    ** page and row overhead for different table types
	    */
	    varp->opv_kpb = dmfptr->tbl_kperpage;
	    tupleperblock = dmfptr->tbl_kperpage;

	    root_blocks =
	    index_blocks = 0.0;
	    previous = 1.0;
	    leaf_blocks =
	    blocks = tupleperblock;
	    for (dirsearch = 1; previous < indexpages; )
	    {
		root_blocks = index_blocks;
		index_blocks = previous;    /* previous level contain only index blocks */
		previous += blocks;	    /* calculate total blocks used so far */
		leaf_blocks = blocks;	    /* lowest level contain leaf pages for btree */
		blocks = blocks * blocks;
		dirsearch += 1;		    /* probably faster than using logs */
	    }
	    blocks = leaf_blocks;	    /* get previous value, i.e. leaf page
					    ** level for btree */
	    varp->opv_dircost = dirsearch - (previous - indexpages)/blocks;  /* 
					    ** calculate the average directory 
					    ** search cost; if fanout is incomplete
					    ** then subtract the probabilistic fraction
					    ** in which one less directory search would be
					    ** made - FIXME
					    ** get fill factor involved in this
					    ** calculation */
	    blocks = (dmfptr->tbl_dpage_count < 1.0) ? 1.0 :
						dmfptr->tbl_dpage_count;
	    varp->opv_leafcount = blocks;

					    /* opv_leafcount is probably not used anymore since DMF
					    ** returns an leaf page count in the tbl_dpage_count field
					    ** for a secondary btree index now */
					    /* used for BTREE secondary indexes which have no leaf pages
					    ** to put an upper bound on the number of pages that
					    ** can be hit on any particular level of the btree, which
					    ** can be thought of as data pages */
	}
    }
    else { 				    /* Temporary relation   */
      /* 
      ** Determine the best possible page size for this relation
      ** Try to fit at least 20 rows in the temp relation if possible.
      */
      pagesize = opn_pagesize(global->ops_cb->ops_server, eqsp->opn_relwid);
    }
    opo_orig(subquery, cop);	    /* initialize the ordeqc of the DB_ORIG
				    ** node */
    if (distributed)
	opd_orig(subquery, cop);    /* initialize the distributed portion of
				    ** the orig node */
    if (keyed_only)
	return(OPN_SIGOK);

    if (cop->opo_cost.opo_pagesize == 0)
      cop->opo_cost.opo_pagesize = pagesize;

    /* FIXME - is not opo_dio 0 here ? */
    {
        OPO_ISORT               ordering;   /* this is the ordering which can be
                                    ** assumed after keying has been done in
                                    ** the relation, - OPE_NOEQLCS if no
                                    ** ordering can be assumed
                                    ** not used here */
        opn_prcost (subquery, cop, &sargtype, &pblk, &hp,
            &imtid, FALSE, &ordering,
            &pt_valid, &dio, &cpu);
    }
    dio += cop->opo_cost.opo_dio; /* add disk i/o 
				    ** cost to restrict/project
                                    ** i.e. cost of reading tuples using
                                    ** keyed access or cost of reading
                                    ** entire relation */
    if (selapplied)
	pr_tuples = rlsp->opn_reltups; /* duplicate this info here
				    ** to help lock chooser.  We will 
				    ** use this to guess how many
				    ** pages in the result relation
				    ** will be touched.  It is also
				    ** printed out in the QEP */
    else
	pr_tuples = rlsp->opn_reltups * selectivity; /* do not
					    ** apply selectivity twice */

    if (pr_tuples < 1.0)
	pr_tuples = 1.0;

    /* FIXME - same thing */
    cpu += cop->opo_cost.opo_cpu; /* get CPU time used to project and
                                    ** restrict the node */

    prcost = opn_cost(subquery, dio, cpu);

    /* count another fragment for trace point op188/op215/op216 */
    subquery->ops_currfragment++;

    /* With traditional enum, the only way to have an ops_cost is to have
    ** a best plan, which implies we looked at all leaves already.  So
    ** presumably enum memory was dumped so that the search could continue.
    ** It's safe to assume that a PR/ORIG subtree that costs more than the
    ** entire best plan so far is not useful, and we might as well reduce
    ** the search space.
    **
    ** Things are very different with the new enumeration.  New enumeration
    ** can and will look for best-plans that do NOT encompass all leaves.
    ** An early pass may generate a very cheap best-fragment that does not
    ** involve some expensive leaf.  A later pass (that includes that
    ** fragment) may require the expensive leaf to be able to form a plan.
    ** If all we make is the ORIG, it makes it look like the var can only
    ** be keyed to, which may in fact not be right.  This could prevent
    ** new enum from finding a valid plan for the larger set of tables, until
    ** it flushes enumeration memory and tries again.  Avoid this by always
    ** saving both the ORIG and PR/ORIG CO-tree forest.
    **
    ** (If somehow we got here as root, act normally;  shouldn't happen
    ** during greedy enum.)
    */
    if ((global->ops_estate.opn_rootflg
	||
	(subquery->ops_mask & OPS_LAENUM) == 0)
	&&
	prcost >= subquery->ops_cost)
    {
	if (op215)
	{
	    global->ops_trace.opt_subquery = subquery;
	    global->ops_trace.opt_conode = cop;
	    opt_cotree(cop);
	    global->ops_trace.opt_conode = NULL; 
	}
	return(OPN_SIGOK);	    /* do not do anything if the cost is larger
                                    ** than the current best plan
                                    */
    }
    ncop = opn_cmemory(subquery);   /* get space for a project-restrict CO node 
                                    */
    ncop->opo_storage = DB_HEAP_STORE; /* no reformat for this CO node */
    {
	OPV_GRV		*grvp;	    /* ptr to global range var descriptor
				    ** for ORIG node */
	if ((cop->opo_storage == DB_HEAP_STORE)  
	    &&
	    (grvp = varp->opv_grv)
	    )
	{
	    if (grvp->opv_gsubselect
		&&
		(
		    (grvp->opv_gsubselect->opv_subquery->ops_sqtype == OPS_RFAGG)
		    ||
		    (grvp->opv_gsubselect->opv_subquery->ops_sqtype == OPS_HFAGG)
		    ||
		    (grvp->opv_gsubselect->opv_subquery->ops_sqtype == OPS_RSAGG)
		    ||
		    (grvp->opv_gsubselect->opv_subquery->ops_sqtype == OPS_VIEW)
		)
	       )
# if 0
	    check for noncorrelated rfagg removed,
	    removed since correlated aggregates need an SEJOIN and these need
            to be projected/restrict always because OPC has a restriction
            in which the parent node of an aggregate result needs to be a
            PST_BOP with a "subselect indicator"
# endif
	    {   /* eliminate node from list to reduce cost calculations needed */
		jsbtp->opn_coback =
		jsbtp->opn_coforw = (OPO_CO *)jsbtp;
	    }
	    if (grvp->opv_gquery 
		&& 
		(   (grvp->opv_created == OPS_RFAGG)
		    ||
		    (grvp->opv_created == OPS_FAGG)
		))
		ncop->opo_storage = DB_SORT_STORE; /* function aggregate has
					** ordered by lists */	    		
	}
    }
    /* create a new CO project restrict node ncop which points to cop */
    cop->opo_pointercnt++;          /* cop has one more CO pointer to it */
    if (!(cop->opo_pointercnt))
	opx_error(E_OP04A8_USAGEOVFL); /* report error is usage count overflows */
    ncop->opo_ordeqc = OPE_NOEQCLS;

    switch (sargtype)		    /* define adjacent duplicate factor and
                                    ** sortedness factor */
    {
    case ADC_KEXACTKEY:		    /* isam or hash or explicit equality TID 
                                    ** clause in the qualification, (assume the
				    ** boolfact sarg constants are sorted and 
                                    ** then used to access the relation, this is
				    ** done by opb_create) */
    {
	if (hp)
	    ncop->opo_cost.opo_adfactor = hp->oph_reptf; /* this may happen 
				    ** with tid access */
	else				
	    ncop->opo_cost.opo_adfactor = 1.0; /* we do not have an ordering
                                    ** equivalence class yet so worst case
                                    ** adjacent duplicate factor */
	ncop->opo_cost.opo_sortfact = rlsp->opn_reltups; /* number of
                                    ** tuples in the relation */
        /* b107351 */
        if ( varp->opv_grv->opv_relation->rdr_rel->tbl_storage_type ==
	     DB_HASH_STORE )
	     ncop->opo_storage = DB_HASH_STORE;
        else
	{
	  if (varp->opv_grv->opv_relation->rdr_rel->tbl_storage_type == DB_BTRE_STORE)
	     ncop->opo_storage = DB_SORT_STORE;  /* mark node as sorted on the
				    ** ordering equivalence class if we
				    ** can guarantee it, this will cause
				    ** opo_pr to lookup the ordering which
				    ** is contained in range var descriptor
                                    ** FIXME- need to check multi-attribute
                                    ** ordering portion which is not ordered
				    */
	  else
	    ncop->opo_storage = varp->opv_grv->opv_relation->rdr_rel->tbl_storage_type;
	}	
	break;
    }

    case ADC_KRANGEKEY:	    /* must be isam or btree */
    {
	/*static*/opn_adfsf (ncop, cop, blocks, pblk, rlsp->opn_reltups);
	break;
    }

      default: 				/* read whole relation */
	    /*static*/opn_adfsf (ncop, cop, blocks, cop->opo_cost.opo_reltotb, 
                rlsp->opn_reltups);
    }

    ncop->opo_outer	= cop;              /* outer node is leaf i.e. original
                                            ** relation */
    ncop->opo_sjpr	= DB_PR;	    /* project restrict node */
    ncop->opo_union.opo_ojid = OPL_NOOUTER; /* not an outer join */
    ncop->opo_maps	= &eqsp->opn_maps;  /* save ptr to equivalence class
                                            ** map of attributes which will
                                            ** be returned */
    ncop->opo_cost.opo_pagesize = pagesize;
    ncop->opo_cost.opo_tpb	= (i4)opn_tpblk(global->ops_cb->ops_server,
						 pagesize, eqsp->opn_relwid); 
					    /* tuples per block */
    ncop->opo_cost.opo_cvar.opo_cache = OPN_SCACHE; /* project restrict will use
					    ** the single page cache prior to
					    ** accessing the block reads */
    ncop->opo_cost.opo_reltotb	= blocks;   /* estimated number of blocks in
                                            ** relation after project restrict*/
    ncop->opo_cost.opo_dio	= dio;      /* disk i/o to do this project
                                            ** restrict */
    ncop->opo_cost.opo_cpu	= cpu;	    /* cpu required for project 
                                            ** restrict*/
    if (pt_valid)
	ncop->opo_cost.opo_pagestouched = pblk; /* number of primary blocks 
					    ** read is an accurate estimate of 
					    ** pages touched */
    else
	ncop->opo_cost.opo_pagestouched = 0.0; /* if it is not accurate then
					    ** estimate 0.0 pages touched so
					    ** that page level locking will
					    ** be used */
    ncop->opo_cost.opo_tups	= pr_tuples; /* number of tuples in PR result */
    if(	(cop->opo_storage == DB_BTRE_STORE) /* if a btree */
	||
    	(cop->opo_storage == DB_ISAM_STORE) /* if a isam, then ordering
                                            ** can be used for partial
                                            ** sort merge, note that
                                            ** ncop->opo_storage is
                                            ** DB_HEAP_STORE so that
                                            ** ordering is not used for
                                            ** a full sort merge */
	||
	(ncop->opo_storage == DB_SORT_STORE)) /* or sorted due to exact
					    ** keys */
	opo_pr(subquery, ncop);		    /* initialize the ordering
					    ** for the project restrict
                                            ** node */

    /* In the following condition we check first whether the relation:
    ** (1) has a multi-attribute key with matching attributes in the
    ** query (opb_count > 1).
    ** (2) has a multi-attribute key with at least one matching boolean
    ** factor in the query (opb_bfcount > 0).
    ** If either is false then there is either no multi-attribute key,
    ** or a file scan is preferable to using the multi-attribute key.
    */
    if ( varp->opv_mbf.opb_count > 1 && varp->opv_mbf.opb_bfcount > 0 )
    {	/* look at all histograms in the relation and determine which
	** have a requirement for special processing due to multi-attribute
	** index restrictivity rules, FIXME, this should be replaced
	** by TRUE multi-attribute processing */
	OPH_HISTOGRAM	    *histp;
	OPZ_AT                 *abase;  /* base of array of ptrs to joinop
					** attribute elements */
	abase = subquery->ops_attrs.opz_base; /* base of array of ptrs to
					    ** joinop attribute elements */
	for (histp = rlsp->opn_histogram; histp; histp = histp->oph_next)
	    if (!(histp->oph_mask & OPH_COMPOSITE) && 
		abase->opz_attnums[histp->oph_attribute]->opz_mask & OPZ_MAINDEX)
	    {
		histp->oph_origtuples = rlsp->opn_reltups;
		histp->oph_odefined = TRUE;
	    }
  
    }
    cop		= ncop;			    /* all reformats go from cop
					    ** (we must make a copy of the
					    ** original relation before
					    ** reformatting) */
    varp->opv_pr = ncop;		    /* save project-restrict for calculation 
					    ** of initial guess of best set of
					    ** relations */
    varp->opv_prtuples = pr_tuples;
    {
	bool		root_flag;

	root_flag = global->ops_estate.opn_rootflg;
	if (distributed)
	{
	    bool		useful;	    /* TRUE if distributed plan
					    ** is useful for at least one site */
	    if (root_flag)
	    {
		OPO_COST	cpu_sortcost;   /* total cpu cost for sort node */
		OPO_COST	dio_sortcost;   /* total dio cost for sort node */
		if (subquery->ops_msort.opo_mask & OPO_MTOPSORT)
		{
		    cpu_sortcost = eqsp->opn_cpu;
		    dio_sortcost = eqsp->opn_dio;
		}
		else
		{
		    cpu_sortcost = 0.0;
		    dio_sortcost = 0.0;
		}
		useful = opd_prleaf(subquery, rlsp, cop, eqsp->opn_relwid, 
			cpu_sortcost, dio_sortcost); /* add 
					    ** distributed costs for this node*/
		if (!useful 
		    ||
		    opd_bestplan(subquery, cop,
			((subquery->ops_msort.opo_mask & OPO_MTOPSORT) != 0),
			cpu_sortcost, dio_sortcost, &sortcop))
		{
		    if (op215)
		    {
			global->ops_trace.opt_subquery = subquery;
			global->ops_trace.opt_conode = cop;
			opt_cotree(cop);
			global->ops_trace.opt_conode = NULL; 
		    }
		    return(OPN_SIGOK);
		}
		if (subquery->ops_msort.opo_mask & OPO_MTOPSORT)
		    opn_createsort(subquery, sortcop, cop, eqsp); /* initialize the fields of
					    ** the top sort node, opd_bestplan has already
					    ** placed the top sort node in the appropriate
					    ** CO plan locations */
	    }
	    else
	    {
		op216 = FALSE; /* op216 traces only the best plans */
		useful = opd_prleaf(subquery, rlsp, cop, eqsp->opn_relwid, 
		    (OPO_CPU)0.0, (OPO_BLOCKS)0.0); /* add 
					    ** distributed costs for this node*/
	    }
	}
	else if (root_flag)
	{
	    OPO_COST		prcost1;

	    prcost1 = opn_cost(subquery, dio, cpu);
	    if (subquery->ops_msort.opo_mask & OPO_MTOPSORT)
	    {	/* FIXME - can detect user specified orderings here and
		** avoid a sort node */
		OPO_ISORT	sordering;
		bool		sortrequired;
		if (cop->opo_storage == DB_SORT_STORE)
		    sordering = cop->opo_ordeqc;
		else
		    sordering = OPE_NOEQCLS;
		sortrequired = FALSE;	/* no deferred semantics problem for
					** single table lookups */

		if (subquery->ops_msort.opo_mask & OPO_MDESC)
		{
		    /* Single node and descending order by. Assure the key
		    ** key structure isn't multi-attr in which the 1st column
		    ** is in a constant BF. Read backwards doesn't work. */
		    OPO_CO	*orig = cop;
		    OPO_EQLIST	*orig_list;
		
		    if ((orig->opo_sjpr == DB_ORIG || (orig = orig->opo_outer)
			&& orig->opo_sjpr == DB_ORIG) &&
			!(orig->opo_ordeqc < subquery->ops_eclass.ope_ev ||
			subquery->ops_bfs.opb_bfeqc == NULL) &&
			(orig->opo_ordeqc > OPE_NOEQCLS ||
			(orig_list = subquery->ops_msort.opo_base->opo_stable
			[orig->opo_ordeqc-subquery->ops_eclass.ope_ev]->opo_eqlist) 
			== NULL || BTtest((i4)orig_list->opo_eqorder[0],
			(char *)subquery->ops_bfs.opb_bfeqc))) sortrequired = TRUE;
		}
		if (opn_checksort(subquery, &subquery->ops_bestco, subquery->ops_cost,
			    dio, cpu, eqsp, &prcost1, sordering, &sortrequired, 
			    cop) )
		{
		    if (op215)
		    {
			global->ops_trace.opt_subquery = subquery;
			global->ops_trace.opt_conode = cop;
			opt_cotree(cop);
			global->ops_trace.opt_conode = NULL; 
		    }
		    return(OPN_SIGOK);	/* return if added cost of sort node, or creating
					** relation is too expensive */
		}
		if (sortrequired)
		    sortcop = opn_cmemory(subquery); /* allocate memory here so that
					** we do  not run out of memory prior to
					** calling opn_dcmemory, which would delete
					** the previous best plan */
	    }
	    opn_dcmemory(subquery, subquery->ops_bestco);
	    if (sortcop)
	    {
		opn_createsort(subquery, sortcop, cop, eqsp);
		subquery->ops_bestco = sortcop;
	    }
	    else
		subquery->ops_bestco	= cop;
	    subquery->ops_besthisto = rlsp->opn_histogram;

	    if (global->ops_gmask & OPS_FPEXCEPTION)
		global->ops_gmask |= OPS_BFPEXCEPTION; /* a plan was found which
					** did not have a floating point exception
					** so skip over subsequent plans with
					** floating point exceptions */
	    else
		global->ops_gmask &= (~OPS_BFPEXCEPTION); /* reset exception
					** flag if plan was found to be free
					** of float exceptions */
            subquery->ops_tcurrent++;	/* increment plan number */
	    subquery->ops_tplan = subquery->ops_tcurrent; /* since this is
					**a single leaf tree, opn_ceval
					** will detect a timeout */
	    /* save the best fragment so far for trace point op188/op215/op216 */
	    subquery->ops_bestfragment = subquery->ops_currfragment;
	    subquery->ops_cost	= prcost1;
	    global->ops_estate.opn_search = FALSE; /* new best CO
					** found so memory garbage
					** collection routines may be
					** useful */
	}

	if (!root_flag)
	{
	    op216 = FALSE; /* op216 traces only the best plans */
	    opn_coinsert (jsbtp, cop);	/* put in co list, do not place into CO
					** if at the root since it may be deallocated
					** when a new best plan is found, and the PR
					** is worthless if it was once the best plan
					** so it does not need to be in the linked list
					** of CO nodes */
	}
    }

    if (op188 || op215 || op216)
    {
	global->ops_trace.opt_subquery = subquery;
	if (!sortcop)
	{
	    global->ops_trace.opt_conode = cop;
	    opt_cotree(cop);
	}
	else
	{
	    global->ops_trace.opt_conode = sortcop;
	    opt_cotree(sortcop);
	}
	global->ops_trace.opt_conode = NULL; 
    }

    if (global->ops_cb->ops_check)
    {
	i4	    first;
	i4	    second;

	/* If we have a usable plan, check for trace point op255 timeout setting */
	if( subquery->ops_bestco &&
	    opt_svtrace( global->ops_cb, OPT_F127_TIMEOUT, &first, &second) &&
	    !(subquery->ops_mask & OPS_LAENUM) &&
	    (first <= subquery->ops_tcurrent) &&
	    /* check if all subqueries should be timed out OR a particular subquery is being searched for */
	    ( (second == 0) || (second == subquery->ops_tsubquery) ) )
	{
	    opx_verror(E_DB_WARN, E_OP0006_TIMEOUT, (OPX_FACILITY)0);
	    opn_exit(subquery, FALSE);
	    /*
	    ** At this point we return the subquery->opn_bestco tree.
	    ** This could also happen in freeco and enumerate 
	    */
	    return(OPN_SIGEXIT);
	}
    }
    return(OPN_SIGOK);
}
Пример #14
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);
}
Пример #15
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;
}
Пример #16
0
/*{
** Name: opl_ioutjoin	- init outer join IDs for this subquery
**
** Description:
**      This routine will initialize an outer join descriptor for 
**      this subquery.  Note that a PSF var may be referenced in several
**	subqueries due to link backs in aggregates, etc. but only
**	one of these subqueries would contain the outer join ID.  So
**	that OPF delays inserting the join ID into the
**	subquery until a parse tree node is discovered which references
**	the join ID.
**
** Inputs:
**      subquery                        subquery which contains outer
**					join
**      varp                            ptr to range table entry
**					containing the outer join var
**
** Outputs:
**
**	Returns:
**	    VOID
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**      14-sep-89 (seputis)
**          initial creation
**      15-feb-93 (ed)
**	    fix outer join placement bug 49317
**	19-jan-94 (ed)
**	    remove obsolete structures
**	15-sep-00 (inkdo01)
**	    Init opl_translate to NOINIT to distinguish from legitimate
**	    parent of OPL_NOOUTER (to fix bug 87175).
[@history_line@]...
[@history_template@]...
*/
OPL_IOUTER
opl_ioutjoin(
	OPS_SUBQUERY       *subquery,
	PST_J_ID	   joinid)
{
    OPS_STATE	    *global;
    OPL_OUTER	    *outjoinp;

    global = subquery->ops_global;
    {   /* this ID has not been added to the current set */
	i4		    ojsize; /* size of structures needed to support
				    ** the outer join descriptor */

	ojsize = sizeof(*outjoinp) +
		sizeof(*outjoinp->opl_innereqc) +
		sizeof(*outjoinp->opl_ojtotal) +
		sizeof(*outjoinp->opl_onclause) +
		sizeof(*outjoinp->opl_ovmap) +
		sizeof(*outjoinp->opl_ivmap) +
		sizeof(*outjoinp->opl_bvmap) +
		sizeof(*outjoinp->opl_ojbvmap) +
		sizeof(*outjoinp->opl_idmap) +
		sizeof(*outjoinp->opl_bfmap) +
		sizeof(*outjoinp->opl_minojmap) +
		sizeof(*outjoinp->opl_maxojmap) +
		sizeof(*outjoinp->opl_ojattr) +
		sizeof(*outjoinp->opl_reqinner);
	outjoinp = (OPL_OUTER *)opu_memory(global, ojsize);
	MEfill(ojsize, (u_char)0, (PTR)outjoinp);
	outjoinp->opl_innereqc = (OPE_BMEQCLS *)&outjoinp[1]; /* ptr to set of equivalence classes
				    ** which represent all the inner relations
				    ** to this outer join */
	outjoinp->opl_id = subquery->ops_oj.opl_lv++; /* query tree ID associated with this
				    ** outer join, which should be found in
				    ** the op node of any qualification */
	outjoinp->opl_gid = joinid; /* global ID used in query tree produced
				    ** by parser, this is different from opl_id
				    ** since opl_id is local to the subquery
				    ** and the op nodes were renamed to 
				    ** be opl_id */
	outjoinp->opl_ojtotal= (OPV_BMVARS *)&outjoinp->opl_innereqc[1]; /* map of 
				    ** all variables which  are considered 
				    ** "inner" to this join ID, this map is used
				    ** for legal placement of variables, this
				    ** includes all secondaries etc. */
	outjoinp->opl_onclause = (OPV_BMVARS *)&outjoinp->opl_ojtotal[1]; ;
	outjoinp->opl_ovmap = (OPV_BMVARS *)&outjoinp->opl_onclause[1]; 
	outjoinp->opl_ivmap = (OPV_BMVARS *)&outjoinp->opl_ovmap[1]; 
	outjoinp->opl_bvmap = (OPV_BMVARS *)&outjoinp->opl_ivmap[1]; 
	outjoinp->opl_ojbvmap = (OPV_BMVARS *)&outjoinp->opl_bvmap[1]; 
	outjoinp->opl_idmap = (OPL_BMOJ *)&outjoinp->opl_ojbvmap[1]; 
	outjoinp->opl_bfmap = (OPB_BMBF *)&outjoinp->opl_idmap[1];
	outjoinp->opl_minojmap = (OPV_BMVARS *)&outjoinp->opl_bfmap[1];
	outjoinp->opl_maxojmap = (OPV_BMVARS *)&outjoinp->opl_minojmap[1];
	outjoinp->opl_ojattr = (OPZ_BMATTS *)&outjoinp->opl_maxojmap[1];
	outjoinp->opl_reqinner = (OPL_BMOJ *)&outjoinp->opl_ojattr[1];
	outjoinp->opl_type = OPL_UNKNOWN; /* type of outer join will
				    ** be determined later */
	outjoinp->opl_mask = 0;	    /* mask of various booleans */
	outjoinp->opl_translate = OPL_NOINIT;

	if ((subquery->ops_oj.opl_lv >= OPL_MAXOUTER)
	    ||
	    (joinid > global->ops_goj.opl_glv))
	    opx_error(E_OP038E_MAXOUTERJOIN);
	subquery->ops_oj.opl_base->opl_ojt[outjoinp->opl_id] = outjoinp;
	if (joinid == PST_NOJOIN)
	    return (outjoinp->opl_id); /* in the case of TID joins this routine
				    ** would be called to create a new joinid
				    ** descriptor for a left join */
	if (joinid > global->ops_goj.opl_glv)
	    opx_error(E_OP0395_PSF_JOINID);	    /* join id is out of range */
	if (global->ops_goj.opl_gbase->opl_gojt[joinid] != OPL_NOOUTER)
	    opx_error(E_OP038F_OUTERJOINSCOPE); /* not expecting another translation
				    ** for the same outer join */
	BTset( (i4)joinid, (char *)&subquery->ops_oj.opl_jmap); /* mark
				    ** outer join ID as being processed */
	global->ops_goj.opl_gbase->opl_gojt[joinid] = outjoinp->opl_id;
    }
    {	/* traverse range table and check for any variables which reference
	** this join id */
	OPV_IVARS	    varno;
	OPV_RT              *vbase;	    /* ptr to base of array of ptrs
					** to joinop variables */
	OPL_PARSER	    *pouter;
	OPL_PARSER	    *pinner;

	vbase = subquery->ops_vars.opv_base; /* init ptr to base of array of
					** ptrs to joinop variables */
	pouter = subquery->ops_oj.opl_pouter;
	pinner = subquery->ops_oj.opl_pinner;
	for (varno = subquery->ops_vars.opv_rv; --varno>=0;)
	{
	    OPV_VARS		*varp;
	    varp = vbase->opv_rt[varno];
	    if (varp->opv_grv 
/*
		&&
		(varp->opv_grv->opv_gmask & OPV_GOJVAR)
OPV_GOJVAR is not reliably set 
*/
		)
	    {
		OPV_IGVARS	    gvar;
		gvar = varp->opv_gvar;	    /* outer join semantics are defined with
					    ** the primary */
		if (BTtest((i4)joinid, (char *)&pouter->opl_parser[gvar]))
		{   /* found variable which is an outer to this join id */
		    if (!varp->opv_ojmap)
		    {   /* allocate a bit map for the outer join */
			varp->opv_ojmap = (OPL_BMOJ *)opu_memory(global,
			    (i4)sizeof(*varp->opv_ojmap));
			MEfill(sizeof(*varp->opv_ojmap), (u_char)0,
			    (PTR)varp->opv_ojmap);
		    }
		    BTset((i4)outjoinp->opl_id, (char *)varp->opv_ojmap);
		}
		if (BTtest((i4)joinid, (char *)&pinner->opl_parser[gvar]))
		{   /* found variable which is an inner to this join id */
		    if (!varp->opv_ijmap)
		    {   /* allocate a bit map for the outer join */
			varp->opv_ijmap = (OPL_BMOJ *)opu_memory(global,
			    (i4)sizeof(*varp->opv_ijmap));
			MEfill(sizeof(*varp->opv_ijmap), (u_char)0,
			    (PTR)varp->opv_ijmap);
		    }
		    BTset((i4)outjoinp->opl_id, (char *)varp->opv_ijmap);
		    BTset((i4)varno, (char *)outjoinp->opl_ojtotal);
		}
	    }
	}
    }
    return (outjoinp->opl_id);
}
Пример #17
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 */
	}
    }
}