/*{ ** Name: QEA_RETROW - build a result row image for a database procedure ** ** Description: ** This function is called to build a result row in a database ** procedure. Since the corresponding action can be contained in ** a loop (for, while or repeat), multiple rows may ultimately ** be returned from one call to the procedure - one per call to ** this function. There may even be multiple return row action ** headers in the same query plan, though they must all build ** identically formatted result rows. ** ** Output buffer position tracking is a little different from the ** usual GET tracking, partly because RETROW's can be interspersed ** with various other actions in the row-producing DBP, and those ** other actions may change the usual rowcount variables. ** RETROW has three variables in the DSH that are used to track ** the current buffer position. "dsh_rpp_rowmax" is the number of ** rows allows to be returned, as passed in from SCF; this is ** set upon (re)entry to the rowproc, although not when re-entering ** QEF after a dependent rule/DBP re-parse/re-load. ** When dsh_rowmax is set, the other two counters, dsh_rpp_rows and ** dsh_rpp_bufs, are cleared. "dsh_rpp_rows" counts rows emitted by ** RETROW actions, and "dsh_rpp_bufs" counts output buffers used. ** (The two are usually the same, but when blobs are around, ** a single row might take up multiple output buffers.) ** The top level of QEF (qeq_query) copies dsh_rpp_rows and ** dsh_rpp_bufs back out to qef_rowcount and qef_count respectively, ** just where SCF wants them. ** ** The current output buffer could be computed from dsh_rpp_bufs, ** but it's easier to track the next buffer to use in dsh_qef_nextout. ** If dsh_qef_nextout isn't set yet, it must be the first row emitted ** since SCF gave us a fresh set of output buffers, and we'll use ** dsh_qef_output (i.e. qef_rcb->qef_output) for the first time. ** ** Note that if the rowproc is interrupted back to SCF to load a ** rule DBP, or for any reason other than "output buffer full", ** SCF is careful to not mess with the output buffers. ** There can only be one top-level row-producing procedure, so ** there's no danger of anything else messing with the output ** in progress. ** ** NOTE: this function was cloned from qea_fetch. The differences in ** which "return row" actions are executed demanded a separation of ** logic from qea_fetch. ** ** Interestingly enough, the separation of rows emitted vs buffers ** used ends up being rather strained, because SCF forgets to make the ** distinction upon input! We can make it work by checking the next- ** buffer link rather than counting buffers. ** ** Inputs: ** action fetch action item ** qef_rcb the qef request block ** function reset flags, not used ** ** Outputs: ** dsh ** .dsh_rpp_rows actual number of rows returned ** .dsh_rpp_bufs number of buffers filled ** .dsh_qef_nextout next output to use ** .dsh_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_QE0015_NO_MORE_ROWS ** Returns: ** E_DB_{OK, WARN, ERROR, FATAL} ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 28-oct-98 (inkdo01) ** written ** 29-dec-03 (inkdo01) ** Changed reset parm to function. ** 28-feb-04 (inkdo01) ** Change qef_rcb parm to dsh in qeq_chk_resources call. ** 2-apr-04 (inkdo01) ** Fix to DSH changes (not updating output buffer list). ** 26-aug-04 (inkdo01) ** Add global base array support for return row results. ** 13-Aug-2005 (schka24) ** Rearrange output buffer tracking variables, revise comments ** to match current reality. ** 1-May-2006 (kschendel) ** Set blob base properly when doing global base arrays. ** 16-May-2010 (kschendel) b123565 ** Caller must pass DSH to make parallel query + table procs ** work properly. ** 21-May-2010 (kschendel) b123775 ** Delete unused sfinit segment test. */ DB_STATUS qea_retrow( QEF_AHD *action, QEF_RCB *qef_rcb, QEE_DSH *dsh, i4 function ) { QEF_CB *qef_cb = qef_rcb->qef_cb; QEF_DATA *output; i4 bufs_used; DB_STATUS status = E_DB_OK; QEN_ADF *qen_adf; ADE_EXCB *ade_excb; #ifdef xDEBUG (VOID) qe2_chk_qp(dsh); #endif /* Check if resource limits are exceeded by this query */ if (action->qhd_obj.qhd_qep.ahd_estimates && (qef_cb->qef_fl_dbpriv & (DBPR_QROW_LIMIT | DBPR_QDIO_LIMIT | DBPR_QCPU_LIMIT | DBPR_QPAGE_LIMIT | DBPR_QCOST_LIMIT)) ) { if (qeq_chk_resource(dsh, action) != E_DB_OK) return (E_DB_ERROR); } /* initialize the output buffer */ qen_adf = action->qhd_obj.qhd_qep.ahd_current; ade_excb = (ADE_EXCB*) dsh->dsh_cbs[qen_adf->qen_pos]; /* Next output buffer to use is "nextout" unless it's not set yet. */ output = dsh->dsh_qef_nextout; if (output == (QEF_DATA *)NULL) output = dsh->dsh_qef_output; bufs_used = 0; /* Materialize the row. Usually just once around the FOR loop, ** unless something odd happens with blobs. */ for (;;) { /* materialize row into output buffer. ** Aim output row (uoutput) at caller supplied output buffer. ** If there are blobs, tell adf that it's the blob area. */ ade_excb->excb_seg = ADE_SMAIN; ade_excb->excb_limited_base = ADE_NOBASE; if (qen_adf->qen_uoutput >= 0) { if (qen_adf->qen_mask & QEN_HAS_PERIPH_OPND) ade_excb->excb_limited_base = qen_adf->qen_uoutput; if (dsh->dsh_qp_ptr->qp_status & QEQP_GLOBAL_BASEARRAY) { dsh->dsh_row[qen_adf->qen_uoutput] = output->dt_data; } else { /* old-fashioned way, uoutput is local base, bias it ** for blobs, set output pointer where old way wants it */ if (qen_adf->qen_mask & QEN_HAS_PERIPH_OPND) ade_excb->excb_limited_base += ADE_ZBASE; ade_excb->excb_bases[ADE_ZBASE+qen_adf->qen_uoutput] = output->dt_data; } ade_excb->excb_size = output->dt_size; } /* process the tuples */ status = ade_execute_cx(dsh->dsh_adf_cb, ade_excb); if (status != E_DB_OK) { #ifdef xDEBUG (VOID) qe2_chk_qp(dsh); #endif if ((status == E_DB_INFO) && (dsh->dsh_adf_cb->adf_errcb.ad_errcode == E_AD0002_INCOMPLETE)) { /* Caught in mid-BLOB. */ dsh->dsh_error.err_code = E_AD0002_INCOMPLETE; ++ bufs_used; if (qen_adf->qen_uoutput >= 0) { output->dt_size = ade_excb->excb_size; ade_excb->excb_limited_base = ADE_NOBASE; if (output->dt_next != NULL) { output = output->dt_next; /* There's room left, loop back and output more */ continue; } } break; } else if ((status = qef_adf_error( &dsh->dsh_adf_cb->adf_errcb, status, qef_cb, &dsh->dsh_error)) != E_DB_OK) { break; } } bufs_used += 1; if (qen_adf->qen_uoutput >= 0) { output->dt_size = ade_excb->excb_size; ade_excb->excb_limited_base = ADE_NOBASE; } break; } /* Don't leave bogus error indication if we managed to fit it all */ if (status == E_DB_OK) dsh->dsh_error.err_code = 0; /* Update counts, buffer addresses. */ ++ dsh->dsh_rpp_rows; /* Always just one row */ dsh->dsh_rpp_bufs += bufs_used; dsh->dsh_qef_nextout = output->dt_next; #ifdef xDEBUG (VOID) qe2_chk_qp(dsh); #endif return (status); }
/*{ ** Name: QEA_SAGG - compute simple aggregate ** ** Description: ** The routines in this file compute simple, scalar aggregates. ** The basic algorithm is: ** 1) initialize the result relation ** 2) read tuples from the QEP ** 3) compute the aggregate for each tuple ** 4) move the result to its output location. ** ** Simple aggregate results are stored in a row buffer. The row buffer ** used is in the qen_output field of the ahd_current QEN_ADF struct. ** ** The new implementation has a choice of two paths for aggregate processing. ** If the involved aggregates are based on a simple table without a solo any ** aggregate,they will invoke a special node called qen_origagg. ** The basic algorithm for this is: ** 1) initialize the result relation ** 2) call the node qen_origagg ** 3) move the result to its output location. ** ** Inputs: ** action QEA_SAGG action item ** ** Outputs: ** qef_rcb ** .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_QE0015_NO_MORE_ROWS ** Returns: ** E_DB_{OK, WARN, ERROR, FATAL} ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 27-aug-86 (daved) ** written ** 02-feb-88 (puree) ** add reset flag to the function arguments. ** 14-jul-93 (ed) ** replacing <dbms.h> by <gl.h> <sl.h> <iicommon.h> <dbdbms.h> ** 3-sep-93 (rickh) ** Flag the referential constraint state machine whether any ** rows qualified or not. This is to support ALTER TABLE ADD ** REFERENTIAL CONSTRAINT. ** 14-jul-95 (ramra01) ** Aggregate Otimization for Count(*) only. This is a forerunner ** for the bit flags in OPF to indicate aggf optimzations for ** count(*), min, max (Any or all of them) in a query with ** restrictions. ** 21-aug-95 (ramra01) ** Simple aggregate optimization extended to all functions ** as long as it is against a base table. ** 01-nov-95 (ramra01) ** count(*) optimize bypass adf calls only when it is the only ** simple aggregate. ** 7-nov-95 (inkdo01) ** Changes to replace QEN_ADF structure instances by pointers in ** QEF_AHD structures. ** 19-feb-96 (inkdo01) ** Minor fix to optimize logic to avoid reference to ADF error ** buffer unless actually in optimize logic (fix for 74283). ** 15-mar-96 (pchang) ** Fixed regression to bug 73095. An earlier fix was broken by changes ** made to enhance the processing of simple aggregate. Specifically, ** processing of simple aggregates is now carried out in DMF via ** qen_origagg() but the qef_remnull flag was not set after we ** returned from the call to DMF. ** 29-oct-97 (inkdo01) ** Added code to support AHD_NOROWS flag (indicating no possible ** rows from underlying query). ** 6-nov-97 (inkdo01) ** Add execution of ahd_constant code. SINIT/FINIT code must be ** executed even if no rows are processed (because of ahd_constant). ** 28-aug-1998 (somsa01 for ocoro02) ** Bug 88218: Only set dmr_cb->dmr_count to 0 if node->qen_type ** = QE_ORIGAGG. Otherwise dmr_cb isn't initialised and causes a ** SEGV. Make sure in future dmr_cb is initialised if used outside ** the scope of QE_ORIGAGG. ** 2-jan-01 (inkdo01) ** Count optimization data ptr now addresses simple i4, not ADF_AG_STRUCT, ** with aggregate code improvements. ** 21-nov-03 (inkdo01) ** Add open_flag parm to qen_func calls for parallel query support. ** 29-dec-03 (inkdo01) ** DSH is now parameter, "function" replaces "reset". ** 16-feb-04 (inkdo01) ** Fix long term latent bug that produces bogus ADF errors. ** 28-feb-04 (inkdo01) ** Change qef_rcb parm to dsh in qeq_chk_resources call. ** 26-aug-04 (inkdo01) ** Support global base array for sagg result. ** 4-Jun-2009 (kschendel) b122118 ** Minor dead code removal. ** 2-Dec-2010 (kschendel) SIR 124685 ** Warning, prototype fixes. */ DB_STATUS qea_sagg( QEF_AHD *action, QEF_RCB *qef_rcb, QEE_DSH *dsh, i4 function ) { QEN_NODE *node = action->qhd_obj.qhd_qep.ahd_qep; bool reset = ((function & FUNC_RESET) != 0); DB_STATUS status; QEF_CB *qef_cb = dsh->dsh_qefcb; ADF_CB *adfcb = dsh->dsh_adf_cb; PTR *cbs = dsh->dsh_cbs; QEN_ADF *qen_adf, *qen_adfconst; ADE_EXCB *ade_excb, *ade_excbconst; i4 rowCount = 0; i4 open_flag = (reset) ? (TOP_OPEN | function) : function; PTR data = NULL; DMR_CB *dmr_cb; ADF_ERROR *adf_errcb; bool origagg = FALSE; i4 instr_cnt = 0; /* Check if resource limits are exceeded by this query */ if (action->qhd_obj.qhd_qep.ahd_estimates && (qef_cb->qef_fl_dbpriv & (DBPR_QROW_LIMIT | DBPR_QDIO_LIMIT | DBPR_QCPU_LIMIT | DBPR_QPAGE_LIMIT | DBPR_QCOST_LIMIT)) ) { if (qeq_chk_resource(dsh, action) != E_DB_OK) return (E_DB_ERROR); } /* initialize the aggregate result */ /* if there is no QEP, this will also create the result value */ qen_adf = action->qhd_obj.qhd_qep.ahd_current; ade_excb = (ADE_EXCB*) cbs[qen_adf->qen_pos]; ade_excb->excb_seg = ADE_SINIT; if (qen_adf->qen_uoutput > -1) { if (dsh->dsh_qp_ptr->qp_status & QEQP_GLOBAL_BASEARRAY) dsh->dsh_row[qen_adf->qen_uoutput] = dsh->dsh_qef_output->dt_data; else ade_excb->excb_bases[ADE_ZBASE + qen_adf->qen_uoutput] = dsh->dsh_qef_output->dt_data; } status = ade_execute_cx(adfcb, ade_excb); if (status != E_DB_OK) { if ((status = qef_adf_error(&adfcb->adf_errcb, status, qef_cb, &dsh->dsh_error)) != E_DB_OK) return (status); } dsh->dsh_qef_remnull |= ade_excb->excb_nullskip; /* check for constant code */ /* if there is some & it returns FALSE, skip to FINIT execution */ qen_adfconst = action->qhd_obj.qhd_qep.ahd_constant; if (qen_adfconst) { ade_excbconst = (ADE_EXCB*) cbs[qen_adfconst->qen_pos]; ade_excbconst->excb_seg = ADE_SMAIN; /* process the constant expression */ status = ade_execute_cx(adfcb, ade_excbconst); if (status != E_DB_OK) { if ((status = qef_adf_error(&adfcb->adf_errcb, status, qef_cb, &dsh->dsh_error)) != E_DB_OK) return (status); } /* handle the condition where the qualification failed */ if (ade_excbconst->excb_value != ADE_TRUE) goto finitit; } ade_countstar_loc( adfcb, ade_excb, &data, &instr_cnt); ade_excb->excb_seg = ADE_SMAIN; /* get the rows */ if (node) { origagg = (node->qen_type == QE_ORIGAGG); if (origagg) { dmr_cb = (DMR_CB*) cbs[node->node_qen.qen_orig.orig_get]; dmr_cb->dmr_agg_excb = (PTR) ade_excb; dmr_cb->dmr_agg_flags = 0; dmr_cb->dmr_qef_adf_cb = (PTR) adfcb; if (action->qhd_obj.qhd_qep.ahd_qepflag & AHD_CSTAROPT) { dmr_cb->dmr_agg_flags |= DMR_AGLIM_COUNT; } if (node->node_qen.qen_orig.orig_flag & ORIG_MAXOPT) { dmr_cb->dmr_agg_flags |= DMR_AGLIM_MAX; } if (node->node_qen.qen_orig.orig_flag & ORIG_MINOPT) { dmr_cb->dmr_agg_flags |= DMR_AGLIM_MIN; } } for (;;) { /* fetch rows or call spl node for aggf optim */ if (action->qhd_obj.qhd_qep.ahd_qepflag & AHD_NOROWS) { /* Negative syllogism assures no rows. */ status = E_DB_WARN; dsh->dsh_error.err_code = E_QE0015_NO_MORE_ROWS; if (origagg) dmr_cb->dmr_count = 0; /* just to be safe */ } else status = (*node->qen_func)(node, qef_rcb, dsh, open_flag); open_flag &= ~(TOP_OPEN | FUNC_RESET); if (status != E_DB_OK) { if (!origagg) /* first, handle errors for non-ORIGAGG */ { if (dsh->dsh_error.err_code == E_QE0015_NO_MORE_ROWS) break; /* end of scan, exit read loop */ else return(status); } if (dsh->dsh_error.err_code == E_QE0015_NO_MORE_ROWS) { if (dmr_cb->dmr_agg_flags & DMR_AGLIM_COUNT && data) { *((i4 *)data) = dmr_cb->dmr_count; } rowCount = dmr_cb->dmr_count; dsh->dsh_qef_remnull |= ade_excb->excb_nullskip; break; } if (dsh->dsh_error.err_code == E_DM0167_AGG_ADE_EXCEPTION || dsh->dsh_error.err_code == E_DM0166_AGG_ADE_FAILED) { adf_errcb = (ADF_ERROR *)&adfcb->adf_errcb; if ((adf_errcb->ad_errclass == ADF_INTERNAL_ERROR) || (adf_errcb->ad_errclass == ADF_USER_ERROR) || (adf_errcb->ad_errclass == ADF_EXTERNAL_ERROR) ) { if ((status = qef_adf_error(&adfcb->adf_errcb, status, qef_cb, &dsh->dsh_error)) != E_DB_OK) return (status); } } return (status); } /* From the comment below this applies only for the select */ /* any constraint and should not apply to agg optim */ rowCount++; if (data && instr_cnt == 1) { *((i4 *)data) += 1; } else { /* process the aggregate */ status = ade_execute_cx(adfcb, ade_excb); if (status != E_DB_OK) { if ((status = qef_adf_error(&adfcb->adf_errcb, status, qef_cb, &dsh->dsh_error)) != E_DB_OK) return (status); } } dsh->dsh_qef_remnull |= ade_excb->excb_nullskip; /* if solo any aggregate, stop */ if (action->qhd_obj.qhd_qep.ahd_qepflag & AHD_ANY) break; } } else { /* process the aggregate */ status = ade_execute_cx(adfcb, ade_excb); if (status != E_DB_OK) { if ((status = qef_adf_error(&adfcb->adf_errcb, status, qef_cb, &dsh->dsh_error)) != E_DB_OK) return (status); } dsh->dsh_qef_remnull |= ade_excb->excb_nullskip; } /* move the result to the result location */ finitit: /* come from ahd_constant = FALSE */ ade_excb->excb_seg = ADE_SFINIT; status = ade_execute_cx(adfcb, ade_excb); if (status != E_DB_OK) { if ((status = qef_adf_error(&adfcb->adf_errcb, status, qef_cb, &dsh->dsh_error)) != E_DB_OK) return (status); } /* ** The ALTER TABLE ADD CONSTRAINT state machine for RERERENTIAL CONSTRAINTS ** cooks up a SELECT ANY( 1 ) statement. Subsequent states in that ** machine need to know whether the SELECT returned 1 or 0 rows. The ** following code flags that piece of information in the session control ** block. */ if ( qef_cb->qef_callbackStatus & QEF_EXECUTE_SELECT_STMT ) { if ( rowCount > 0 ) { qef_cb->qef_callbackStatus |= QEF_SELECT_RETURNED_ROWS; } else { qef_cb->qef_callbackStatus &= ~( QEF_SELECT_RETURNED_ROWS ); } } return (E_DB_OK); }