Example #1
0
/*
** Name: QEN_PRINT_ROW	- This routine prints the current row for a given 
**								    QEN_NODE
**
** Description:
**      This routine prints out the current row for the given QEN_NODE. 
**      This is done with the help of ADF, who converts data into a 
**      printable form, similiar to what the terminal monitor needs to do. 
**
**	FIXME this really belongs in qenutl since it applies to any node,
**	not just fsm-joins.
**
** Inputs:
**	node -
**	    The qen_node that refers to the row.
**	qef_rcb -
**	    The request control block that we get most of our query common 
**	    info from, like cbs, the dsh, dsh rows, etc.
**
** Outputs:
**
**	Returns:
**	    DB_STATUS
**	Exceptions:
**	    none
**
** Side Effects:
**	    causes a row to be printed to the FE (via SCC_TRACE).
**
** History:
**      17-may-89 (eric)
**          created
**	6-aug-04 (inkdo01)
**	    Drop increment of node_rcount - it's already incremented in the 
**	    node handlers and this simply doubles its value.
**	13-Dec-2005 (kschendel)
**	    Can count on qen-ade-cx now.
**      11-Mar-2010 (hanal04) Bug 123415 
**          Make sure QE99 and OP173 do not interleave output with
**          using parallel query.
**	12-May-2010 (kschendel) Bug 123720
**	    Above doesn't quite go far enough, mutex the buffer.
**          Take qef_trsem for valid parallel-query output.
*/
DB_STATUS
qen_print_row(
QEN_NODE	*node,
QEF_RCB		*qef_rcb,
QEE_DSH		*dsh )
{
    QEN_STATUS	    *qen_status = dsh->dsh_xaddrs[node->qen_num]->qex_status;
    QEN_ADF	    *qen_adf;
    DB_STATUS	    status = E_DB_OK;
    char	    *cbuf = dsh->dsh_qefcb->qef_trfmt;
    i4		    cbufsize = dsh->dsh_qefcb->qef_trsize;

    qen_adf = node->qen_prow;		/* Checked non-NULL by caller */
    if (qen_status->node_rcount > 0)
    {
	/* process the row print expression */
	status = qen_execute_cx(dsh, dsh->dsh_xaddrs[node->qen_num]->qex_prow);
	if (status != E_DB_OK)
	{
# ifdef	xDEBUG
	    (VOID) qe2_chk_qp(dsh);
# endif
	    return (status);
	
	}

	/* Print in one operation to avoid interleaved output */
	CSp_semaphore(1, &dsh->dsh_qefcb->qef_trsem);
	STprintf(cbuf, "Row %d of node %d\n%s\n\n", 
			    qen_status->node_rcount, node->qen_num,
			    dsh->dsh_row[qen_adf->qen_output]);
        qec_tprintf(qef_rcb, cbufsize, cbuf);
	CSv_semaphore(&dsh->dsh_qefcb->qef_trsem);
    }			
    return(E_DB_OK);
}
Example #2
0
/*
** {
** Name: QEA_RUPDATE		- update the current cursor
**
**  External QEF call:	    status = qef_call(QEQ_REPLACE, &qef_rcb);
**
** Description:
**      The current row in the named cursor (QP) is
**  updated and sent to DMF 
**
** Inputs:
**	action			Update current row of cursor action.
**      qef_rcb
**	assoc_dsh		DSH for QP for cursor to be updated.
**	reset
**	state			DSH_CT_INITIAL if this is an action call
**				DSH_CT_CONTINUE for call back after processing
**				a rule action list.
**
** Outputs:
**      qef_rcb
**	    .qef_remnull	SET if an aggregate computation found a NULL val
**	    .qef_rowcount	number of rows replaced
**	    .qef_targcount	number of attempted replaces 
**	    .error.err_code	one of the following
**				E_QE0000_OK
**				E_QE0017_BAD_CB
**				E_QE0018_BAD_PARAM_IN_CB
**				E_QE0019_NON_INTERNAL_FAIL
**				E_QE0002_INTERNAL_ERROR
**				E_QE0008_CURSOR_NOT_OPENED
**				E_QE0021_NO_ROW
**				E_QE0009_NO_PERMISSION
**				E_QE000A_READ_ONLY
**				E_QE0012_DUPLICATE_KEY
**				E_QE0010_DUPLIATE_ROW
**				E_QE0011_AMBIGUOUS_REPLACE
**				E_QE0013_INTEGRITY_FAILED
**				E_QE0024_TRANSACTION_ABORTED
**				E_QE0034_LOCK_QUOTA_EXCEEDED
**				E_QE0035_LOCK_RESOURCE_BUSY
**				E_QE0036_LOCK_TIMER_EXPIRED
**				E_QE002A_DEADLOCK
**	Returns:
**	    E_DB_{OK,WARN,ERROR,FATAL}
**	Exceptions:
**	    none
**
** Side Effects:
**	    none
**
** History:
**	11-JUN-86 (daved)
**          written
**	22-may-87 (daved)
**	    catch cursor not positioned errors
**	14-oct-87 (puree)
**	    make sure that qen_error is called in case of E_DB_ERROR from
**	    qeq_validate.
**	02-feb-88 (puree)
**	    added reset flag to qeq_validate and all qea_xxxx call sequence.
**	29-aug-88 (puree)
**	    implement replace cursor by tid if the cursor was position on
**	    a secondary index table and by current position if the cursor
**	    is positioned on the base table.
**	    fix duplicate handling error.
**	12-oct-88 (puree)
**	    If a table validation fails during cursor replace, abort the
**	    cursor.
**	23-feb-89 (paul)
**	    Clean up qef_cb state before returning.
**	17-apr-89 (paul)
**	    Processing cursor updates integrated into qeq_query. Update cursor
**	    logic moved from qeq.c to this file (qearupd.c). This routine is
**	    a simplified version of what was previously called qeq_replace.
**	25-sep-89 (paul)
**	    Added support for using TIDs as parameters to procedures invoked
**	    from rules.
**	25-jan-90 (nancy) -- set status to E_DB_OK when replace cursor
**	    causes dups and duplicate handling is set to SKIP_DUPS.
**	29-jan-90 (nancy) -- fix bug 8388 (8575), use assoc dsh for replace
**	    by tid.
**	29-jun-90 (davebf)
**	    Handle rows which were accessed by TID only  (bug 31113)
**       5-Nov-1993 (fred)
**          Remove any remaining large object temporaries.
**       7-Apr-1994 (fred)
**          Commented out large object removal pending better way...
**	7-nov-95 (inkdo01)
**	    Changes to replace QEN_ADF structure instances by pointers in
**	    QEF_AHD structures.
**	29-dec-03 (inkdo01)
**	    DSH is now parameter, "function" replaces "reset".
**	5-feb-04 (inkdo01)
**	    Add support for partitioned tables.
**	12-mar-04 (inkdo01)
**	    Fix to handle bigtids on byte-swapped machines.
**	19-mar-04 (inkdo01)
**	    Fix for bigtids and rules.
**	12-may-04 (inkdo01)
**	    Call qeq_part_open unconditionally - it'll determine whether
**	    partition cbs are already prepared.
**	8-Jul-2004 (schka24)
**	    Still some tid byte-ordering confusion on x86, make tid handling
**	    more portable.
**	26-aug-04 (inkdo01)
**	    Add global base array support for update buffer.
**	24-Feb-05 (hweho01)
**	    The copying of tid needs to be handled differently if   
**          TID_SWAP is defined, so the tid value can be preserved. 
**          Star #13864021.
**	28-Feb-2005 (schka24)
**	    Rework the above fix, it turns out that the underlying problem
**	    was the 4byte-tidp flag not getting set.  Now that it's set
**	    properly, obey it here.  Also, do partition opens against the
**	    cursor query, not the RUP -- it's the one with the valid list..
**	18-Jul-2005 (schka24)
**	    Well, I fixed by-tid updates, but not true in-place updates!
**	    Use new ahd_ruporig machinery to get at the current partition
**	    number for in-place updating.
**	13-Dec-2005 (kschendel)
**	    Can count on qen-ade-cx now.
**	16-Jan-2006 (kschendel)
**	    Access qen-status thru xaddrs.
**	20-june-06 (dougi)
**	    Add support for BEFORE triggers.
**	1-Nov-2006 (kschendel)
**	    Some fixes to BEFORE triggers, make sure things are set up.
**	7-mar-2007 (dougi)
**	    Slight change to address the DMR_CB (broken by BEFORE changes).
**	8-Sep-2008 (kibro01) b120693
**	    Remove unused action parameter from qeq_part_open
**	09-Sep-2009 (thaju02) B122374
**	    If BEFORE trigger has been executed, do not use ahd_upd_colmap.
**	    ahd_upd_colmap may not reflect cols updated by the rule/proc.
**	15-Jan-2010 (jonj)
**	    SIR 121619 MVCC: If E_DM0029_ROW_UPDATE_CONFLICT returned,
**	    invalidate the QP and retry.
**	1-Jul-2010 (kschendel) b124004
**	    Minor changes for RUP from scrollable keyset cursors;  the
**	    fetched row is always in dsh-qef-output.
*/
DB_STATUS
qea_rupdate(
QEF_AHD		    *act,
QEF_RCB		    *qef_rcb,
QEE_DSH		    *dsh,
QEE_DSH		    *assoc_dsh,
i4		    function,
i4		    state )
{
    i4		err;
    DB_STATUS		status	= E_DB_OK;
    QEF_CB		*qef_cb = dsh->dsh_qefcb;
    ADF_CB		*adfcb = dsh->dsh_adf_cb;
    DMR_CB		*dmr_cb, *pdmr_cb;
    QEN_ADF		*qen_adf;
    ADE_EXCB		*ade_excb;
    PTR			*assoc_cbs = assoc_dsh->dsh_cbs;
    PTR			output, save_addr;
    char		*tidinput;  /* location of tid */
    DB_TID8		oldbigtid, newbigtid;
    u_i2		oldpartno = 0;
    u_i2		newpartno = 0;
    i4			odmr_cb_ix;	/* CB number of DMR_CB */
    i4			orig_node;
    
    status = E_DB_OK;
    
    for (;;)
    {
	/* A cursor update affects only a single row. If we are called back  */
	/* after rules processing simply return. */
	if (state == DSH_CT_CONTINUE)
	    break;

	/* Address the DMR_CB used to access the row.  The odmr_cb_ix is in
	** the associated (cursor) QP context.  We'll adjust for partitioning
	** a bit later.
	*/
	odmr_cb_ix = act->qhd_obj.qhd_qep.ahd_odmr_cb;
	dmr_cb = (DMR_CB*) assoc_cbs[odmr_cb_ix];
	if (status == DSH_CT_CONTINUE3)
	{
	    /* Resuming from a BEFORE trigger, reset variables */
	    MEcopy(dsh->dsh_row[act->qhd_obj.qhd_qep.u1.s1.ahd_a_tid],
			sizeof(DB_TID8), (PTR) &newbigtid);
	    MEcopy(dsh->dsh_row[act->qhd_obj.qhd_qep.u1.s1.ahd_b_tid],
			sizeof(DB_TID8), (PTR) &oldbigtid);
	    oldpartno = oldbigtid.tid.tid_partno;
	    newpartno = newbigtid.tid.tid_partno;
	    output = dsh->dsh_row[act->qhd_obj.qhd_qep.u1.s1.ahd_a_row];
	    if (act->qhd_obj.qhd_qep.ahd_part_def != NULL)
		dmr_cb = (DMR_CB *) assoc_cbs[odmr_cb_ix + oldpartno + 1];
	}
	else
	{
	    /* Normal path */
	    dsh->dsh_qef_remnull = 0;
	    dsh->dsh_qef_rowcount = 0;

	    /* Use fetch row for holding update row if need be */
	    output = assoc_dsh->dsh_row[assoc_dsh->dsh_qp_ptr->qp_fetch_ahd->qhd_obj.qhd_qep.ahd_ruprow];

	    /* make sure we have a positioned record */
	    if (assoc_dsh->dsh_positioned == FALSE)
	    {
		/* no record could be valid in this action's updatable control block.
		** Nor do we know what we will be looking at when we access the
		** update control block index number
		*/
		dsh->dsh_error.err_code = E_QE0021_NO_ROW;
		status = E_DB_ERROR;
		break;
	    }

	    /* check that this query plan has update privilege */
	    if ((assoc_dsh->dsh_qp_ptr->qp_status & QEQP_UPDATE) == 0)
	    {
		/* no permission */
		dsh->dsh_error.err_code = E_QE0009_NO_PERMISSION;
		status = E_DB_ERROR;
		break;
    	    }

	    if (qef_cb->qef_c1_distrib & DB_3_DDB_SESS)	/* distributed? */
	    {
		/* DDB processing */

		status = qeq_c5_replace(qef_rcb, dsh);
		if (status == E_DB_OK)
		    dsh->dsh_qef_rowcount = 1;
		else
		    dsh->dsh_qef_rowcount = 0;
		return(status);
	    }

	    /* LDB processing */

	    /* process the qualification expression */
	    qen_adf     = act->qhd_obj.qhd_qep.ahd_constant;
	    if (qen_adf != NULL)
	    {
		ade_excb = (ADE_EXCB*) dsh->dsh_cbs[qen_adf->qen_pos];
		if (dsh->dsh_qp_ptr->qp_status & QEQP_GLOBAL_BASEARRAY)
		    dsh->dsh_row[qen_adf->qen_uoutput] = output;
		else ade_excb->excb_bases[ADE_ZBASE + qen_adf->qen_uoutput] = output;
		status = qen_execute_cx(dsh, ade_excb);
		if (status != E_DB_OK)
		    break;

		/* handle condition where qualification failed. */
		if (ade_excb->excb_value != ADE_TRUE)
		{
		    dsh->dsh_qef_targcount = 1;
		    dsh->dsh_qef_rowcount = 0;
		    break;
		}
	    }

	    /* REPLACE THE TUPLE
	    ** if the cursor is positioned on the base table (ahd_tidoffset is
	    ** -1), replace the tuple at the current position.  Otherwise the
	    ** cursor must have been positioned via a secondary index.  Get
	    ** the tid, re-fetch the original row by tid, and replace the
	    ** base table tuple by the tid.
	    */

	    if (act->qhd_obj.qhd_qep.ahd_tidoffset == -1)
	    {
		/* If partitioned, dig the current partition number out of
		** the node status for the orig (or kjoin/tjoin) node that
		** fetched the row in the first place.  OPC has kindly put
		** the node number of same into ahd_ruporig.
		*/
		if (act->qhd_obj.qhd_qep.ahd_part_def != NULL)
		{
		    orig_node = act->qhd_obj.qhd_qep.ahd_ruporig;
		    if (orig_node == -1)
		    {
			TRdisplay("%@ qea_rupd: missing ahd_ruporig\n");
			dsh->dsh_error.err_code = E_QE0002_INTERNAL_ERROR;
			status = E_DB_ERROR;
			break;
		    }
		    oldpartno = assoc_dsh->dsh_xaddrs[orig_node]->
						qex_status->node_ppart_num;
		    dmr_cb = (DMR_CB *) assoc_cbs[odmr_cb_ix + oldpartno + 1];
		}
		dmr_cb->dmr_flags_mask = DMR_CURRENT_POS;
	    }
	    else
	    {
		/* set the tid */
		tidinput  = 
		    (char *) assoc_dsh->dsh_row[act->qhd_obj.qhd_qep.ahd_tidrow]
				 + act->qhd_obj.qhd_qep.ahd_tidoffset;

		if (act->qhd_obj.qhd_qep.ahd_qepflag & AHD_4BYTE_TIDP)
		{
		    I4ASSIGN_MACRO(*tidinput, oldbigtid.tid_i4.tid );
		    oldbigtid.tid_i4.tpf = 0;
		}
		else
		{
		    I8ASSIGN_MACRO(*tidinput, oldbigtid);
		}
		oldpartno = oldbigtid.tid.tid_partno;
		newpartno = oldpartno;
	
		/* Check for partitioning and set up DMR_CB. */
		if (act->qhd_obj.qhd_qep.ahd_part_def)
		{
		    /* Open against "get" action.  The RUP action dmtix and
		    ** flags are the same as the cursor's, thanks to OPC.
		    */
		    status = qeq_part_open(qef_rcb, assoc_dsh, NULL, 0, 
			odmr_cb_ix,
			act->qhd_obj.qhd_qep.ahd_dmtix, oldpartno);
			
		    if (status != E_DB_OK)
		    {
			dsh->dsh_error.err_code = assoc_dsh->dsh_error.err_code;
			return(status);
		    }
		    pdmr_cb = (DMR_CB *)assoc_cbs[odmr_cb_ix + oldpartno+1];

		    STRUCT_ASSIGN_MACRO(dmr_cb->dmr_data, pdmr_cb->dmr_data);
		    dmr_cb = pdmr_cb;
		}
		dmr_cb->dmr_tid = oldbigtid.tid_i4.tid;
		dmr_cb->dmr_flags_mask = DMR_BY_TID;
	
		/* get the tuple to be updated. */
		save_addr = dmr_cb->dmr_data.data_address;
		dmr_cb->dmr_data.data_address = output;
		status = dmf_call(DMR_GET, dmr_cb);
		dmr_cb->dmr_data.data_address = save_addr;
		if (status != E_DB_OK)
		{
		    dsh->dsh_error.err_code = dmr_cb->error.err_code;
		    return (status);
		}
	    }

	    /* If we have rules, save a copy of the row before updating */
	    if (act->qhd_obj.qhd_qep.ahd_after_act != NULL ||
		act->qhd_obj.qhd_qep.ahd_before_act != NULL)
	    {
		MEcopy(output, act->qhd_obj.qhd_qep.ahd_repsize,
		    dsh->dsh_row[act->qhd_obj.qhd_qep.u1.s1.ahd_b_row]);
		oldbigtid.tid_i4.tpf = 0;
		oldbigtid.tid_i4.tid = dmr_cb->dmr_tid;
		oldbigtid.tid.tid_partno = oldpartno;
	    }

	    /* process the update expression */
	    qen_adf     = act->qhd_obj.qhd_qep.ahd_current;
	    ade_excb    = (ADE_EXCB*) dsh->dsh_cbs[qen_adf->qen_pos];	
	    if (dsh->dsh_qp_ptr->qp_status & QEQP_GLOBAL_BASEARRAY)
		dsh->dsh_row[qen_adf->qen_uoutput] = output;
	    else ade_excb->excb_bases[ADE_ZBASE+qen_adf->qen_uoutput] = output;

	    status = qen_execute_cx(dsh, ade_excb);
	    if (status != E_DB_OK)
		break;

	    /* Check for partitioning and see if we changed target partition. */
	    if (act->qhd_obj.qhd_qep.ahd_part_def && 
		(act->qhd_obj.qhd_qep.ahd_qepflag & AHD_PCOLS_UPDATE))
	    {
		status = adt_whichpart_no(adfcb,
			act->qhd_obj.qhd_qep.ahd_part_def, 
			output, &newpartno);
		if (status != E_DB_OK)
		    return(status);
	    }
	    /* Process BEFORE triggers (if any). */
	    if (act->qhd_obj.qhd_qep.ahd_before_act != NULL)
	    {
		/*
		** Place the TID in a known location so it can be used
		** as a parameter to the procedure fired by the rule.
		*/
		newbigtid.tid_i4.tpf = 0;
		newbigtid.tid_i4.tid = dmr_cb->dmr_tid;
		newbigtid.tid.tid_partno = newpartno;
		MEcopy ((PTR)&newbigtid, sizeof(DB_TID8),
			dsh->dsh_row[act->qhd_obj.qhd_qep.u1.s1.ahd_a_tid]);   
		MEcopy ((PTR)&oldbigtid, sizeof(DB_TID8),
			dsh->dsh_row[act->qhd_obj.qhd_qep.u1.s1.ahd_b_tid]);   

		MEcopy(output, act->qhd_obj.qhd_qep.ahd_repsize,
			dsh->dsh_row[act->qhd_obj.qhd_qep.u1.s1.ahd_a_row]);
		dsh->dsh_ctx_act[dsh->dsh_depth_act].dsh_ct_ptr = act;
		dsh->dsh_ctx_act[dsh->dsh_depth_act].dsh_ct_status =
			DSH_CT_CONTINUE3;
		dsh->dsh_depth_act++;
		dsh->dsh_act_ptr = act->qhd_obj.qhd_qep.ahd_before_act;
		dsh->dsh_error.err_code = E_QE0120_RULE_ACTION_LIST;
		status = E_DB_ERROR;
		break;
	    }
	}    /* end of !DSH_CT_CONTINUE3 */

	state = DSH_CT_INITIAL;			/* signal resumption after
						** BEFORE trigger */

	if (act->qhd_obj.qhd_qep.ahd_qepflag & AHD_PCOLS_UPDATE &&
		oldpartno != newpartno)
	{
	    /* Partition has changed for row in partitioned table.
	    ** Delete from 1st partition and insert into next.
	    ** We have to hope that DMF can keep it all straight.
	    */
	    status = dmf_call(DMR_DELETE, dmr_cb);
	    if (status != E_DB_OK)
	    {
		if (dmr_cb->error.err_code == E_DM0055_NONEXT)
		    dsh->dsh_error.err_code = 4599;
		else
		    dsh->dsh_error.err_code = dmr_cb->error.err_code;
		break;
	    }

	    /* Get output partition DMR_CB, then do DMR_PUT.
	    ** Don't change the fetching DSH's current dmr-cb in case
	    ** we're running a true in-place cursor.
	    */
	    status = qeq_part_open(qef_rcb, assoc_dsh, NULL, 0, 
		odmr_cb_ix,
		act->qhd_obj.qhd_qep.ahd_dmtix, newpartno);
			
	    if (status != E_DB_OK)
	    {
		dsh->dsh_error.err_code = assoc_dsh->dsh_error.err_code;
		return(status);
	    }
	    dmr_cb = (DMR_CB *)assoc_cbs[odmr_cb_ix + newpartno+1];

	    dsh->dsh_qef_targcount = 1;
	    /* Final preparation of new partition DMR_CB. */
	    dmr_cb->dmr_flags_mask = 0;
	    dmr_cb->dmr_val_logkey = 0;
	    dmr_cb->dmr_data.data_address = output;
	    dmr_cb->dmr_data.data_in_size  =
				act->qhd_obj.qhd_qep.ahd_repsize;
	    if (act->qhd_obj.qhd_qep.ahd_duphandle != QEF_SKIP_DUP)
		dmr_cb->dmr_flags_mask |= DMR_DUP_ROLLBACK;
	    status = dmf_call(DMR_PUT, dmr_cb);

	}
	else
	{
	    dsh->dsh_qef_targcount = 1;
	    if ((act->qhd_obj.qhd_qep.ahd_upd_colmap) && 
		(act->qhd_obj.qhd_qep.ahd_before_act == NULL))
		dmr_cb->dmr_attset = 
			(char *)act->qhd_obj.qhd_qep.ahd_upd_colmap;
	    else
		dmr_cb->dmr_attset = (char *)0;

	    if (act->qhd_obj.qhd_qep.ahd_duphandle != QEF_SKIP_DUP)
		dmr_cb->dmr_flags_mask |= DMR_DUP_ROLLBACK;
	    status = dmf_call(DMR_REPLACE, dmr_cb);
	}
	    
	if (status == E_DB_OK || 
		((dmr_cb->error.err_code == E_DM0046_DUPLICATE_RECORD ||
		  dmr_cb->error.err_code == E_DM0045_DUPLICATE_KEY    ||
		  dmr_cb->error.err_code == E_DM0048_SIDUPLICATE_KEY) &&
		 act->qhd_obj.qhd_qep.ahd_duphandle == QEF_SKIP_DUP))
	{
	    if (status == E_DB_OK)
	    {
	    	dsh->dsh_qef_rowcount = 1;
		/* Look for rules to apply */
		if (act->qhd_obj.qhd_qep.ahd_after_act != NULL)
		{
		    /*
		    ** Place the TID in a known location so it can be used
		    ** as a parameter to the procedure fired by the rule.
		    */
		    newbigtid.tid_i4.tpf = 0;
		    newbigtid.tid_i4.tid = dmr_cb->dmr_tid;
		    newbigtid.tid.tid_partno = newpartno;
		    MEcopy ((PTR)&newbigtid, sizeof(DB_TID8),
			dsh->dsh_row[act->qhd_obj.qhd_qep.u1.s1.ahd_a_tid]);   
		    MEcopy ((PTR)&oldbigtid, sizeof(DB_TID8),
			dsh->dsh_row[act->qhd_obj.qhd_qep.u1.s1.ahd_b_tid]);   

		    MEcopy(output, act->qhd_obj.qhd_qep.ahd_repsize,
			dsh->dsh_row[act->qhd_obj.qhd_qep.u1.s1.ahd_a_row]);
		    dsh->dsh_ctx_act[dsh->dsh_depth_act].dsh_ct_ptr = act;
		    dsh->dsh_ctx_act[dsh->dsh_depth_act].dsh_ct_status =
			DSH_CT_CONTINUE;
		    dsh->dsh_depth_act++;
		    dsh->dsh_act_ptr = act->qhd_obj.qhd_qep.ahd_after_act;
		    dsh->dsh_error.err_code = E_QE0120_RULE_ACTION_LIST;
		    status = E_DB_ERROR;
		    break;
		}
	    }
	    else
	    {
		dsh->dsh_qef_rowcount = 0;
		status = E_DB_OK;
	    }	
	    break;	    
	}
	
	if (dmr_cb->error.err_code == E_DM0055_NONEXT)
	    dsh->dsh_error.err_code = 4599;
	else
	if ( dmr_cb->error.err_code == E_DM0029_ROW_UPDATE_CONFLICT )
	{
	    dsh->dsh_qp_status |= DSH_QP_OBSOLETE;
	    dsh->dsh_error.err_code = E_QE0023_INVALID_QUERY;
	    status = E_DB_WARN;
	}
	else
	    dsh->dsh_error.err_code = dmr_cb->error.err_code;
	break;
    }

    return (status);
}
Example #3
0
DB_STATUS
qen_tsort(
QEN_NODE           *node,
QEF_RCB            *qef_rcb,
QEE_DSH		   *dsh,
i4		    function )
{
    QEF_CB	*qef_cb = dsh->dsh_qefcb;
    PTR		*cbs = dsh->dsh_cbs;
    ADE_EXCB	*ade_excb;
    DMR_CB	*dmr_load = (DMR_CB*)cbs[node->node_qen.qen_tsort.tsort_load];
    DMR_CB	*dmr_get = (DMR_CB*) cbs[node->node_qen.qen_tsort.tsort_get];
    QEN_NODE	*out_node = node->node_qen.qen_tsort.tsort_out;
    QEE_XADDRS	*node_xaddrs = dsh->dsh_xaddrs[node->qen_num];
    QEN_STATUS	*qen_status = node_xaddrs->qex_status;
    QEN_SHD	*shd = dsh->dsh_shd[node->node_qen.qen_tsort.tsort_shd];
    DB_STATUS	status;
    bool	reset = FALSE;
    bool	heap_sort = TRUE, no_qef = FALSE;
    i4		rowno;
    i4		out_func = NO_FUNC;
    DM_MDATA	dm_mdata;
    i4	val1;
    i4	val2;
    TIMERSTAT	timerstat;
    i4          loop_cntr = 0;

    if (function != 0)
    {
	if (function & FUNC_RESET)
	{
	    reset = TRUE;
	    out_func = FUNC_RESET;
	}

	/* Do open processing, if required. Only if this is the root node
	** of the query tree do we continue executing the function. */
	if ((function & TOP_OPEN || function & MID_OPEN) && 
	    !(qen_status->node_status_flags & QEN1_NODE_OPEN))
	{
	    status = (*out_node->qen_func)(out_node, qef_rcb, dsh, MID_OPEN);
	    qen_status->node_status_flags = QEN1_NODE_OPEN;
	    if (function & MID_OPEN)
		return(E_DB_OK);
	    function &= ~TOP_OPEN;
	}
	/* Do close processing, if required. */
	if (function & FUNC_CLOSE)
	{
	    if (!(qen_status->node_status_flags & QEN8_NODE_CLOSED))
	    {
		status = (*out_node->qen_func)(out_node, qef_rcb, dsh, FUNC_CLOSE);
		qen_status->node_status_flags = 
		    (qen_status->node_status_flags & ~QEN1_NODE_OPEN) | QEN8_NODE_CLOSED;
	    }
	    return(E_DB_OK);
	}
	if (function & FUNC_EOGROUP)
	{
	    /* End of partition group request ends our sort if we're returning
	    ** rows.  If we aren't in the middle of returning rows, pass the
	    ** EOG request on down so that the child skips the upcoming group
	    ** and moves on to the next one.
	    */
	    if (qen_status->node_status == QEN3_GET_NEXT_INNER)
	    {
		status = qen_ts_reset(dsh, node, qen_status);
		/* FIXME we should do better at remembering whether this
		** sort load got EOF or EOG, and pass it on up now.  At
		** present (Apr '07), tsorts aren't very common except under
		** merge join plans, where early eof detection doesn't
		** matter much.  (the tsort is on the outer side.)
		** For now, pass EOG back up, if we're really at EOF caller
		** will find out soon enough.
		*/
		if (status == E_DB_OK)
		{
		    status = E_DB_WARN;
		    dsh->dsh_error.err_code = E_QE00A5_END_OF_PARTITION;
		}
	    }
	    else
	    {
		status = (*out_node->qen_func)(out_node, qef_rcb, dsh, FUNC_EOGROUP);
		/* Pass resulting EOF or EOG indication in dsh up to caller */
	    }
	    return(status);
	}
    } /* if function != 0 */


    /* Check for cancel, context switch if not MT */
    CScancelCheck(dsh->dsh_sid);
    if (QEF_CHECK_FOR_INTERRUPT(qef_cb, dsh) == E_DB_ERROR)
	return (E_DB_ERROR);

    /* If the trace point qe90 is turned on then gather cpu and dio stats */
    if (dsh->dsh_qp_stats)
    {
	qen_bcost_begin(dsh, &timerstat, qen_status);
    }

    if (node->node_qen.qen_tsort.tsort_dups == DMR_NODUPLICATES &&
        ult_check_macro(&qef_cb->qef_trace, 92, &val1, &val2))
            heap_sort = FALSE;

    if (ult_check_macro(&qef_cb->qef_trace, 94, &val1, &val2))
    {
	no_qef = TRUE;
	qen_status->node_u.node_sort.node_sort_status = QEN9_DMF_SORT;
    }

#ifdef xDEBUG
    (VOID) qe2_chk_qp(dsh);
#endif

    for (;;)	    /* to break off in case of error */
    {
	if (reset && qen_status->node_status != QEN0_INITIAL)
	{
	    status = qen_ts_reset(dsh, node, qen_status);
	    if (status != E_DB_OK)
		break;
	}

	/* If NO MORE ROWS from this node, just return */
	if (qen_status->node_status == QEN4_NO_MORE_ROWS)
	{
	    dsh->dsh_error.err_code = E_QE0015_NO_MORE_ROWS;
	    status = E_DB_WARN;
	    break;
	}

	rowno = node->node_qen.qen_tsort.tsort_mat->qen_output;
	ade_excb = node_xaddrs->qex_otmat;

	/* If this is the first time execution, or if the node is reset, 
	** initialize the sorter. If this is not, just go get a tuple.
	*/
	if (qen_status->node_status == QEN0_INITIAL || 
	    qen_status->node_status == QEN1_EXECUTED)
	{
	    if (qen_status->node_status == QEN0_INITIAL)
	    {
		if (qen_status->node_u.node_sort.node_sort_status == QEN0_QEF_SORT)
		{
		    status = qes_init(dsh, shd, node, rowno,
				node->node_qen.qen_tsort.tsort_dups);
		    if (status != E_DB_OK)
		    {
			if (dsh->dsh_error.err_code == E_QE000D_NO_MEMORY_LEFT)
			{
			    /* out of space, convert it to DMF sort */
			    qen_status->node_u.node_sort.node_sort_status = QEN9_DMF_SORT;
			    status = E_DB_OK;
			}
			else
			{
			    break;
			}
		    }
		}

		if (qen_status->node_u.node_sort.node_sort_status == QEN9_DMF_SORT)
		{
		    status = qen_ts_dump(shd, dsh, node, 
					rowno, heap_sort, no_qef);
		    if (status != E_DB_OK)
			break;
		}

		if (node->node_qen.qen_tsort.tsort_pqual != NULL)
		    qeq_join_pqreset(dsh, node->node_qen.qen_tsort.tsort_pqual);

		qen_status->node_status = QEN1_EXECUTED;
	    }

	    /* Common code for QEN0_INITIAL and QEN1_EXECUTED */

	    /* Get dm_mdata ready for DMF loading  */
	    dm_mdata.data_address = dsh->dsh_row[rowno];
	    dm_mdata.data_size = dsh->dsh_qp_ptr->qp_row_len[rowno];
	    dmr_load->dmr_mdata = &dm_mdata;

            dsh->dsh_qp_status |= DSH_SORT;

	    /* Get rows from the underneath node and append them to the 
	    ** sorter */
	    for (;;)
	    {
		/* fetch rows */
		status = (*out_node->qen_func)(out_node, qef_rcb, dsh, out_func);
		out_func = NO_FUNC;
		if (status != E_DB_OK)
		{
		    /* the error.err_code field in qef_rcb should already be
		    ** filled in.
		    */
		    if (dsh->dsh_error.err_code == E_QE0015_NO_MORE_ROWS)
		    {
			status = E_DB_OK;
		    }
		    else if (dsh->dsh_error.err_code == E_QE00A5_END_OF_PARTITION &&
			(node->qen_flags & QEN_PART_SEPARATE))
		    {
			/* End of rows from partitioning group. Flag the
			** fact and make it look like regular "eof". */
			qen_status->node_status_flags |= QEN2_OPART_END;
			dsh->dsh_error.err_code = E_QE0015_NO_MORE_ROWS;
			status = E_DB_OK;
		    }

		    break;
		}
		
		/* project the attributes into sort tuple row */
		status = qen_execute_cx(dsh, ade_excb);
		if (status != E_DB_OK)
		    break;

		/* If we're generating partition qualifications on behalf
		** of a parent FSM join, eval against this row.
		*/
		if (node->node_qen.qen_tsort.tsort_pqual != NULL)
		{
		    status = qeq_join_pquals(dsh, node->node_qen.qen_tsort.tsort_pqual);
		    if (status != E_DB_OK)
			break;
		}

                /* append the sort tuple into the sorter - note periodic
                ** differences between heap sort and trace point mandated
                ** insert sort */
		if (qen_status->node_u.node_sort.node_sort_status == QEN0_QEF_SORT)
		{
		    if (heap_sort) status = qes_putheap(dsh, shd, 
                         node->node_qen.qen_tsort.tsort_cmplist,
                         node->node_qen.qen_tsort.tsort_scount); 
                     else status = qes_insert(dsh, shd,
                         node->node_qen.qen_tsort.tsort_cmplist,
                         node->node_qen.qen_tsort.tsort_scount);

		    if (status != E_DB_OK && 
			dsh->dsh_error.err_code == E_QE000D_NO_MEMORY_LEFT)
		    {
			/* out of space, convert it to DMF sort */
			qen_status->node_u.node_sort.node_sort_status = QEN9_DMF_SORT;
			status = qen_ts_dump(shd, dsh, 
					node, rowno, heap_sort, no_qef);
		    }
		    if (status != E_DB_OK)
			break;
		}

		if(qen_status->node_u.node_sort.node_sort_status == QEN9_DMF_SORT)  
		{
		    status = dmf_call(DMR_LOAD, dmr_load);
		    if (status != E_DB_OK)
		    {
			dsh->dsh_error.err_code = dmr_load->error.err_code; 
			break;
		    }
		}

                if (!(Qef_s_cb->qef_state & QEF_S_IS_MT) && (loop_cntr++ >1000))
                {
                   /* On an Internal threaded system, after processing 1000 rows
                   ** give another thread a chance.
                   */

                   loop_cntr = 0;

                   CSswitch();
                }
	    }
	    if (status != E_DB_OK)
		break;

	    /* End of loading loop */
	    /* Finish up join-time part qual if we're doing it */
	    if (node->node_qen.qen_tsort.tsort_pqual != NULL)
	    {
		qeq_join_pqeof(dsh, node->node_qen.qen_tsort.tsort_pqual);
	    }
	    if(qen_status->node_u.node_sort.node_sort_status == QEN0_QEF_SORT)
	    {
		if (!heap_sort) qes_endsort(dsh, shd);
						/* prep return of tups */

	    }
	    else   /* DMF */
	    {
		/* Tell DMF that there are no more rows */
		dmr_load->dmr_flags_mask = (DMR_ENDLOAD | DMR_SORT_NOCOPY);
		status = dmf_call(DMR_LOAD, dmr_load);
		if (status != E_DB_OK)
		{
		    dsh->dsh_error.err_code = dmr_load->error.err_code;
		    break;
		}

		/* position the table for reading sorted tuples */
		dmr_get->dmr_flags_mask = DMR_SORTGET;
		dmr_get->dmr_position_type = DMR_ALL;
		status = dmf_call(DMR_POSITION, dmr_get);
		if (status != E_DB_OK)
		{
		    dsh->dsh_error.err_code = dmr_get->error.err_code;
		    break;
		}
	    }

	    /* Mark the node is ready to return tuples */
	    qen_status->node_status = QEN3_GET_NEXT_INNER;
	}


	/* 
	** Return a tuple from the sorter to the caller.
	*/
	if (qen_status->node_u.node_sort.node_sort_status == QEN0_QEF_SORT)
	{
	    if (heap_sort) 
	    {
		status = qes_getheap(dsh, shd, 
                         node->node_qen.qen_tsort.tsort_cmplist,
                         node->node_qen.qen_tsort.tsort_scount); 
		if (status != E_DB_OK) 
		{
		    if (dsh->dsh_error.err_code == E_QE0015_NO_MORE_ROWS)
		    {
			qen_status->node_status = QEN4_NO_MORE_ROWS;
			status = E_DB_WARN;
		    }
		    break;
		}
	    }
	    else
	    {		/* rows come straight from pointer array */
	 	/* Check for the end of buffer */
		if (shd->shd_next_tup == shd->shd_tup_cnt)
		{
		    qen_status->node_status = QEN4_NO_MORE_ROWS;
		    dsh->dsh_error.err_code = E_QE0015_NO_MORE_ROWS;
		    status = E_DB_WARN;
		    break;
		}

		/* Copy tuple to the row buffer */
		MEcopy((PTR)(shd->shd_vector[shd->shd_next_tup]), 
		    shd->shd_width, (PTR)shd->shd_row);
		++shd->shd_next_tup;
		status = E_DB_OK;
	    }
	}
	else
	{
	    dmr_get->dmr_flags_mask = (DMR_NEXT | DMR_SORTGET);
	    status = dmf_call(DMR_GET, dmr_get);
	    if (status != E_DB_OK)
	    {
		if (dmr_get->error.err_code == E_DM0055_NONEXT)
		{
		    qen_status->node_status = QEN4_NO_MORE_ROWS;
		    dsh->dsh_error.err_code = E_QE0015_NO_MORE_ROWS;
		    status = E_DB_WARN;
		}
		else
		{
		    dsh->dsh_error.err_code = dmr_get->error.err_code;
		}
		break;
	    }
	}
	/* status has to be OK here */

	/* Increment the count of rows that this node has returned */
	qen_status->node_rcount++;
	dsh->dsh_error.err_code = 0;

	/* print tracing information DO NOT xDEBUG THIS */
	if (node->qen_prow &&
		(ult_check_macro(&qef_cb->qef_trace, 100+node->qen_num,
				    &val1, &val2) ||
		    ult_check_macro(&qef_cb->qef_trace, 99,
				    &val1, &val2)
		)
	    )
	{
	    (void) qen_print_row(node, qef_rcb, dsh);
	}

	break;
    }	    /* end of error-break loop */

#ifdef xDEBUG
    (VOID) qe2_chk_qp(dsh);
#endif

    if (dsh->dsh_qp_stats)
    {
	qen_ecost_end(dsh, &timerstat, qen_status);
    }

    dsh->dsh_qp_status &= ~DSH_SORT;

    if (dsh->dsh_error.err_code == E_QE0015_NO_MORE_ROWS &&
	(qen_status->node_status_flags & QEN2_OPART_END))
    {
	/* If this was just the end of a partition, reset error code
	** to notify the caller. */
	qen_status->node_status_flags &= ~QEN2_OPART_END;
	dsh->dsh_error.err_code = E_QE00A5_END_OF_PARTITION;
	status = E_DB_WARN;
    }
    return (status);
}
Example #4
0
DB_STATUS
qen_fsmjoin(
QEN_NODE	*node,
QEF_RCB		*qef_rcb,
QEE_DSH		*dsh,
i4		function )
{
    QEF_CB	    *qef_cb = dsh->dsh_qefcb;
    DMR_CB	    *dmrcb;
    QEN_NODE	    *out_node = node->node_qen.qen_sjoin.sjn_out;
    QEN_NODE	    *in_node = node->node_qen.qen_sjoin.sjn_inner;
    QEE_XADDRS	    *node_xaddrs = dsh->dsh_xaddrs[node->qen_num];
    QEN_STATUS	    *qen_status = node_xaddrs->qex_status;
    ADE_EXCB	    *ade_excb;
    ADE_EXCB	    *jqual_excb = node_xaddrs->qex_jqual;
    QEN_HOLD	    *qen_hold;
    QEN_HOLD	    *ijFlagsHold = (QEN_HOLD *)NULL;
    QEN_SHD         *qen_shd;
    QEN_SHD         *ijFlagsShd;
    DB_STATUS	    status = E_DB_OK;
    bool	    reset = FALSE;
    bool	    out_reset = FALSE;
    bool	    in_reset = FALSE;
    bool            ojoin = (node->node_qen.qen_sjoin.sjn_oj != NULL); 
    bool            ljoin = FALSE;
    bool            rjoin = FALSE;
    bool	    innerTupleJoined;
    bool	    rematerializeInnerTuple = TRUE;
			/* During full joins, the last driving tuple may left
			** join.  This 0s all special eqcs from the
			** re-scannable stream.  The current re-scannable
			** tuple will right join.  To recover the state of
			** its special eqcs, simply re-materialize the inner
			** tuple.  That's what this variable is for.
			*/
    i4		    new_to_old;
    i4		    join_result;
    i4	    val1;
    i4	    val2;
    TIMERSTAT	    timerstat;
    bool potential_card_violation = FALSE;

#ifdef xDEBUG
    (VOID) qe2_chk_qp(dsh);
#endif

    if (function != 0)
    {
	if (function & FUNC_RESET)
	{
	    reset = in_reset = out_reset = TRUE;
	}

	/* Do open processing, if required. Only if this is the root node
	** of the query tree do we continue executing the function. */
	if ((function & TOP_OPEN || function & MID_OPEN) 
	    && !(qen_status->node_status_flags & QEN1_NODE_OPEN))
	{
	    status = (*out_node->qen_func)(out_node, qef_rcb, dsh, MID_OPEN);
	    status = (*in_node->qen_func)(in_node, qef_rcb, dsh, MID_OPEN);
	    qen_status->node_status_flags |= QEN1_NODE_OPEN;
	    if (function & MID_OPEN)
		return(E_DB_OK);
	    function &= ~TOP_OPEN;
	}
	/* Do close processing, if required. */
	if (function & FUNC_CLOSE)
	{
	    if (!(qen_status->node_status_flags & QEN8_NODE_CLOSED))
	    {
		/* Ideally we would clean up all of our own shd crap here
		** instead of making qee do it...
		*/
		status = (*out_node->qen_func)(out_node, qef_rcb, dsh, FUNC_CLOSE);
		status = (*in_node->qen_func)(in_node, qef_rcb, dsh, FUNC_CLOSE);
		qen_status->node_status_flags = 
		    (qen_status->node_status_flags & ~QEN1_NODE_OPEN) | QEN8_NODE_CLOSED;
	    }
	    return(E_DB_OK);
	}

	/* End of partition group call just gets passed down. */
	if (function & FUNC_EOGROUP)
	{
	    status = (*out_node->qen_func)(out_node, qef_rcb, dsh, FUNC_EOGROUP);
	    status = (*in_node->qen_func)(in_node, qef_rcb, dsh, FUNC_EOGROUP);
	    return(E_DB_OK);
	}
    } /* if function */

    /* If the trace point qe90 is turned on then gather cpu and dio stats */
    if (dsh->dsh_qp_stats)
    {
	qen_bcost_begin(dsh, &timerstat, qen_status);
    }
     

    /* Check for cancel, context switch if not MT */
    CScancelCheck(dsh->dsh_sid);
    if (QEF_CHECK_FOR_INTERRUPT(qef_cb, dsh) == E_DB_ERROR)
	return (E_DB_ERROR);

    dsh->dsh_error.err_code = E_QE0000_OK;

    qen_hold = dsh->dsh_hold[node->node_qen.qen_sjoin.sjn_hfile];
    qen_shd = dsh->dsh_shd[dsh->dsh_qp_ptr->qp_sort_cnt +
                            node->node_qen.qen_sjoin.sjn_hfile];

    if( ojoin && node->node_qen.qen_sjoin.sjn_oj->oj_ijFlagsFile >= 0 )
    {
        ijFlagsHold =
	  dsh->dsh_hold[node->node_qen.qen_sjoin.sjn_oj->oj_ijFlagsFile];
        ijFlagsShd = dsh->dsh_shd[dsh->dsh_qp_ptr->qp_sort_cnt +
                            node->node_qen.qen_sjoin.sjn_oj->oj_ijFlagsFile];
    }

    if ( ojoin ) switch(node->node_qen.qen_sjoin.sjn_oj->oj_jntype)
    {
       case DB_LEFT_JOIN:
         ljoin = TRUE;
         break;
       case DB_RIGHT_JOIN:
         rjoin = TRUE;
         break;
       case DB_FULL_JOIN:
         ljoin = TRUE;
         rjoin = TRUE;
         break;
       default: break;
    }

    /* If the node is to be reset, dump the hold file and reset the
    ** inner/outer nodes */

loop_reset:
    if (reset)
    {
	if (qen_status->node_status != QEN0_INITIAL && 
	    in_node->qen_type != QE_SORT)
	{
	    /* reset in memory or dump dmf hold if it has been created */
	    status = qen_u9_dump_hold(qen_hold, dsh, qen_shd);
	    if(status) goto errexit;
	    qen_hold->hold_medium = HMED_IN_MEMORY;  /* set back to mem */
	}
	qen_hold->hold_buffer_status = HFILE6_BUF_EMPTY;

	if ( qen_status->node_status != QEN0_INITIAL && ijFlagsHold )
	{
	    /* dump tid hold file if it has been created */

	    status = qen_u9_dump_hold( ijFlagsHold, dsh, ijFlagsShd );
	    if(status) goto errexit;  
	    ijFlagsHold->hold_medium = HMED_IN_MEMORY;  /* set back to mem */
	}

	qen_status->node_status = QEN0_INITIAL;  /* reset = reintialize */
	qen_status->node_u.node_join.node_inner_status = QEN0_INITIAL;
	qen_status->node_u.node_join.node_outer_status = QEN0_INITIAL;
	qen_status->node_u.node_join.node_outer_count = 0;

	qen_status->node_access = (	QEN_READ_NEXT_OUTER |
					QEN_READ_NEXT_INNER |
					QEN_OUTER_HAS_JOINED	);  
    }

    if (qen_status->node_status == QEN0_INITIAL)  
    {
	qen_status->node_u.node_join.node_outer_status = QEN0_INITIAL;

	/* set num entries in mem_hold in case we build one */
	/* this may not be a hard number in future */
	/* qen_shd->shd_tup_cnt = 20; */

	/* by setting it to -1, the required memory will be configured to */
	/* suit the condition. if it is < 20, it will use the dmf hold mem*/
	/* ramra01 19-oct-94 */
	qen_shd->shd_tup_cnt = -1;

	if( ijFlagsHold )
	{
	    ijFlagsHold->hold_status = HFILE0_NOFILE;	/* default */
	    ijFlagsHold->hold_status2 = 0;		/* default */
	    ijFlagsHold->hold_medium = HMED_IN_MEMORY;  /* default */
	    /* in case we build a hold file
	    ** tell qen_u1_append to calculate its size in memory
	    ** or go to DMF hold */
	    ijFlagsShd->shd_tup_cnt = -1;
	}

	if(rjoin)
	{
	    /* consistency check */
	    if( !ijFlagsHold )
	    {
		/* rjoin and no hold file for inner join flags */
		dsh->dsh_error.err_code = E_QE0002_INTERNAL_ERROR;
		status = E_DB_ERROR;
		goto errexit;           
	    }
	}

	qen_status->node_access = (	QEN_READ_NEXT_OUTER |
					QEN_READ_NEXT_INNER |
					QEN_OUTER_HAS_JOINED	);  
    }

    for (;;)   /* The loop */
    {
	status = E_DB_OK;

	/*********************************************************
	**	
	**	LOGIC TO READ FROM THE OUTER TUPLE STREAM
	**
	**
	**
	*********************************************************/

	if( qen_status->node_access & QEN_READ_NEXT_OUTER )
	{

	    /*
	    ** If the previous outer tuple did not inner join with
	    ** any inner tuples, then it's an outer join.  Return
	    ** it along with nulls for the right side if it passes
	    ** the WHERE clause.
	    */

	    if ( ljoin && !( qen_status->node_access & QEN_OUTER_HAS_JOINED ) )
	    {
		/*
		** Set the "outer has joined" flag so that if we emit
		** a left join, we won't come back into this conditional
		** the next time through this fmsjoin node.
		*/
		qen_status->node_access |= QEN_OUTER_HAS_JOINED;

		/* now execute oj_lnull */
		status = qen_execute_cx(dsh, node_xaddrs->qex_lnull);
		if (status != E_DB_OK)
		    goto errexit;   /* if ade error, return error */

		/* Execute jqual restriction, if any */
		if ( jqual_excb == NULL)
		    break;	/* emit a left join */
		else
		{
		    status = qen_execute_cx(dsh, jqual_excb);
		    if (status != E_DB_OK)
			goto errexit;   /* if ade error, return error */
		    if (jqual_excb->excb_value == ADE_TRUE)
			break;	/* emit a left join */
		}
	    }	/* endif previous outer did not join */

	    qen_status->node_access &= ~(	QEN_READ_NEXT_OUTER |
						QEN_OUTER_HAS_JOINED	);
	    /* get a new outer */

	newouter:
	    if ( qen_status->node_u.node_join.node_outer_status == QEN8_OUTER_EOF )
	    {
	        status = E_DB_WARN;
		dsh->dsh_error.err_code = E_QE0015_NO_MORE_ROWS;
	    }
	    else	/* this is where we actually read the outer stream! */
	    {
	        status = (*out_node->qen_func)(out_node, qef_rcb, dsh,
				(out_reset) ? FUNC_RESET : NO_FUNC);
		if (status == E_DB_OK)
		    qen_status->node_u.node_join.node_outer_count++;
	    }
	    out_reset = FALSE;

	    /* a little error handling.  check for end of outer stream */
	    if (status != E_DB_OK)
	    {
		if (dsh->dsh_error.err_code == E_QE0015_NO_MORE_ROWS ||
		    dsh->dsh_error.err_code == E_QE00A5_END_OF_PARTITION &&
		    node->qen_flags & QEN_PART_SEPARATE)
		{
		    /* If no outer rows were read and we're doing partition
		    ** grouping, skip the next inner partition to re-sync. */
		    if (dsh->dsh_error.err_code == E_QE00A5_END_OF_PARTITION &&
			qen_status->node_u.node_join.node_outer_count <= 0)
		     if (!rjoin)
		     {
			if (node->qen_flags & QEN_PART_SEPARATE)
			{
			    status = (*in_node->qen_func)(in_node, qef_rcb, 
						dsh, FUNC_EOGROUP);
			    if (dsh->dsh_error.err_code == 
						E_QE00A5_END_OF_PARTITION)
				qen_status->node_status_flags |=
						QEN4_IPART_END;
					/* if just EOPartition, flag it */
			}
		     goto errexit;
		     }

		    qen_status->node_access |= QEN_OUTER_HAS_JOINED;
                    qen_status->node_u.node_join.node_outer_status = QEN8_OUTER_EOF;

		    if (dsh->dsh_error.err_code == E_QE00A5_END_OF_PARTITION)
			qen_status->node_status_flags |= QEN2_OPART_END;
					/* if just EOPartition, flag it */

		    /*
		    ** If ( the inner stream is exhausted and there's nothing
		    ** to rescan ) or we're not right joining,
		    ** then there are no more tuples to return.  This should
		    ** be the only way to end this fsmjoin node.
		    */
		    if ( INNER_STREAM_EXHAUSTED || rjoin == FALSE  )
                    {
		       qen_status->node_status = QEN4_NO_MORE_ROWS;
                       break;    /* done */
                    }    
                    
                    else	/* we must check some more inner tuples */
		    {
                       dsh->dsh_error.err_code = 0;   /*reset*/

		       if(qen_status->node_status == QEN0_INITIAL)  /* empty */
		       {
			    qen_status->node_status = QEN1_EXECUTED;
			    qen_hold->hold_status = HFILE0_NOFILE;
			    qen_hold->hold_status2 = 0;
			    qen_hold->hold_medium = HMED_IN_MEMORY;
			    if(in_node->qen_type == QE_SORT)
			    {
				status = qen_u32_dosort(in_node, 
							qef_rcb, 
							dsh, 
							qen_status, 
							qen_hold,
							qen_shd, 
					(in_reset) ? FUNC_RESET : NO_FUNC);
				if(status) goto errexit;
			    }
			    in_reset = FALSE;
		       }	/* endif first time through */
		    }	/* endif no more inner tuples to check */
		}
		else if (dsh->dsh_error.err_code == E_QE00A5_END_OF_PARTITION)
		{
		    /* No more rows in partitioning group - read from 
		    ** next group. */
		    out_reset = TRUE;
		    goto newouter;
		}
		else  /* return error from reading outer stream */
		{
		    break;
		}
	    }	/* end of error handling for outer stream EOF */


            if(qen_status->node_status == QEN0_INITIAL)
            {
		qen_status->node_status = QEN1_EXECUTED;  /* init done */
		qen_hold->hold_status = HFILE0_NOFILE;  /* default */
		qen_hold->hold_status2 = 0;		/* default */
		qen_hold->hold_medium = HMED_IN_MEMORY;  /* default */
		if(in_node->qen_type  == QE_SORT)
		{

		    status = qen_u32_dosort(in_node, 
					    qef_rcb, 
					    dsh, 
					    qen_status, 
					    qen_hold,
					    qen_shd, 
					(in_reset) ? FUNC_RESET : NO_FUNC);
		    if(status) goto errexit;
		    in_reset = FALSE;
		}

		/* now materialize the first join key */
		status = qen_execute_cx(dsh, node_xaddrs->qex_okmat);
		if (status != E_DB_OK)
		    goto errexit;   /* if ade error, return error */
	    }

	    /* If not the first time */
	    else
	    {
		if ( qen_status->node_u.node_join.node_outer_status == QEN8_OUTER_EOF )
		{
		    new_to_old = NEW_GT_OLD;
		}
		else	/* outer not at EOF */
		{
		    /* compare the old outer key to the new one. */
		    new_to_old = ADE_1EQ2;
		    if ((ade_excb = node_xaddrs->qex_kcompare) != NULL)
		    {
			status = qen_execute_cx(dsh, ade_excb);
			if (status != E_DB_OK)
			    goto errexit;   /* if ade error, return error */
			new_to_old = ade_excb->excb_cmp;
		    }

		    /* Materialize the new outer key if the old and the 
		    ** new outer keys are not equal */
		    if (new_to_old != ADE_1EQ2)
		    {
			status = qen_execute_cx(dsh, node_xaddrs->qex_okmat);
			if (status != E_DB_OK)
			    goto errexit;   /* if ade error, return error */
		    }
		    else if ((node->qen_flags & QEN_CARD_MASK) == QEN_CARD_01L)
		    {
			/* Right outer - note cardinality */
			potential_card_violation = (new_to_old == ADE_1EQ2);
		    }
		}	/* endif outer not at EOF */

		/*
		** If there are inner tuples to rescan, decide whether
		** to thumb through them again or dump them.
		*/


		if ( qen_status->node_access & QEN_RESCAN_MARKED )
		{
		    if ( new_to_old == ADE_1EQ2 )
		    {
		        status = repositionInnerStream( node, dsh );
		        if(status != E_DB_OK) break;   /* to error */
		        continue;
		    }

		    else	/* key has changed */
		    {
		        if ( rjoin )
		        {
		            status = repositionInnerStream( node, dsh );
		            if(status != E_DB_OK) break;   /* to error */
		            qen_status->node_access |= QEN_LOOKING_FOR_RIGHT_JOINS;
		            continue; /* to get a new inner */
		        }
		        else	/* don't have to return right joins */
		        {
		            status = clearHoldFiles( node, dsh );
		            if(status != E_DB_OK) break;   /* to error */
		        }
		    }	/* endif comparison of new and old keys */
		}	/* endif there are inner tuples to rescan */
	    }	/* end first or subsequent times */
	}  /* end if read_outer  */

	/*********************************************************
	**	
	**	LOGIC TO READ FROM THE INNER TUPLE STREAM
	**
	**
	*********************************************************/

	if( qen_status->node_access & QEN_READ_NEXT_INNER )
	{
            qen_status->node_access &= ~QEN_READ_NEXT_INNER;

	    if ( !INNER_STREAM_EXHAUSTED )
	    {
		/*
		** If we're rescanning the hold files and will eventually
		** have to look for right joins, read from the hold file
		** of inner join flags.
		*/
		if ( rjoin &&
		     ( ijFlagsHold->hold_status2 & HFILE_REPOSITIONED )  )
		{
		    if (qen_u40_readInnerJoinFlag( ijFlagsHold, dsh,
	    				ijFlagsShd,
					&innerTupleJoined ) != E_DB_OK)
		    {
			/*
			** If innerJoinFlags is exhausted and we were
			** looking for right joins, then we've found
			** all the right joins for this key.  Dump the
			** hold files.
			*/
		        if (dsh->dsh_error.err_code == E_QE0015_NO_MORE_ROWS)
		        {
			    /* Hold file ends, mark this. Continue reading. */
			    ijFlagsHold->hold_buffer_status = HFILE6_BUF_EMPTY;
			    ijFlagsHold->hold_status = HFILE2_AT_EOF;
			    if ( qen_status->node_access &
				 QEN_LOOKING_FOR_RIGHT_JOINS )
			    {
				qen_status->node_access &= 
				    ~( QEN_LOOKING_FOR_RIGHT_JOINS |
				       QEN_READ_NEXT_OUTER );
				qen_status->node_access |= 
				    QEN_READ_NEXT_INNER;
				status = clearHoldFiles( node, dsh );
				if(status != E_DB_OK) break;   /* to error */
				continue;	/* get next inner */
			    }
			}
			else	/* other errors are fatal */
			{
			    break;
			}
		    }	/* endif innerJoinFlags read wasn't OK */
		}	/* endif rjoin and rescanning hold files */

	        /* Read from hold file if it is positioned */
	        if (qen_hold->hold_status == HFILE3_POSITIONED)
	        {
		    if (qen_u4_read_positioned(qen_hold, dsh,
	    			       qen_shd) != E_DB_OK)
		    {
		        if (dsh->dsh_error.err_code == E_QE0015_NO_MORE_ROWS)
		        {
			    /* Hold file ends, must read from inner node */
			    qen_hold->hold_buffer_status = HFILE6_BUF_EMPTY;
			    qen_hold->hold_status = HFILE2_AT_EOF;
			    dsh->dsh_error.err_code = 0;
			    qen_status->node_access |= QEN_READ_NEXT_INNER;
			    if (node->qen_flags & QEN_PART_SEPARATE &&
				!(qen_hold->hold_status2 &
						HFILE_LAST_PARTITION))
			    {
				qen_status->node_status_flags |=
							QEN4_IPART_END;
				dsh->dsh_error.err_code = 
						E_QE00A5_END_OF_PARTITION;
			    }
			    continue;	/* to read a new inner */
		        }
			else	/* other, presumably fatal error */
			{
			    break;
			}
		    }   /* end if hold end */

		    if(in_node->qen_type == QE_SORT)  /* if hold from sort */
		    {
		        /* Materialize the inner tuple from sort's row buffer
		           into my row buffer. */
			status = qen_execute_cx(dsh, node_xaddrs->qex_itmat);
			if (status != E_DB_OK)
			    goto errexit;   /* if ade error, return error */
			rematerializeInnerTuple = FALSE;
		    } /* end if hold from sort */

		    qen_hold->hold_buffer_status = HFILE7_FROM_HOLD;
	        }	/* end if positioned */
	        /* if not EOF on stream */
	        else if (qen_status->node_u.node_join.node_inner_status != QEN11_INNER_ENDS)
	        {
		    if(qen_hold->unget_status)   /* if occupied */
                    {
                        /* put unget in row buffer */
                        MEcopy((PTR)qen_hold->unget_buffer, 
			   qen_shd->shd_width,
                           (PTR)qen_shd->shd_row);
                        qen_hold->unget_status = 0;   /* set no unget */
		        qen_hold->hold_buffer_status = HFILE8_FROM_INNER;
                    }
                    else   /* get new from stream */
                    {
		newinner:
		        status = (*in_node->qen_func)(in_node, qef_rcb, dsh,
					(in_reset) ? FUNC_RESET : NO_FUNC);
		        in_reset = FALSE;
		        if (status != E_DB_OK)
		        {
			    if (dsh->dsh_error.err_code == E_QE0015_NO_MORE_ROWS ||
				dsh->dsh_error.err_code == E_QE00A5_END_OF_PARTITION
				&& (node->qen_flags & QEN_PART_SEPARATE))
			    {
			        qen_hold->hold_buffer_status = HFILE6_BUF_EMPTY;

				if (dsh->dsh_error.err_code == E_QE00A5_END_OF_PARTITION)
				    qen_status->node_status_flags |= QEN4_IPART_END;
					/* if just EOPartition, flag it */

			        /* mark EOF on stream */
			        qen_status->node_u.node_join.node_inner_status = QEN11_INNER_ENDS;
			        if(qen_hold->hold_status == HFILE2_AT_EOF ||
			           qen_hold->hold_status == HFILE0_NOFILE ||
			           qen_hold->hold_status == HFILE1_EMPTY )
			        {
			           qen_status->node_u.node_join.node_hold_stream = 
			        	    QEN5_HOLD_STREAM_EOF;
			        }
			    }
			    else if (dsh->dsh_error.err_code == E_QE00A5_END_OF_PARTITION)
			    {
				/* No more rows in partitioning group - read 
				** from next group. */
				in_reset = TRUE;
				goto newinner;
			    }
			    else
			    {
			        break;	/* other, fatal error */
			    }
		        }
			else	/* inner tuple successfully read */
			{
		            /* Materialize the inner tuple into row buffer. */
			    status = qen_execute_cx(dsh, node_xaddrs->qex_itmat);
			    if (status != E_DB_OK)
				goto errexit;
		            qen_hold->hold_buffer_status = HFILE8_FROM_INNER;
			    rematerializeInnerTuple = FALSE;
			}
                    }  /* end if unget occupied */
	        }   /* end of read from hold/inner */
	    }	/* endif inner stream not exhausted */
	}    /* end if read_inner */

	/***************************************************************
	**
	**	LOOK FOR RIGHT JOINS
	**
	**
	***************************************************************/

	if ( qen_status->node_access & QEN_LOOKING_FOR_RIGHT_JOINS )
	{
	    qen_status->node_access &= ~QEN_READ_NEXT_OUTER;
	    qen_status->node_access |= QEN_READ_NEXT_INNER;

	    if ( innerTupleJoined == FALSE )
	    {
		status = qen_execute_cx(dsh, node_xaddrs->qex_rnull);
		if (status != E_DB_OK)
		    goto errexit;   /* if ade error, return error */
		if (jqual_excb == NULL)
		    break;	/* return right join */
		else
		{
		    status = qen_execute_cx(dsh, jqual_excb);
		    if (status != E_DB_OK)
			goto errexit;   /* if ade error, return error */
		    if (jqual_excb->excb_value == ADE_TRUE)
			break;  /* to return right join */
		}
	    }	/* endif inner tuple joined with some outer */

	    continue;	/* evaluate next inner tuple for right joins */
	}	/* endif looking for right joins */

	/***************************************************************
	**
	**	COMPARE THE INNER AND OUTER JOIN KEYS
	**
	**
	***************************************************************/


	if ( INNER_STREAM_EXHAUSTED ||
	     qen_hold->hold_buffer_status == HFILE6_BUF_EMPTY )
	{
	    join_result = OUTER_LT_INNER;
	}

	else if(qen_status->node_u.node_join.node_outer_status == QEN8_OUTER_EOF)
	{
	    join_result = OUTER_GT_INNER;
	}

	else	/* we have an inner and outer.  join them on the join key. */
	{
	    join_result = ADE_1EQ2;
	    if ((ade_excb = node_xaddrs->qex_joinkey) != NULL)
	    {
		status = qen_execute_cx(dsh, ade_excb);
		if (status != E_DB_OK)
		    goto errexit;   /* if ade error, return error */
		join_result = ade_excb->excb_cmp;
	    }

	    if (join_result == ADE_BOTHNULL)
	    {  
	        join_result = OUTER_GT_INNER;
	    }

	    else if (join_result == ADE_1ISNULL)
	    {
	        join_result = OUTER_GT_INNER;
	    }

	    else if (join_result == ADE_2ISNULL)
	    {
	        join_result = OUTER_LT_INNER;
	    }
	}	/* endif we have inner and outer */

	/***************************************************************
	**
	**	OUTER AND INNER KEYS NOW JOINED.  PERFORM OTHER
	**	QUALIFICATIONS NOW.  EMIT JOINS WHERE APPROPRIATE.
	**
	***************************************************************/


	if (join_result == OUTER_LT_INNER)
	{                           
	    qen_status->node_access |= QEN_READ_NEXT_OUTER;
	    qen_status->node_access &= ~QEN_READ_NEXT_INNER;
	    continue;	/* get next outer */
	}

	if ( join_result == OUTER_GT_INNER )
	{
	    qen_status->node_access &= ~QEN_READ_NEXT_OUTER;
	    qen_status->node_access |= QEN_READ_NEXT_INNER;

	    if ( rjoin )
	    {
		/* rematerialize inner tuple if the ultimate outer tuple
		** just left joined.  rematerialization will reset the
		** special equivalence classes from the inner stream.
		*/

		if ( rematerializeInnerTuple == TRUE )
		{
		    status = qen_execute_cx(dsh, node_xaddrs->qex_itmat);
		    if (status != E_DB_OK)
			goto errexit;   /* if ade error, return error */
		    rematerializeInnerTuple = FALSE;
		}

		/* execute oj_rnull */
		status = qen_execute_cx(dsh, node_xaddrs->qex_rnull);
		if (status != E_DB_OK)
		    goto errexit;   /* if ade error, return error */

		if (jqual_excb == NULL)
		    break;	/* return right join */
		else
		{
		    status = qen_execute_cx(dsh, jqual_excb);
		    if (status != E_DB_OK)
			goto errexit;   /* if ade error, return error */
		    if (jqual_excb->excb_value == ADE_TRUE)
			break;  /* to return right join */
		}
	    }
	    continue;	/* get next inner */
	}    /* endif outer greater than inner */
		
	/* We come to this point when joinkey returns OUTER_EQ_INNER */ 

	if ( join_result != OUTER_EQ_INNER )
	{
	    /* consistency check */
	    dsh->dsh_error.err_code = E_QE0002_INTERNAL_ERROR;
	    status = E_DB_ERROR;
	    goto errexit;           
	}	/* end consistency check */

	if (qen_hold->hold_buffer_status == HFILE8_FROM_INNER)
	{
	    /* append to hold */
            status = qen_u1_append(qen_hold, qen_shd, dsh); 
	    if(status) break;  /* to return error */
	}	

	/* If this is the first inner that joins with the current
	** outer, save the hold file TID so we can reposition it later.
	*/
	if ( !( qen_status->node_access & QEN_RESCAN_MARKED ) )
	{
	    if ( qen_u5_save_position(qen_hold, qen_shd) )
		goto errexit;
	    qen_status->node_access |= QEN_RESCAN_MARKED;
	}
	else if ((node->qen_flags & QEN_CARD_MASK) == QEN_CARD_01R &&
	    (qen_status->node_access & QEN_OUTER_HAS_JOINED) != 0)
	{
	    /* Left outer - note cardinality */
	    potential_card_violation = TRUE;
	}

	qen_status->node_access &= ~QEN_READ_NEXT_OUTER;
	qen_status->node_access |= QEN_READ_NEXT_INNER;

	/* execute OQUAL */
	ade_excb = node_xaddrs->qex_onqual;
	status = qen_execute_cx(dsh, ade_excb);
	if (status != E_DB_OK)
	    goto errexit;   /* if ade error, return error */
	if (ade_excb == NULL || ade_excb->excb_value == ADE_TRUE)
	{
	    /* not OJ, or OQUAL succeeds.  Remember that a join occurred. */
	    qen_status->node_access |= QEN_OUTER_HAS_JOINED;  

	    if ( rjoin )
	    {
		if ( status = qen_u41_storeInnerJoinFlag( ijFlagsHold,
		ijFlagsShd, dsh, innerTupleJoined,
		     ( i4 ) TRUE ) ) goto errexit;   /* error */
	    }

	    /* set the special eqcs to "inner join" state */
	    status = qen_execute_cx(dsh, node_xaddrs->qex_eqmat);
	    if (status != E_DB_OK)
		goto errexit;   /* if ade error, return error */

	    if (jqual_excb != NULL)
	    {
		status = qen_execute_cx(dsh, jqual_excb);
		if (status != E_DB_OK)
		    goto errexit;   /* if ade error, return error */
	    }
	    if( jqual_excb == NULL || jqual_excb->excb_value == ADE_TRUE)
	    {
		/* JQUAL succeeds */
		if(node->node_qen.qen_sjoin.sjn_kuniq)  /* if kuniq */
		{
		    /* make next entry read new outer bit not new inner */
		    qen_status->node_access |= QEN_READ_NEXT_OUTER;
		    qen_status->node_access &= ~QEN_READ_NEXT_INNER;
		}	/* endif key unique */
		if (potential_card_violation)
		{
		    /* We only want to act on seltype violation after
		    ** qualification and that is now. */
		    qen_status->node_status = QEN7_FAILED;
		    dsh->dsh_error.err_code = E_QE004C_NOT_ZEROONE_ROWS;
		    status = E_DB_ERROR;
		    goto errexit;
		}

		break;  /* emit inner join */
	    }
	}
	else	/* OQUAL failed */
	{
	    if ( rjoin )
	    {
	        if ( status = qen_u41_storeInnerJoinFlag( ijFlagsHold,
	            ijFlagsShd, dsh, innerTupleJoined,
		    ( i4 ) FALSE ) )
	        goto errexit;   /* error */
	    }

	}	/* end check of OQUAL status */

	/* OQUAL or JQUAL failed.  Get next inner. */

	continue;

    }	/* end of get loop */

    /********************************************************************
    **
    **	CLEANUP.  MATERIALIZE FUNCTION ATTRIBUTES.  ERROR EXIT WHEN
    **	APPROPRIATE.
    **
    ********************************************************************/

    if (status == E_DB_OK)
    {
	status = qen_execute_cx(dsh, node_xaddrs->qex_fatts);
	if (status != E_DB_OK)
	    goto errexit;   

	/* Increment the count of rows that this node has returned */
	qen_status->node_rcount++;

	/* print tracing information DO NOT xDEBUG THIS */
	if (node->qen_prow != NULL &&
		(ult_check_macro(&qef_cb->qef_trace, 100+node->qen_num,
				    &val1, &val2) ||
		    ult_check_macro(&qef_cb->qef_trace, 99,
				    &val1, &val2)
		)
	    )
	{
	    if (status == E_DB_OK)
	    {
		status = qen_print_row(node, qef_rcb, dsh);
		if (status != E_DB_OK)
		{
		    goto errexit;
		}
	    }
	}

#ifdef xDEBUG
	(VOID) qe2_chk_qp(dsh);
#endif 
    }
    else
    {
	if(in_node->qen_type == QE_SORT)  /* if sort child */
	/* release the memory now if in memory */
	{
	    qen_u31_release_mem(qen_hold, dsh,
	    	  dsh->dsh_shd[in_node->node_qen.qen_sort.sort_shd] );
	}
	else
	if(qen_hold)  /* if hold file */
	{
	    /* release our hold file, if in memory */
	    qen_u31_release_mem(qen_hold, dsh, qen_shd );
	}
    }

errexit:

    if ((dsh->dsh_error.err_code == E_QE0015_NO_MORE_ROWS ||
	dsh->dsh_error.err_code == E_QE00A5_END_OF_PARTITION) &&
	(qen_status->node_status_flags & 
			(QEN2_OPART_END | QEN4_IPART_END)))
    {
	/* Restart using next partitioning group. */
	out_reset = in_reset = reset = TRUE;
	qen_status->node_status_flags &=
			~(QEN2_OPART_END | QEN4_IPART_END);
	goto loop_reset;
    }

    if (dsh->dsh_qp_stats)
    {
	qen_ecost_end(dsh, &timerstat, qen_status);
    }

    return (status);	    

}