/*{ ** Name: opx_init - initialize the AIC and PAINE handlers ** ** Description: ** This routine will inform SCF of the AIC and PAINE handlers ** ** Inputs: ** opf_cb - ptr to caller's control block ** ** Outputs: ** Returns: ** E_DB_OK ** E_DB_ERROR - if initialization failed ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 31-mar-87 (seputis) ** initial creation ** 12-dec-88 (seputis) ** return E_DB_OK [@history_template@]... */ DB_STATUS opx_init( OPF_CB *opf_cb) { SCF_CB scf_cb; DB_STATUS scf_status; scf_cb.scf_length = sizeof(scf_cb); scf_cb.scf_type = SCF_CB_TYPE; scf_cb.scf_facility = DB_OPF_ID; scf_cb.scf_ptr_union.scf_afcn = opx_call; scf_cb.scf_session = DB_NOSESSION; scf_cb.scf_nbr_union.scf_amask = SCS_PAINE_MASK; scf_status = scf_call(SCS_DECLARE, &scf_cb); # ifdef E_OP0090_PAINE if (DB_FAILURE_MACRO(scf_status)) { opx_rverror( opf_cb, scf_status, E_OP0090_PAINE, scf_cb.scf_error.err_code); return(E_DB_ERROR); } # endif scf_cb.scf_nbr_union.scf_amask = SCS_AIC_MASK; scf_status = scf_call(SCS_DECLARE, &scf_cb); # ifdef E_OP0091_AIC if (DB_FAILURE_MACRO(scf_status)) { opx_rverror( opf_cb, scf_status, E_OP0091_AIC, scf_cb.scf_error.err_code); return(E_DB_ERROR); } # endif return(E_DB_OK); }
/*{ ** Name: psy_kuser - Destroy user ** ** INTERNAL PSF call format: status = psy_kuser(&psy_cb, sess_cb); ** ** Description: ** This procedure deletes a user. If the ** user does not exist, the statement is aborted. ** If the user does exist, the associated ** iiuser tuple is deleted. ** This procedure is called for SQL language only. ** ** Inputs: ** psy_cb ** .psy_usrq user list ** sess_cb Pointer to session control block ** (Can be NULL) ** ** Outputs: ** psy_cb ** .psy_error Filled in if error happens ** Returns: ** E_DB_OK Function completed normally. ** E_DB_INFO Function completed with warning(s). ** E_DB_WARN One ore more roles were rejected. ** E_DB_ERROR Function failed; non-catastrophic error ** Exceptions: ** none ** ** Side Effects: ** Removed tuples from iiuser. ** ** History: ** 04-sep-89 (ralph) ** written ** 12-mar-90 (andre) ** set rdr_2types_mask to 0. ** 22-may-90 (teg) ** init rdr_instr to RDF_NO_INSTR */ DB_STATUS psy_kuser( PSY_CB *psy_cb, PSS_SESBLK *sess_cb) { DB_STATUS status, stat; RDF_CB rdf_cb; register RDR_RB *rdf_rb = &rdf_cb.rdf_rb; DU_USER ustuple; register DU_USER *ustup = &ustuple; PSY_USR *psy_usr; /* This code is called for SQL only */ /* ** Fill in the part of RDF request block that will be constant. */ pst_rdfcb_init(&rdf_cb, sess_cb); rdf_rb->rdr_update_op = RDR_DELETE; rdf_rb->rdr_status = DB_SQL; rdf_rb->rdr_types_mask = RDR_USER; rdf_rb->rdr_qrytuple = (PTR) ustup; rdf_rb->rdr_qtuple_count = 1; ustup->du_gid = 0; ustup->du_mid = 0; ustup->du_status = 0; ustup->du_flagsmask = 0; MEfill(sizeof(ustup->du_group), (u_char)' ', (PTR)&ustup->du_group); status = E_DB_OK; for (psy_usr = (PSY_USR *) psy_cb->psy_usrq.q_next; psy_usr != (PSY_USR *) &psy_cb->psy_usrq; psy_usr = (PSY_USR *) psy_usr->queue.q_next ) { /* STRUCT_ASSIGN_MACRO(psy_usr->psy_usrnm, ustup->du_name); */ MEcopy((PTR)&psy_usr->psy_usrnm, sizeof(ustup->du_name), (PTR)&ustup->du_name); stat = rdf_call(RDF_UPDATE, (PTR)&rdf_cb); status = (stat > status) ? stat : status; if (DB_FAILURE_MACRO(stat)) break; } if (DB_FAILURE_MACRO(status)) (VOID) psf_rdf_error(RDF_UPDATE, &rdf_cb.rdf_error, &psy_cb->psy_error); return (status); }
/*{ ** Name: psy_create_synonym - Insert a tuple into IISYNONYM. ** ** Description: ** Call RDF_UPDATE to insert a tuple into IISYNONYM. ** Inputs: ** psy_cb PSY control block. ** sess_cb PSF session control block. ** Outputs: ** Exceptions: ** none ** Returns: ** E_DB_OK synonym tuple has been inserted successfully; ** error status from RDF otherwise ** ** Side Effects: ** Modifies system catalogs. ** ** History: ** 19-apr-90 (andre) ** Created. ** 22-may-90 (teg) ** init rdr_instr to RDF_NO_INSTR ** 03-aug-92 (barbara) ** Invalidate base object's infoblk from the RDF cache. ** 10-aug-93 (andre) ** fixed causes of compiler warnings */ DB_STATUS psy_create_synonym( PSY_CB *psy_cb, PSS_SESBLK *sess_cb) { RDF_CB rdf_cb; register RDR_RB *rdf_rb = &rdf_cb.rdf_rb; DB_STATUS status; i4 err_code; /* Initialize the RDF request block. */ pst_rdfcb_init(&rdf_cb, sess_cb); STRUCT_ASSIGN_MACRO(sess_cb->pss_user, rdf_rb->rdr_owner); rdf_rb->rdr_2types_mask = (RDF_TYPES) RDR2_SYNONYM; rdf_rb->rdr_update_op = RDR_APPEND; rdf_rb->rdr_qrytuple = psy_cb->psy_tupptr; rdf_rb->rdr_tabid.db_tab_base = DM_B_SYNONYM_TAB_ID; rdf_rb->rdr_tabid.db_tab_index = DM_I_SYNONYM_TAB_ID; /* Insert a tuple into IISYNONYM */ status = rdf_call(RDF_UPDATE, (PTR) &rdf_cb); if (DB_FAILURE_MACRO(status)) { if (rdf_cb.rdf_error.err_code == E_RD0143_CREATE_SYNONYM) { DB_IISYNONYM *syn = (DB_IISYNONYM *) psy_cb->psy_tupptr; (VOID) psf_error(E_PS0454_CREATE_SYN_ERROR, 0L, PSF_USERERR, &err_code, &psy_cb->psy_error, 1, psf_trmwhite(sizeof(DB_SYNNAME), (char *) &syn->db_synname), &syn->db_synname); } else { (VOID) psf_rdf_error(RDF_UPDATE, &rdf_cb.rdf_error, &psy_cb->psy_error); } return (status); } /* Invalidate the base object's info block from the RDF cache */ { DB_IISYNONYM *syn_tuple = (DB_IISYNONYM *) psy_cb->psy_tupptr; pst_rdfcb_init(&rdf_cb, sess_cb); STRUCT_ASSIGN_MACRO(syn_tuple->db_syntab_id, rdf_rb->rdr_tabid); status = rdf_call(RDF_INVALIDATE, (PTR) &rdf_cb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_rdf_error(RDF_INVALIDATE, &rdf_cb.rdf_error, &psy_cb->psy_error); } } return (status); }
/*{ ** Name: psy_kgroup - Destroy group ** ** INTERNAL PSF call format: status = psy_kgroup(&psy_cb, sess_cb); ** ** Description: ** This procedure deletes group identifiers. No members can be ** in the group to be deleted. If members exist in a group, ** the group is not deleted. ** This procedure is called for SQL language only. ** ** Inputs: ** psy_cb ** .psy_tblq head of group queue ** sess_cb Pointer to session control block ** (Can be NULL) ** ** Outputs: ** psy_cb ** .psy_error Filled in if error happens ** Returns: ** E_DB_OK Function completed normally. ** E_DB_INFO Function completed with message(s). ** E_DB_WARN One or more groups were rejected. ** E_DB_ERROR Function failed; non-catastrophic error ** Exceptions: ** none ** ** Side Effects: ** Removed tuples from iiusergroup. ** ** History: ** 13-mar-89 (ralph) ** written ** 20-may-89 (ralph) ** Allow multiple groups to be specified. ** 12-mar-90 (andre) ** set rdr_2types_mask to 0. ** 22-may-90 (teg) ** init rdr_instr to RDF_NO_INSTR */ DB_STATUS psy_kgroup( PSY_CB *psy_cb, PSS_SESBLK *sess_cb) { DB_STATUS status, stat; RDF_CB rdf_cb; register RDR_RB *rdf_rb = &rdf_cb.rdf_rb; DB_USERGROUP ugtuple; register DB_USERGROUP *ugtup = &ugtuple; PSY_TBL *psy_tbl; PSY_USR *psy_usr; /* This code is called for SQL only */ /* ** Fill in the part of RDF request block that will be constant. */ pst_rdfcb_init(&rdf_cb, sess_cb); rdf_rb->rdr_update_op = RDR_DELETE; rdf_rb->rdr_status = DB_SQL; rdf_rb->rdr_types_mask = RDR_GROUP; rdf_rb->rdr_qrytuple = (PTR) ugtup; rdf_rb->rdr_qtuple_count = 1; MEfill(sizeof(DB_OWN_NAME), (u_char)' ', (PTR)&ugtup->dbug_member); MEfill(sizeof(ugtup->dbug_reserve), (u_char)' ', (PTR)ugtup->dbug_reserve); status = E_DB_OK; for (psy_tbl = (PSY_TBL *) psy_cb->psy_tblq.q_next; psy_tbl != (PSY_TBL *) &psy_cb->psy_tblq; psy_tbl = (PSY_TBL *) psy_tbl->queue.q_next ) { /* STRUCT_ASSIGN_MACRO(psy_tbl->psy_tabnm, ugtup->dbug_group); */ MEcopy((PTR)&psy_tbl->psy_tabnm, sizeof(ugtup->dbug_group), (PTR)&ugtup->dbug_group); stat = rdf_call(RDF_UPDATE, (PTR) &rdf_cb); status = (stat > status) ? stat : status; if (DB_FAILURE_MACRO(stat)) break; } if (DB_FAILURE_MACRO(status)) (VOID) psf_rdf_error(RDF_UPDATE, &rdf_cb.rdf_error, &psy_cb->psy_error); return (status); }
/*{ ** Name: OPU_RELEASE - Return memory to the global memory pool ** ** Description: ** This routine returns memory from the given mark to the end of the ** ULM memory stream to the global memory pool. [@comment_line@]... ** ** Inputs: ** global - ** State info for the current query. ** ulmrcb - ** address of local control block, or NULL if ** global->ops_mstate.ops_ulmrcb should be used ** mark - ** The mark stating where to start returning memory. ** ** Outputs: ** ** Returns: ** none ** Exceptions: ** none ** ** Side Effects: ** Memory is returned to the global memory pool ** ** History: ** 18-apr-87 (seputis) ** initial creation ** 11-oct-2006 (hayke02) ** Send E_OP0002_NOMEMORY to errlog.log. This change fixes bug 116309. [@history_template@]... */ VOID opu_release( OPS_STATE *global, ULM_RCB *ulmrcb, ULM_SMARK *mark) { DB_STATUS ulmstatus; /* return status from ULM */ if (!ulmrcb) { ulmrcb = &global->ops_mstate.ops_ulmrcb; /* use global ulmrcb if ** control block is not defined */ ulmrcb->ulm_streamid_p = &global->ops_mstate.ops_streamid; /* mark memory ** in the global stream */ } /* store the mark to be initialized */ ulmrcb->ulm_smark = mark; ulmstatus = ulm_reclaim( ulmrcb ); if ( DB_FAILURE_MACRO(ulmstatus) ) { if (ulmrcb->ulm_error.err_code == E_UL0005_NOMEM) { opx_lerror(E_OP0002_NOMEMORY, 0, 0, 0, 0, 0); opx_error( E_OP0002_NOMEMORY); /* out of memory */ } #ifdef E_OP0093_ULM_ERROR else opx_verror( ulmstatus, E_OP0093_ULM_ERROR, ulmrcb->ulm_error.err_code); /* check for error */ #endif } }
/*{ ** Name: psq_tinsert - Add a piece of query text to a chain in front ** of some piece. ** ** Description: ** This function inserts a piece of query text to an existing chain. ** New text is added in front of the existing piece specified by the ** caller . ** A need for the routine arises from the fact that the owner names need ** to qualify database object names in case of database procedures. ** Another use for the function is when synonym_name has to be ** replaced with owner.actual_table_name in the text stored for views, ** integrities, and permits. ** These names become known when the scanner has already 'seen' the ** object name (and therefore has already added it to the text chain). ** ** Inputs: ** header Pointer to chain header ** piece Pointer to piece of text ** size Size of piece ** result Place to put pointer to new piece ** oldpiece Piece in front of which the new piece ** will be inserted (should not be NULL). ** err_blk Filled in if an error happens ** ** Outputs: ** result Filled in with pointer to chain element ** err_blk Filled in if an error happens ** Returns: ** E_DB_OK Success ** E_DB_ERROR Non-catastrophic failure ** E_DB_FATAL Catastrophic failure ** Exceptions: ** none ** ** Side Effects: ** Allocates memory ** ** History: ** 02-jun-88 (stec) ** written ** 04-jun-90 (andre) ** renamed from psq_tb1add() to psq_tinsert(); ** changed the description to describe use of this function in ** conjunction with synonyms. */ DB_STATUS psq_tinsert( PTR header, u_char *piece, i4 size, PTR *result, PTR oldpiece, DB_ERROR *err_blk) { PSQ_THEAD *hp = (PSQ_THEAD *) header; PSQ_TEXT *tp, *op = (PSQ_TEXT *) oldpiece; i4 err_code; DB_STATUS status; if (op == (PSQ_TEXT *) NULL) { /* Should not be reached */ return (E_DB_ERROR); } /* Allocate enough space for PSQ_TEXT structure containing piece */ hp->psq_tmem.ulm_psize = size + sizeof(PSQ_TEXT) - 1; status = ulm_palloc(&hp->psq_tmem); if (DB_FAILURE_MACRO(status)) { if (hp->psq_tmem.ulm_error.err_code == E_UL0005_NOMEM) { psf_error(E_PS0F02_MEMORY_FULL, 0L, PSF_CALLERR, &err_code, err_blk, 0); } else (VOID) psf_error(E_PS0371_ALLOC_TEXT_CHAIN, hp->psq_tmem.ulm_error.err_code, PSF_INTERR, &err_code,err_blk, 0); return (status); } *result = hp->psq_tmem.ulm_pptr; tp = (PSQ_TEXT*) *result; /* Fill in text piece */ MEcopy((char *) piece, size, (char *) tp->psq_tval); tp->psq_psize = size; /* Hook it up to the chain */ if (tp->psq_prev = op->psq_prev) tp->psq_prev->psq_next = tp; tp->psq_next = op; op->psq_prev = tp; if (hp->psq_first == op) hp->psq_first = tp; /* Add in the length to the total for the chain */ hp->psq_tsize += size; return (E_DB_OK); }
FUNC_EXTERN DB_STATUS IICXcreate_icas_xn_cb( IICX_ID *cx_id, /* IN */ IICX_CB **cx_cb_p_p /* OUT */ ) { DB_STATUS db_status; STATUS cl_status; char ebuf[20]; IICX_ICAS_XN_CB *icas_xn_cb_p; i4 i; /* LOCK the ICAS XN MAIN CB. CHECK !!! */ if (IIcx_icas_xn_main_cb->num_free_icas_xn_cbs > 0) { *cx_cb_p_p = IIcx_icas_xn_main_cb->icas_xn_cb_free_list; IIcx_icas_xn_main_cb->icas_xn_cb_free_list = (*cx_cb_p_p)->cx_next; if ((*cx_cb_p_p)->cx_next != NULL) (*cx_cb_p_p)->cx_next->cx_prev = (*cx_cb_p_p)->cx_prev; IIcx_icas_xn_main_cb->num_free_icas_xn_cbs--; } if (*cx_cb_p_p != NULL) { /* UNLOCK the ICAS XN Main CB. CHECK !!! */ return( E_DB_OK ); } /* UNLOCK the ICAS XN Main CB. CHECK !!! */ /* No free ICAS XN CBs. Create one if appropriate */ db_status = IICXget_new_cx_cb( cx_cb_p_p ); if (DB_FAILURE_MACRO(db_status)) { return(db_status); } if ((icas_xn_cb_p = (IICX_ICAS_XN_CB *)MEreqmem((u_i4)0, (u_i4)sizeof(IICX_ICAS_XN_CB), TRUE, (STATUS *)&cl_status)) == NULL) { CVna((i4)cl_status, ebuf); IICXerror(GE_NO_RESOURCE, E_CX0002_CX_CB_ALLOC, 2, ebuf, ERx("IICX_ICAS_XN_CB")); return( E_DB_FATAL ); } (*cx_cb_p_p)->cx_sub_cb.icas_xn_cb_p = icas_xn_cb_p; return( E_DB_OK ); } /* IICXcreate_icas_xn_cb */
/*{ ** Name: psy_put - Put characters in blocks for output ** ** Description: ** This function buffers up characters to be sent to the user by SCF. ** It puts as much of the current request in the current block as ** possible. If there is any overflow, it allocates another block and ** puts the excess there. It keeps doing this until all the input is ** used up. ** ** Inputs: ** mstream Memory stream to use for allocating ** blocks. ** inbuf Characters to send to user ** len Number of chars to send to user ** block Pointer to pointer to block to use ** err_blk Filled in if an error happens ** ** Outputs: ** block Filled in with string to send to user. ** A new block may be allocated and this ** pointer modified to point to new one. ** err_blk Filled in if an error happened ** Returns: ** E_DB_OK Success ** E_DB_FATAL Failure ** Exceptions: ** none ** ** Side Effects: ** Can allocate memory ** ** History: ** 15-jul-86 (jeff) ** written */ DB_STATUS psy_put( PSF_MSTREAM *mstream, register char *inbuf, register i4 len, PSY_QTEXT **block, DB_ERROR *err_blk) { i4 tocopy; i4 left; char *outplace; PSY_QTEXT *newblock; DB_STATUS status; #ifdef xDEBUG i4 val1, val2; /* tracing temps */ PSS_SESBLK *sess_cb; sess_cb = psf_sesscb(); #endif while (len > 0) { /* Copy as much of input as will fit in current block */ left = PSY_QTSIZE - (*block)->psy_qcount; tocopy = len > left ? left : len; outplace = (*block)->psy_qtext + (*block)->psy_qcount; #ifdef xDEBUG if (ult_check_macro(&sess_cb->pss_trace, PSS_PSY_PUT_TRACE, &val1, &val2)) { TRdisplay("%.#s", tocopy, inbuf); } #endif MEcopy(inbuf, tocopy, outplace); len -= tocopy; (*block)->psy_qcount += tocopy; inbuf += tocopy; /* If there is more input, create another block and link it up */ if (len > 0) { status = psy_txtalloc(mstream, &newblock, err_blk); if (DB_FAILURE_MACRO(status)) return (status); (*block)->psy_qnext = newblock; /* Use the new block until a new one is allocated */ *block = newblock; } } return (E_DB_OK); }
/* PSY_SQLVIEW - IS THIS AN SQL VIEW ** ** Description: ** This routine takes a range entry and determines if it is an ** SQL view. This routine assumes that we know that the range entry ** refers to a view. ** ** History: ** 29-sep-92 (andre) ** RDF may choose to allocate a new info block and return its address ** in rdf_info_blk - we need to copy it over to pss_rdrinfo to avoid ** memory leak and other assorted unpleasantries */ DB_STATUS psy_sqlview( PSS_RNGTAB *rngvar, PSS_SESBLK *sess_cb, DB_ERROR *err_blk, i4 *issql) { DB_STATUS status = E_DB_OK; RDF_CB rdf_cb; PST_PROCEDURE *pnode; PST_QTREE *vtree; i4 err_code; *issql = FALSE; pst_rdfcb_init(&rdf_cb, sess_cb); STRUCT_ASSIGN_MACRO(rngvar->pss_tabid, rdf_cb.rdf_rb.rdr_tabid); rdf_cb.rdf_rb.rdr_types_mask = RDR_VIEW | RDR_QTREE ; rdf_cb.rdf_rb.rdr_qtuple_count = 1; rdf_cb.rdf_info_blk = rngvar->pss_rdrinfo; status = rdf_call(RDF_GETINFO, (PTR) &rdf_cb); /* ** RDF may choose to allocate a new info block and return its address in ** rdf_info_blk - we need to copy it over to pss_rdrinfo to avoid memory ** leak and other assorted unpleasantries */ rngvar->pss_rdrinfo = rdf_cb.rdf_info_blk; if (DB_FAILURE_MACRO(status)) { if (rdf_cb.rdf_error.err_code == E_RD0002_UNKNOWN_TBL) { (VOID) psf_error(E_PS0903_TAB_NOTFOUND, rdf_cb.rdf_error.err_code, PSF_INTERR, &err_code, err_blk, 1, psf_trmwhite(sizeof(DB_TAB_NAME), (char *) &rngvar->pss_tabname), &rngvar->pss_tabname); } else { (VOID) psf_rdf_error(RDF_GETINFO, &rdf_cb.rdf_error, err_blk); } return (status); } pnode = (PST_PROCEDURE *) rdf_cb.rdf_info_blk->rdr_view->qry_root_node; vtree = pnode->pst_stmts->pst_specific.pst_tree; if (vtree->pst_qtree->pst_sym.pst_value.pst_s_root.pst_qlang == DB_SQL) *issql = TRUE; return (status); }
/*{ ** Name: psq_tout - Copy a text chain to QSF memory ** ** Description: ** This function copies a text chain to QSF memory, as a text object. ** It allocates it as a contiguous piece. ** ** Inputs: ** rngtab if non-NULL, range statements will be ** generated for all entries of the range table ** that are active (pss_used && pss_rgno >= 0); ** should be non-NULL only for QUEL queries ** header Pointer to chain header ** mstream Pointer to unopened memory stream ** err_blk Filled in if an error happens ** ** Outputs: ** mstream Filled with object id for new text ** object ** err_blk Filled in if an error happened ** Returns: ** E_DB_OK Success ** E_DB_ERROR Non-catastrophic failure ** E_DB_FATAL Catastrophic failure ** Exceptions: ** none ** ** Side Effects: ** Allocates memory ** ** History: ** 18-jul-86 (jeff) ** written ** 2-sep-86 (seputis) ** changed MEcopy routine parameter to be &hp->psq_tsize ** 6-may-87 (daved) ** clear mstream lock id after unlocking memory stream ** 29-jun-87 (daved) ** emit the range table entries if a range table is provided. ** 10-aug-93 (andre) ** removed declaration of qsf_call() */ DB_STATUS psq_tout( PSS_SESBLK *sess_cb, PSS_USRRANGE *rngtab, PTR header, PSF_MSTREAM *mstream, DB_ERROR *err_blk) { DB_STATUS status; i4 err_code; u_char *buf; QSF_RCB qsf_rb; /* Open the QSF memory stream for the text object */ status = psf_mopen(sess_cb, QSO_QTEXT_OBJ, mstream, err_blk); if (status != E_DB_OK) return (status); status = psq_store_text(sess_cb, rngtab, header, mstream, (PTR *) &buf, (bool) FALSE, err_blk); if (DB_FAILURE_MACRO(status)) return(status); /* Set the root of the object */ status = psf_mroot(sess_cb, mstream, (PTR) buf, err_blk); if (status != E_DB_OK) return (status); /* Unlock the object */ qsf_rb.qsf_type = QSFRB_CB; qsf_rb.qsf_ascii_id = QSFRB_ASCII_ID; qsf_rb.qsf_length = sizeof(qsf_rb); qsf_rb.qsf_owner = (PTR)DB_PSF_ID; qsf_rb.qsf_sid = sess_cb->pss_sessid; STRUCT_ASSIGN_MACRO(mstream->psf_mstream, qsf_rb.qsf_obj_id); qsf_rb.qsf_lk_id = mstream->psf_mlock; status = qsf_call(QSO_UNLOCK, &qsf_rb); if (status != E_DB_OK) { (VOID) psf_error(E_PS0375_UNLOCK_QSF_TEXT, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, err_blk, 0); return (status); } mstream->psf_mlock = 0; return (E_DB_OK); }
static VOID psy_push(PSY_STK *base, PTR val, STATUS *sts) { /* Point to true list head block */ PSY_STK *stk = base->head ? base->head : base; if (stk->sp >= N_STK) { base->head = (PSY_STK*)MEreqmem(0, sizeof(PSY_STK), FALSE, sts); if (!base->head || DB_FAILURE_MACRO(*sts)) return; base->head->head = stk; stk = base->head; stk->sp = 0; } stk->list[stk->sp++] = val; }
/*{ ** Name: opu_allocate - allocate a new private memory stream ** ** Description: ** This routine will allocate a new private memory stream from the ULM ** ** Inputs: ** global ptr to global state variable ** ** Outputs: ** Returns: ** PTR which represents the new memory stream ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 16-jun-86 (seputis) ** initial creation ** 11-oct-2006 (hayke02) ** Send E_OP0002_NOMEMORY to errlog.log. This change fixes bug 116309. [@history_line@]... */ PTR opu_allocate( OPS_STATE *global) { DB_STATUS ulmstatus; /* return status from ulm */ /* Tell ULM to return streamid into ulm_streamid */ global->ops_mstate.ops_ulmrcb.ulm_streamid_p = &global->ops_mstate.ops_ulmrcb.ulm_streamid; ulmstatus = ulm_openstream(&global->ops_mstate.ops_ulmrcb); if (DB_FAILURE_MACRO(ulmstatus)) { opx_lerror(E_OP0002_NOMEMORY, 0); opx_verror(ulmstatus, E_OP0002_NOMEMORY, global->ops_mstate.ops_ulmrcb.ulm_error.err_code); } return ( global->ops_mstate.ops_ulmrcb.ulm_streamid ); }
/*{ ** Name: psf_symall Allocates memory for a symbol block. ** ** Description: ** Symbol blocks are used mainly by scanners to pass additional data ** to parsers. They also hold emmitted text. This routine allocates ** a new block and takes care of initializing appropriate fields in the ** session control block. ** ** Inputs: ** pss_cb session control block pointer. ** psq_cb query control block. ** size amount of memory to be allocated. ** ** Outputs: ** pss_cb session control block with ** appropriate fields initialized. ** psq_cb query control block with psq_error ** field initialized, if error. ** Returns: ** E_DB_OK ** status return by ULM ** ** Exceptions: ** none ** ** Side Effects: ** Allocates memory. ** ** History: ** 25-nov-87 (stec) ** written ** 31-jan-97 (pchang) ** psf_symall(), when allocating memory for a symbol table block, ** failed to take into account the space needed for the next block ** pointer 'pss_sbnext' which later caused memory overrun in the data ** area 'pss_symdata'. In some cases, the corruption is neutralized ** when the user of the violated adjacent memory space overlays it ** with new data. Whereas, if the violated memory space is occupied ** by a less volatile entity such as a pointer for a linked list, ** what follows could be a random SEGV. (B75105?) */ DB_STATUS psf_symall( PSS_SESBLK *pss_cb, PSQ_CB *psq_cb, i4 size) { DB_STATUS status = E_DB_OK; ULM_RCB ulm_rcb; i4 err_code; /* If no more blocks, allocate another */ if (pss_cb->pss_symblk->pss_sbnext==(PSS_SYMBLK *) NULL) { /* If out of room, allocate some more */ ulm_rcb.ulm_facility = DB_PSF_ID; ulm_rcb.ulm_streamid_p = &pss_cb->pss_symstr; ulm_rcb.ulm_psize = size + sizeof(PTR); /* add space for fwd ptr */ ulm_rcb.ulm_memleft = &pss_cb->pss_memleft; status = ulm_palloc(&ulm_rcb); if (DB_FAILURE_MACRO(status)) { if (ulm_rcb.ulm_error.err_code == E_UL0005_NOMEM) { psf_error(E_PS0F02_MEMORY_FULL, 0L, PSF_CALLERR, &err_code, &psq_cb->psq_error, 0); } else { psf_error(2704L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error, 0); } return (status); } pss_cb->pss_symblk->pss_sbnext = (PSS_SYMBLK*) ulm_rcb.ulm_pptr; pss_cb->pss_symblk->pss_sbnext->pss_sbnext = NULL; } /* Set current block and next character pointer */ pss_cb->pss_symblk = pss_cb->pss_symblk->pss_sbnext; pss_cb->pss_symnext = pss_cb->pss_symblk->pss_symdata; return (E_DB_OK); }
DB_STATUS IICXdelete_icas_xn_cb( IICX_CB *cx_cb_p /* IN */ ) { DB_STATUS db_status; IICX_ICAS_XN_CB *icas_xn_cb_p; i4 i; /* LOCK the ICAS XN MAIN CB. CHECK !!! */ if ( IIcx_icas_xn_main_cb->num_free_icas_xn_cbs == IIcx_icas_xn_main_cb->max_free_icas_xn_cbs ) { /* UNLOCK the ICAS XN MAIN CB. CHECK !!! */ db_status = IICXfree_cb( cx_cb_p ); if (DB_FAILURE_MACRO( db_status )) return( db_status ); } else { IIcx_icas_xn_main_cb->num_free_icas_xn_cbs++; if (cx_cb_p->cx_prev != NULL) cx_cb_p->cx_prev->cx_next = cx_cb_p->cx_next; else IIcx_icas_xn_main_cb->icas_xn_list = cx_cb_p->cx_next; if (cx_cb_p->cx_next != NULL) cx_cb_p->cx_next->cx_prev = cx_cb_p->cx_prev; if (IIcx_icas_xn_main_cb->icas_xn_cb_free_list != NULL) { IIcx_icas_xn_main_cb->icas_xn_cb_free_list->cx_prev = cx_cb_p; } cx_cb_p->cx_next = IIcx_icas_xn_main_cb->icas_xn_cb_free_list; cx_cb_p->cx_prev = NULL; IIcx_icas_xn_main_cb->icas_xn_cb_free_list = cx_cb_p; } return( E_DB_OK ); } /* IICXdelete_icas_xn_cb */
/*{ ** Name: ops_alter - alter session or server state for OPF ** ** Description: ** This routine contains all processing of the SET commands for the ** optimizer. These commands can be at the server or session level. ** There is currently no protection mechanism available for which ** users are able to access these SET commands. There probably should ** be a system catalog created to decide privileges and min and max ** values allowable for a session. Currently, all users can do anything ** at all. This routine will not do anything in terms of checking ** privileges. ** ** SET [[SESSION] | SERVER] CPUFACTOR <value> ** SET [[SESSION] | SERVER] TIMEOUT <value> ** SET [[SESSION] | SERVER] QEP ** SET [[SESSION] | SERVER] NOQEP ** SET [[SESSION] | SERVER] RET_INTO <storage_structure> ** ** FIXME need to use semaphores to access global structures. ** ** Inputs: ** opf_cb ptr to caller's control block ** ** Outputs: ** Returns: ** E_DB_OK ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 30-jun-86 (seputis) ** initial creation [@history_line@]... */ DB_STATUS ops_alter( OPF_CB *opf_cb) { DB_STATUS status; /* user return status */ switch (opf_cb->opf_level) { case OPF_SERVER: { /* change operating characteristics of the server */ DB_STATUS temp_status; /* get exclusive access before updating */ status = ops_exlock(opf_cb, &((OPS_CB *)(opf_cb->opf_scb))->ops_server->opg_semaphore); if (DB_FAILURE_MACRO(status)) break; temp_status = ops_change( opf_cb, &((OPG_CB*)opf_cb->opf_server)->opg_alter); /* release exclusive access */ status = ops_unlock(opf_cb, &((OPS_CB *)(opf_cb->opf_scb))->ops_server->opg_semaphore); if (DB_SUCCESS_MACRO(status)) status = temp_status; break; } case OPF_SESSION: { /* change operating characteristics of this session */ status = ops_change( opf_cb, &((OPS_CB *)(opf_cb->opf_scb))->ops_alter); break; } default: # ifdef E_OP0089_ALTER { opx_rerror( opf_cb, E_OP0089_ALTER); return( E_DB_ERROR ); } #endif } return (status); }
/*{ ** Name: ops_qsfdestroy - destroy a query plan ** ** Description: ** Since the QP can be in several states, in order to destroy it ** several error recovery paths may be needed. ** ** Inputs: ** global ptr to global state variable ** ** Outputs: ** Returns: ** VOID ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 13-apr-89 (seputis) ** initial creation [@history_template@]... */ static DB_STATUS ops_qsfdestroy( OPS_STATE *global) { DB_STATUS qsfqpstatus; qsfqpstatus = qsf_call( QSO_DESTROY, &global->ops_qsfcb); if (DB_FAILURE_MACRO(qsfqpstatus)) { /* attempt to get a lock on the object, since and error ** may have occurred between the time the OPC released ** the object and prior to returning to SCF */ global->ops_qsfcb.qsf_lk_state = QSO_SHLOCK; qsfqpstatus = qsf_call( QSO_LOCK, &global->ops_qsfcb ); /* ignore the error from ** this routine since we ** will attempt to delete the ** object again anyway */ qsfqpstatus = qsf_call( QSO_DESTROY, &global->ops_qsfcb); } return(qsfqpstatus); }
/*{ ** Name: psy_txtalloc - Allocate a new text block ** ** Description: ** This function allocates a new text block in a memory stream, ** fills it in, and returns a pointer to it. ** ** Inputs: ** mstream Pointer to memory stream ** newblock Place to put pointer to new block ** err_blk Filled in if error happens ** ** Outputs: ** newblock Filled in with pointer to new block ** err_blk Filled in if an error happened ** Returns: ** E_DB_OK Success ** E_DB_ERROR Failure ** Exceptions: ** none ** ** Side Effects: ** Allocates memory ** ** History: ** 15-jul-86 (jeff) ** written */ DB_STATUS psy_txtalloc( PSF_MSTREAM *mstream, PSY_QTEXT **newblock, DB_ERROR *err_blk) { DB_STATUS status; PSS_SESBLK *sess_cb; sess_cb = psf_sesscb(); /* Allocate the block */ status = psf_malloc(sess_cb, mstream, sizeof(PSY_QTEXT), (PTR *) newblock, err_blk); if (DB_FAILURE_MACRO(status)) return (status); /* Fill in the block */ (*newblock)->psy_qnext = (PSY_QTEXT *) NULL; (*newblock)->psy_qcount = 0; return (E_DB_OK); }
/*{ ** Name: opu_memory - get joinop memory ** ** Description: ** This routine will allocate the requested size of memory from the ** joinop memory stream. Memory in this stream is not deallocated ** until the optimization has completed. The allocated memory will ** be aligned for any datatype. ** ** Inputs: ** global ptr to global state variable ** size size of memory block requested. ** ** Outputs: ** Returns: ** PTR to aligned memory of "size" bytes ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 16-jun-86 (seputis) ** initial creation ** 4-mar-91 (seputis) ** make initialization of memory an xDEBUG feature ** 11-oct-2006 (hayke02) ** Send E_OP0002_NOMEMORY to errlog.log. This change fixes bug 116309. ** 5-oct-2007 (dougi) ** Accumulate memory acquisition stats. */ PTR opu_memory( OPS_STATE *global, i4 size) { DB_STATUS ulmstatus; /* return status from ULM */ global->ops_mstate.ops_countalloc++; if (size >= 2048) global->ops_mstate.ops_count2kalloc++; global->ops_mstate.ops_totalloc += size; global->ops_mstate.ops_ulmrcb.ulm_streamid_p = &global->ops_mstate.ops_streamid; /* allocate memory ** from the global stream */ global->ops_mstate.ops_ulmrcb.ulm_psize = size; /* size of request */ ulmstatus = ulm_palloc( &global->ops_mstate.ops_ulmrcb ); if (DB_FAILURE_MACRO(ulmstatus)) { if (global->ops_mstate.ops_ulmrcb.ulm_error.err_code == E_UL0005_NOMEM) { opx_lerror(E_OP0002_NOMEMORY, 0, 0, 0, 0, 0); opx_error( E_OP0002_NOMEMORY); /* out of memory */ } #ifdef E_OP0093_ULM_ERROR else opx_verror( ulmstatus, E_OP0093_ULM_ERROR, global->ops_mstate.ops_ulmrcb.ulm_error.err_code); /* check for error */ #endif } #ifdef xDEBUG MEfill( size, (u_char)247, (PTR)global->ops_mstate.ops_ulmrcb.ulm_pptr); /*FIXME ** remove this initialization after ** test for uninitialized memory ** is not required any more */ #endif return( global->ops_mstate.ops_ulmrcb.ulm_pptr ); /* return the allocated ** memory */ }
/*{ ** Name: psq_parseqry - Parse a query and return a data structure ** ** INTERNAL PSF call format: status = psq_parseqry(&psq_cb, &sess_cb); ** ** EXTERNAL call format: status = psq_call(PSQ_PARSEQRY, &psq_cb, &sess_cb); ** ** Description: ** This function will parse a query in QSF memory. It will return a ** data structure (such as a query tree), and a code telling what kind of ** query was just parsed (e.g. a copy statement). This code will be ** called the "query mode". For qrymod definitions, it will store a ** modified version of the query text in QSF, for later insertion in the ** iiqrytext system relation. ** ** This function will check the syntax of each statement, and will perform ** some semantic validations. The syntax checking will be rather simple: ** each statement will either be right or wrong, and a syntax error will ** cause the whole go block to be aborted. Semantic checks will be more ** complicated; some of them will cause warnings (which will allow the ** statement to continue), and some will cause errors (causing the ** statement to be aborted). It should be noted that the semantic checks ** will not be the same in the Jupiter version as in previous versions; ** in the Jupiter version, for instance, the parser won't check for whether ** the statement is valid inside a multi-query transaction. ** ** Sometimes it will be found that the definition of a view, permit, or ** integrity is out of date. When this happens, this function will return ** an error status saying so, and an id for finding the query text in the ** iiqrytext relation, so that the definition can be re-parsed and ** re-stored. Accordingly, this function has an option by which one can ** tell it to get the query text out of the iiqrytext relation instead of ** QSF. ** ** When a statement is parsed that creates a new cursor, the parser will ** assign a cursor id to the cursor, create a cursor control block ** containing information about the cursor, and return the cursor id inside ** with the query tree representing the cursor. This cursor id will be ** used throughout the session to uniquely identify the cursor. When a ** "close cursor" statement is parsed, the parser will deallocate the ** control block associated with the cursor. ** ** The parser will automatically apply view, permit, and integrity ** processing to any data manipulation query it parses. This will not ** require a separate call to the parser. ** ** Multi-statement go blocks are no longer allowed, as they used to be in ** previous versions. This parser can handle only one statement at a time. ** ** Inputs: ** psq_cb ** .psq_qid A unique identifier for getting the ** query text from QSF. ** .psq_iiqrytext TRUE means to get the query text from ** the iiqrytext relation, not QSF. ** .psq_txtid Query text id key into iiqrytext ** relation; used only if above is true. ** sess_cb Pointer to the session control block ** ** Outputs: ** psq_cb ** .psq_qlang The language of the query text. ** .psq_mode The query mode (a code telling what ** kind of query was just parsed). ** .psq_result QSF id for the data structure produced ** (query tree, or control block stored as ** QEP). ** .psq_txtid Query text id key into iiqrytext ** relation; filled in if some qrymod ** object needs redefining. ** .psq_mnyfmt Set on a "set money_format" or ** "set money_prec" statement ** .psq_dtefmt Set on a "set date_format" statement ** .psq_decimal Set on a "set decimal" statement ** .psq_error Standard error block ** E_PS0000_OK Success ** E_PS0001_USER_ERROR Mistake by user ** E_PS0002_INTERNAL_ERROR Internal inconsistency inside PSF ** E_PS0B01_OUTDATED_VIEW View out of date; must re-define ** E_PS0B02_OUTDATED_PERMIT Permit out of date; must re-define ** E_PS0B03_OUTDATED_INTEG Integrity out of date; must re-def. ** E_PS0B04_CANT_GET_TEXT Can't get query text ** .psq_txtout QSF id for the create procedure stmt to ** be stored in the system catalog. ** ** Returns: ** E_DB_OK Function completed normally. ** E_DB_WARN Function completed with warning(s) ** E_DB_ERROR Function failed; non-catastrophic error ** E_DB_FATAL Function failed; catastrophic error ** Exceptions: ** none ** ** Side Effects: ** Stores query tree or control block in QSF. ** Can open a cursor. ** ** History: ** 01-oct-85 (jeff) ** written ** 19-sep-86 (daved) ** end of qry should point to last char. This char should be a space. ** this makes the scanner's job easier ** 27-jan-87 (daved) ** add the printqry set command. ** 02-oct-87 (stec) ** Removed pss_journaling flag initialization; ** must be initialized in psqbgnses.c ** 19-jan-88 (stec) ** Changed initialization od pst_resloc. ** 25-may-88 (stec) ** Made changes in connection with DB procedures. ** QSF object of QP type has to be destroyed if ** translate_or_define worked for CREATE PROCEDURE. ** 23-aug-88 (stec) ** Initialize qso_handle in QSO_OBIDs in psq_cb. ** 21-apr-89 (neil) ** Extracted some initialization (psq_cbinit) from psq_parseqry to ** allow it to be called from other routines as well. ** 11-dec-89 (ralph) ** Change interface to QSO for dbprocs ** 12-sep-90 (teresa) ** fix faulty pss_retry logic. ** 15-jun-92 (barbara) ** Sybil merge. Pass in sess control block to pst_clrrng. ** 23-nov-92 (barbara) ** For Star, accept range statement as the one and only allowable ** QUEL statement. This is to support old FE's which use the range ** statement as a quick way to ascertain table existence. FEs of ** >= 6.5 vintage use a table_resolve() function, so at some point ** we can remove the QUEL range table statement support for Star. ** 24-nov-92 (ralph) ** CREATE SCHEMA: ** Initialize pss_prvgoval ** 22-dec-92 (rblumer) ** clean up after pss_tchain2 just like pss_tchain. ** 25-may-93 (rog) ** Move clean-up/exit code into psq_cbreturn() and then call it. ** 11-oct-93 (swm) ** Bug #56448 ** Declared trbuf for psf_display() to pass to TRformat. ** TRformat removes `\n' chars, so to ensure that psf_scctrace() ** outputs a logical line (which it is supposed to do), we allocate ** a buffer with one extra char for NL and will hide it from TRformat ** by specifying length of 1 byte less. The NL char will be inserted ** at the end of the message by psf_scctrace(). ** 16-mar-94 (andre) ** if performing an internal PSF retry and trace point ps129 (same as *8 SET PRINTQRY) is set, instead of redisplaying the query we will *8 tell the user that we are retrying the last query. ** 28-feb-2005 (wanfr01) ** Bug 64899, INGSRV87 ** Add stack overflow handler for TRU64. ** 17-mar-06 (dougi) ** Init pss_hintcount to 0 for optimizer hints project. ** 23-june-06 (dougi) ** Init pss_stmtno to 1 for procedure debugging. ** 05-sep-06 (toumi01) ** Init pss_stmtno to 0 (to match new "help procedure" numbering) ** lest we point, PC register like, to the _following_ statement. ** 15-Sep-2008 (kibro01) b120571 ** Use same session ID as available in iimonitor ** 16-Sep-2008 (kibro01) b120571 ** Remove compilation error from cast of sessid ** 16-Feb-2009 (kibro01) b121674 ** Add version to SESSION BEGINS message in sc930 and give out ** data type of parameters. ** 10-Jul-2009 (kibro01) b122299 ** Increase the version number due to dates being printed out now. ** 15-Jul-2009 (kibro01) b122172 ** Change QUERY or QUEL to REQUERY or REQUEL when a DB procedure is ** reparsed due to being invalidated. ** 23-Jul-2009 (kibro01) b122172 ** Separate the print_qry_buffer logic to avoid stack size problems. ** 3-Aug-2009 (kibro01) b122393 ** Use print_qry_buffer_ptr to avoid inlining. ** 4-Aug-2009 (kibro01) b122172 ** Allow REQUERY/REQUEL through even if the RECREATE flag is set so ** we get the useful debug output. ** 28-Oct-2009 (maspa05) b122725 ** ult_print_tracefile now uses integer constants instead of string ** for type parameter - SC930_LTYPE_PARM instead of "PARM" and so on ** Also moved function definitions for SC930 tracing to ulf.h ** The functions involved were - ult_always_trace, ult_open_tracefile ** ult_print_tracefile and ult_close_tracefile */ DB_STATUS psq_parseqry( register PSQ_CB *psq_cb, register PSS_SESBLK *sess_cb) { DB_STATUS status; DB_STATUS ret_val; QSF_RCB qsf_rb; i4 err_code; PSQ_QDESC *qdesc; i4 val1 = 0; i4 val2 = 0; i4 i; char trbuf[PSF_MAX_TEXT + 1]; /* last char for `\n' */ if ((status = psq_cbinit(psq_cb, sess_cb)) != E_DB_OK) return (status); /* ** The following is particular to queries that must be parsed. ** Get query text from QSF and put it in session control block ** Initialize the qbuf, nextchar, prevtok, and bgnstmt pointers */ qsf_rb.qsf_type = QSFRB_CB; qsf_rb.qsf_ascii_id = QSFRB_ASCII_ID; qsf_rb.qsf_length = sizeof(qsf_rb); qsf_rb.qsf_owner = (PTR)DB_PSF_ID; qsf_rb.qsf_sid = sess_cb->pss_sessid; qsf_rb.qsf_obj_id.qso_handle = psq_cb->psq_qid; status = qsf_call(QSO_INFO, &qsf_rb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_error(E_PS0B04_CANT_GET_TEXT, 0L, PSF_CALLERR, &err_code, &psq_cb->psq_error, 0); return (E_DB_ERROR); } qdesc = (PSQ_QDESC*) qsf_rb.qsf_root; /* print the qry buffer as long as this isn't a retry ** - although allow through the RECREATE case since it's useful ** for debugging to log reparsing an object */ if ((ult_always_trace() & SC930_TRACE) && ( ((sess_cb->pss_retry & PSS_REFRESH_CACHE)==0) || (sess_cb->pss_dbp_flags & PSS_RECREATE) != 0 ) ) { (*print_qry_buffer_ptr)(psq_cb, qdesc, sess_cb); } if (ult_check_macro(&sess_cb->pss_trace, PSS_PRINT_QRY_TRACE, &val1, &val2)) { if (psf_in_retry(sess_cb, psq_cb)) { psf_display(psf_scctrace, 0, trbuf, sizeof(trbuf) - 1, "\n...retrying last query...\n"); } else { psf_display(psf_scctrace, 0, trbuf, sizeof(trbuf) - 1, "\nQUERY BUFFER:\n"); psf_display(psf_scctrace, 0, trbuf, sizeof(trbuf) - 1, "%.#s\n", qdesc->psq_qrysize, qdesc->psq_qrytext); psf_display(psf_scctrace, 0, trbuf, sizeof(trbuf) - 1, "\nQUERY PARAMETERS:\n"); for (i = 0; i < qdesc->psq_dnum; i++) { psf_display(psf_scctrace, 0, trbuf, sizeof(trbuf) - 1, "Parameter : %d\n", i); adu_2prvalue(psf_relay, qdesc->psq_qrydata[i]); psf_display(psf_scctrace, 0, trbuf, sizeof(trbuf) - 1, "\n"); } } } sess_cb->pss_bgnstmt = (u_char*) qdesc->psq_qrytext; sess_cb->pss_prvgoval = (u_char*) NULL; sess_cb->pss_prvtok = (u_char*) qdesc->psq_qrytext; sess_cb->pss_qbuf = (u_char*) qdesc->psq_qrytext; sess_cb->pss_nxtchar = (u_char*) qdesc->psq_qrytext; sess_cb->pss_endbuf = sess_cb->pss_qbuf + qdesc->psq_qrysize - 1; sess_cb->pss_dmax = qdesc->psq_dnum; sess_cb->pss_qrydata = qdesc->psq_qrydata; *sess_cb->pss_endbuf = ' '; sess_cb->pss_lineno = 1; /* Start out at line one */ sess_cb->pss_stmtno = 0; /* and statement at zero */ sess_cb->pss_dval = 0; sess_cb->pss_hintcount = 0; psl_yinit(sess_cb); if (psq_cb->psq_qlang == DB_QUEL) { if (sess_cb->pss_distrib & DB_3_DDB_SESS) { char *c; char *r = "range"; /* skip leading white space chars, if any */ for (c = qdesc->psq_qrytext; c <= (char *) sess_cb->pss_endbuf && CMwhite(c); CMnext(c) ) ; /* compare the first word with "range" */ for (; *r != EOS && c <= (char *) sess_cb->pss_endbuf && !CMcmpnocase(c,r); CMnext(c), CMnext(r) ) ; /* ** we will go on to parse this statement iff ** 1) first non-white chars are "range" AND ** 2) 'e' is followed by a white space */ if (*r != EOS || c >= (char *) sess_cb->pss_endbuf || !CMwhite(c)) { (VOID) psf_error(5212L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error,0); return(E_DB_ERROR); } } sess_cb->pss_parser = pslparse; } else { sess_cb->pss_parser = pslsparse; } IIEXtry { status = (*sess_cb->pss_parser)(sess_cb, psq_cb); } IIEXcatch(pthread_stackovf_e) { (VOID) psf_error(5212L, 0L, PSF_USERERR, &err_code, &psq_cb->psq_error,0); } IIEXendtry ret_val = psq_cbreturn(psq_cb, sess_cb, status); return (ret_val); }
/* ** Name: psq_destr_dbp_qep - destroy QSF object created for a dbproc ** ** Description: ** If an error occurs in PSF in the course of parsing a dbproc definition ** or recreating a dbproc QEP, we need to destroy the QEP object created ** in PSF before exiting. Otherwise, subsequent attempt to parse or ** recreate the dbproc will result in SC0206 because the QP object created ** during the first (aborted) attempt will still be locked in QSF ** ** Input: ** handle QP object handle ** ** Output: ** err_blk filled in if an error occurs ** ** Side effects: ** ** History: ** 27-aug-93 (andre) ** extracted from psq_cbreturn() to enable psq_recreate() call it as ** well */ DB_STATUS psq_destr_dbp_qep( PSS_SESBLK *sess_cb, PTR handle, DB_ERROR *err_blk) { DB_STATUS status; PST_PROCEDURE *pnode; QSF_RCB qsf_rb; i4 err_code; /* ** first get the root of the query tree object - using it, we will ** derive dbp QP object alias */ qsf_rb.qsf_type = QSFRB_CB; qsf_rb.qsf_ascii_id = QSFRB_ASCII_ID; qsf_rb.qsf_length = sizeof(qsf_rb); qsf_rb.qsf_owner = (PTR)DB_PSF_ID; qsf_rb.qsf_sid = sess_cb->pss_sessid; qsf_rb.qsf_obj_id.qso_handle = handle; status = qsf_call(QSO_INFO, &qsf_rb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_error(E_PS0A0A_CANTGETINFO, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, err_blk, 0); return(status); } /* ** We are interested in the proc node, because it holds the identifier for ** the QP object we want to destroy. */ pnode = (PST_PROCEDURE *) qsf_rb.qsf_root; /* ** We can only proceed if root has been set - otherwise it is safe to ** assume that the QP object has not been created - so we are done */ if (pnode == (PST_PROCEDURE *) NULL) return(E_DB_OK); qsf_rb.qsf_obj_id.qso_type = QSO_QP_OBJ; qsf_rb.qsf_obj_id.qso_lname = sizeof(DB_CURSOR_ID); (VOID) MEcopy((PTR) &pnode->pst_dbpid, sizeof(DB_CURSOR_ID), (PTR) qsf_rb.qsf_obj_id.qso_name); /* Lock it in shared mode before destroying. */ qsf_rb.qsf_lk_state = QSO_SHLOCK; /* Get handle for the QP object to be destroyed */ status = qsf_call(QSO_GETHANDLE, &qsf_rb); if (DB_FAILURE_MACRO(status)) { /* ** looks like the error occurred before the dbproc QP object was ** created - we are done */ if (qsf_rb.qsf_error.err_code == E_QS0019_UNKNOWN_OBJ) return(E_DB_OK); (VOID) psf_error(E_PS0A0B_CANTGETHNDLE, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, err_blk, 0); return(status); } /* Now it is ready to be destroyed */ status = qsf_call(QSO_DESTROY, &qsf_rb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_error(E_PS0A09_CANTDESTROY, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, err_blk, 0); return(status); } return(E_DB_OK); }
/*{ ** Name: psy_subsvars - Scan query tree and replace VAR nodes ** ** Description: ** Scans a tree and finds all VAR nodes for this variable. ** These nodes are looked up in the translation tree and ** replaced by the value found there. If this is for a ** view, the corresponding node must exist in the translation ** tree. If not for a view, a 'zero' node (of a type appropriate based ** on the context) is created and inserted, or, if for a "replace ** cursor" command, a CURVAL node is substituted for the VAR ** node. ** ** This routine is one half of the guts of the whole view ** algorithm. ** ** VAR nodes are detached and replaced with the replacement ** as defined by the view. Note that there can never be any ** problems here with update anomalies, since VAR nodes are ** only used in retrieve contexts. ** ** It does some extra processing with RESDOM nodes with ** resno = 0. These nodes specify a 'tid' domain, and are ** included by the parser on REPLACE and DELETE commands ** Subsvars will allow this construct iff the right hand pointer is a ** VAR node with attno = 0. In pre-Jupiter versions, this used ** to update the variable number in these tid RESDOMs; in Jupiter, ** however, it was changed to keep the same result variable number ** throughout the qrymod process, so this should be unnecessary. ** This is because the resvar is the variable number of the one and ** only underlying base relation of the view on an update ** (which is presumably the only case where this can come ** up). Psy_vrscan has already insured that there can only be ** a single base relation in this case. ** ** This whole messy thing is only done with view substitutions. ** NOTE: THIS IS NOT TRUE! IT IS ALSO DONE FOR INTEGRITY SUBSTITUTIONS! ** I DON'T KNOW WHY THIS COMMENT IS HERE. ** ** In order to fix the handling of aggregates over views, subsvars ** calls psy_apql at the appropriate place to append a ** view qualification (if any). It is done here to handle nested ** aggregates correctly since after psy_subsvars we no longer know ** which nested aggregates actually contained the view variable. ** The view qual will be appended if and only if the view var ** appears explicitly within the immediate scope of a root node ** (NOT in a nested aggregate.) ** ** If at any scope we encounter a var node, we add the qualification ** to that scope. Once a var node has been found in a scope (and ** the qualifaction added), for example, a nested aggregate, the ** qualification is not added to an outer scope unless a var node ** in the view or integ has been found in that outer scope. ** ** ** Inputs: ** proot Pointer to pointer to root of tree ** to be updated ** rngvar view variable range table entry ** transtree Pointer to the target list of the ** translation tree ** vmode PSQ_VIEW if called from view processor, ** PSQ_APPEND is called from the integrity ** processor with an APPEND command, else ** something else. Mostly, changes ** handling of tid nodes, and forces an ** error on a view if the VAR node in the ** scanned tree does not exist in the ** vtree. ** vqual View qualification to be appended, ** if any. ** resvar Range table entry for result variable ** in query being modified. ** from_list from_list from view getting added. ** qmode Query mode of user query. ** cursid Cursor id of current cursor, if any ** result Pointer to indicator for result. ** dup_rb Ptr to dup. request block ** pss_op_mask -- 0 ** pss_num_joins -- PST_NOJOIN ** pss_tree_info -- NULL ** pss_mstream -- ptr to memory stream to be used ** pss_err_blk -- ptr to error block ** ** Outputs: ** proot User query tree can be updated ** result Filled in with TRUE if view variable ** was found, FALSE if not. Valid only ** if E_DB_OK returned. ** dup_rb ** pss_err_blk Filled in if an error happens. ** Returns: ** E_DB_OK Success ** E_DB_ERROR Failure ** Exceptions: ** none ** ** Side Effects: ** Can allocate memory ** ** History: ** 19-jun-86 (jeff) ** written ** 1-oct-86 (daved) ** set return to TRUE if a VAR node is found. ** 23-dec-86 (daved) ** copy qualification before appending it. this avoids graphs in ** the tree as well as, and more importantly, the re-use of the ** memory used by the qualification before its time. That is, ** if vqual is in temporary memory and gets deleted but the ** proot tree thinks the memory is still around, bad things happen. ** 12-aug-87 (stec) ** Removed test for special 'tid' attribute case, which, according ** to Jeff is no longer needed. ** Check for special 'tid' resdom now includes open cursor stmt. ** 15-oct-87 (stec) ** Added the removed test for special 'tid' attribute case; ** it's necessary for checking cases like "retrieve (viewname.tid)". ** 03-dec-87 (stec) ** Change psy_apql interface. ** 31-dec-87 (stec) ** Cleanup. ** 08-feb-88 (stec) ** Modify psy_subsvars to generate CURVAL nodes for replace cursor statement. ** 04-nov-88 (stec) ** Fix a view substitution bug. When visiting an AGHEAD node it may happen ** that count(*), or count(const) were defined, in which case there are ** no VAR nodes and the applicability of the view has to be determined from ** the relation bitmap in the node. This anomaly exists only in SQL. ** 14-dec-88 (stec) ** Fix correlated subqueries bug. ** 05-apr-89 (andre) ** simplify the test for when reference to the view in pst_tvrm is to ** be replaced with the tables used to define the view. We no longer ** care if there were any variables found below the root node, instead, ** we do it for every root node which has a bit corresponding to the ** view set in pst_tvrm. ** As a part of the fix, qualification of the view will be appended to ** the tree whenever the view is found in the pst_tvrm of the root ** node. ** Besides allowing us to get rid of calling recursive psy_fixmap() in ** psy_view, but it also fixes bugs such as: ** "select const from view" returning more rows than there are in the ** view. ** 04-may-89 (andre) ** for the time being, set pst_maks1 in all ROOT-type nodes to 0. ** 01-jun-89 (andre) ** The preceding fix was not perfect. ** "create view v as select * from t where <qual>; ** select <aggregate> from v\g" ** would result in as many rows containing result of applying ** <aggregate> as there are rows in v. This happens only in SQL. The ** problem is that <qual> gets appended to both AGGHEAD node and the ** ROOT node. The solution is as follows: ** For every node N s.t. N is of type ROOT or SUBSELECT, remember ** if <qual> has been applied to an AGGHEAD node in the left ** subtree of N (in SQL you can not have AGGHEADs in the ** "where-clause"). If <qual> has been applied to AGGHEAD(s) in ** the left subtree of N, do not append it to the right subtree of ** N. ** 22-jun-89 (andre) ** And yet another fix for the previous bug fix. I have incorrectly ** assumed that there may be no AGGHEADs in the right subtrre of ** ROOT/SUBSEL (select ... having agg(col)). Before setting *mask to ** indicate that an AGGHEAD has been seen, make sure that we are in the ** left subtree of the immediate ROOT/SUBSEL parent ** (mask != (i4 *) NULL). If mask is NULL, we must be in the right ** subtree, and the fact that we saw an AGGHEAD is of no importance ** (or shall I add "or so I believe"?) ** 13-sep-89 (andre) ** receive ptr to PSS_DUPRB which will point at memopry stream and ** error block + it will be used when calling pst_treedup(). The ** fields in dup_rb must be set as follows: ** pss_op_mask -- 0 ** pss_num_joins -- PST_NOJOIN ** pss_tree_info -- NULL ** pss_mstream -- ptr to memory stream to be used ** pss_err_blk -- ptr to error block ** 14-sep-92 (andre) ** do not zero out pst_mask1 in PST_ROOT and PST_SUBSEL node ** (fix for bug 45238) ** 11-feb-93 (andre) ** if a query tree involved a reference to a TID attribute of a view V, ** replace it with a reference to TID attribute of V's underlying table ** or view; this is accomplished by replacing variable number found in ** the PST_VAR node with the variable number of the view's underlying ** table/view (which can be found by looking for the first set bit in ** from_list) ** 27-nov-02 (inkdo01) ** Range table expansion (i4 changed to PSAT_J_MASK). ** 13-Jun-2006 (kschendel) ** Barf if we translate a var node to a seqop default in an INSERT. ** This only happens if we're translating an integrity where-clause ** tree, and a var in that where-clause isn't mentioned in the ** values list, so we stick the default in instead. Seqops aren't ** allowed in where clauses. (It would imply that the insert ** integrity-permission depends on the sequence value, which is ** silly at best.) ** 15-May-2007 (kiria01) b111992 ** Flatten out much of the recursion of this function to reduce ** runtime stack usage - especially bad with views containing ** massive IN clauses (>5K elements). ** 28-nov-2007 (dougi) ** Add PSQ_REPDYN to PSQ_DEFCURS test for cached dynamic qs. ** 05-Nov-2009 (kiria01) b122841 ** Use psl_mk_const_similar to cast default values directly. ** 12-Nov-2009 (kiria01) b122841 ** Corrected psl_mk_const_similar parameters with explicit ** mstream. ** 5-Feb-2010 (hanal04) Bug 123209 ** psy_integ() calls psy_subsvars() to subsitute VARs in the ** integrity tree with the corresponding nodes from the ** user query. When a VAR is replaced with a CONST cast the ** CONST to the VAR's datatype. This stops the substitution from ** breaking ADE_COMPAREN & ADE_NCOMPAREN processing if the ** VAR was part of an IN LIST. ** 18-May-2010 (kiria01) b123442 ** Force psl_mk_const_similar to generate coercion to cleanly ** enable correct datatype to be represented when substituting ** default values. */ DB_STATUS psy_subsvars( PSS_SESBLK *cb, PST_QNODE **proot, PSS_RNGTAB *rngvar, PST_QNODE *transtree, i4 vmode, PST_QNODE *vqual, PSS_RNGTAB *resvar, PST_J_MASK *from_list, i4 qmode, DB_CURSOR_ID *cursid, i4 *mask, PSS_DUPRB *dup_rb) { PSY_STK stk = {0, 0, {0, }};/* Backtrack stack */ PST_QNODE *t; /* Temporary for *proot */ i4 vn = rngvar ? rngvar->pss_rgno : -1; /* can be NULL on replace cursor statements */ i4 err_code; DB_STATUS status = E_DB_OK; while(proot && (t = *proot)) { /* These 3 mask variables are only used for ROOT, SUBSEL and AGHEAD */ i4 newmask; /* For receiving result from recursive call */ i4 *l_mask; /* For propagating state to recursive caller */ i4 *r_mask; /* .. */ /* ** The recursive nature of this function has been restructured to ** allow for most of the processing to be achieved in an iterative ** manner. Unlike with the other functions in this module, the ** flattening could not be complete due to the 'mask' output parameter ** which requires local storage for certain node types: ROOT, SUBSEL ** and AGHEAD. If we have one of these node types we recurse 1 level ** to process the left and right sub-trees with the correct scoping of ** the 'mask' variable. */ switch (t->pst_sym.pst_type) { case PST_ROOT: /* Handle any unions */ if (t->pst_sym.pst_value.pst_s_root.pst_union.pst_next) { /* ** Defer the tree representing the next subselect in the UNION ** status = psy_subsvars(cb, ** &t->pst_sym.pst_value.pst_s_root.pst_union.pst_next, rngvar, transtree, ** vmode, vqual, resvar, from_list, qmode, cursid, (i4 *) NULL, ** dup_rb); */ psy_push(&stk, (PTR)&t->pst_sym.pst_value.pst_s_root.pst_union.pst_next, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } } /*FALLTHROUGH*/ case PST_SUBSEL: /* ** The following applies when language is SQL: ** if this is a ROOT or a SUBSELECT node, we want to know if the left ** subtree contains AGGHEAD nodes to which view qualification have been ** applied; we are not concerned with AGGHEAD nodes in the ** qualification (there shouldn't be any, anyway) or in other members ** of the union. */ if (cb->pss_lang == DB_SQL) { newmask = 0; l_mask = &newmask; /* we don't care about the right subtree */ r_mask = (i4 *) NULL; } /*FALLTHROUGH*/ case PST_AGHEAD: /* ** The following applies when language is SQL: ** If this is an AGGHEAD node, set a bit in 'mask' to remember that ** we saw it. */ if (t->pst_sym.pst_type == PST_AGHEAD) { if (cb->pss_lang == DB_SQL) { if (l_mask = r_mask = mask) /* ** If we are in the right subtree of the immediate ROOT/SUBSELECT ** parent, mask will be NULL, since we are not concerned with ** AGHEADs in the right subtrees. */ *mask |= PSS_1SAW_AGG; } /* ** pst_mask1 in PST_AGHEAD node is neither used nor set; I think it ** would be a good idea to set it, but at this point there is not a heck ** of a lot that we can do. Here we will zero out PST_AGHEAD.pst_mask1 ** purely for esthetic reasons. */ t->pst_sym.pst_value.pst_s_root.pst_mask1 = 0; } /* ** Recurse 1 level to process the left & right subtrees completly ** so that we can complete the processing of this node */ status = psy_subsvars(cb, &t->pst_left, rngvar, transtree, vmode, vqual, resvar, from_list, qmode, cursid, l_mask, dup_rb); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* Process the right branch */ status = psy_subsvars(cb, &t->pst_right, rngvar, transtree, vmode, vqual, resvar, from_list, qmode, cursid, r_mask, dup_rb); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* Add `from' list to bitmap, remove entry for replaced var */ if (BTtest(vn, (char*)&t->pst_sym.pst_value.pst_s_root.pst_tvrm)) { BTclear(vn, (char*)&t->pst_sym.pst_value.pst_s_root.pst_tvrm); BTor(PST_NUMVARS, (char *)from_list, (char *)&t->pst_sym.pst_value.pst_s_root.pst_tvrm); t->pst_sym.pst_value.pst_s_root.pst_tvrc = BTcount((char*)&t->pst_sym.pst_value.pst_s_root.pst_tvrm, BITS_IN(t->pst_sym.pst_value.pst_s_root.pst_tvrm)); /* ** We will append qualification (if there is one) if the ** following holds: ** 1) This is not an SQL (must be QUEL) query OR ** 2) if node is ROOT or SUBSEL (i.e. not an AGHEAD) then there ** were no AGHEADs found in its left subtree ** ** Let QUAL <==> there is a qualification, ** SQL <==> language is SQL ** ROOT <==> node type is ROOT ** SUBSEL <==> node type is SUBSEL ** AGG <==> node type is AGHEAD ** SAW_AGG <==> mask & PSS_1SAW_AGG. Then ** ** (Do not apply qualification) <==> ** !QUAL + SQL * (ROOT + SUBSEL) * SAW_AGG --> ** (Apply qualification) <==> ** !(!QUAL + SQL * (ROOT + SUBSEL) * SAW_AGG) <==> ** QUAL * !(SQL * (ROOT + SUBSEL) * SAW_AGG) <==> ** QUAL * (!SQL + !((ROOT + SUBSEL) * SAW_AGG)) <==> ** QUAL * (!SQL + !(ROOT + SUBSEL) + !SAW_AGG) <==> ** QUAL * (!SQL + AGG + !SAW_AGG) */ if (vqual && (cb->pss_lang != DB_SQL || t->pst_sym.pst_type == PST_AGHEAD || (*l_mask & PSS_1SAW_AGG) == 0)) { PST_QNODE *vqual_copy; dup_rb->pss_tree = vqual; dup_rb->pss_dup = &vqual_copy; status = pst_treedup(cb, dup_rb); dup_rb->pss_tree = (PST_QNODE *)NULL; dup_rb->pss_dup = (PST_QNODE **)NULL; if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* append view qualification */ status = psy_apql(cb, dup_rb->pss_mstream, vqual_copy, t, dup_rb->pss_err_blk); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } } } /* left & right have been processed */ break; case PST_VAR: /* ** This is a terminal node - the expectation is that left & right are 0 */ /* ** Check for a VAR node but of a different variable than the one ** we are substituting for. REPLACE CURSOR (quel version) is an ** exception because the substitution variable (resvar) is not ** defined, in that case we do not want to execute the code ** below, but want to continue the translation process. */ if (vn != -1 && t->pst_sym.pst_value.pst_s_var.pst_vno != vn) break; /* ** if this is a reference to a TID attribute of a view (which is not ** a "real" attribute), it needs to be translated into a reference ** to the TID attribute of the view's underlying table or view */ if (t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id == 0 && vmode == PSQ_VIEW) { t->pst_sym.pst_value.pst_s_var.pst_vno = BTnext(-1, (char *) from_list, sizeof(*from_list)); } else { PST_QNODE *v; /* find var in vtree */ status = psy_vfind((u_i2)t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id, transtree, &v, dup_rb->pss_err_blk); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } if (v == (PST_QNODE *)NULL) { /* attribute not defined in view */ if (vmode == PSQ_VIEW) { psf_error(E_PS0D03_ATT_NOT_FOUND, 0L, PSF_INTERR, &err_code, dup_rb->pss_err_blk, 0); status = E_DB_SEVERE; proot = NULL; /* Exiting to return error */ break; } /* append defaults for integrity. Integrity might exist on a value ** we are appending by default. I.e., the attribute was not mentioned ** in the target list. We replace the var node in the integrity with ** a default value so that the integrity will read 'default value' ? ** value. */ else if (vmode == PSQ_APPEND) { status = psl_make_default_node(cb, dup_rb->pss_mstream, resvar, t->pst_sym.pst_value .pst_s_var.pst_atno.db_att_id, &v, dup_rb->pss_err_blk); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* Try to cast to column type */ status = psl_mk_const_similar(cb, dup_rb->pss_mstream, &t->pst_sym.pst_dataval, &v, dup_rb->pss_err_blk, NULL); if (DB_FAILURE_MACRO(status)) return(status); /* If we ended up with a sequence default, fail. This is an ** unreasonable situation, integrity where tests should not apply ** to sequence defaults. */ if (v->pst_sym.pst_type == PST_SEQOP) { psf_error(6319, 0, PSF_USERERR, &err_code, dup_rb->pss_err_blk, 0); status = E_DB_ERROR; proot = NULL; /* Exiting to return error */ break; } } /* we would like to delete the qualification for this node since the ** value is not changing and thus we don't need to worry about integrity ** constaints on it. However, we don't do that. Instead we have the ** integrity refer to the value in the current row that reflects the ** value for the attribute. In the replace statement (not replace ** cursor) we just perform the retrieve, the qualification is unneeded ** but doesn't hurt anything. We want to avoid causing a retrieve for ** each update cursor; therefore, we change the varnode to refer to the ** current value (ie the retrieve has already been done). */ else if (vmode == PSQ_REPCURS) { PST_CRVAL_NODE curval; /* Create a CURVAL node for the corresponding column */ curval.pst_curcol.db_att_id = t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id; STRUCT_ASSIGN_MACRO(*cursid, curval.pst_cursor); status = pst_node(cb, dup_rb->pss_mstream, (PST_QNODE *)NULL, (PST_QNODE *)NULL, PST_CURVAL, (PTR)&curval, sizeof(curval), t->pst_sym.pst_dataval.db_datatype, t->pst_sym.pst_dataval.db_prec, t->pst_sym.pst_dataval.db_length, (DB_ANYTYPE *)t->pst_sym.pst_dataval.db_data, &v, dup_rb->pss_err_blk, (i4) 0); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } } } else { dup_rb->pss_tree = v; dup_rb->pss_dup = &v; status = pst_treedup(cb, dup_rb); dup_rb->pss_tree = (PST_QNODE *)NULL; dup_rb->pss_dup = (PST_QNODE **)NULL; if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* When called from psy_integ() v will be found but ** may still need constants to be cast. */ if ((v != (PST_QNODE *)NULL) && (v->pst_sym.pst_type == PST_CONST)) { bool handled; /* Try to cast to column type */ status = psl_mk_const_similar(cb, dup_rb->pss_mstream, &t->pst_sym.pst_dataval, &v, dup_rb->pss_err_blk, &handled); if (DB_FAILURE_MACRO(status)) return(status); } } /* replace VAR node */ if (v != (PST_QNODE *)NULL) { *proot = v; } } /* left and right should have been null as we are on a terminal */ break; case PST_RESDOM: /* Process `TID' resdom used by DELETE, REPLACE and OPEN CURSOR */ if (t->pst_sym.pst_value.pst_s_rsdm.pst_rsno == 0 && (qmode == PSQ_DELETE || qmode == PSQ_REPLACE || qmode == PSQ_DEFCURS || qmode == PSQ_REPDYN)) { /* ** If resvar not specified, or if not resvar, ignore leaf. */ if (resvar && vn == resvar->pss_rgno) { /* t->right better be VAR node, attno 0 */ t = t->pst_right; if (t->pst_sym.pst_type != PST_VAR || t->pst_sym.pst_value.pst_s_var.pst_atno.db_att_id != 0 || t->pst_sym.pst_value.pst_s_var.pst_vno != vn) { (VOID) psf_error(E_PS0D02_BAD_TID_NODE, 0L, PSF_INTERR, &err_code, dup_rb->pss_err_blk, 0); status = E_DB_SEVERE; proot = NULL; /* Exiting to return error */ break; } } else if (t->pst_right) /* Process the right branch */ psy_push(&stk, (PTR)&t->pst_right, &status); } else if (t->pst_right) /* Process the right branch */ psy_push(&stk, (PTR)&t->pst_right, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } /* Process left branch */ if (t->pst_left) psy_push(&stk, (PTR)&t->pst_left, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } break; default: /* ** Just ensure that we traverse the tree */ if (t->pst_right) psy_push(&stk, (PTR)&t->pst_right, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } if (t->pst_left) psy_push(&stk, (PTR)&t->pst_left, &status); if (DB_FAILURE_MACRO(status)) { proot = NULL; /* Exiting to return error */ break; } break; } if (!proot) /* We're breaking out early to exit */ break; /* ** Get next deferred node */ proot = (PST_QNODE**)psy_pop(&stk); } /* Release any outstanding entries */ psy_pop_all(&stk); return status; }
ADF_CB * FEadfcb () { if (!done) { char *cp; DB_STATUS rval; PTR srv_cb_ptr; SIZE_TYPE srv_size; SIZE_TYPE dbinfo_size; PTR dbinfoptr; STATUS tmtz_status; STATUS date_status; char date_type_alias[100]; char col[] = "udefault"; STATUS status = OK; CL_ERR_DESC sys_err; ADUUCETAB *ucode_ctbl; /* unicode collation information */ PTR ucode_cvtbl; done = TRUE; /* first, get the size of the ADF's server control block */ srv_size = adg_srv_size(); /* allocate enough memory for it */ if ((srv_cb_ptr = MEreqmem(0, srv_size, FALSE, (STATUS *)NULL)) == NULL) { EXsignal(EXFEBUG, 1, ERx("FEadfcb(alloc)")); } /* ** Fix up code so that "now" works properly for frontends. ** Only allocating enough for one (1) dbmsinfo() request. */ dbinfo_size = sizeof(ADF_TAB_DBMSINFO); if ((dbinfoptr = MEreqmem(0, dbinfo_size, TRUE, (STATUS*)NULL)) == NULL) { IIUGbmaBadMemoryAllocation(ERx("FEadfcb")); } dbinfo = (ADF_TAB_DBMSINFO *) dbinfoptr; dbinfo->tdbi_next = NULL; dbinfo->tdbi_prev = NULL; dbinfo->tdbi_length = dbinfo_size; dbinfo->tdbi_type = ADTDBI_TYPE; dbinfo->tdbi_s_reserved = -1; dbinfo->tdbi_l_reserved = (PTR)-1; dbinfo->tdbi_owner = (PTR)-1; dbinfo->tdbi_ascii_id = ADTDBI_ASCII_ID; dbinfo->tdbi_numreqs = 1; /* ** Now define request. */ dbinfo->tdbi_reqs[0].dbi_func = IIUGnfNowFunc; dbinfo->tdbi_reqs[0].dbi_num_inputs = 0; dbinfo->tdbi_reqs[0].dbi_lenspec.adi_lncompute = ADI_FIXED; STcopy("_BINTIM", dbinfo->tdbi_reqs[0].dbi_reqname); dbinfo->tdbi_reqs[0].dbi_reqcode = 1; dbinfo->tdbi_reqs[0].dbi_lenspec.adi_fixedsize = 4; dbinfo->tdbi_reqs[0].dbi_dtr = DB_INT_TYPE; /* ** set timezone in ADF cb - Any errors must be reported ** after adg_init() called. */ if ( (tmtz_status = TMtz_init(&cb.adf_tzcb)) == OK) { tmtz_status = TMtz_year_cutoff(&cb.adf_year_cutoff); } /* Always use INGRESDATE by default, and check ** 'ii.<node>.config.date_alias' (kibro01) b118702 */ cb.adf_date_type_alias = AD_DATE_TYPE_ALIAS_INGRES; STprintf(date_type_alias,ERx("ii.%s.config.date_alias"),PMhost()); date_status = PMget(date_type_alias, &cp); if (date_status == OK && cp != NULL && STlength(cp)) { if (STbcompare(cp, 0, ERx("ansidate"), 0, 1) == 0) cb.adf_date_type_alias = AD_DATE_TYPE_ALIAS_ANSI; else if (STbcompare(cp, 0, ERx("ingresdate"), 0, 1) == 0) cb.adf_date_type_alias = AD_DATE_TYPE_ALIAS_INGRES; else cb.adf_date_type_alias = AD_DATE_TYPE_ALIAS_NONE; } /* set timezone table to NULL in ADF cb, adg_init() will fill it */ /* cb.adf_tz_table = NULL; - not needed, done in declaration above */ /* Start up ADF */ rval = adg_startup(srv_cb_ptr, srv_size, dbinfo, 0); if (DB_FAILURE_MACRO(rval)) { /* ** Before bailing out, try to be helpful. Since the environment ** is likely not set up correctly, write hard coded messages ** since error message fetch will likely fail in this situation. */ char *tempchar; i4 dummy; LOCATION temploc; NMgtAt("II_SYSTEM", &tempchar); if (tempchar && *tempchar) { LOfroms(PATH, tempchar, &temploc); if (LOexist(&temploc)) { tempchar = ERx("FEadfcb: II_SYSTEM DOES NOT EXIST\n"); SIwrite(STlength(tempchar), tempchar, &dummy, stderr); } else { NMloc(ADMIN, FILENAME, ERx("config.dat"), &temploc); if (LOexist(&temploc)) { tempchar = ERx("FEadfcb: II_SYSTEM IS NOT SET CORRECTLY\n"); SIwrite(STlength(tempchar), tempchar, &dummy, stderr); } } } else { tempchar = ERx("FEadfcb: II_SYSTEM IS NOT SET\n"); SIwrite(STlength(tempchar), tempchar, &dummy, stderr); } EXsignal(EXFEBUG, 1, ERx("FEadfcb(start)")); } /* put the pointer to ADF's server control block in the ADF_CB */ cb.adf_srv_cb = srv_cb_ptr; /* set up the error message buffer */ cb.adf_errcb.ad_ebuflen = DB_ERR_SIZE; cb.adf_errcb.ad_errmsgp = ebuffer; /* initialize the ADF_CB */ rval = adg_init(&cb); if (DB_FAILURE_MACRO(rval)) { EXsignal(EXFEBUG, 1, ERx("FEadfcb(init)")); } /* find out which natural language we should speak */ cb.adf_slang = iiuglcd_langcode(); /* Always QUEL; for SQL explictly change this with 'IIAFdsDmlSet()'. */ cb.adf_qlang = DB_QUEL; /* lets get the multinational info */ /* ** Defaults for all of these are initially set statically, above. */ NMgtAt(ERx("II_DATE_FORMAT"), &cp); if ( cp != NULL && *cp != EOS ) { FEapply_date_format(&cb,cp); } NMgtAt(ERx("II_MONEY_FORMAT"), &cp); if ( cp != NULL && *cp != EOS ) { FEapply_money_format(&cb,cp); } NMgtAt(ERx("II_MONEY_PREC"), &cp); if ( cp != NULL && *cp != EOS ) { FEapply_money_prec(&cb,cp); } NMgtAt(ERx("II_DECIMAL"), &cp); if ( cp != NULL && *cp != EOS ) { FEapply_decimal(&cb,cp); } NMgtAt(ERx("II_UNICODE_SUBS"), &cp); if ( !cp ) { cb.adf_unisub_status = AD_UNISUB_OFF; } else { /* As with SET unicode_substitution take the first char */ cb.adf_unisub_status = AD_UNISUB_ON; *cb.adf_unisub_char = cp[0]; } NMgtAt(ERx("II_NULL_STRING"), &cp); if ( cp != NULL && *cp != EOS ) { FEapply_null(&cb,cp); } /* If there was a Timezone error other than the expected ingbuild ** error TM_PMFILE_OPNERR, then lets report it now. */ if (tmtz_status && (tmtz_status != TM_PMFILE_OPNERR)) { LOCATION loc_root; STATUS status = OK; status = NMloc(FILES, PATH, ERx("zoneinfo"), &loc_root); #if defined(conf_BUILD_ARCH_32_64) && defined(BUILD_ARCH64) status = LOfaddpath(&loc_root, ERx("lp64"), &loc_root); #endif #if defined(conf_BUILD_ARCH_64_32) && defined(BUILD_ARCH32) { /* ** Reverse hybrid support must be available in ALL ** 32bit binaries */ char *rhbsup; NMgtAt("II_LP32_ENABLED", &rhbsup); if ( (rhbsup && *rhbsup) && ( !(STbcompare(rhbsup, 0, "ON", 0, TRUE)) || !(STbcompare(rhbsup, 0, "TRUE", 0, TRUE)))) status = LOfaddpath(&loc_root, "lp32", &loc_root); } #endif /* ! LP64 */ if ( status == OK ) { status = LOexist(&loc_root); } if ( status == OK ) { IIUGerr(tmtz_status, 0, 0); /* As this may disappear from the screen quickly, ** let's pause to allow the users to reflect on the ** fact that the timezone info is (probably) wrong. */ if (!IIugefa_err_function) PCsleep(5000); } } /* Initialize unicode collation for UTF8 installations */ if (CMischarset_utf8()) { if (status = aduucolinit(col, MEreqmem, &ucode_ctbl, &ucode_cvtbl, &sys_err)) { EXsignal(EXFEBUG, 1, ERx("FEadfcb(alloc)")); } cb.adf_ucollation = (PTR) ucode_ctbl; /* Set unicode normalization to NFC just in case ** Front end will need it. */ if ((status = adg_setnfc(&cb)) != E_DB_OK) EXsignal(EXFEBUG, 1, ERx("FEadfcb(alloc)")); } /* lets not do this again! */ } return &cb; }
DB_STATUS qee_d8_undefrpt( QEF_RCB *qef_rcb, PTR i_qso_handle) { DB_STATUS status = E_DB_OK, sav_status = E_DB_OK; DB_ERROR sav_err; i4 err; GLOBALREF QEF_S_CB *Qef_s_cb; QEE_DSH *dsh_p = (QEE_DSH *)NULL; QEF_QP_CB *qp; bool have_qp = FALSE; QSF_RCB qsf_rcb; ULM_RCB ulm; ULH_RCB ulh_rcb; struct { PTR qso_handle; CS_SID session_id; } ulh_name; /* 1. acquire the QP using the handle */ qsf_rcb.qsf_next = (QSF_RCB *)NULL; qsf_rcb.qsf_prev = (QSF_RCB *)NULL; qsf_rcb.qsf_length = sizeof(QSF_RCB); qsf_rcb.qsf_type = QSFRB_CB; qsf_rcb.qsf_owner = (PTR)DB_QEF_ID; qsf_rcb.qsf_ascii_id = QSFRB_ASCII_ID; qsf_rcb.qsf_sid = qef_rcb->qef_cb->qef_ses_id; ulh_rcb.ulh_hashid = Qef_s_cb->qef_ulhid; qsf_rcb.qsf_obj_id.qso_handle = i_qso_handle; qsf_rcb.qsf_lk_state = QSO_SHLOCK; status = qsf_call(QSO_LOCK, &qsf_rcb); if (status) { qef_rcb->error.err_code = E_QE0019_NON_INTERNAL_FAILURE; return(status); } have_qp = TRUE; qp = (QEF_QP_CB *) qsf_rcb.qsf_root; /* For sharable QP, sometimes the QP may have been destroyed by ** another session. So check for a NULL root. If so, release the ** lock and return. */ if (qp == (QEF_QP_CB *) NULL) { qsf_call(QSO_UNLOCK, &qsf_rcb); return(E_DB_OK); } /* 2. gotten the QP, acquire the DSH in ULH's cache */ STRUCT_ASSIGN_MACRO(Qef_s_cb->qef_d_ulmcb, ulm); ulh_name.qso_handle = qsf_rcb.qsf_obj_id.qso_handle; ulh_name.session_id = qef_rcb->qef_cb->qef_ses_id; status = ulh_getmember(&ulh_rcb, (unsigned char *) &qp->qp_id, (i4)sizeof(DB_CURSOR_ID), (unsigned char *) & ulh_name, (i4)sizeof(ulh_name), ULH_DESTROYABLE, 0); if (DB_FAILURE_MACRO(status)) { qef_error(ulh_rcb.ulh_error.err_code, 0L, status, & err, &qef_rcb->error, 0); qef_rcb->error.err_code = E_QE0102_CATALOG_DSH_OBJECT; } else { dsh_p = (QEE_DSH *)(ulh_rcb.ulh_object->ulh_uptr); ulm.ulm_streamid_p = &ulh_rcb.ulh_object->ulh_streamid; if (dsh_p != (QEE_DSH *)NULL) { /* 3. reset the defined state of the REPEAT QUERY in the DSH */ dsh_p->dsh_ddb_cb->qee_d3_status &= ~QEE_03Q_DEF; /* 4. must release the acquired DSH */ ulh_rcb.ulh_object = (ULH_OBJECT *)dsh_p->dsh_handle; } status = ulh_release(& ulh_rcb); if (status != E_DB_OK) { qef_error(ulh_rcb.ulh_error.err_code, 0L, status, & err, &qef_rcb->error, 0); qef_rcb->error.err_code = E_QE0104_RELEASE_DSH_OBJECT; } /* must fall thru to release the QP */ } /* 5. release the QP */ if (have_qp) { qsf_call(QSO_UNLOCK, & qsf_rcb); } if (status == E_DB_OK && sav_status) { status = sav_status; STRUCT_ASSIGN_MACRO(sav_err, qef_rcb->error); } return(status); }
/*{ ** Name: psy_dbpriv - GRANT/REVOKE database privileges ** ** INTERNAL PSF call format: status = psy_dbpriv(&psy_cb, sess_cb); ** EXTERNAL call format: status = psy_call (PSY_DBPRIV,&psy_cb, sess_cb); ** ** Description: ** This procedure is sued to grant and revoke database privileges. ** A template iidbpriv tuple is built and shipped to RDF. ** Eventually, the tuple is added/merged into iidbpriv. ** This procedure is called for SQL language only. ** ** Inputs: ** psy_cb ** .psy_usrq list of grantees ** .psy_tblq list of objects ** .psy_gtype grantee type ** .psy_grant statement type (GRANT or REVOKE) ** .psy_fl_dbpriv control flags ** .psy_qdio_limit query I/O limit ** .psy_qrow_limit query row limit ** .psy_qcpu_limit query CPU limit ** .psy_qpage_limit query page limit ** .psy_qcost_limit query cost limit ** .psy_idle_limit idle time limit ** .psy_connect_limit connect time limit ** .psy_priority_limit priority limit ** sess_cb Pointer to session control block ** (Can be NULL) ** ** Outputs: ** psy_cb ** .psy_error Filled in if error happens ** Returns: ** E_DB_OK Function completed normally. ** E_DB_WARN Function completed with warning(s); ** E_DB_ERROR Function failed; non-catastrophic error ** E_DB_SEVERE Function failed; catastrophic error ** Exceptions: ** none ** ** Side Effects: ** None. ** ** History: ** 10-apr-89 (ralph) ** written ** 06-jun-89 (ralph) ** corrected unix portability problems ** 24-jun-89 (ralph) ** corrected internal error message ** 06-sep-89 (ralph) ** added support for GRANT/REVOKE ON ALL DATABASES ** removed DBGR_DEFAULT ** 12-mar-90 (andre) ** set rdr_2types_mask to 0. ** 22-may-90 (teg) ** init rdr_instr to RDF_NO_INSTR ** 19-jul-92 (andre) ** changes were made to enable a user to specify privileges to PUBLIC ** along with one or more user authorization identifiers ** 10-aug-93 (andre) ** fixed cause of a compiler warning */ DB_STATUS psy_dbpriv( PSY_CB *psy_cb, PSS_SESBLK *sess_cb) { DB_STATUS status = E_DB_OK; DB_STATUS stat; i4 err_code; RDF_CB rdf_cb; register RDR_RB *rdf_rb = &rdf_cb.rdf_rb; DB_PRIVILEGES dbprtuple; register DB_PRIVILEGES *dbprtup = &dbprtuple; PSY_USR *psy_usr; PSY_TBL *psy_tbl; bool grant_to_public; bool leave_loop = TRUE; /* This code is called for SQL only */ do { /* Fill in the RDF request block */ pst_rdfcb_init(&rdf_cb, sess_cb); if (psy_cb->psy_grant == PSY_DGRANT) rdf_rb->rdr_update_op = RDR_APPEND; else if (psy_cb->psy_grant == PSY_DREVOKE) rdf_rb->rdr_update_op = RDR_DELETE; else { /* Invalid operation specified in psy_grant */ err_code = E_PS0D45_INVALID_GRANT_OP; (VOID) psf_error(E_PS0D45_INVALID_GRANT_OP, 0L, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); status = E_DB_ERROR; break; } rdf_rb->rdr_status = DB_SQL; rdf_rb->rdr_types_mask = RDR_DBPRIV; rdf_rb->rdr_qrytuple = (PTR) dbprtup; rdf_rb->rdr_qtuple_count = 1; /* Initialize the template iidbpriv tuple */ dbprtup->dbpr_gtype = psy_cb->psy_gtype; dbprtup->dbpr_control = psy_cb->psy_ctl_dbpriv; dbprtup->dbpr_dbflags = 0; dbprtup->dbpr_flags = psy_cb->psy_fl_dbpriv; dbprtup->dbpr_qrow_limit = psy_cb->psy_qrow_limit; dbprtup->dbpr_qdio_limit = psy_cb->psy_qdio_limit; dbprtup->dbpr_qcpu_limit = psy_cb->psy_qcpu_limit; dbprtup->dbpr_qpage_limit = psy_cb->psy_qpage_limit; dbprtup->dbpr_qcost_limit = psy_cb->psy_qcost_limit; dbprtup->dbpr_connect_time_limit = psy_cb->psy_connect_limit; dbprtup->dbpr_idle_time_limit = psy_cb->psy_idle_limit; dbprtup->dbpr_priority_limit = psy_cb->psy_priority_limit; MEfill(sizeof(dbprtup->dbpr_database), (u_char) ' ', (PTR) &dbprtup->dbpr_database); MEfill(sizeof(dbprtup->dbpr_reserve), (u_char) ' ', (PTR) dbprtup->dbpr_reserve); /* Point to first database object, if any */ psy_tbl = (PSY_TBL *)psy_cb->psy_tblq.q_next; /* No database objects means GRANT/REVOKE ON ALL DATABASES */ if (psy_tbl == (PSY_TBL *) &psy_cb->psy_tblq) dbprtup->dbpr_dbflags |= DBPR_ALLDBS; /* ** Pass the template tuples to RDF for each database/grantee. ** RDF/QEF will return E_DB_INFO if a database/grantee is rejected, ** or E_DB_WARN is a database is rejected. Anything worse than ** E_DB_WARN is a fatal error. */ /* Process each database/grantee */ for (;;) { /* ** Copy in database name if one was provided. ** None will be provided if ON ALL DATABASES was specified. */ if (psy_tbl != (PSY_TBL *) &psy_cb->psy_tblq) MEcopy((PTR)&psy_tbl->psy_tabnm, sizeof(dbprtup->dbpr_database), (PTR)&dbprtup->dbpr_database); grant_to_public = (psy_cb->psy_flags & PSY_PUBLIC) != 0; psy_usr = (PSY_USR *) psy_cb->psy_usrq.q_next; do { if (grant_to_public) { /* ** user specified TO/FROM PUBLIC, possibly along with other ** user auth ids; we will process TO/FROM PUBLIC first */ dbprtup->dbpr_gtype = DBGR_PUBLIC; MEmove(sizeof("public") - 1, (PTR) "public", ' ', sizeof(dbprtup->dbpr_gname), (PTR) &dbprtup->dbpr_gname); } else { STRUCT_ASSIGN_MACRO(psy_usr->psy_usrnm, dbprtup->dbpr_gname); } stat = rdf_call(RDF_UPDATE, (PTR) &rdf_cb); status = (stat > status) ? stat : status; if (stat > E_DB_INFO) break; if (grant_to_public) { dbprtup->dbpr_gtype = psy_cb->psy_gtype; /* ** remember that we have processed GRANT TO PUBLIC on this ** database or on the installation */ grant_to_public = FALSE; } else { psy_usr = (PSY_USR *) psy_usr->queue.q_next; } } while (psy_usr != (PSY_USR *) &psy_cb->psy_usrq); /* Break out if failure */ if (DB_FAILURE_MACRO(status)) break; /* Point to next database object */ psy_tbl = (PSY_TBL *) psy_tbl->queue.q_next; /* Break out if no more objects */ if (psy_tbl == (PSY_TBL *) &psy_cb->psy_tblq) break; } /* We are done */ /* leave_loop has already been set to TRUE */ } while (!leave_loop); if (DB_FAILURE_MACRO(status)) (VOID) psf_rdf_error(RDF_UPDATE, &rdf_cb.rdf_error, &psy_cb->psy_error); return (status); }
/*{ ** Name: psy_dpermit - Define a permit. ** ** INTERNAL PSF call format: status = psy_dpermit(&psy_cb, sess_cb); ** ** EXTERNAL call format: status = psy_call(PSY_DPERMIT, &psy_cb, sess_cb); ** ** Description: ** Given all of the parameters necessary to CREATE/DEFINE a permit on a ** table or view, this function will store the permission in the system ** catalogs. This will include storing the query tree in the tree table, ** storing the text of the query in the iiqrytext table (really done by ** QEF), storing a row in the protect table, and issuing an "alter table" ** operation to DMF to indicate that there are permissions on the given ** table. ** ** Inputs: ** psy_cb ** .psy_qrytext Id of query text as stored in QSF. ** .psy_cols[] Array of columns on which to grant ** permission ** .psy_numcols Number of columns listed above; 0 means ** give permission on all columns ** .psy_intree QSF id of query tree representing the ** where clause in the permit ** .psy_opctl Bit map of defined operations ** .psy_opmap Bit map of permitted operations ** .psy_user Name of user who will get permission ** .psy_terminal Terminal at which permission is given ** (blank if none specified) ** .psy_timbgn Time of day at which the permission ** begins (minutes since 00:00) ** .psy_timend Time of day at which the permission ends ** (minutes since 00:00) ** .psy_daybgn Day of week at which the permission ** begins (0 = Sunday) ** .psy_dayend Day of week at which the permission ends ** (0 = Sunday) ** .psy_grant ** PSY_CPERM CREATE/DEFINE PERMIT ** .psy_tblq head of table queue ** .psy_colq head of column queue ** .psy_usrq head of user queue ** .psy_qlen length of first iiqrytext ** .psy_flags useful info ** PSY_EXCLUDE_COLUMNS user specified a list of columns to ** which privilege should not apply ** sess_cb Pointer to session control block ** (Can be NULL) ** ** Outputs: ** psy_cb ** .psy_txtid Id of query text as stored in the ** iiqrytext system relation. ** .psy_error Filled in if error happens ** Returns: ** E_DB_OK Function completed normally. ** E_DB_WARN Function completed with warning(s); ** E_DB_ERROR Function failed; non-catastrophic error ** E_DB_FATAL Function failed; catastrophic error ** Exceptions: ** none ** ** Side Effects: ** Stores text of query in iiqrytext relation, query tree in tree ** relation, row in protect relation identifying the permit. Does ** an alter table DMF operation to indicate that there are permissions ** on the table. ** ** History: ** 02-oct-85 (jeff) ** written ** 03-sep-86 (seputis) ** changed some psy_cb. to psy_cb-> ** added .db_att_id reference ** changed rdr_cb. rdr_cb-> ** 02-dec-86 (daved) ** bug fixing. check for permit on tables owned by user and not ** view. ** 29-apr-87 (stec) ** Implemented changes for GRANT statement. ** 10-may-88 (stec) ** Make changes for db procs. ** 03-oct-88 (andre) ** Modified call to pst_rgent to pass 0 as a query mode since it is ** clearly not PSQ_DESTROY ** 06-feb-89 (ralph) ** Added support for 300 attributes: ** Use DB_COL_BITS in place of DB_MAX_COLS ** Loop over domset array using DB_COL_WORDS ** 06-mar-89 (ralph) ** GRANT Enhancements, Phase 1: ** Initialize new DB_PROTECTION fields, dbp_seq and dbp_gtype ** 03-apr-89 (ralph) ** GRANT Enhancements, Phase 2: ** Use DBGR_USER when initializing dbp_gtype ** 08-may-89 (ralph) ** Initialize reserved field to blanks (was \0) ** 04-jun-89 (ralph) ** Initialize dbp_fill1 to zero ** Fix unix portability problems ** 02-nov-89 (neil) ** Alerters: Allowed privileges for events. ** 1-mar-90 (andre) ** If processing a GRANT on tables, check if ** ALL-TO-ALL or RETRIEVE-TO-ALL has already been granted, and if so, ** mark psy_mask appropriately. ** If user tried to CREATE ALL/RETRIEVE-TO-ALL, and one already exists, ** skip to the exit. ** 12-mar-90 (andre) ** set rdr_2types_mask to 0. ** 22-may-90 (teg) ** init rdr_instr to RDF_NO_INSTR ** 08-aug-90 (ralph) ** Initialize new fields in iiprotect tuple ** 14-dec-90 (ralph) ** Disallow use of GRANT by non-DBA if xORANGE ** 11-jan-90 (ralph) ** Allow user "$ingres" to use GRANT if xORANGE. ** This was done for CREATEDB (UPGRADEFE). ** 20-feb-91 (andre) ** For CREATE/DEFINE PERMIT, grantee type was stored in ** psy_cb->psy_gtype. ** 24-jun-91 (andre) ** IIPROTECT tuples for table permits will contain exactly one ** privilege. IIQRYTEXT template built for table-wide privileges ** contains a placeholder for a privilege name which will be filled in ** with each of the table-wide privileges being granted, one at a time. ** PSY_CB.psy_opmap will be set to correspond with privilege name ** stored in the IIQRYTEXT permit. ** 16-jul-91 (andre) ** responsibility for splitting permit tuples will passed on to ** qeu_cprot(). If a permit specified only one privilege, we will ** substitute the appropriate privilege name here and will not ask ** qeu_cprot() to split tuples. ** 06-aug-91 (andre) ** before proceeding to CREATE a permit on a view owned by the current ** user, we will call psy_tbl_grant_check() to ensure that this user ** may create a permit on his view. If the object is not owned by the ** current user, we will not try to verify that the user may ** CREATE/DEFINE a permit since (until the relevant FE changes are ** made) we intend to continue allowing any user with CATUPD to ** CREATE/DEFINE permits on catalogs and the dba will be allowed to ** CREATE/DEFINE permits on extended catalogs ** 11-nov-91 (rblumer) ** merged from 6.4: 26-feb-91 (andre) ** PST_QTREE was changed to store the range table as an array of ** pointers to PST_RNGENTRY structure. ** 14-feb-92 (andre) ** we will no longer have to fill in privilege name for permits ** specifying one privilege - it will be handled in respective ** grammars. ** 15-jun-92 (barbara) ** For Sybil, change interface to pst_rgent(), Star returns from ** psy_dpermit before permits get stored. ** 07-jul-92 (andre) ** DB_PROTECTION tuple will contain an indicator of how the permit was ** created, i.e. whether it was created using SQL or QUEL and if the ** former, then whether it was created using GRANT statement. Having ** this information will facilitate merging similar and identical ** permit tuples. ** 14-jul-92 (andre) ** semantics of GRANT ALL [PRIVILEGES] is different from that of ** CREATE PERMIT ALL in that the former (as dictated by SQL92) means ** "grant all privileges which the current auth id posesses WGO" ** whereas the latter (as is presently interpreted) means "grant all ** privileges that can be defined on the object" which in case of ** tables and views means SELECT, INSERT, DELETE, UPDATE. ** psy_tbl_grant_check() (function responsible for determining whether ** a user may grant specified privilege on a specified table or view) ** will have to be notified whether we are processing GRANT ALL. Its ** behaviour will change as follows: ** - if processing GRANT ALL and psy_tbl_grant_check() determines ** that the user does not possess some (but not all) of the ** privileges passed to it by the caller it will not treat it as an ** error, but will instead inform the caller of privileges that the ** user does not posess, ** - if processing GRANT ALL and psy_tbl_grant_check() determines ** that the user does not possess any of the privileges passed to ** it by the caller it will treat it as an error ** - if processing a statement other than GRANT ALL and ** psy_tbl_grant_check() determines that the user does not possess ** some of the privileges passed to it by the caller it will treat ** it as an error ** 16-jul-92 (andre) ** if a permit being created depends on some privileges, build a ** structure describing these privileges and store its address in ** rdf_cb->rdr_indep. ** 18-jul-92 (andre) ** we will no longer be telling QEF to turn off DMT_ALL_PROT or ** DMT_RETRIEVE_PRO when a user creates ALL/RETRIEVE TO ALL permit. ** QEF will figure out on its own whether PUBLIC now has RETRIEVE or ** ALL on a table/view ** 20-jul-92 (andre) ** if user specified a list of columns to which privilege(s) should ** not apply, set dbp_domset correctly ** 03-aug-92 (barbara) ** Invalidate base table infoblk from RDF cache for CREATE PERMIT ** and CREATE SEC_ALARM. ** 16-sep-92 (andre) ** privilege maps are build using bitwise ops, so care should be ** exercised when accessing it using BT*() functions ** 17-jun-93 (andre) ** changed interface of psy_secaudit() to accept PSS_SESBLK ** 5-jul-93 (robf) ** changed interface of psy_secaudit() to accept security label ** 7-jan-94 (swm) ** Bug #58635 ** Added PTR cast for qsf_owner which has changed type to PTR. ** 06-mar-96 (nanpr01) ** Move the QSF request block initialization up. because if ** pst_rgnent returns a failure status code, subsequent QSF ** calls get bad control block error. */ DB_STATUS psy_dpermit( PSY_CB *psy_cb, PSS_SESBLK *sess_cb) { RDF_CB rdf_cb; register RDR_RB *rdf_rb = &rdf_cb.rdf_rb; QSF_RCB qsf_rb; DB_STATUS status; DB_STATUS stat; DB_PROTECTION ptuple; register DB_PROTECTION *protup = &ptuple; i4 *domset = ptuple.dbp_domset; register i4 i, j; i4 err_code; PSS_RNGTAB *rngvar; PSS_USRRANGE *rngtab; PST_PROCEDURE *pnode; PST_QTREE *qtree; DB_ERROR *err_blk = &psy_cb->psy_error; i4 textlen; i4 tree_lock = 0; i4 text_lock = 0; DB_TAB_ID tabids[PST_NUMVARS]; PSQ_INDEP_OBJECTS indep_objs; PSQ_OBJPRIV obj_priv; /* space for independent DELETE */ PSQ_COLPRIV col_privs[2]; /* ** space for independent INSERT and ** UPDATE */ PST_VRMAP varmap; PSY_TBL *psy_tbl; DB_TIME_ID timeid; DB_NAME *objname; /* ** For CREATE/DEFINE PERMIT execute code below. */ /* initialize the QSF control block */ qsf_rb.qsf_type = QSFRB_CB; qsf_rb.qsf_ascii_id = QSFRB_ASCII_ID; qsf_rb.qsf_length = sizeof(qsf_rb); qsf_rb.qsf_owner = (PTR)DB_PSF_ID; qsf_rb.qsf_sid = sess_cb->pss_sessid; rngtab = &sess_cb->pss_auxrng; /* table info is stored in the only entry in the table queue */ psy_tbl = (PSY_TBL *) psy_cb->psy_tblq.q_next; status = pst_rgent(sess_cb, rngtab, -1, "", PST_SHWID, (DB_TAB_NAME *) NULL, (DB_TAB_OWN *) NULL, &psy_tbl->psy_tabid, TRUE, &rngvar, (i4) 0, err_blk); if (DB_FAILURE_MACRO(status)) goto exit; /* In STAR, we do not actually store permits */ if (sess_cb->pss_distrib & DB_3_DDB_SESS) { qsf_rb.qsf_lk_state = QSO_EXLOCK; goto exit; } /* Fill in the RDF request block */ pst_rdfcb_init(&rdf_cb, sess_cb); /* The table which is receiving the permit */ STRUCT_ASSIGN_MACRO(psy_tbl->psy_tabid, rdf_rb->rdr_tabid); /* Tell RDF we're doing a permit definition */ rdf_rb->rdr_update_op = RDR_APPEND; rdf_rb->rdr_types_mask = RDR_PROTECT; rdf_rb->rdr_qrytuple = (PTR) protup; /* initialize independent object structure */ indep_objs.psq_objs = (PSQ_OBJ *) NULL; indep_objs.psq_objprivs = (PSQ_OBJPRIV *) NULL; indep_objs.psq_colprivs = (PSQ_COLPRIV *) NULL; indep_objs.psq_grantee = &sess_cb->pss_user; rdf_rb->rdr_indep = (PTR) &indep_objs; /* ** populate the IIPROTECT tuple */ /* Zero out the template */ (VOID)MEfill(sizeof(ptuple), (u_char) 0, (PTR) protup); /* store grantee type */ protup->dbp_gtype = psy_cb->psy_gtype; /* Init reserved block */ (VOID)MEfill(sizeof(protup->dbp_reserve), (u_char) ' ', (PTR) protup->dbp_reserve); /* Init obj name */ STRUCT_ASSIGN_MACRO(psy_tbl->psy_tabnm, protup->dbp_obname); /*@FIX_ME@ Where does this come from? */ protup->dbp_obstat = ' '; /* store the object type indicator */ if (psy_tbl->psy_mask & PSY_OBJ_IS_TABLE) { protup->dbp_obtype = DBOB_TABLE; } else if (psy_tbl->psy_mask & PSY_OBJ_IS_VIEW) { protup->dbp_obtype = DBOB_VIEW; } else { protup->dbp_obtype = DBOB_INDEX; } STRUCT_ASSIGN_MACRO(psy_tbl->psy_owner, protup->dbp_obown); STRUCT_ASSIGN_MACRO(sess_cb->pss_user, protup->dbp_grantor); TMnow((SYSTIME *)&timeid); protup->dbp_timestamp.db_tim_high_time = timeid.db_tim_high_time; protup->dbp_timestamp.db_tim_low_time = timeid.db_tim_low_time; /* The table on which we're giving permission */ STRUCT_ASSIGN_MACRO(psy_tbl->psy_tabid, protup->dbp_tabid); /* Beginning and ending times of day */ protup->dbp_pdbgn = psy_cb->psy_timbgn; protup->dbp_pdend = psy_cb->psy_timend; /* Beginning and ending days of week */ protup->dbp_pwbgn = psy_cb->psy_daybgn; protup->dbp_pwend = psy_cb->psy_dayend; if (psy_cb->psy_numcols != 0 && ~psy_cb->psy_flags & PSY_EXCLUDE_COLUMNS) { /* user specified a list of columns to which privilege(s) will apply */ /* Bit map of permitted columns */ psy_fill_attmap(domset, ((i4) 0)); for (i = 0; i < psy_cb->psy_numcols; i++) { BTset((i4)psy_cb->psy_cols[i].db_att_id, (char *) domset); } } else { /* ** user specified table-wide privilege(s) or a list of columns L s.t. ** privilege(s) will apply to the entire table except for columns in L */ psy_fill_attmap(domset, ~((i4) 0)); if (psy_cb->psy_flags & PSY_EXCLUDE_COLUMNS) { /* ** exclude specified columns from the list of columns to which ** privilege(s) will apply */ for (i = 0; i < psy_cb->psy_numcols; i++) { BTclear((i4) psy_cb->psy_cols[i].db_att_id, (char *) domset); } } } if (rngvar->pss_tabdesc->tbl_status_mask & DMT_VIEW) { /* ** if view is owned by the current user, psy_tbl_grant_check() will ** determine if the permit can, indeed, be created; as long as we are ** preserving the kludge that allows users with CATUPD create permits on ** catalogs and DBAs to create permits on extended catalogs, we shall ** not call psy_tbl_grant_check() on view not owned by the current user, ** since it is likely to result in psy_tbl_grant_check() complaining ** about inadequate permissions */ if (!MEcmp((PTR) &rngvar->pss_ownname, (PTR) &sess_cb->pss_user, sizeof(sess_cb->pss_user))) { i4 tbl_wide_privs; PSY_COL_PRIVS col_specific_privs, *csp, indep_col_specific_privs; DB_TAB_ID indep_id; i4 indep_tbl_wide_privs; bool insuf_privs, quel_view; i4 val1, val2; /* ** build maps of table-wide and column-specific privileges for ** psy_tbl_grant_check() ** if a column list was specified with CREATE PERMIT and ** privileges specified in the statement include a set of ** privileges S s.t. for all P in S, P can only be specified as ** table-wide with GRANT statement (currently this includes ** SELECT, INSERT, DELETE), we will make ** psy_tbl_grant_check() think that privileges in S are ** table-wide. ** This will work correctly since if the view was defined over ** some objects owned by other user(s), for every P in S we ** would need table-wide privilege WGO on the underlying object. ** ** For the purposes of providing more descriptive output for ** trace point ps131, if column-list was specified, we will pass ** the map of attributes even if column-specific UPDATE was not ** specified */ if (psy_cb->psy_numcols != 0 && (psy_cb->psy_opmap & DB_REPLACE || ult_check_macro(&sess_cb->pss_trace, 3, &val1, &val2) ) ) { i4 *ip; csp = &col_specific_privs; /* ** column-specific UPDATE privilege will not be translated into ** a table-wide privilege since GRANT allows for specification ** of column-specific UPDATE privilege */ csp->psy_col_privs = psy_cb->psy_opmap & DB_REPLACE; tbl_wide_privs = psy_cb->psy_opmap & ~DB_REPLACE; /* ** if creating a permit on a set of columns and UPDATE is not ** one of the privileges named in the statement, store the ** attribute map in the first element of the attribute map list */ ip = (csp->psy_col_privs) ? csp->psy_attmap[PSY_UPDATE_ATTRMAP].map : csp->psy_attmap->map; /* copy the attribute map */ for (i = 0; i < DB_COL_WORDS; i++, ip++) { *ip = domset[i]; } } else { tbl_wide_privs = psy_cb->psy_opmap; csp = (PSY_COL_PRIVS *) NULL; } status = psy_tbl_grant_check(sess_cb, (i4) PSQ_PROT, &rngvar->pss_tabid, &tbl_wide_privs, csp, &indep_id, &indep_tbl_wide_privs, &indep_col_specific_privs, psy_cb->psy_flags, &insuf_privs, &quel_view, &psy_cb->psy_error); if (DB_FAILURE_MACRO(status)) { goto exit; } if (insuf_privs) { /* must audit failure to create a permit */ if ( Psf_srvblk->psf_capabilities & PSF_C_C2SECURE ) { DB_ERROR e_error; /* Must audit CREATE PERMIT failure. */ status = psy_secaudit(FALSE, sess_cb, (char *)&rngvar->pss_tabdesc->tbl_name, &rngvar->pss_tabdesc->tbl_owner, sizeof(DB_TAB_NAME), SXF_E_TABLE, I_SX2016_PROT_TAB_CREATE, SXF_A_FAIL | SXF_A_CREATE, &e_error); status = (status > E_DB_ERROR) ? status : E_DB_ERROR; } goto exit; } else if (quel_view) { goto exit; } /* ** If user is trying to grant one or more of ** INSERT/DELETE/UPDATE on his/her view whose underlying table ** or view is owned by another user, psy_tbl_grant_check() will ** return id of the underlying object along with map of ** privileges. We will convert maps of independent privileges ** into elements of independent privilege list and pass them ** along to QEF */ if ( indep_id.db_tab_base != (i4) 0 && ( indep_id.db_tab_base != rngvar->pss_tabid.db_tab_base || indep_id.db_tab_index != rngvar->pss_tabid.db_tab_index ) ) { if (indep_tbl_wide_privs & DB_DELETE) { /* ** the only expected independent table-wide privilege ** is DELETE */ obj_priv.psq_next = (PSQ_OBJPRIV *) NULL; obj_priv.psq_objtype = PSQ_OBJTYPE_IS_TABLE; obj_priv.psq_privmap = (i4) DB_DELETE; obj_priv.psq_objid.db_tab_base = indep_id.db_tab_base; obj_priv.psq_objid.db_tab_index = indep_id.db_tab_index; indep_objs.psq_objprivs = &obj_priv; } if (indep_col_specific_privs.psy_col_privs) { i4 i, j; PSQ_COLPRIV *csp; i4 *att_map, *p; i4 priv_map = 0; /* ** privilege map is built using bitwise operators, but ** here using BTnext() makes code much more palatable, ** so convert a privilege map */ if (indep_col_specific_privs.psy_col_privs & DB_APPEND) BTset(DB_APPP, (char *) &priv_map); if (indep_col_specific_privs.psy_col_privs & DB_REPLACE) BTset(DB_REPP, (char *) &priv_map); for (i = -1, csp = col_privs; (i = BTnext(i, (char *) &priv_map, BITS_IN(priv_map))) != -1; csp++ ) { csp->psq_next = indep_objs.psq_colprivs; indep_objs.psq_colprivs = csp; csp->psq_objtype = PSQ_OBJTYPE_IS_TABLE; csp->psq_tabid.db_tab_base = indep_id.db_tab_base; csp->psq_tabid.db_tab_index = indep_id.db_tab_index; switch (i) { case DB_APPP: /* INSERT privilege */ { csp->psq_privmap = (i4) DB_APPEND; att_map = indep_col_specific_privs. psy_attmap[PSY_INSERT_ATTRMAP].map; break; } case DB_REPP: { csp->psq_privmap = (i4) DB_REPLACE; att_map = indep_col_specific_privs. psy_attmap[PSY_UPDATE_ATTRMAP].map; break; } } for (p = csp->psq_attrmap, j = 0; j < DB_COL_WORDS; j++) { *p++ = *att_map++; } } } } } else { /* ** either this is a catalog and the user has CATUPD or ** this is an extended catalog and the user is the DBA; ** since we may be allowing a user to create a permit by ** circumventing the permit system, we only need to ascertain that ** this is an SQL view */ i4 issql = 0; status = psy_sqlview(rngvar, sess_cb, err_blk, &issql); if (status) { goto exit; } if (!issql) { /* can only have permits on SQL views */ psf_error(3598L, 0L, PSF_USERERR, &err_code, err_blk, 1, psf_trmwhite(sizeof(rngvar->pss_tabname), (char *) &rngvar->pss_tabname), &rngvar->pss_tabname); status = E_DB_ERROR; goto exit; } } } /* Name of user getting permission */ STRUCT_ASSIGN_MACRO(psy_cb->psy_user, protup->dbp_owner); /* Terminal at which permission given */ STRUCT_ASSIGN_MACRO(psy_cb->psy_terminal, protup->dbp_term); /* Give RDF pointer to query tree, if any */ if (!psy_cb->psy_istree) { rdf_rb->rdr_qry_root_node = (PTR) NULL; } else { PST_VRMAP varset; i4 j; STRUCT_ASSIGN_MACRO(psy_cb->psy_intree, qsf_rb.qsf_obj_id); qsf_rb.qsf_lk_state = QSO_EXLOCK; status = qsf_call(QSO_LOCK, &qsf_rb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_error(E_PS0D19_QSF_INFO, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, err_blk, 0); goto exit; } tree_lock = qsf_rb.qsf_lk_id; pnode = (PST_PROCEDURE *) qsf_rb.qsf_root; qtree = (PST_QTREE *) pnode->pst_stmts->pst_specific.pst_tree; rdf_rb->rdr_qry_root_node = (PTR) pnode; /* check for no views in the qualification. */ (VOID)psy_varset(qtree->pst_qtree, &varset); j = BTnext(-1, (char *) &varset, BITS_IN(varset)); for ( ; j >= 0; j = BTnext(j, (char *) &varset, BITS_IN(varset))) { status = pst_rgent(sess_cb, rngtab, -1, "", PST_SHWID, (DB_TAB_NAME *) NULL, (DB_TAB_OWN *) NULL, &qtree->pst_rangetab[j]->pst_rngvar, TRUE, &rngvar, (i4) 0, err_blk); if (status) goto exit; if (rngvar->pss_tabdesc->tbl_status_mask & DMT_VIEW) { psf_error(3597L, 0L, PSF_USERERR, &err_code, err_blk, 1, psf_trmwhite(sizeof(rngvar->pss_tabname), (char *) &rngvar->pss_tabname), &rngvar->pss_tabname); status = E_DB_ERROR; goto exit; } } } /* Give RDF a pointer to the query text to be stored in iiqrytext */ STRUCT_ASSIGN_MACRO(psy_cb->psy_qrytext, qsf_rb.qsf_obj_id); qsf_rb.qsf_lk_state = QSO_EXLOCK; status = qsf_call(QSO_LOCK, &qsf_rb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_error(E_PS0D19_QSF_INFO, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, err_blk, 0); goto exit; } text_lock = qsf_rb.qsf_lk_id; MEcopy((char *) qsf_rb.qsf_root, sizeof(i4), (char *) &textlen); rdf_rb->rdr_l_querytext = textlen; rdf_rb->rdr_querytext = ((char *) qsf_rb.qsf_root) + sizeof(i4); rdf_rb->rdr_status = (sess_cb->pss_lang == DB_SQL) ? DB_SQL : 0; /* determine if the permit specifies exactly one privilege */ if (BTcount((char *) &psy_cb->psy_opmap, BITS_IN(psy_cb->psy_opmap)) > 1) { /* ** if permit specified more than one privilege, notify QEF that it will ** have to split the permit into multiple IIPROTECT tuples */ rdf_rb->rdr_instr |= RDF_SPLIT_PERM; } else if (psy_cb->psy_opmap & DB_RETRIEVE) { /* ** if qeu_cprot() will not be splitting the permit into multiple tuples ** and RETRIEVE is the privilege mentioned in it, set the two bits ** associated with DB_RETRIEVE */ psy_cb->psy_opmap |= DB_TEST | DB_AGGREGATE; psy_cb->psy_opctl |= DB_TEST | DB_AGGREGATE; } /* Null out the DMU control block pointer, just in case */ rdf_rb->rdr_dmu_cb = (PTR) NULL; /* produce list of dependent tables */ rdf_rb->rdr_cnt_base_id = 0; if (psy_cb->psy_istree && qtree->pst_qtree) { j = 0; (VOID)psy_varset(qtree->pst_qtree, &varmap); for (i = -1; (i = BTnext(i, (char*) &varmap, PST_NUMVARS)) > -1;) { /* if this is the table that is getting the permit, ignore */ if (qtree->pst_rangetab[i]->pst_rngvar.db_tab_base != psy_tbl->psy_tabid.db_tab_base || qtree->pst_rangetab[i]->pst_rngvar.db_tab_index != psy_tbl->psy_tabid.db_tab_index ) { rdf_rb->rdr_cnt_base_id++; STRUCT_ASSIGN_MACRO(qtree->pst_rangetab[i]->pst_rngvar, tabids[j++]); } } rdf_rb->rdr_base_id = tabids; } protup->dbp_popctl = psy_cb->psy_opctl; protup->dbp_popset = psy_cb->psy_opmap; /* ** store an indication of whether this permit is being created using SQL or ** QUEL */ protup->dbp_flags = (sess_cb->pss_lang == DB_SQL) ? DBP_SQL_PERM : (i2) 0; protup->dbp_flags |= DBP_65_PLUS_PERM; /* Now let RDF do all the work of the permit definition */ status = rdf_call(RDF_UPDATE, (PTR) &rdf_cb); if (DB_FAILURE_MACRO(status)) { if (rdf_cb.rdf_error.err_code == E_RD0002_UNKNOWN_TBL) { (VOID) psf_error(E_PS0903_TAB_NOTFOUND, 0L, PSF_USERERR, &err_code, err_blk, 1, psf_trmwhite(sizeof(psy_tbl->psy_tabnm), (char *) &psy_tbl->psy_tabnm), &psy_tbl->psy_tabnm); } else { (VOID) psf_rdf_error(RDF_UPDATE, &rdf_cb.rdf_error, &psy_cb->psy_error); } goto exit; } /* ** Invalidate base object's infoblk from RDF cache. */ pst_rdfcb_init(&rdf_cb, sess_cb); STRUCT_ASSIGN_MACRO(psy_cb->psy_tables[0], rdf_rb->rdr_tabid); status = rdf_call(RDF_INVALIDATE, (PTR) &rdf_cb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_rdf_error(RDF_INVALIDATE, &rdf_cb.rdf_error, &psy_cb->psy_error); } exit: qsf_rb.qsf_lk_state = QSO_EXLOCK; if (psy_cb->psy_istree) { /* Destroy query tree */ STRUCT_ASSIGN_MACRO(psy_cb->psy_intree, qsf_rb.qsf_obj_id); if ((qsf_rb.qsf_lk_id = tree_lock) == 0) { stat = qsf_call(QSO_LOCK, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_error(E_PS0D18_QSF_LOCK, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (!status || stat == E_DB_FATAL) status = stat; } tree_lock = qsf_rb.qsf_lk_id; } stat = qsf_call(QSO_DESTROY, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_error(E_PS0D1A_QSF_DESTROY, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (!status || stat == E_DB_FATAL) status = stat; } tree_lock = 0; } /* Destroy query text */ STRUCT_ASSIGN_MACRO(psy_cb->psy_qrytext, qsf_rb.qsf_obj_id); if ((qsf_rb.qsf_lk_id = text_lock) == 0) { stat = qsf_call(QSO_LOCK, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_error(E_PS0D18_QSF_LOCK, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (!status || stat == E_DB_FATAL) status = stat; } text_lock = qsf_rb.qsf_lk_id; } stat = qsf_call(QSO_DESTROY, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_error(E_PS0D1A_QSF_DESTROY, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (!status || stat == E_DB_FATAL) status = stat; } return (status); }
/*{ ** Name: psy_integ - Apply integrity constraints ** ** Description: ** This function applies integrity constraints. It gets the constraints ** from RDF and puts them in the query where appropriate. ** ** Inputs: ** mstream QSF memory stream to allocate from ** root Root of query tree to constrain ** rngtab Pointer to the range table ** resvar Pointer to the result range variable ** qmode Query mode of user's query ** sess_cb session control block ** result Place to put pointer to constrained tree ** err_blk Filled in if an error happens ** ** Outputs: ** root Integrity constraints may be appended ** result Filled in with pointer to constrained ** tree ** err_blk Filled in if an error happened ** Returns: ** E_DB_OK Success ** E_DB_ERROR Failure ** Exceptions: ** none ** ** Side Effects: ** Allocates memory ** ** History: ** 19-jun-86 (jeff) ** Adapted from integrity.c in 4.0. ** 02-sep-86 (seputis) ** changes for new RDF interface ** fixed bug for no integrity case ** 04-dec-86 (daved) ** process define and replace cursor. Use copy of saved qual for ** replace cursor. ** 03-dec-87 (stec) ** Change psy_apql interface. ** 11-may-88 (stec) ** Make changes for db procs. ** 06-feb-89 (ralph) ** Modified to use DB_COL_WORDS*2 as extent of dset array ** 23-Feb-89 (andre) ** Changed the way the tree consisting of qualifications obtained from ** the integrity trees is constructed and merged with the ** qualifications found in the original tree. ** 18-may-89 (neil) ** Use session memory for cursors with integrities (bug fix). ** 12-mar-90 (andre) ** set rdr_2types_mask to 0. ** 22-may-90 (teg) ** init rdr_instr to RDF_NO_INSTR ** 04-sep-90 (andre) ** fixed bug 32976: for OPEN CURSOR (qmode==PSQ_DEFCURS) we need to ** compare attribute number(s) found in the integrity ** tuple with attribute number found in the VAR ** node(s) found in the portion of the target list ** which was built to represent the FOR UPDATE list ** (such node(s) are right children of RESDOM nodes ** with pst_rsupdt set to TRUE; note that for ** RESDOM nodes built to represent the real target ** list of SELECT, this field is set to FALSE.) ** 29-sep-92 (andre) ** RDF may choose to allocate a new info block and return its address ** in rdf_info_blk - we need to copy it over to pss_rdrinfo to avoid ** memory leak and other assorted unpleasantries ** 05-dec-92 (rblumer) ** ignore FIPS constraints during INGRES integrity processing ** 10-jan-93 (andre) ** after calling rdf_call() for IIINTEGRITY tuples, compare status to ** E_DB_OK instead of using DB_FAILURE_MACRO() since if there are fewer ** than 20 rows, rdf_call() sets err_code to E_RD0011 and status to ** E_DB_WARN ** 10-Feb-93 (Teresa) ** Changed RDF_GETINFO to RDF_READTUPLES for new RDF I/F ** 23-May-1997 (shero03) ** Save the rdr_info_blk after an UNFIX ** 22-Jul-2004 (schka24) ** Delete old ifdef'ed out normalization call ** 28-nov-2007 (dougi) ** Add PSQ_REPDYN to PSQ_DEFCURS test (cached dynamic). */ DB_STATUS psy_integ( PSF_MSTREAM *mstream, PST_QNODE *root, PSS_USRRANGE *rngtab, PSS_RNGTAB *resvar, i4 qmode, PSS_SESBLK *sess_cb, PST_QNODE **result, DB_ERROR *err_blk) { register PST_QNODE *r; PSC_CURBLK *curblk = sess_cb->pss_crsr; CS_SID sessid = sess_cb->pss_sessid; PTR db_id = sess_cb->pss_dbid; i2 dset[DB_COL_WORDS*2]; i2 doms; i2 *domset; bool subset; PST_QNODE *p; register i4 i; PST_QNODE *iqual; PST_QNODE **tmp1; DB_INTEGRITY *inttup; i4 err_code; PST_QTREE *qtree; PST_PROCEDURE *pnode; DB_STATUS status = E_DB_OK; i4 found; RDF_CB rdf_cb; QEF_DATA *qp; RDF_CB rdf_tree_cb; PSS_DUPRB dup_rb; i4 tupcount; i4 map[PST_NUMVARS]; /* Map for reassigning varnos */ r = root; /* Initialize fields in dup_rb */ dup_rb.pss_op_mask = 0; dup_rb.pss_num_joins = PST_NOJOIN; dup_rb.pss_tree_info = (i4 *) NULL; dup_rb.pss_mstream = mstream; dup_rb.pss_err_blk = err_blk; if (qmode == PSQ_REPCURS) { if (!curblk->psc_integ) { *result = r; return (E_DB_OK); } /* ** On a replace cursor, get the query tree that was stored in the ** cursor control block. */ /* copy the qual fragment so we can change below. */ dup_rb.pss_tree = curblk->psc_integ; dup_rb.pss_dup = &iqual; status = pst_treedup(sess_cb, &dup_rb); if (DB_FAILURE_MACRO(status)) { return (status); } } else { /* ** Check to see if we should apply the integrity ** algorithm. ** ** This means checking to insure that we have an update ** and seeing if any integrity constraints apply. */ if ( resvar == 0 || (resvar->pss_tabdesc->tbl_status_mask & DMT_INTEGRITIES) == 0 ) { *result = r; return (E_DB_OK); } /* ** Create a set of the domains updated in this query. */ for (i = 0; i < DB_COL_WORDS*2; i++) dset[i] = 0; for (p = r->pst_left, doms = 0; p != (PST_QNODE *) NULL && p->pst_sym.pst_type != PST_TREE; p = p->pst_left) { if (p->pst_sym.pst_type != PST_RESDOM) { psf_error(E_PS0D0C_NOT_RESDOM, 0L, PSF_INTERR, &err_code, err_blk, 0); return (E_DB_SEVERE); } /* ** if we are defining a cursor, RESDOM numbers are meaningless as ** at best they reflect position of an attribute in the target list ** of a subselect or, at worst, they are set to 1 for all columns ** mentioned in the FOR UPDATE LIST. We really are interested only ** in the VAR nodes which are children of RESDOM nodes built to ** repersent columns appearing in the FOR UPDATE list, so we will ** skip over RESDOM nodes which represent a true subselect ** (i.e. pst_rsupdt == FALSE) */ if (qmode == PSQ_DEFCURS) { if (p->pst_sym.pst_value.pst_s_rsdm.pst_rsupdt) { PST_QNODE *r_child = p->pst_right; /* ** this RESDOM was built to represent the column in the FOR ** UPDATE list */ /* ** make sure the right child is a VAR node; otherwise flag ** an error */ if (r_child == (PST_QNODE *) NULL || r_child->pst_sym.pst_type != PST_VAR) { psf_error(E_PS0C04_BAD_TREE, 0L, PSF_INTERR, &err_code, err_blk, 0); return (E_DB_SEVERE); } BTset((i4) r_child-> pst_sym.pst_value.pst_s_var.pst_atno.db_att_id, (char *) dset); ++doms; } } else { BTset((i4) p->pst_sym.pst_value.pst_s_rsdm.pst_rsno, (char *) dset); ++doms; } } /* ** Note if we are appending a subset of the relation's domains. ** If we are, we'll need to be extra careful to avoid violating ** constraints on those attributes not being explicitly appended: */ subset = ((doms < resvar->pss_tabdesc->tbl_attr_count) && (qmode == PSQ_APPEND)); /* ** Scan integrity catalog for possible tuples. If found, ** include them in the integrity qualification. */ iqual = (PST_QNODE *) NULL; /* Set up constant part of rdf query tree control block */ pst_rdfcb_init(&rdf_tree_cb, sess_cb); STRUCT_ASSIGN_MACRO(resvar->pss_tabid, rdf_tree_cb.rdf_rb.rdr_tabid); rdf_tree_cb.rdf_rb.rdr_types_mask = RDR_INTEGRITIES | RDR_QTREE; rdf_tree_cb.rdf_rb.rdr_qtuple_count = 1; /* Get 1 integ tree at a time */ rdf_tree_cb.rdf_info_blk = resvar->pss_rdrinfo; /* Get all integrity tuples */ pst_rdfcb_init(&rdf_cb, sess_cb); STRUCT_ASSIGN_MACRO(resvar->pss_tabid, rdf_cb.rdf_rb.rdr_tabid); rdf_cb.rdf_rb.rdr_types_mask = RDR_INTEGRITIES; rdf_cb.rdf_rb.rdr_update_op = RDR_OPEN; rdf_cb.rdf_rb.rdr_rec_access_id = NULL; rdf_cb.rdf_rb.rdr_qtuple_count = 20; /* Get 20 integrities at a time */ rdf_cb.rdf_info_blk = resvar->pss_rdrinfo; /* For each group of 20 integrities */ while (rdf_cb.rdf_error.err_code == 0) { status = rdf_call(RDF_READTUPLES, (PTR) &rdf_cb); /* ** RDF may choose to allocate a new info block and return its address ** in rdf_info_blk - we need to copy it over to pss_rdrinfo to avoid ** memory leak and other assorted unpleasantries */ if (rdf_cb.rdf_info_blk != resvar->pss_rdrinfo) { resvar->pss_rdrinfo = rdf_tree_cb.rdf_info_blk = rdf_cb.rdf_info_blk; } /* ** Must not use DB_FAILURE_MACRO because E_RD0011 returns E_DB_WARN ** that would be missed. */ if (status != E_DB_OK) { if ( rdf_cb.rdf_error.err_code == E_RD0011_NO_MORE_ROWS || rdf_cb.rdf_error.err_code == E_RD0013_NO_TUPLE_FOUND) { status = E_DB_OK; if (rdf_cb.rdf_error.err_code == E_RD0013_NO_TUPLE_FOUND) continue; } else if (rdf_cb.rdf_error.err_code == E_RD0002_UNKNOWN_TBL) { (VOID) psf_error(2117L, 0L, PSF_USERERR, &err_code, err_blk, 1, psf_trmwhite(sizeof(DB_TAB_NAME), (char *) &resvar->pss_tabname), &resvar->pss_tabname); status = E_DB_ERROR; goto exit; } else { (VOID) psf_rdf_error(RDF_READTUPLES, &rdf_cb.rdf_error, err_blk); goto exit; } } rdf_cb.rdf_rb.rdr_update_op = RDR_GETNEXT; /* FOR EACH INTEGRITY */ for ( qp = rdf_cb.rdf_info_blk->rdr_ituples->qrym_data, tupcount = 0; tupcount < rdf_cb.rdf_info_blk->rdr_ituples->qrym_cnt; qp = qp->dt_next, tupcount++ ) { inttup = (DB_INTEGRITY*) qp->dt_data; /* ** Ignore FIPS constraints (i.e. SQL92 constraints) ** (they are implemented via rules instead) */ if (inttup->dbi_consflags != 0) { continue; } /* check for some domain set overlap */ domset = (i2*) inttup->dbi_columns.db_domset; for (i = 0; i < DB_COL_WORDS*2; i++) { if ((dset[i] & domset[i]) != 0) break; } /* ** Check for appends where defaults don't satisfy integrity. */ if ((i >= DB_COL_WORDS*2) && !subset) { continue; } /* Get integrity tree and make qtree point to it */ STRUCT_ASSIGN_MACRO(inttup->dbi_tree, rdf_tree_cb.rdf_rb.rdr_tree_id); rdf_tree_cb.rdf_rb.rdr_qrymod_id = inttup->dbi_number; rdf_tree_cb.rdf_rb.rdr_update_op = 0; rdf_tree_cb.rdf_rb.rdr_rec_access_id = NULL; rdf_tree_cb.rdf_info_blk = resvar->pss_rdrinfo; rdf_tree_cb.rdf_rb.rdr_integrity = NULL; STRUCT_ASSIGN_MACRO(inttup->dbi_tabid, rdf_tree_cb.rdf_rb.rdr_tabid); rdf_tree_cb.rdf_rb.rdr_sequence = inttup->dbi_number; status = rdf_call(RDF_GETINFO, (PTR) &rdf_tree_cb); /* ** RDF may choose to allocate a new info block and return its ** address in rdf_info_blk - we need to copy it over to pss_rdrinfo ** to avoid memory leak and other assorted unpleasantries */ if (rdf_tree_cb.rdf_info_blk != resvar->pss_rdrinfo) { resvar->pss_rdrinfo = rdf_cb.rdf_info_blk = rdf_tree_cb.rdf_info_blk; } if (DB_FAILURE_MACRO(status)) { if (rdf_tree_cb.rdf_error.err_code == E_RD0002_UNKNOWN_TBL) { (VOID) psf_error(E_PS0903_TAB_NOTFOUND, 0L, PSF_USERERR, &err_code, err_blk, 1, psf_trmwhite(sizeof(DB_TAB_NAME), (char *) &resvar->pss_tabname), &resvar->pss_tabname); } else { (VOID) psf_rdf_error(RDF_GETINFO, &rdf_tree_cb.rdf_error, err_blk); } goto exit; } pnode = (PST_PROCEDURE *) rdf_tree_cb.rdf_rb.rdr_integrity->qry_root_node; qtree = pnode->pst_stmts->pst_specific.pst_tree; /* trim off (null) target list */ p = qtree->pst_qtree->pst_right; /* use a copy of the qtree because the qtree is in RDF memory */ dup_rb.pss_tree = qtree->pst_qtree->pst_right; dup_rb.pss_dup = &p; status = pst_treedup(sess_cb, &dup_rb); { /* unfix the query tree no matter what the above status is */ DB_STATUS temp_status; temp_status = rdf_call(RDF_UNFIX, (PTR) &rdf_tree_cb); resvar->pss_rdrinfo = rdf_cb.rdf_info_blk = rdf_tree_cb.rdf_info_blk; if (DB_FAILURE_MACRO(temp_status)) { (VOID) psf_rdf_error(RDF_UNFIX, &rdf_tree_cb.rdf_error, err_blk); status = temp_status; } } if (DB_FAILURE_MACRO(status)) goto exit; /* close integrity file */ /* ** Make the result variable for the integrity the same as the result ** variable for the user query. ** I AM NOT SURE THE FOLLOWING COMMENT APPLIES SO I AM MERGING ** THE RANGE VAR FOR APPENDS. ** This is not done for append because ** append doesn't have a result range variable. */ for (i = 0; i < PST_NUMVARS; i++) map[i] = i; i = inttup->dbi_resvar; map[i] = resvar->pss_rgno; status = psy_mapvars(p, map, err_blk); if (DB_FAILURE_MACRO(status)) goto exit; /* add to integrity qual */ if (iqual == NULL) { status = pst_node(sess_cb, mstream, p, (PST_QNODE *) NULL, PST_AND, (PTR) NULL, sizeof(PST_OP_NODE), DB_NODT, (i2) 0, (i4) 0, (DB_ANYTYPE *) NULL, &iqual, err_blk, (i4) 0); if (DB_FAILURE_MACRO(status)) goto exit; /* close integrity file */ /* ** Note that tmp1 will contain address of the pst_right ptr ** of the bottom AND node in the tree constructed from the ** qualifications found in the integrities */ tmp1 = &iqual->pst_right; } else { PST_QNODE *newnode; status = pst_node(sess_cb, mstream, p, iqual, PST_AND, (PTR) NULL, sizeof(PST_OP_NODE), DB_NODT, (i2) 0, (i4) 0, (DB_ANYTYPE *) NULL, &newnode, err_blk, (i4) 0); if (DB_FAILURE_MACRO(status)) goto exit; /* close integrity file */ iqual = newnode; } } } if (rdf_cb.rdf_rb.rdr_rec_access_id != NULL) { DB_STATUS temp_status; /* unfix integrity tuples */ rdf_cb.rdf_rb.rdr_update_op = RDR_CLOSE; temp_status = rdf_call(RDF_READTUPLES, (PTR) &rdf_cb); resvar->pss_rdrinfo = rdf_tree_cb.rdf_info_blk = rdf_cb.rdf_info_blk; if (DB_FAILURE_MACRO(temp_status)) { (VOID) psf_rdf_error(RDF_READTUPLES, &rdf_cb.rdf_error, err_blk); status = temp_status; } } } if (qmode == PSQ_DEFCURS || qmode == PSQ_REPDYN) { /* ** On a "define cursor", keep a copy of the integrity qualification ** for later use. */ if (iqual == NULL) { curblk->psc_integ = (PST_QNODE *) NULL; } else { status = pst_trdup(curblk->psc_stream, iqual, &curblk->psc_integ, &sess_cb->pss_memleft, err_blk); if (DB_FAILURE_MACRO(status)) { return (status); } } } else { /* ** Clean up the integrity qualification so that it will merge ** nicely into the tree, and then append it to the user's ** qualification. */ if (iqual != NULL) { /* replace VAR nodes by corresponding user afcn from user's ** query. For example, if the integ says r.a > 1 and the user's ** query says resdom 4 = 4 + 3, the integ is modified to 4 + 3 > 1. ** ** The following paragraph refers to the case where an integrity ** refers to a variable not updated in the target list. ** ** If there is no resdom for r.a, if the user didn't specify r.a ** in the target list of the query, and the query is an append, ** r.a > 1 is replaced with 'default val for col a' > 1. If the ** query is a replace statement, we do nothing because r.a will ** be retrieved but is not in the targ list. We will be verifying ** what we know should already hold (ie the integrity constraint). ** If we have replace cursor, we wan't to replace r.a with a value ** so a retrieve can be avoided on the replace command. We can't ** have col a = 5 where r.a > 1. We do, however, have the value r.a ** in the row to be updated. QEF has this value. We replace r.a with ** a curval node that refers to the value r.a. */ /* will pass dup_rb, but first null out pss_tree and pss_dup */ dup_rb.pss_tree = (PST_QNODE *) NULL; dup_rb.pss_dup = (PST_QNODE **) NULL; status = psy_subsvars(sess_cb, &iqual, resvar, r->pst_left, qmode, (PST_QNODE *) NULL, resvar, (i4) 0, qmode, &curblk->psc_blkid, &found, &dup_rb); if (DB_FAILURE_MACRO(status)) { return (status); } /* ** for REPLACE CURSOR, we need to traverse down the pst_right's ** until we encounter NULL in place of which we will append the ** qualification from the ROOT ** Note that iqual is guaranteed to be an AND node with BOP for a ** left child and either AND or NULL for the right child. */ if (qmode == PSQ_REPCURS) { for (tmp1 = &iqual->pst_right; (*tmp1) != (PST_QNODE *) NULL; tmp1 = &(*tmp1)->pst_right) ; } /* ** append qualification from the tree to the integrities and ** make the result the new qualification for the tree */ (*tmp1) = r->pst_right; r->pst_right = iqual; } } exit: if ((qmode != PSQ_REPCURS) && (rdf_cb.rdf_rb.rdr_rec_access_id != NULL)) { DB_STATUS temp_status; /* unfix integrity tuples */ rdf_cb.rdf_rb.rdr_update_op = RDR_CLOSE; temp_status = rdf_call(RDF_READTUPLES, (PTR) &rdf_cb); resvar->pss_rdrinfo = rdf_tree_cb.rdf_info_blk = rdf_cb.rdf_info_blk; if (DB_FAILURE_MACRO(temp_status)) { (VOID) psf_rdf_error(RDF_READTUPLES, &rdf_cb.rdf_error, err_blk); status = temp_status; } } if (status == E_DB_OK) { *result = r; } return (status); }
/*{ ** Name: psy_kinteg - Destroy one or more integrities for a table. ** ** INTERNAL PSF call format: status = psy_kinteg(&psy_cb, sess_cb); ** ** EXTERNAL call format: status = psy_call(PSY_KINTEG, &psy_cb, sess_cb); ** ** Description: ** The psy_kinteg function removes the definitions of one or more ** integrities on a table from the system relations (integrities, ** tree, and iiqrytext). ** Optionally, one can tell this function to destroy all of the ** integrities on the given table. ** ** Inputs: ** psy_cb ** .psy_tables[0] Id of table from which to remove ** integrities ** .psy_numbs[] Array of integrity id numbers telling ** which integrities to destroy (at most ** 20) ** .psy_numnms Number telling how many integrity ** numbers there are. Zero means to ** destroy all of the integrities ** on the given table. ** .psy_tabname[0] Name of table from which to remove ** integrities ** sess_cb Pointer to session control block ** (Can be NULL) ** ** Outputs: ** psy_cb ** .psy_error Filled in if an error happens ** .err_code What the error was ** E_PS0000_OK Success ** E_PS0001_USER_ERROR User made a mistake ** E_PS0002_INTERNAL_ERROR Internal inconsistency in PSF ** Returns: ** E_DB_OK Function completed normally. ** E_DB_WARN Function completed with warning(s) ** E_DB_ERROR Function failed; non-catastrophic error ** E_DB_FATAL Function failed; catastrophic error ** Exceptions: ** none ** ** Side Effects: ** Removes query tree(s) representing predicates of integrities from ** tree relation, text of defining query (or queries) from iiqrytext ** relation, rows defining integrity (or integrities) from integrities ** relation. If there are no more integrities on this table, will ** use DMF alter table function to indicate this. ** ** History: ** 02-oct-85 (jeff) ** written ** 30-aug-88 (andre) ** check if the user specified integrity 0 to be deleted, ** and if so, warn that it doesn't exist (this will prevent someone ** from inadvertently deleting all integrities by specifying that ** integrity 0 be deleted.) ** 30-aug-88 (andre) ** When looking at individual integrities, return E_DB_ERROR if any ** errors were found when processing individual integrities. ** 12-mar-90 (andre) ** set rdr_2types_mask to 0. ** 22-may-90 (teg) ** init rdr_instr to RDF_NO_INSTR ** 03-aug-1992 (barbara) ** Call pst_rdfcb_init to initialize RDF_CB before calling RDF. ** Invalidate infoblk from RDF cache after dropping integrity. ** 07-aug-92 (teresa) ** RDF_INVALID must be called for the base object that the ** integrity was defined on as well as for the integrity trees. ** 14-sep-92 (barbara) ** Set type field and tableid in the RDF cb used to invalidate ** the cached entries. ** 14-jul-93 (ed) ** replacing <dbms.h> by <gl.h> <sl.h> <iicommon.h> <dbdbms.h> ** 10-aug-93 (andre) ** removed declaration of rdf_call() ** 30-sep-93 (stephenb) ** Pass tablename from psy_cb to rdf_rb, so that we can use the ** information to audit in QEF. */ DB_STATUS psy_kinteg( PSY_CB *psy_cb, PSS_SESBLK *sess_cb) { RDF_CB rdf_cb; RDF_CB rdf_inv_cb; /* For invalidation */ register RDR_RB *rdf_rb = &rdf_cb.rdf_rb; DB_STATUS status = E_DB_OK; i4 err_code; register i4 i; /* Fill in RDF control block */ pst_rdfcb_init(&rdf_cb, sess_cb); pst_rdfcb_init(&rdf_inv_cb, sess_cb); STRUCT_ASSIGN_MACRO(psy_cb->psy_tables[0], rdf_rb->rdr_tabid); STRUCT_ASSIGN_MACRO(psy_cb->psy_tables[0], rdf_inv_cb.rdf_rb.rdr_tabid); rdf_rb->rdr_types_mask = RDR_INTEGRITIES; rdf_inv_cb.rdf_rb.rdr_types_mask = RDR_INTEGRITIES; rdf_rb->rdr_update_op = RDR_DELETE; rdf_rb->rdr_name.rdr_tabname = psy_cb->psy_tabname[0]; /* No integrity numbers means destroy all integrities. */ if (psy_cb->psy_numnms == 0) { /* Zero integrity number means destroy all integs. */ rdf_rb->rdr_qrymod_id = 0; status = rdf_call(RDF_UPDATE, (PTR) &rdf_cb); if (DB_FAILURE_MACRO(status)) { if (rdf_cb.rdf_error.err_code == E_RD0002_UNKNOWN_TBL) { (VOID) psf_error(E_PS0903_TAB_NOTFOUND, 0L, PSF_USERERR, &err_code, &psy_cb->psy_error, 1, psf_trmwhite(sizeof(psy_cb->psy_tabname[0]), (char *) &psy_cb->psy_tabname[0]), &psy_cb->psy_tabname[0]); } else { (VOID) psf_rdf_error(RDF_UPDATE, &rdf_cb.rdf_error, &psy_cb->psy_error); } return (status); } /* Invalidate table info from RDF cache */ /* first invalidate the object from the relation cache */ status = rdf_call(RDF_INVALIDATE, (PTR) &rdf_inv_cb); if (DB_FAILURE_MACRO(status)) (VOID) psf_rdf_error(RDF_INVALIDATE, &rdf_inv_cb.rdf_error, &psy_cb->psy_error); /* now invalidate any integrity trees from the cache */ rdf_inv_cb.rdf_rb.rdr_2types_mask |= RDR2_CLASS; status = rdf_call(RDF_INVALIDATE, (PTR) &rdf_inv_cb); if (DB_FAILURE_MACRO(status)) (VOID) psf_rdf_error(RDF_INVALIDATE, &rdf_inv_cb.rdf_error, &psy_cb->psy_error); } else { DB_STATUS stat; /* Run through the integrity numbers, destroying each one */ for (i = 0; i < psy_cb->psy_numnms; i++) { /* if user specified 0, complain and proceed with next integrity # */ if ((rdf_rb->rdr_qrymod_id = psy_cb->psy_numbs[i]) == 0) { /* remember error to report later */ status = (status > E_DB_ERROR) ? status : E_DB_ERROR; (VOID) psf_error(5203, 0L, PSF_USERERR, &err_code, &psy_cb->psy_error, 1, sizeof (rdf_rb->rdr_qrymod_id), &rdf_rb->rdr_qrymod_id); continue; } stat = rdf_call(RDF_UPDATE, (PTR) &rdf_cb); /* remember the highest status seen so far */ status = (status > stat) ? status : stat; if (DB_FAILURE_MACRO(stat)) { switch (rdf_cb.rdf_error.err_code) { case E_RD0002_UNKNOWN_TBL: (VOID) psf_error(E_PS0903_TAB_NOTFOUND, 0L, PSF_USERERR, &err_code, &psy_cb->psy_error, 1, psf_trmwhite(sizeof(psy_cb->psy_tabname[0]), (char *) &psy_cb->psy_tabname[0]), &psy_cb->psy_tabname[0]); break; case E_RD0013_NO_TUPLE_FOUND: (VOID) psf_error(5203, 0L, PSF_USERERR, &err_code, &psy_cb->psy_error, 1, sizeof (rdf_rb->rdr_qrymod_id), &rdf_rb->rdr_qrymod_id); continue; default: (VOID) psf_rdf_error(RDF_UPDATE, &rdf_cb.rdf_error, &psy_cb->psy_error); } return (status); } /* invalidate the integrity tree from the cache. */ rdf_inv_cb.rdf_rb.rdr_sequence = psy_cb->psy_numbs[i]; rdf_inv_cb.rdf_rb.rdr_2types_mask |= RDR2_ALIAS; status = rdf_call(RDF_INVALIDATE, (PTR) &rdf_inv_cb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_rdf_error(RDF_INVALIDATE, &rdf_inv_cb.rdf_error, &psy_cb->psy_error); break; } } /* now invalidate the integrity base object from the relation cache */ if (DB_SUCCESS_MACRO(status)) { rdf_inv_cb.rdf_rb.rdr_sequence = 0; rdf_inv_cb.rdf_rb.rdr_2types_mask &= ~RDR2_ALIAS; status = rdf_call(RDF_INVALIDATE, (PTR) &rdf_inv_cb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_rdf_error(RDF_INVALIDATE, &rdf_inv_cb.rdf_error, &psy_cb->psy_error); } } } return (status); }
/*{ ** Name: rdf_trace - Call RDF trace operation. ** ** External call format: status = rdf_trace(&db_debug_cb) ** ** Description: ** This function is the standard entry point to RDF for setting and ** clearing tracepoints(the "set trace point" command). Because RDF ** is a service facility, trace point for RDF can only be set on ** the server basis. There is no session level trace point. ** Db_debug_cb is the tracing control block that contains the trace ** flag information. ** ** See file <rdftrace.h> for a description of all possible ** RDF trace points. ** ** Inputs: ** debug_cb Pointer to a DB_DEBUG_CB ** .db_trswitch What operation to perform ** DB_TR_NOCHANGE None ** DB_TR_ON Turn on a tracepoint ** DB_TR_OFF Turn off a tracepoint ** .db_trace_point Trace point ID(the flag number) ** .db_value_count The number of values specified in ** the value array ** .db_vals[2] Optional values, to be interpreted ** differently for each tracepoint ** Outputs: ** none ** ** Returns: ** E_DB_OK Function completed normally. ** E_DB_ERROR Function failed due to error by caller; ** E_DB_FATAL Function failed due to some internal ** problem; ** Exceptions: ** none ** ** Side Effects: ** The trace vector in the server control block of RDF will be updated ** to contain the trace information. The trace information will be persist ** throughout the life of the server. ** ** History: ** 15-apr-86 (ac) ** written ** 02-mar-87 (puree) ** replace global server control block with global pointer. ** 14-dec-1989 (teg) ** modify to go get svcb from SCF instead of using a global pointer ** to it. ** 16-jul-92 (teresa) ** prototypes ** 16-sep-92 (teresa) ** implement trace points rd3 and rd4 to clear ldbdesc cache. ** 22-apr-94 (teresa) ** added trace points rd11 and rd12 to dump memory statistics or all ** statistics. This is an action trace point -- the dump occurs when ** the trace point is selected rather than during query execution. ** 20-nov-2007 (thaju02) ** If trace point RD0022/RDU_CHECKSUM specified, invalidate ** relcache. Entries need rdr_checksum to be calc/init'd ** otherwise E_RD010D errors may be reported. (B119499) */ DB_STATUS rdf_trace(DB_DEBUG_CB *debug_cb) { i4 flag; i4 firstval; i4 secondval; DB_STATUS status; i4 trace_scope; /* assure flag is legal */ flag = debug_cb->db_trace_point; if (flag >= RDF_NB) { return (E_DB_ERROR); } /* There can be UP TO two values, but maybe they weren't given */ firstval = (debug_cb->db_value_count > 0) ? debug_cb->db_vals[0] : 0L; secondval = (debug_cb->db_value_count > 1) ? debug_cb->db_vals[1] : 0L; /* see if this is an action trace. Action traces require an immediate ** action rather than turning trace flags on/off/etc. */ if ( (debug_cb->db_trswitch==DB_TR_ON) && (flag <= RD_ACT_MAX) ) { /* see which action is requested. Not all actions are implemented ** yet, so its possible that this call may become a no-opt */ switch (flag) { case RD0001: case RD0002: case RD0003: case RD0004: case RD0005: case RD0010: status=rdt_clear_cache(flag); if (DB_FAILURE_MACRO(status)) { return(E_DB_ERROR); } break; case RD0011: /* dump memory info. This trace is used by tech support when ** debugging out of memory errors. */ TRdisplay("\n...............................................\n"); TRdisplay("RDF Cache Memory Available: %d\n",Rdi_svcb->rdv_memleft); TRdisplay("RDF memory cache size : %d\n", Rdi_svcb->rdv_pool_size); TRdisplay("Max number of objects allowed on Cache:\n"); TRdisplay(" RELcache: %d, QTREE Cache: %d, \n", Rdi_svcb->rdv_cnt0_rdesc, Rdi_svcb->rdv_cnt1_qtree); TRdisplay(" LDBDesc Cache %d, DEFAULTS cache: %d\n", Rdi_svcb->rdv_cnt2_dist, Rdi_svcb->rdv_cnt3_default); TRdisplay("Hashids:\n"); TRdisplay(" RELcache: %d, QTREE Cache: %d, \n", Rdi_svcb->rdv_rdesc_hashid, Rdi_svcb->rdv_qtree_hashid); TRdisplay(" LDBDesc Cache %d, DEFAULTS cache: %d\n", Rdi_svcb->rdv_dist_hashid, Rdi_svcb->rdv_def_hashid); TRdisplay("...............................................\n"); break; case RD0012: /* dump all of the RDF statistics */ rdf_report ( &Rdi_svcb->rdvstat ); break; default: break; } } else { /* ** determine if this is a session wide trace or a server wide trace ** and process accordingly */ trace_scope = trace_type(flag); if (trace_scope == SVR_WIDE_TRACE) { /* turn trace on in svcb ** ** Three possible actions: Turn on flag, turn it off, or do nothing. */ switch (debug_cb->db_trswitch) { case DB_TR_ON: if ((flag == RD0022) && !(ult_check_macro(&Rdi_svcb->rdf_trace, flag, &firstval, &secondval))) { /* setting RDU_CHECKSUM */ RDF_GLOBAL global; RDF_CB rdfcb; RDI_FCB fcb; global.rdfcb = &rdfcb; rdfcb.rdf_rb.rdr_fcb = (PTR)&fcb; rdfcb.rdf_info_blk = NULL; rdfcb.rdf_rb.rdr_db_id = NULL; rdfcb.rdf_rb.rdr_types_mask = 0; rdfcb.rdf_rb.rdr_2types_mask = 0; CSget_sid(&rdfcb.rdf_rb.rdr_session_id); fcb.rdi_fac_id = DB_RDF_ID; status = rdf_invalidate(&global, &rdfcb); if (DB_FAILURE_MACRO(status)) return(E_DB_ERROR); } ult_set_macro(&Rdi_svcb->rdf_trace, flag, firstval, secondval); break; case DB_TR_OFF: ult_clear_macro(&Rdi_svcb->rdf_trace, flag); break; case DB_TR_NOCHANGE: /* Do nothing */ break; default: return (E_DB_ERROR); }; } else { CS_SID sid; RDF_SESS_CB *rdf_sess_cb; /* ** this trace point is session specific, so use the session control ** block for this trace point. */ CSget_sid(&sid); rdf_sess_cb = GET_RDF_SESSCB(sid); /* ** Three possible actions: Turn on flag, turn it off, or do nothing. */ switch (debug_cb->db_trswitch) { case DB_TR_ON: ult_set_macro(&rdf_sess_cb->rds_strace, flag, firstval, secondval); break; case DB_TR_OFF: ult_clear_macro(&rdf_sess_cb->rds_strace, flag); break; case DB_TR_NOCHANGE: /* Do nothing */ break; default: return (E_DB_ERROR); } } } return (E_DB_OK); }
/* ** { ** Name: adu_redeem - Redeem an ADF_COUPON ** ** Description: ** This routine redeems an ADF_COUPON, i.e. it materializes the object ** represented by the coupon. Since objects represented by coupons ** may not fit in memory, this routine must deal with returning partial ** results. In such cases, it will store its partial status in its fourth ** argument, which is to be returned for each call. ** ** Inputs: ** adf_scb Standard ADF session control block ** result_dv Ptr to DB_DATA_VALUE into which to ** place the result. ** coupon_dv Ptr to DB_DATA_VALUE which contains ** the input to redeem. ** workspace_dv Ptr to DB_DATA_VALUE which points to ** the workspace. This workspace is ** expected to be maintained across ** calls to this routine for any given ** coupon. (I.e. it can be "new" only ** when "continuation" (next param) is ** zero. ** continuation Is this a continuation of a previous ** call. ** ** Outputs: ** adf_scb->adf_errcb Filled as appropriate. ** result_dv->db_length Filled with the length of the result ** area actually used. ** *workspace->db_data Filled with info to be used by ** subsequent invocations of this routine. ** Returns: ** E_DB_ERROR In case of error ** E_DB_INFO/E_AD0002_INCOMPLETE ** When more calls are necessary ** E_DB_OK When complete. ** ** Exceptions: ** None. ** ** Side Effects: ** None. ** ** History: ** 07-dec-1989 (fred) ** Prototyped. ** 06-oct-1992 (fred) ** Altered to work with timezone integration. As a temporary measure, ** this routine was using the ADF_CB.adf_4rsvd field to store ** some context across calls. In that this field disappeared, a new, ** more appropriately named field was added (adf_lo_context). This ** new ADF_CB field is now used in this file. ** 30-Oct-1992 (fred) ** Fixed to correctly handle being called with a length too short ** for the original header. ** 20-Nov-1992 (fred) ** Rewritten for better clarity & better delineation of ** function in support of OME large objects. ** 18-Oct-1993 (fred) ** Moved check for FEXI function so that STAR, which has ** none, can send null blobs sometimes. It helps get a tiny ** bit of support in to STAR. ** 13-Apr-1994 (fred) ** Altered to hand status from FEXI call straight back. By ** not losing the actual status, interrupts may avoid logging ** too much stuff. ** 14-Sep-1995 (shust01/thaju02) ** fixed problem of endless loop when we are finished, but we ** come in just to get the NULL byte. work->adw_shared.shd_o_used ** had old size (which was max value), so we never end. Set ** work->adw_shared.shd_o_used = 0. ** 24-Oct-2001 (thaju02) ** If adc_lvch_xform() was unable to fit the 2-byte segment ** length in the result buffer, decrement result length ** with unused byte and flush segment. (B104122) [@history_template@]... */ DB_STATUS adu_redeem( ADF_CB *adf_scb, DB_DATA_VALUE *result_dv, DB_DATA_VALUE *coupon_dv, DB_DATA_VALUE *workspace_dv, i4 continuation) { ADP_LO_WKSP *work = (ADP_LO_WKSP *) workspace_dv->db_data; DB_DT_ID dtid; ADP_PERIPHERAL *p = (ADP_PERIPHERAL *) result_dv->db_data; DB_STATUS status; i4 done = FALSE; i4 flush; i4 for_gca = 1; i4 loop_around; if (result_dv->db_datatype != coupon_dv->db_datatype) return(adu_error(adf_scb, E_AD9999_INTERNAL_ERROR, 0)); dtid = ADI_DT_MAP_MACRO(abs(result_dv->db_datatype)); if ( dtid <= 0 || dtid > ADI_MXDTS || Adf_globs->Adi_dtptrs[dtid] == NULL || !Adf_globs->Adi_dtptrs[dtid]->adi_under_dt ) return(adu_error(adf_scb, E_AD2004_BAD_DTID, 0)); if ((!continuation) || (work->adw_fip.fip_state == ADW_F_STARTING)) { if (!continuation) work->adw_fip.fip_state = ADW_F_INITIAL; status = adu_rdm1_setup(adf_scb, result_dv, coupon_dv, work, for_gca); if (status || work->adw_fip.fip_done) return(status); } else if (work->adw_fip.fip_state == ADW_F_DONE_NEED_NULL) { /* ** This is an indication that we finished, but didn't ** send the NULL byte, and must do so now... */ work->adw_shared.shd_o_used = 0; status = adu_rdm2_finish(adf_scb, result_dv, coupon_dv, work, for_gca); return(status); } else { if (Adf_globs->Adi_fexi[ADI_01PERIPH_FEXI].adi_fcn_fexi == NULL) return(adu_error(adf_scb, E_AD9999_INTERNAL_ERROR, 0)); work->adw_shared.shd_o_used = 0; work->adw_shared.shd_o_length = result_dv->db_length; work->adw_shared.shd_o_area = (char *) result_dv->db_data; work->adw_fip.fip_pop_cb.pop_continuation = 0; work->adw_fip.fip_pop_cb.pop_coupon = coupon_dv; } for (flush = 0; !done && (!flush) && (work->adw_shared.shd_l1_check < work->adw_fip.fip_l1_value); /* No update action */) { switch (work->adw_shared.shd_exp_action) { case ADW_FLUSH_SEGMENT: case ADW_START: if (for_gca) { /* ** Since we got a segment back, insert the `here ** comes another segment indicator'. */ i4 one = 1; if ( (work->adw_shared.shd_o_length - work->adw_shared.shd_o_used) < sizeof(one)) { /* Then the next segment marker won't fit. Dump */ /* the current segment and move on... */ /* fix_me -- better error... */ return(adu_error(adf_scb, E_AD9999_INTERNAL_ERROR, 0)); } I4ASSIGN_MACRO(one, work->adw_shared.shd_o_area[work->adw_shared.shd_o_used]); work->adw_shared.shd_o_used += sizeof(one); } /* Fall thru -- if out of input data, get some */ case ADW_FLSH_SEG_NOFIT_LIND: case ADW_NEXT_SEGMENT: if ( (work->adw_shared.shd_exp_action == ADW_NEXT_SEGMENT) && (work->adw_shared.shd_inp_tag == ADP_P_COUPON)) { /* ** For [DBMS style] coupons, each segment is in a row ** unto itself, regardless of the length of the row. ** Since this routine is datatype independent, it ** cannot tell when a segment is up, and thus must ** rely on the called routine. Thus, if the callee ** asks for a new segment when processing a DBMS ** coupon, this routine will assume that the current ** set of data has been all used up... */ work->adw_shared.shd_i_used = work->adw_shared.shd_i_length; } /* Fall thru... */ case ADW_GET_DATA: if (work->adw_shared.shd_i_used == work->adw_shared.shd_i_length) { /* ** If no unmoved segment data, then get some more. */ if (work->adw_fip.fip_done) { done = TRUE; status = E_DB_OK; } else { status = (*Adf_globs->Adi_fexi[ADI_01PERIPH_FEXI].adi_fcn_fexi) (ADP_GET, &work->adw_fip.fip_pop_cb); if (status) { if (work->adw_fip.fip_pop_cb.pop_error.err_code == E_AD7001_ADP_NONEXT) { work->adw_fip.fip_done = TRUE; if (status == E_DB_WARN) status = E_DB_OK; else break; } else { return(adu_error(adf_scb, work->adw_fip.fip_pop_cb.pop_error.err_code, 0)); } } work->adw_shared.shd_i_used = 0; work->adw_shared.shd_i_length = work->adw_fip.fip_seg_dv.db_length; work->adw_shared.shd_i_area = work->adw_fip.fip_seg_dv.db_data; } } break; case ADW_CONTINUE: /* This shouldn't happen */ default: /* Nor should anything else */ return(adu_error(adf_scb, E_AD9999_INTERNAL_ERROR, 0)); break; } if (done) break; status = adc_xform(adf_scb, (PTR)work); if (DB_FAILURE_MACRO(status)) { return(adu_error(adf_scb, E_AD7003_ADP_BAD_RECON, 0)); } if (work->adw_shared.shd_exp_action == ADW_FLUSH_SEGMENT) { flush = 1; } /* B104122 */ if (work->adw_shared.shd_exp_action == ADW_FLSH_SEG_NOFIT_LIND) { result_dv->db_length = work->adw_shared.shd_o_used; flush = 1; } /* B?????? */ if (work->adw_shared.shd_exp_action == ADW_FLUSH_INCOMPLETE_SEGMENT) { result_dv->db_length = work->adw_shared.shd_o_used; work->adw_shared.shd_exp_action = ADW_FLUSH_SEGMENT; flush = 1; } work->adw_fip.fip_pop_cb.pop_continuation = 0; } if (work->adw_shared.shd_l1_check == work->adw_fip.fip_l1_value) { done = TRUE; } if (work->adw_fip.fip_test_mode) { work->adw_fip.fip_test_sent += work->adw_shared.shd_o_used; if ((work->adw_fip.fip_test_length - work->adw_fip.fip_test_sent - work->adw_fip.fip_null_bytes - (for_gca * sizeof(i4))) <= 0) { /* ** Then, for test purposes, we've sent all that we can. ** Emulate completion. */ work->adw_fip.fip_done = done = TRUE; work->adw_shared.shd_i_used = work->adw_shared.shd_i_length; work->adw_shared.shd_l1_check = work->adw_fip.fip_l1_value; work->adw_shared.shd_o_used = work->adw_fip.fip_test_length - work->adw_fip.fip_test_sent - sizeof(i4) /* end marker */ - work->adw_fip.fip_null_bytes; /* null indicator */ work->adw_fip.fip_test_sent = work->adw_fip.fip_test_length - sizeof(i4) - work->adw_fip.fip_null_bytes; } } if (done && ((work->adw_shared.shd_exp_action == ADW_NEXT_SEGMENT) || (work->adw_shared.shd_exp_action == ADW_FLUSH_SEGMENT))) { /* ** If we think we are done and have used all the input,... */ status = adu_rdm2_finish(adf_scb, result_dv, coupon_dv, work, for_gca); } else { /* ** Otherwise, if we are surpassing the amount of data which ** be available... */ if (work->adw_shared.shd_l1_check > work->adw_fip.fip_l1_value) { /* ** FIX_ME -- better error message... ** Then we would be sending an inconsistent blob. ** That would be bad. */ return(adu_error(adf_scb, E_AD7004_ADP_BAD_BLOB,0)); } adf_scb->adf_errcb.ad_errcode = E_AD0002_INCOMPLETE; status = E_DB_INFO; } 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); }