/*{ ** Name: QEA_PRC_INSERT - Do insert for set input procedure. ** ** Description: ** The current execution environment is saved and an ** environment is created in which to execute the ** named procedure. ** ** This procedure is only called when a nested procedure is invoked. ** This can occur the first time through, or if the procedure is not ** found (LOAD_QP) or the plan was deemed invalid (INVALID_QUERY) then ** the procedure is re-entered in this routine. ** ** If rules are turned off (QEF_T_NORULES) then this procedure returns ** immediately. ** ** Inputs: ** action Delete current row of cursor action. ** dsh ** reset ** state DSH_CT_INITIAL if this is an action call ** DSH_CT_CONTINUE for call back after processing ** a rule action list. ** ** Outputs: ** dsh ** .error.err_code one of the following ** E_QE0119_LOAD_QP - Load a procedure QP ** E_QE0125_RULES_INHIBIT - Rules are turned off. ** E_QE0000_OK ** Returns: ** E_DB_{OK,WARN,ERROR,FATAL} ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 17-nov-92 (jhahn) ** Created. ** 15-mar-93 (anitap) ** Prototyped. ** 15-mar-94 (jhahn) ** Made external. ** 06-mar-96 (nanpr01) ** Parameter change in qen_putIntoTempTable for passing the temp ** table page size. ** 13-Dec-2005 (kschendel) ** Inlined QEN_ADF changed to pointer, fix here. */ DB_STATUS qea_prc_insert( QEF_AHD *act, QEE_DSH *dsh, i4 function, /* Unused */ i4 state) /* Unused */ { DB_STATUS status = E_DB_OK; PTR *cbs = dsh->dsh_cbs; QEN_ADF *qen_adf; ADE_EXCB *ade_excb; qen_adf = act->qhd_obj.qhd_proc_insert.ahd_procparams; if (qen_adf != NULL) { /* Compute the actual parameters for this row of procedure ** call. */ ade_excb = (ADE_EXCB *)cbs[qen_adf->qen_pos]; ade_excb->excb_seg = ADE_SMAIN; status = ade_execute_cx(dsh->dsh_adf_cb, ade_excb); if (status != E_DB_OK) { status = qef_adf_error(&dsh->dsh_adf_cb->adf_errcb, status, dsh->dsh_qefcb, &dsh->dsh_error); if (status != E_DB_OK) return (status); } return(qen_putIntoTempTable(dsh, act->qhd_obj.qhd_proc_insert. ahd_proc_temptable)); } return(E_DB_OK); }
/* ** Name: qeu_evraise ** ** Description: ** This routine raises an event, it processes the external request ** QEU_RAISE_EVENT, typically when raising the event associated with ** a security alarm. ** ** Note this *does* do event tracing (SET PRINTEVENTS/LOGEVENTS) ** but does *not* do security checks/auditing on the operation, this ** is assumed to be handled at a higher level. ** ** Inputs: ** qef_rcb.evname - Event name ** qef_rcb.evowner - Event owner ** qef_rcb.evtext - Event text ** qef_rcb.ev_l_text- Length of text ** ** History: ** 26-nov-93 (robf) ** Created for secure 2.0 ** 12-Jan-1998 (kinpa04/merja01) ** Remove (i4) casting of session id. This caused the ** session ID to get truncated on axp_osf while raising events. ** 10-Jan-2001 (jenjo02) ** We know this session's id; pass it to SCF. ** 30-Dec-2005 (kschendel) ** Update call to qef-adf-error. ** */ DB_STATUS qeu_evraise( QEF_CB *qef_cb, QEF_RCB *qef_rcb ) { DB_DATA_VALUE tm; /* Timestamp */ DB_DATE tm_date; SCF_ALERT scfa; SCF_CB scf_cb; SCF_SCI sci_list[1]; DB_ALERT_NAME alert; i4 err; char *errstr; DB_STATUS status=E_DB_OK; i4 tr1 = 0, tr2 = 0; /* Dummy trace values */ /* ** Build alert name */ STRUCT_ASSIGN_MACRO(*qef_rcb->qef_evname, alert.dba_alert); STRUCT_ASSIGN_MACRO(*qef_rcb->qef_evowner, alert.dba_owner); scf_cb.scf_length = sizeof(SCF_CB); scf_cb.scf_type = SCF_CB_TYPE; scf_cb.scf_facility = DB_QEF_ID; scf_cb.scf_session = qef_cb->qef_ses_id; /* Get the database name */ scf_cb.scf_ptr_union.scf_sci = (SCI_LIST *) sci_list; scf_cb.scf_len_union.scf_ilength = 1; sci_list[0].sci_length = sizeof(DB_DB_NAME); sci_list[0].sci_code = SCI_DBNAME; sci_list[0].sci_aresult = (char *) &alert.dba_dbname; sci_list[0].sci_rlength = NULL; status = scf_call(SCU_INFORMATION, &scf_cb); if (status != E_DB_OK) { _VOID_ qef_error(E_QE022F_SCU_INFO_ERROR, 0L, status, &err, &qef_rcb->error, 0); return E_DB_ERROR; } /* Format SCF event request block */ scf_cb.scf_ptr_union.scf_alert_parms = &scfa; scfa.scfa_name = &alert; scfa.scfa_text_length = 0; scfa.scfa_user_text = NULL; scfa.scfa_flags = 0; if (qef_rcb->qef_ev_l_text > 0) /* No need for empty strings */ { if (qef_rcb->qef_ev_l_text > DB_EVDATA_MAX) qef_rcb->qef_ev_l_text = DB_EVDATA_MAX; scfa.scfa_text_length = qef_rcb->qef_ev_l_text; scfa.scfa_user_text = qef_rcb->qef_evtext; } tm.db_datatype = DB_DTE_TYPE; /* Get time stamp for the event */ tm.db_prec = 0; tm.db_length = sizeof(tm_date); tm.db_data = (PTR)&tm_date; tm.db_collID = -1; status = adu_datenow(qef_cb->qef_adf_cb, &tm); if (status != E_DB_OK) { if ((status = qef_adf_error(&qef_cb->qef_adf_cb->adf_errcb, status, qef_cb, &qef_rcb->error)) != E_DB_OK) return (status); } scfa.scfa_when = &tm_date; /* If tracing and/or logging events then display event information */ if (ult_check_macro(&qef_cb->qef_trace, QEF_T_EVENTS, &tr1, &tr2)) qea_evtrace(qef_rcb, QEF_T_EVENTS, &alert, &tm, (i4)qef_rcb->qef_ev_l_text, (char*)qef_rcb->qef_evtext); if (ult_check_macro(&qef_cb->qef_trace, QEF_T_LGEVENTS, &tr1, &tr2)) qea_evtrace(qef_rcb, QEF_T_LGEVENTS, &alert, &tm, 0, (char *)NULL); /* ** Raise the event in SCF */ status = scf_call(SCE_RAISE, &scf_cb); if (status != E_DB_OK) { char *enm, *onm; /* Event and owner names */ enm = (char *)&alert.dba_alert; onm = (char *)&alert.dba_owner; errstr="RAISE"; switch (scf_cb.scf_error.err_code) { case E_SC0270_NO_EVENT_MESSAGE: _VOID_ qef_error(E_QE019A_EVENT_MESSAGE, 0L, status, &err, &qef_rcb->error, 1, (i4)STlength(errstr), errstr); break; case E_SC0280_NO_ALERT_INIT: _VOID_ qef_error(E_QE0200_NO_EVENTS, 0L, status, &err, &qef_rcb->error, 1, (i4)STlength(errstr), errstr); break; default: _VOID_ qef_error(E_QE020F_EVENT_SCF_FAIL, 0L, status, &err, &qef_rcb->error, 4, (i4)STlength(errstr), errstr, (i4)sizeof(i4), (PTR)&scf_cb.scf_error.err_code, qec_trimwhite(DB_OWN_MAXNAME, onm), onm, qec_trimwhite(DB_EVENT_MAXNAME, enm), enm); break; } qef_rcb->error.err_code = E_QE0025_USER_ERROR; status = E_DB_ERROR; } /* If SCF not ok */ return status; }
/*{ ** 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_CALLPROC - call the named procedure ** ** Description: ** The current execution environment is saved and an ** environment is created in which to execute the ** named procedure. ** ** This procedure is only called when a nested procedure is invoked. ** This can occur the first time through, or if the procedure is not ** found (LOAD_QP) or the plan was deemed invalid (INVALID_QUERY) then ** the procedure is re-entered in this routine. ** ** If rules are turned off (QEF_T_NORULES) then this procedure returns ** immediately. ** ** Inputs: ** action Callproc action header ** qef_rcb ** call_dsh DSH doing the callproc ** function unused ** state unused ** ** Outputs: ** call_dsh ** .error.err_code one of the following ** E_QE0119_LOAD_QP - Load a procedure QP ** E_QE0125_RULES_INHIBIT - Rules are turned off. ** E_QE0000_OK ** Returns: ** E_DB_{OK,WARN,ERROR,FATAL} ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 20-apr-89 (paul) ** Moved from qeq.c ** 09-may-89 (neil) ** Added rule name tracing. ** 26-may-89 (paul) ** Increase statement #, cleanup DSH on error recovery. ** 31-may-89 (neil) ** Cleanup tracing; modify when context count is set; reset "saved ** resource" bit if procedure is loaded. ** 23-jun-89 (neil) ** Extended trace for nested procedures: Indicate procedure nesting ** level and rule depth. ** 26-sep-89 (neil) ** Support for SET NORULES. ** 02-jan-90 (neil) ** DSH initialization error handling improved to indicate problem ** specifics. ** 03-jan-90 (ralph) ** Change interface to QSO_JUST_TRANS ** 10-jan-90 (neil) ** Improved DSH cleanup on DSH initialization errors. Made sure ** to confirm allocation error, and to pass a FALSE "release" flag ** to qeq_cleanup. ** 09-feb-90 (neil) ** Auditing cleanup: only audit the firing of rules and only if ** auditing is on (for performance). ** 09-nov-92 (jhahn) ** Added handling for byrefs. ** 14-dec-92 (jhahn) ** Cleaned up handling of byrefs. ** 12-feb-93 (jhahn) ** Added support for statement level rules. (FIPS) ** 24-mar-93 (jhahn) ** Various fixes for support of statement level rules. (FIPS) ** 02-apr-93 (jhahn) ** Made set input procedures called from rules bump qef_rule_depth. ** 01-may-93 (jhahn) ** Undid above change. Instead added new action QEA_INVOKE_RULE. ** 06-jul-93 (robf) ** Pass security label to qeu_secaudit ** 01-sep-93 (jhahn) ** Added support for multiple query plans for set input procedures. ** 7-jan-94 (swm) ** Bug #58635 ** Added PTR cast for qsf_owner which has changed type to PTR. ** 18-may-94 (anitap) ** Bug #63465 ** If error in nested procedure, the whole transaction was being ** rolled back. ** 7-nov-95 (inkdo01) ** Changes to replace QEN_ADF structure instances by pointers in ** QEF_AHD structures. ** 24-jul-96 (inkdo01) ** Added support of global temp table proc parms. ** 4-mar-97 (inkdo01) ** Added display of error QE030B (row rule calls SET OF proc). ** 23-may-97 (inkdo01) ** Change QE030B to only pass procname, since it is mapped to a ** US error in qeferror, anyway. ** 17-aug-99 (thaju02) ** initialize dmt_show.db_tab_id.db_tab_base to 0 prior to calling ** dmt_show, to avoid falsely reporting E_QE0018. (b98431) ** 27-apr-01 (inkdo01) ** Add code to detect nested/dynamic calls to row procs. ** 15-mar-04 (inkdo01) ** dsh_tempTables is now an array of ptrs. ** 17-mar-04 (inkdo01) ** We be fixin' a bug in error handlin' when qeq_dsh() doesn't ** return a dsh. ** 13-may-04 (inkdo01) ** Preserve status across qeq_cleanup calls. ** 18-may-04 (inkdo01) ** Quick fix to eliminate QE0018 when QE030D (US09AF) happens. ** 13-Jul-2004 (schka24) ** Straighten out which dsh is used where, fix loss of error ** code when finding called qp leading to QE0018. ** 13-Dec-2005 (kschendel) ** Inlined QEN_ADF changed to pointer, fix here. ** 29-May-2008 (gefei01) ** Prototype change for qeq_dsh(). ** 22-Apr-2009 (hanal04) Bug 121970 ** In printrule tracing print the ahd_pcount not qef_pcount ** which is set to zero if we are in a sub-procedure. ** 21-Jun-2010 (kschendel) b123775 ** Combine is-tproc and in-progress args to qeq-dsh. ** Make dbp alias a real QSO_NAME. */ DB_STATUS qea_callproc( QEF_AHD *act, QEF_RCB *qef_rcb, QEE_DSH *call_dsh, i4 function, /* Unused */ i4 state ) /* Unused */ { i4 err; DB_STATUS status = E_DB_OK, savestat; QEF_CB *qef_cb = call_dsh->dsh_qefcb; PTR *cbs = call_dsh->dsh_cbs; QEE_DSH *proc_dsh; /* DSH for called dbp */ QEN_ADF *qen_adf; ADE_EXCB *ade_excb; QSF_RCB qsf_rcb; i4 tr1 = 0, tr2 = 0; /* Dummy trace values */ DB_ERROR e_error; /* To pass to qeu_secaudit */ i4 page_count; bool is_deferred = act->qhd_obj.qhd_callproc.ahd_proc_temptable != NULL; bool gttparm = act->qhd_obj.qhd_callproc.ahd_gttid.db_tab_base != 0; bool need_cleanup = FALSE; bool need_release; bool from_rule = act->qhd_obj.qhd_callproc.ahd_rulename.db_name[0] != EOS; QEE_TEMP_TABLE *proc_temptable; char *cbuf = qef_cb->qef_trfmt; i4 cbufsize = qef_cb->qef_trsize; i4 open_count; do { /* ** This action is called back if a request to CREATE a QP for a ** CALLPROC fails. In this case we just continue with error processing. ** No need for another error as CLEAN_RSRC is only set if a client on ** the outside issued an error knowing we have a stacked environment. ** (Note: as of the 123775 fixes, we shouldn't ever get here with ** CLEAN_RSRC set, since qeq-query no longer attempts to execute ** the action from the sequencer callback. I'm leaving the code ** here for now, though.) */ if ((qef_rcb->qef_intstate & QEF_CLEAN_RSRC) != 0) { call_dsh->dsh_error.err_code = E_QE0025_USER_ERROR; qef_rcb->qef_intstate &= ~QEF_CLEAN_RSRC; status = E_DB_ERROR; break; } if (is_deferred) { proc_temptable = call_dsh->dsh_tempTables[act->qhd_obj.qhd_callproc .ahd_proc_temptable->ttb_tempTableIndex]; if (proc_temptable->tt_statusFlags & TT_EMPTY) if (from_rule) break; /* empty ttab in statement rule - ** nothing to do at all */ else status = openTempTable(qef_rcb, call_dsh, act->qhd_obj.qhd_callproc .ahd_proc_temptable->ttb_tempTableIndex, gttparm); else status = qen_rewindTempTable(call_dsh, act->qhd_obj. qhd_callproc.ahd_proc_temptable->ttb_tempTableIndex); if (status != E_DB_OK) break; } /* If called from a rule & SET NORULES is on, then inhibit execution */ if ( from_rule && ult_check_macro(&qef_cb->qef_trace, QEF_T_NORULES, &tr1, &tr2) ) { /* Trace inhibited rule if required */ if (ult_check_macro(&qef_cb->qef_trace, QEF_T_RULES, &tr1, &tr2)) { char *rn = act->qhd_obj.qhd_callproc.ahd_rulename.db_name; STprintf(cbuf, "PRINTRULES: Rule '%.*s' suppressed\n", qec_trimwhite(DB_RULE_MAXNAME, rn), rn); qec_tprintf(qef_rcb, cbufsize, cbuf); } call_dsh->dsh_error.err_code = E_QE0125_RULES_INHIBIT; status = E_DB_ERROR; break; } /* If rules off */ /* ** Security audit rule firing - check that is first time (not ** recreation), is a rule and, for performance, that we really ** need to audit. */ if ( (qef_rcb->qef_intstate & QEF_DBPROC_QP) == 0 && (act->qhd_obj.qhd_callproc.ahd_rulename.db_name[0] != EOS) && (Qef_s_cb->qef_state & QEF_S_C2SECURE) ) { status = qeu_secaudit(FALSE, qef_cb->qef_ses_id, act->qhd_obj.qhd_callproc.ahd_rulename.db_name, (DB_OWN_NAME *)&act->qhd_obj.qhd_callproc.ahd_ruleowner, sizeof(act->qhd_obj.qhd_callproc.ahd_rulename), SXF_E_RULE, I_SX202F_RULE_ACCESS, SXF_A_SUCCESS | SXF_A_EXECUTE, &e_error); if (status != E_DB_OK) { call_dsh->dsh_error.err_code = e_error.err_code; break; } } /* Actually execute a CALLPROC. */ /* We generate an actual parameter list, save the current */ /* execution context, stack the DSH and setup the new */ /* execution context. Processing then continues with the */ /* new QP. */ /* Save the current execution context as represented by */ /* QEF_RCB and the current DSH. */ STRUCT_ASSIGN_MACRO(*qef_rcb, *call_dsh->dsh_saved_rcb); call_dsh->dsh_act_ptr = act; qen_adf = act->qhd_obj.qhd_callproc.ahd_procparams; if (qen_adf != NULL) { /* Compute the actual parameters for this procedure */ /* call. */ qef_rcb->qef_pcount = act->qhd_obj.qhd_callproc.ahd_pcount; ade_excb = (ADE_EXCB *)cbs[qen_adf->qen_pos]; ade_excb->excb_seg = ADE_SMAIN; status = ade_execute_cx(call_dsh->dsh_adf_cb, ade_excb); if (status != E_DB_OK) { status = qef_adf_error(&call_dsh->dsh_adf_cb->adf_errcb, status, qef_cb, &call_dsh->dsh_error); if (status != E_DB_OK) break; } } else { /* No actual parameters */ qef_rcb->qef_pcount = 0; } /* ** If tracing rules and first time through then display ** rule/procedure information. */ if ((qef_rcb->qef_intstate & QEF_DBPROC_QP) == 0) { if (ult_check_macro(&qef_cb->qef_trace, QEF_T_RULES, &tr1, &tr2)) { char *rn, *pn; /* Rule/procedure names */ rn = act->qhd_obj.qhd_callproc.ahd_rulename.db_name; pn = act->qhd_obj.qhd_callproc.ahd_dbpalias.qso_n_id.db_cur_name; /* Tailor trace for rules vs nested procedures */ STprintf(cbuf, *rn == EOS ? "PRINTRULES 1: Executing procedure '%.*s'\n" : "PRINTRULES 1: Executing procedure '%.*s' from rule '%.*s'\n", qec_trimwhite(DB_DBP_MAXNAME, pn), pn, qec_trimwhite(DB_RULE_MAXNAME, rn), rn); qec_tprintf(qef_rcb, cbufsize, cbuf); STprintf(cbuf, "PRINTRULES 2: Rule/procedure depth = %d/%d, parameters passed = %d\n", qef_rcb->qef_rule_depth, qef_rcb->qef_context_cnt + 1, act->qhd_obj.qhd_callproc.ahd_pcount); qec_tprintf(qef_rcb, cbufsize, cbuf); } /* If tracing rules */ } /* ** Indicate that we have nested one more level, generate an error if ** we are nested too deeply. Context count is actually set later. */ if (qef_cb->qef_max_stack < qef_rcb->qef_context_cnt + 1) { status = E_DB_ERROR; qef_error(E_QE0208_EXCEED_MAX_CALL_DEPTH, 0L, status, &err, &call_dsh->dsh_error, 1, sizeof(qef_cb->qef_max_stack), &qef_cb->qef_max_stack); call_dsh->dsh_error.err_code = E_QE0122_ALREADY_REPORTED; break; } if (gttparm) { STRUCT_ASSIGN_MACRO(act->qhd_obj.qhd_callproc.ahd_gttid, qef_rcb->qef_setInputId); /* copy temptab ID */ page_count = 1; is_deferred = TRUE; } else if (is_deferred) { DMT_CB *dmt_cb = proc_temptable->tt_dmtcb; DMT_SHW_CB dmt_show; DMT_TBL_ENTRY dmt_tbl_entry; dmt_show.type = DMT_SH_CB; dmt_show.length = sizeof(DMT_SHW_CB); dmt_show.dmt_session_id = qef_cb->qef_ses_id; dmt_show.dmt_db_id = qef_rcb->qef_db_id; dmt_show.dmt_tab_id.db_tab_base = 0; dmt_show.dmt_flags_mask = DMT_M_TABLE | DMT_M_ACCESS_ID; dmt_show.dmt_char_array.data_address = NULL; dmt_show.dmt_table.data_address = (PTR) &dmt_tbl_entry; dmt_show.dmt_table.data_in_size = sizeof(DMT_TBL_ENTRY); dmt_show.dmt_record_access_id = dmt_cb->dmt_record_access_id; status = dmf_call(DMT_SHOW, &dmt_show); if (status != E_DB_OK) { STRUCT_ASSIGN_MACRO(dmt_show.error, call_dsh->dsh_error); break; } page_count = dmt_tbl_entry.tbl_page_count; STRUCT_ASSIGN_MACRO(dmt_cb->dmt_id, qef_rcb->qef_setInputId); } else { page_count = -1; MEfill(sizeof(DB_TAB_ID), (u_char)0, (PTR)&qef_rcb->qef_setInputId); } STRUCT_ASSIGN_MACRO(act->qhd_obj.qhd_callproc.ahd_procedureID, qef_rcb->qef_dbpId); /* Get the id of the procedure to call */ STRUCT_ASSIGN_MACRO(act->qhd_obj.qhd_callproc.ahd_dbpalias.qso_n_id, qef_rcb->qef_qp); qef_rcb->qef_qso_handle = NULL; /* Set the full name of the procedure into the RCB in case */ /* we have to call PSF to define the procedure. */ qef_rcb->qef_dbpname = act->qhd_obj.qhd_callproc.ahd_dbpalias; /* Lookup this procedure name as a QSF alias. At this time */ /* we do not have a valid DB_CURSOR_ID because we do not */ /* have the timestamp for the QP object. Ask QSF to look */ /* this up for us. If found, we continue executing, if not */ /* return to SCF to define this procedure. */ qsf_rcb.qsf_type = QSFRB_CB; qsf_rcb.qsf_ascii_id = QSFRB_ASCII_ID; qsf_rcb.qsf_length = sizeof(QSF_RCB); qsf_rcb.qsf_owner = (PTR)DB_QEF_ID; qsf_rcb.qsf_sid = qef_rcb->qef_cb->qef_ses_id; qsf_rcb.qsf_feobj_id.qso_type = QSO_ALIAS_OBJ; qsf_rcb.qsf_feobj_id.qso_lname = sizeof(qsf_rcb.qsf_feobj_id.qso_name); MEcopy((PTR)&act->qhd_obj.qhd_callproc.ahd_dbpalias, sizeof(qsf_rcb.qsf_feobj_id.qso_name), (PTR)qsf_rcb.qsf_feobj_id.qso_name); qsf_rcb.qsf_lk_state = QSO_FREE; status = qsf_call(QSO_JUST_TRANS, &qsf_rcb); if (DB_FAILURE_MACRO(status)) { /* No such procedure in QSF, ask SCF to define it */ /* Tell SCF to call us back even if the porcedure */ /* cannot be loaded. We will need to clean up */ qef_rcb->qef_intstate |= QEF_DBPROC_QP; call_dsh->dsh_error.err_code = E_QE0119_LOAD_QP; break; } else { /* ** The procedure was found - make sure "saved" bit isn't on for ** the scope of this execution. It may get turned back on if ** the QP or DSH is found to be invalid but then we'll re-enter ** here (to try again) and turn it off. */ qef_rcb->qef_intstate &= ~QEF_DBPROC_QP; } /* Increase context count now that we've loaded the QP */ qef_rcb->qef_context_cnt++; /* Procedure in QSF, load the timestamp into the QEF_RCB */ /* to allow normal QEF processing to continue. */ MEcopy((PTR)qsf_rcb.qsf_obj_id.qso_name, sizeof(DB_CURSOR_ID), (PTR)&qef_rcb->qef_qp); status = qeq_dsh(qef_rcb, 0 , &proc_dsh, QEQDSH_IN_PROGRESS, page_count); if (DB_FAILURE_MACRO(status)) { char *rn, *pn; /* Rule/procedure names */ STRUCT_ASSIGN_MACRO(qef_rcb->error, call_dsh->dsh_error); if (call_dsh->dsh_error.err_code == E_QE0023_INVALID_QUERY) { /* No such procedure in QSF, ask SCF to define it */ /* Tell SCF to call us back even if the porcedure */ /* cannot be loaded. We will need to clean up */ qef_cb->qef_dsh = (PTR) call_dsh; qef_rcb->qef_qp = call_dsh->dsh_saved_rcb->qef_qp; qef_rcb->qef_intstate |= QEF_DBPROC_QP; call_dsh->dsh_error.err_code = E_QE0119_LOAD_QP; qef_rcb->qef_context_cnt--; break; } /* ** The QP DSH is invalid for some reason. Generate error and return. ** If any allocation error then change to useful error message. */ if ( call_dsh->dsh_error.err_code == E_UL0005_NOMEM || call_dsh->dsh_error.err_code == E_QS0001_NOMEM || call_dsh->dsh_error.err_code == E_QE001E_NO_MEM || call_dsh->dsh_error.err_code == E_QE000D_NO_MEMORY_LEFT || call_dsh->dsh_error.err_code == E_QE030B_RULE_PROC_MISMATCH) { pn = act->qhd_obj.qhd_callproc.ahd_dbpalias.qso_n_id.db_cur_name; rn = act->qhd_obj.qhd_callproc.ahd_rulename.db_name; if (*rn == EOS) rn = "NULL "; if (call_dsh->dsh_error.err_code == E_QE030B_RULE_PROC_MISMATCH) qef_error(E_QE030B_RULE_PROC_MISMATCH, 0L, status, &err, &call_dsh->dsh_error, 1, /* qec_trimwhite(DB_RULE_MAXNAME, rn), rn, */ qec_trimwhite(DB_DBP_MAXNAME, pn), pn); else qef_error(E_QE0199_CALL_ALLOC, 0L, status, &err, &call_dsh->dsh_error, 3, qec_trimwhite(DB_DBP_MAXNAME, pn), pn, qec_trimwhite(DB_RULE_MAXNAME, rn), rn, sizeof(qef_rcb->qef_context_cnt),&qef_rcb->qef_context_cnt); call_dsh->dsh_error.err_code = E_QE0122_ALREADY_REPORTED; } /* ** Now clean up and restore to state before routine entry. ** Pass in FALSE for release as if the DSH is NULL we do NOT want ** to cause cleanup to release all DSH's for this session ** (qee_cleanup). If the DSH is not NULL it will be cleaned up at ** the end of the query. */ need_cleanup = TRUE; need_release = FALSE; break; } if (proc_dsh->dsh_qp_ptr->qp_status & QEQP_ROWPROC) { char *pn; /* Row producing procs cannot be invoked by QEA_CALLPROC (which ** implies either nested proc call or dynamic SQL proc call). */ pn = act->qhd_obj.qhd_callproc.ahd_dbpalias.qso_n_id.db_cur_name; qef_error(E_QE030D_NESTED_ROWPROCS, 0L, E_DB_ERROR, &err, &call_dsh->dsh_error, 1, qec_trimwhite(DB_DBP_MAXNAME, pn), pn); status = E_DB_ERROR; break; } /* Found QP and DSH, stack old context */ proc_dsh->dsh_stack = call_dsh; proc_dsh->dsh_stmt_no = qef_cb->qef_stmt++; qef_cb->qef_dsh = (PTR) proc_dsh; qef_cb->qef_open_count++; /* Initialize procedure parameters (& user params - even if wrong) */ if (proc_dsh->dsh_qp_ptr->qp_ndbp_params != 0 || qef_rcb->qef_pcount > 0) { status = qee_dbparam(qef_rcb, proc_dsh, call_dsh, act->qhd_obj.qhd_callproc.ahd_params, act->qhd_obj.qhd_callproc.ahd_pcount, TRUE); if (DB_FAILURE_MACRO(status)) { /* If we fail after acquiring the DSH, we need to */ /* deallocate the DSH and recover back to the original */ /* calling state. */ STRUCT_ASSIGN_MACRO(proc_dsh->dsh_error, call_dsh->dsh_error); qef_cb->qef_open_count--; need_cleanup = TRUE; need_release = TRUE; break; } } if (is_deferred) { /* FIXME should error if it's null */ if (proc_dsh->dsh_qp_ptr->qp_setInput != NULL) STRUCT_ASSIGN_MACRO(qef_rcb->qef_setInputId, * (DB_TAB_ID *)(proc_dsh->dsh_row[proc_dsh->dsh_qp_ptr-> qp_setInput->vl_tab_id_index])); } } while (FALSE); if (need_cleanup) { /* error in a nested DB procedure, abort up to the beginning of the ** procedure if there are no other cursors opened. We ** guarantee that by decrementing the qef_open_count. If the ** count becomes zero, qeq_cleanup will abort to the last internal ** savepoint. Fix for bug 63465. */ savestat = status; open_count = qef_cb->qef_open_count; while (qef_cb->qef_open_count > 0) qef_cb->qef_open_count--; status = qeq_cleanup(qef_rcb, status, need_release); status = savestat; qef_cb->qef_open_count = open_count; qef_cb->qef_dsh = (PTR) call_dsh; qef_rcb->qef_context_cnt--; qef_rcb->qef_pcount = call_dsh->dsh_saved_rcb->qef_pcount; qef_rcb->qef_usr_param = call_dsh->dsh_saved_rcb->qef_usr_param; qef_rcb->qef_qp = call_dsh->dsh_saved_rcb->qef_qp; qef_rcb->qef_qso_handle = call_dsh->dsh_saved_rcb->qef_qso_handle; call_dsh->dsh_qef_rowcount = call_dsh->dsh_saved_rcb->qef_rowcount; call_dsh->dsh_qef_targcount = call_dsh->dsh_saved_rcb->qef_targcount; call_dsh->dsh_qef_output = call_dsh->dsh_saved_rcb->qef_output; call_dsh->dsh_qef_count = call_dsh->dsh_saved_rcb->qef_count; } 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); }