/*{ ** Name: psf_mopen - Open a memory stream. ** ** Description: ** All memory allocation within the parser is done by means of streams. ** A memory stream must be opened before it is used. To open it, one ** specifies the type of object to be allocated (query text, query tree, ** or query plan), and gives a place to put the stream id and the lock ** id. The stream id and lock id are used for all future references to ** the memory stream. ** ** Inputs: ** sess_cb Ptr to session's CB. ** objtype The type of memory to be allocated. ** mstream Pointer to structure to contain stream ** id and lock id, which will uniquely ** identify object in future. ** err_blk Place to put the error information. ** E_PS0A05_BADMEMREQ Bad memory request ** ** Outputs: ** mstream Filled in with stream id and lock id ** err_blk Filled in with error number and text. ** Returns: ** E_DB_OK Function completed normally. ** E_DB_WARN Function completed with warning(s); ** error status in psq_cb.psq_error chain ** of error blocks. ** E_DB_ERROR Function failed due to error by caller; ** error status in psq_cb.psq_error chain ** of error blocks. ** E_DB_FATAL Function failed due to some internal ** problem; error status in ** psq_cb.psq_error chain of error blocks. ** ** Exceptions: ** none ** ** Side Effects: ** Allocates memory. ** ** History: ** 27-dec-85 (jeff) ** written */ DB_STATUS psf_mopen( PSS_SESBLK *sess_cb, i4 objtype, PSF_MSTREAM *mstream, DB_ERROR *err_blk) { i4 err_code; DB_STATUS status; sess_cb->pss_qsf_rcb.qsf_obj_id.qso_type = objtype; sess_cb->pss_qsf_rcb.qsf_obj_id.qso_lname = 0; status = qsf_call(QSO_CREATE, &sess_cb->pss_qsf_rcb); if (status != E_DB_OK) { i4 qerr; qerr = sess_cb->pss_qsf_rcb.qsf_error.err_code; if (qerr == E_QS0001_NOMEM) (VOID) psf_error(qerr, qerr, PSF_CALLERR, &err_code, err_blk, 0); else (VOID) psf_error(E_PS0A05_BADMEMREQ, qerr, PSF_INTERR, &err_code, err_blk, 0); mstream->psf_mstream.qso_handle = (PTR) NULL; mstream->psf_mlock = 0; return (status); } mstream->psf_mstream.qso_handle = sess_cb->pss_qsf_rcb.qsf_obj_id.qso_handle; mstream->psf_mlock = sess_cb->pss_qsf_rcb.qsf_lk_id; return (E_DB_OK); }
/*{ ** 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); }
/*{ ** Name: psq_tadd - Add a piece of query text to a chain ** ** Description: ** This function adds a piece of query text to an existing 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 ** 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: ** 18-jul-86 (jeff) ** written */ DB_STATUS psq_tadd( PTR header, u_char *piece, i4 size, PTR *result, DB_ERROR *err_blk) { PSQ_THEAD *hp = (PSQ_THEAD *) header; PSQ_TEXT *tp; i4 err_code; DB_STATUS status; /* 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 (status != E_DB_OK) { 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 */ tp->psq_next = (PSQ_TEXT *) NULL; if (hp->psq_last != (PSQ_TEXT *) NULL) { hp->psq_last->psq_next = tp; tp->psq_prev = hp->psq_last; } else { tp->psq_prev = NULL; } hp->psq_last = tp; if (hp->psq_first == (PSQ_TEXT *) NULL) hp->psq_first = tp; /* Add in the length to the total for the chain */ hp->psq_tsize += size; return (E_DB_OK); }
/*{ ** Name: psf_mclose - Close a memory stream. ** ** Description: ** This function closes a memory stream, which amounts to deallocating ** all of the memory held by it. ** ** Inputs: ** sess_cb Ptr to session's CB. ** stream Pointer to the stream to be closed. ** err_blk Place to put error information. ** ** Outputs: ** err_blk Filled in with error information. ** E_PS0006_CANTFREE Error when freeing memory. ** ** Returns: ** E_DB_OK Operation succeeded. ** E_DB_ERROR Error by caller (e.g. bad parameter). ** E_DB_FATAL Operation failed (e.g. out of memory) ** Exceptions: ** none ** ** Side Effects: ** Deallocates memory. ** ** History: ** 27-dec-85 (jeff) ** written ** 6-may-87 (daved) ** lock memory stream if not already locked. ** This condition can happen if, for example, a syntax error ** is discovered after psq_tout has been called and we now ** want to delete the memory stream. */ DB_STATUS psf_mclose( PSS_SESBLK *sess_cb, PSF_MSTREAM *stream, DB_ERROR *err_blk) { i4 err_code; DB_STATUS status; if (stream->psf_mlock == 0) { status = psf_mlock(sess_cb, stream, err_blk); if (status != E_DB_OK) return (status); } sess_cb->pss_qsf_rcb.qsf_obj_id.qso_handle = stream->psf_mstream.qso_handle; sess_cb->pss_qsf_rcb.qsf_lk_id = stream->psf_mlock; status = qsf_call(QSO_DESTROY, &sess_cb->pss_qsf_rcb); if (status != E_DB_OK) { (VOID) psf_error(E_PS0A06_CANTFREE, sess_cb->pss_qsf_rcb.qsf_error.err_code, PSF_INTERR, &err_code, err_blk, 0); return (status); } return (E_DB_OK); }
/* ** Name psl_lm2_setlockkey() - perform semantic action for SETLOCKKEY production ** ** Description: ** perform semantic action for SETLOCKKEY production in QUEL and SQL ** grammars ** ** Input: ** sess_cb PSF session CB ** pss_distrib DB_3_DDB_SESS if distributed thread ** pss_stmt_flags PSS_SET_LOCKMODE_SESS if SET LOCKMODE SESSION ** (distributed thread only) ** char_name name of a characteristic ** ** Output: ** char_type a number corresponding to this characteristic type ** err_blk filled in if an error occurred ** ** Returns: ** E_DB_{OK, ERROR} ** ** Side effects: ** none ** ** History: ** 07-mar-91 (andre) ** plagiarized from SETLOCKKEY production ** 20-apr-92 (barbara) ** Updated for Sybil. Added session cb to interface. For the ** distributed thread, TIMEOUT is the only option supported on a ** SESSION basis. ** 06-oct-93 (barbara) ** Fixed bug 53492. Star now supports all set lockmode session ** statements. ** 04-apr-1995 (dilma04) ** Add support for READLOCK=READ_COMMITTED/REPEATABLE_READ. ** 14-oct-97 (stial01) ** psl_lm2_setlockkey() Isolation levels are not valid readlock values. ** */ DB_STATUS psl_lm2_setlockkey( PSS_SESBLK *sess_cb, char *char_name, i4 *char_type, DB_ERROR *err_blk) { i4 err_code; /* Decode "set lockmode" parameter. Error if unknown. */ if (!STcompare(char_name, "level")) { *char_type = LOCKLEVEL; } else if (!STcompare(char_name, "readlock")) { *char_type = READLOCK; } else if (!STcompare(char_name, "maxlocks")) { *char_type = MAXLOCKS; } else if (!STcompare(char_name, "timeout")) { *char_type = TIMEOUT; } else { (VOID) psf_error(5928L, 0L, PSF_USERERR, &err_code, err_blk, 1, (i4) STlength(char_name), char_name); return (E_DB_ERROR); /* non-zero return means error */ } return(E_DB_OK); }
/*{ ** 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: psq_topen - Open a query text chain ** ** Description: ** This function opens a query text chain by opening a memory stream, ** allocating a header, and filling it in. ** ** Inputs: ** header Place to put pointer to header ** memleft Pointer to memory left ** err_blk Filled in if an error happens ** ** Outputs: ** header Filled in with pointer to header ** 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 ** 02-sep-86 (seputis) ** err_blk becomes a DB_ERROR * */ DB_STATUS psq_topen( PTR *header, SIZE_TYPE *memleft, DB_ERROR *err_blk) { ULM_RCB ulm_rcb; DB_STATUS status; i4 err_code; PSQ_THEAD *hp; extern PSF_SERVBLK *Psf_srvblk; /* Open the stream and allocate memory for the header */ ulm_rcb.ulm_facility = DB_PSF_ID; ulm_rcb.ulm_poolid = Psf_srvblk->psf_poolid; ulm_rcb.ulm_blocksize = 512; ulm_rcb.ulm_memleft = memleft; ulm_rcb.ulm_streamid_p = &ulm_rcb.ulm_streamid; ulm_rcb.ulm_flags = ULM_SHARED_STREAM | ULM_OPEN_AND_PALLOC; ulm_rcb.ulm_psize = sizeof(PSQ_THEAD); if ((status = ulm_openstream(&ulm_rcb)) != E_DB_OK) { if (ulm_rcb.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_PS0370_OPEN_TEXT_CHAIN, ulm_rcb.ulm_error.err_code, PSF_INTERR, &err_code, err_blk, 0); return (status); } *header = ulm_rcb.ulm_pptr; /* Fill in the header */ hp = (PSQ_THEAD*) ulm_rcb.ulm_pptr; hp->psq_first = (PSQ_TEXT *) NULL; hp->psq_last = (PSQ_TEXT *) NULL; hp->psq_tsize = 0; STRUCT_ASSIGN_MACRO(ulm_rcb, hp->psq_tmem); hp->psq_tmem.ulm_streamid_p = &hp->psq_tmem.ulm_streamid; return (E_DB_OK); }
/*{ ** 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); }
/*{ ** Name: psf_malloc - Allocate memory from a stream. ** ** Description: ** This function allocates memory from a memory stream. If there is room ** in the current block, it will use it. If not, it will allocate another ** block and use that. ** ** Inputs: ** sess_cb Ptr to session's CB. ** stream Pointer to the stream to allocate from. ** size Number of bytes to allocate ** result Place to put pointer to allocated memory ** err_blk Place to put error information. ** ** Outputs: ** result Filled in with pointer to alloc. memory ** err_blk Filled in with error information. ** E_PS0A05_BADMEMREQ Bad memory request ** ** Returns: ** E_DB_OK Operation succeeded. ** E_DB_ERROR Error by caller (e.g. bad parameter). ** E_DB_FATAL Operation failed (e.g. out of memory) ** Exceptions: ** none ** ** Side Effects: ** Can allocate memory. ** ** History: ** 27-dec-85 (jeff) ** written */ DB_STATUS psf_malloc( PSS_SESBLK *sess_cb, PSF_MSTREAM *stream, i4 size, void *result, DB_ERROR *err_blk) { i4 err_code; DB_STATUS status; /* ** Caller error if non-positive request. */ if (size <= 0) { psf_error(E_PS0A05_BADMEMREQ, 0L, PSF_INTERR, &err_code, err_blk, 0); *(void **) result = NULL; return (E_DB_SEVERE); } /* Allocate the object from QSF */ sess_cb->pss_qsf_rcb.qsf_obj_id.qso_handle = stream->psf_mstream.qso_handle; sess_cb->pss_qsf_rcb.qsf_lk_id = stream->psf_mlock; sess_cb->pss_qsf_rcb.qsf_sz_piece = size; status = qsf_call(QSO_PALLOC, &sess_cb->pss_qsf_rcb); if (status != E_DB_OK) { i4 qerr = sess_cb->pss_qsf_rcb.qsf_error.err_code; if (qerr == E_QS0001_NOMEM) (VOID) psf_error(qerr, qerr, PSF_CALLERR, &err_code, err_blk, 0); else (VOID) psf_error(E_PS0A05_BADMEMREQ, qerr, PSF_INTERR, &err_code, err_blk, 0); *(void **) result = NULL; return (E_DB_ERROR); } *(void **) result = sess_cb->pss_qsf_rcb.qsf_piece; return (E_DB_OK); }
/*{ ** Name: psf_umalloc - Allocate memory from ULM. ** ** Description: ** General purpose interface into allocating from ULM for the current ** session. ** ** Inputs: ** sess_cb Session control block pointer. ** mstream ULM stream id. ** msize Size of memory. ** ** Outputs: ** sess_cb Session control block: ** .pss_memleft Memory left in current session. ** mresult Pointer to result object. ** err_blk.err_code Error returned ** E_PS0F02_MEMORY_FULL No more memory for session ** E_PS0A02_BADALLOC Some other ULM error ** ** Returns: ** status return by ULM ** E_DB_FATAL If memory corrupted. ** ** Exceptions: ** none ** ** Side Effects: ** Allocates memory. ** ** History: ** 19-apr-89 (neil) ** Written */ DB_STATUS psf_umalloc( PSS_SESBLK *sess_cb, PTR mstream, i4 msize, PTR *mresult, DB_ERROR *err_blk) { DB_STATUS status; ULM_RCB ulm_rcb; i4 err_code; ulm_rcb.ulm_facility = DB_PSF_ID; ulm_rcb.ulm_streamid_p = &mstream; ulm_rcb.ulm_psize = msize; ulm_rcb.ulm_memleft = &sess_cb->pss_memleft; status = ulm_palloc(&ulm_rcb); if (status != E_DB_OK) { if (ulm_rcb.ulm_error.err_code == E_UL0005_NOMEM) { _VOID_ psf_error(E_PS0F02_MEMORY_FULL, 0L, PSF_CALLERR, &err_code, err_blk, 0); } else { _VOID_ psf_error(E_PS0A02_BADALLOC, 0L, PSF_INTERR, &err_code, err_blk, 0); } if (ulm_rcb.ulm_error.err_code == E_UL0004_CORRUPT) status = E_DB_FATAL; *mresult = NULL; } else { *mresult = ulm_rcb.ulm_pptr; } return (status); } /* psf_umalloc */
/* 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); }
/*{ ** Name: psq_tclose - Close the memory stream for a text chain ** ** Description: ** This function closes the memory stream for a text chain, deallocating ** the memory. ** ** Inputs: ** header Pointer to header of text chain ** err_blk Filled in if an error happens ** ** Outputs: ** 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: ** Deallocates memory ** ** History: ** 18-jul-86 (jeff) ** written */ DB_STATUS psq_tclose( PTR header, DB_ERROR *err_blk) { DB_STATUS status; ULM_RCB ulm_rcb; i4 err_code; STRUCT_ASSIGN_MACRO(((PSQ_THEAD*)header)->psq_tmem, ulm_rcb); status = ulm_closestream(&ulm_rcb); if (status != E_DB_OK) { (VOID) psf_error(E_PS0372_CLOSE_TEXT_CHAIN, ulm_rcb.ulm_error.err_code, PSF_INTERR, &err_code, err_blk, 0); return (status); } return (E_DB_OK); }
/*{ ** Name: psf_mroot - Set the root for a memory stream ** ** Description: ** This function sets the root for a memory stream. When the next ** facility looks at the object allocated by the stream, it can ask ** for the root. ** ** Inputs: ** sess_cb Ptr to session's CB. ** mstream Pointer to the memory stream ** root Pointer to the root of the object ** ** Outputs: ** None ** Returns: ** E_DB_OK Success ** E_DB_ERROR Non-catastrophic failure ** E_DB_FATAL Catastrophic failure ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 30-may-86 (jeff) ** written */ DB_STATUS psf_mroot( PSS_SESBLK *sess_cb, PSF_MSTREAM *mstream, PTR root, DB_ERROR *err_blk) { DB_STATUS status; i4 err_code; sess_cb->pss_qsf_rcb.qsf_obj_id.qso_handle = mstream->psf_mstream.qso_handle; sess_cb->pss_qsf_rcb.qsf_root = root; sess_cb->pss_qsf_rcb.qsf_lk_id = mstream->psf_mlock; status = qsf_call(QSO_SETROOT, &sess_cb->pss_qsf_rcb); if (status != E_DB_OK) { (VOID) psf_error(E_PS0A05_BADMEMREQ, sess_cb->pss_qsf_rcb.qsf_error.err_code, PSF_INTERR, &err_code, err_blk, 0); return (status); } return (E_DB_OK); }
/*{ ** Name: psy_ckdbpr - Check database privileges for specific values ** ** INTERNAL PSF call format: status = psy_ckdbpr(dbprmask) ** ** Description: ** This procedure checks the session's database privileges mask ** to ensure the specified privileges are allowed. ** ** Inputs: ** psq_cb ** .psq_error error code ** dbprmask privileges required ** ** Outputs: ** None. ** Returns: ** E_DB_OK the session has the required privileges. ** E_DB_ERROR the session lacks one or more of the ** required privileges. ** E_DB_SEVERE scu_information() failed ** Exceptions: ** none ** ** Side Effects: ** None. ** ** History: ** 24-may-89 (ralph) ** written */ DB_STATUS psy_ckdbpr( PSQ_CB *psq_cb, u_i4 dbprmask) { u_i4 dbprivs; SCF_CB scf_cb; SCF_SCI sci_list[1]; /* This code is called for SQL only */ /* Make sure user is authorized */ scf_cb.scf_length = sizeof (SCF_CB); scf_cb.scf_type = SCF_CB_TYPE; scf_cb.scf_facility = DB_PSF_ID; scf_cb.scf_session = DB_NOSESSION; scf_cb.scf_ptr_union.scf_sci = (SCI_LIST *) sci_list; scf_cb.scf_len_union.scf_ilength = 1; sci_list[0].sci_length = sizeof(dbprivs); sci_list[0].sci_code = SCI_DBPRIV; sci_list[0].sci_aresult = (char *) &dbprivs; sci_list[0].sci_rlength = NULL; if (scf_call(SCU_INFORMATION, &scf_cb) != E_DB_OK) { (VOID) psf_error(E_PS0D13_SCU_INFO_ERR, 0L, PSF_INTERR, &scf_cb.scf_error.err_code, &psq_cb->psq_error, 0); return(E_DB_SEVERE); } if (dbprmask != (dbprmask & dbprivs)) return(E_DB_ERROR); else return(E_DB_OK); }
/*{ ** Name: psf_munlock - Unlock a QSO object stream ** ** Description: ** This function unlocks the specified QSO object. ** ** Inputs: ** sess_cb Ptr to session's CB. ** mstream Pointer to the memory stream ** .psf_mstream.qso_handle Object handle ** err_block Pointer to error control block ** ** Outputs: ** mstream Pointer to the memory stream ** .psf_mlock Lock id returned by QSF ** err_blk Filled in with error information. ** E_PS0B05_CANT_UNLOCK Error when unlocking object type. ** ** Returns: ** E_DB_OK Success ** E_DB_ERROR Non-catastrophic failure ** ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 29-nov-93 (rblumer) ** Written. */ DB_STATUS psf_munlock( PSS_SESBLK *sess_cb, PSF_MSTREAM *mstream, DB_ERROR *err_blk) { DB_STATUS status; i4 err_code; sess_cb->pss_qsf_rcb.qsf_lk_id = mstream->psf_mlock; STRUCT_ASSIGN_MACRO(mstream->psf_mstream, sess_cb->pss_qsf_rcb.qsf_obj_id); status = qsf_call(QSO_UNLOCK, &sess_cb->pss_qsf_rcb); if (status != E_DB_OK) { (VOID) psf_error(E_PS0B05_CANT_UNLOCK, sess_cb->pss_qsf_rcb.qsf_error.err_code, PSF_INTERR, &err_code, err_blk, 0); return (status); } mstream->psf_mlock = 0; return (E_DB_OK); } /* end psf_munlock */
/*{ ** Name: psf_mlock - Lock an QSO object stream ** ** Description: ** This function locks the specified QSO object. ** ** Inputs: ** sess_cb Ptr to session's CB. ** mstream Pointer to the memory stream ** .psf_mstream.qso_handle Object handle ** err_block Pointer to error control block ** ** Outputs: ** mstream Pointer to the memory stream ** .psf_mlock Lock id returned by QSF ** err_blk Filled in with error information. ** E_PS0A08_CANTLOCK Error when locking object type. ** ** Returns: ** E_DB_OK Success ** E_DB_ERROR Non-catastrophic failure ** ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 03-apr-87 (puree) ** Written. */ DB_STATUS psf_mlock( PSS_SESBLK *sess_cb, PSF_MSTREAM *mstream, DB_ERROR *err_blk) { DB_STATUS status; i4 err_code; sess_cb->pss_qsf_rcb.qsf_obj_id.qso_handle = mstream->psf_mstream.qso_handle; sess_cb->pss_qsf_rcb.qsf_lk_state = QSO_EXLOCK; sess_cb->pss_qsf_rcb.qsf_obj_id.qso_lname = 0; status = qsf_call(QSO_LOCK, &sess_cb->pss_qsf_rcb); if (status != E_DB_OK) { (VOID) psf_error(E_PS0A08_CANTLOCK, sess_cb->pss_qsf_rcb.qsf_error.err_code, PSF_INTERR, &err_code, err_blk, 0); return (status); } mstream->psf_mlock = sess_cb->pss_qsf_rcb.qsf_lk_id; return (E_DB_OK); } /* end psf_mlock */
/*{ ** Name: psf_mchtyp - Change object type for a memory stream. ** ** Description: ** This function changes the object type for a memory stream. ** ** Inputs: ** sess_cb Ptr to session's CB. ** mstream Pointer to the memory stream ** newtype New type for the object ** err_block Pointer to error control block ** ** Outputs: ** err_blk Filled in with error information. ** E_PS0A07_CANTCHANGE Error when changing object type. ** ** Returns: ** E_DB_OK Success ** E_DB_ERROR Non-catastrophic failure ** ** Exceptions: ** none ** ** Side Effects: ** none ** ** History: ** 18-mar-87 (stec) ** Written. */ DB_STATUS psf_mchtyp( PSS_SESBLK *sess_cb, PSF_MSTREAM *mstream, i4 objtype, DB_ERROR *err_blk) { DB_STATUS status; i4 err_code; sess_cb->pss_qsf_rcb.qsf_obj_id.qso_handle = mstream->psf_mstream.qso_handle; sess_cb->pss_qsf_rcb.qsf_obj_id.qso_type = objtype; sess_cb->pss_qsf_rcb.qsf_obj_id.qso_lname = 0; sess_cb->pss_qsf_rcb.qsf_lk_id = mstream->psf_mlock; status = qsf_call(QSO_CHTYPE, &sess_cb->pss_qsf_rcb); if (status != E_DB_OK) { (VOID) psf_error(E_PS0A07_CANTCHANGE, sess_cb->pss_qsf_rcb.qsf_error.err_code, PSF_INTERR, &err_code, err_blk, 0); return (status); } return (E_DB_OK); }
/*{ ** Name: psy_cproc - Create a database procedure definition in ** the system catalogs. ** ** Description: ** ** Inputs: ** ** Outputs: ** Exceptions: ** none ** ** Side Effects: ** Modifies system catalogs. ** ** History: ** 27-apr-88 (stec) ** Created. ** 23-aug-88 (stec) ** Added comments, initialize new fields in DB_PROCEDURE. ** 26-apr-89 (andre) ** For internal procedures, set db_mask[0] to DB_IPROC. ** 12-mar-90 (andre) ** set rdr_2types_mask to 0. ** 22-may-90 (teg) ** init rdr_instr to RDF_NO_INSTR ** 12-jun-90 (andre) ** when trying to destroy a dbproc QEP, use "private" alias, which is ** always defined as opposed to the "public" alias which would not be ** defined if the dbproc is not grantable. ** 04-mar-92 (andre) ** set DB_ACTIVE_DBP in db_mask[0] to indicate that the dbproc is not ** abandoned. If the dbproc is grantable, set DB_DBPGRANT_OK in ** db_mask[0]. ** 18-may-92 (andre) ** call psy_dbp_status() to verify that the new dbproc is not abandoned ** and trust psy_dbp_status() to determine whether the dbproc is ** grantable or just active. ** 19-may-92 (andre) ** in pslsgram.yi, pss_dependencies_stream was opened to collect info ** about objects/privileges on which the new dbproc depends; ** this stream must be closed before leaving this function since the ** dependence information will be of no use once we return ** 01-jun-92 (andre) ** pass information about objects/privileges on which the new database ** procedure depends to QEF. ** 26-jun-92 (andre) ** enter information about the dbproc into IIPROCEDURE and the list of ** objects and privileges on which it depends into IIDBDEPENDS and ** IIPRIV respectively before calling psy_dbp_status() to determine ** whether it is active. This is necessary since otherwise it would be ** impossible to create mutually recursive database procedures (if P1 ** calls P2 and we are trying to create P2 calling P1, psy_dbp_status() ** will report that P1 is dormant and prevent us from creating P2) ** ** Since we won't know whether the dbproc is active and/or grantable ** until psy_dbp_status() is done, we will set only DB_DBP_INDEP_LIST ** bit in IIPROCEDURE.dbp_mask1 here, unless the independent ** object/privilege list is empty, in which case there is no reason to ** call psy_dbp_status(), so we will set DB_DBPGRANT_OK and ** DB_ACTIVE_DBP bits in IIPROCEDURE.dbp_mask1. ** ** If the independent object/privilege list is not empty and the ** procedure is not dormant, psy_dbp_status() will set appropriate bits ** (DB_DBPGRANT_OK and/or DB_ACTIVE_DBP) in IIPROCEDURE.dbp_mask1 once ** it has determined whether the dbproc is active or grantable ** 09-sep-92 (andre) ** psy_dbp_status() no longer accepts or returns an indicator of ** whether cache had to be flushed in psy_dbp_priv_check() ** 07-nov-92 (andre) ** we will no longer create private aliases for dbproc QPs ** 22-feb-93 (rblumer) ** initialize new RDF proc_param variables for both normal and ** set-input procedures; reverse order of MEcopy and I4assign in ** QP cleanup code so that qsf id gets set up correctly. ** 13-apr-93 (andre) ** if creating a system-generated procedure, independent ** object/privilege list will be empty - QEF will insert IIDBDEPENDS ** tuple recording dependence of the dbproc on a constraint (for ** constraint-enforcing dbprocs) or view (for CHECK OPTION-enforcing ** dbprocs.) Here we will set bits indicating that there will be ** independent object list and that the dbproc is active but not ** grantable (we don't want the user to grant privileges on ** system-generated dbprocs) ** 01-sep-93 (andre) ** in the course of parsing a dbproc definition, we will try to ** determine id of a base table on which the dbproc depends; here we ** will copy it into proctuple.dbPdbp_ubt_id to ensure that it gets ** inserted into IIPROCEDURE ** 22-oct-93 (rblumer) ** normal procedures will now have their parameters inserted into the ** catalogs (previously just set-input procedures did); changed ** initialization of db_parameterCount, rdr_proc_param_cnt and ** rdr_proc_params to work for both types of procedures. ** 30-apr-94 (andre) ** fix for bug 61087: ** proctuple.db_mask[0] was being set before proctuple was MEfill'd ** with NULLCHAR; moved the line initializing proctuple.db_mask[0] ** below call to MEfill() ** 16-jun-94 (andre) ** Bug #64395 ** it is dangerous to cast a (char *) into a (DB_CURSOR_ID *) and then ** dereference the resulting pointer because the chat ptr may not be ** pointing at memory not aligned on an appopriate boundary. Instead, ** we will allocate a DB_CURSOR_ID structure on the stack, initialize ** it and MEcopy() it into the char array ** 19-june-06 (dougi) ** Add DBP_DATA_CHANGE flag for BEFORE trigger validation. ** 28-march-2008 (dougi) ** Changes to support table procedures and named result row elements. ** 4-feb-2009 (dougi) ** Tidy up computation of table procedure result row length. ** 30-mar-2009 (toumi01) b121871 ** Rewrite and simplify the computation of table procedure input ** and output parameter count and width (fixes a logic error that ** caused the result width to be decremented by the width of the ** first result parameter when there were no input parameters). */ DB_STATUS psy_cproc( PSY_CB *psy_cb, PSS_SESBLK *sess_cb) { RDF_CB rdf_cb; register RDR_RB *rdf_rb = &rdf_cb.rdf_rb; i4 textlen; QSF_RCB qsf_rb; DB_PROCEDURE proctuple; DB_STATUS status, stat; i4 err_code; PSY_TBL dbp_descr; i4 dbp_mask[2]; bool empty_indep_list, rowproc; /* If this is a recreate case, and this code gets called ** just return since there is no work to be done, not even ** recovery of resources. */ if (sess_cb->pss_dbp_flags & PSS_RECREATE) return(E_DB_OK); /* determine if the new dbproc depends on any object or privileges */ if ( sess_cb->pss_indep_objs.psq_objs || sess_cb->pss_indep_objs.psq_objprivs || sess_cb->pss_indep_objs.psq_colprivs ) { empty_indep_list = FALSE; } else { empty_indep_list = TRUE; } /* ** Initialize the header of the QSF control block ** NOTE: it is important that we init qsf_rb before calling ** psy_dbp_status() - if the dbproc cannot be created, code under ** exit: will expect the control block set up for deleting the query ** text QSF object and the query plan QSF 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(psy_cb->psy_qrytext, qsf_rb.qsf_obj_id); /* Retrieve info about the procedure text object */ status = qsf_call(QSO_INFO, &qsf_rb); if (DB_FAILURE_MACRO(status)) { goto exit; } /* Get the text length. */ MEcopy((PTR) qsf_rb.qsf_root, sizeof(i4), (PTR) &textlen); /* Initialize the II_PROCEDURE tuple. */ MEfill(sizeof(proctuple), NULLCHAR, (PTR) &proctuple); (VOID) MEcopy((PTR)&psy_cb->psy_tabname[0], sizeof(DB_DBP_NAME), (PTR)&proctuple.db_dbpname); /* Current user is the owner. */ STRUCT_ASSIGN_MACRO(sess_cb->pss_user, proctuple.db_owner); proctuple.db_txtlen = textlen; TMnow((SYSTIME *) &proctuple.db_txtid); /* ** if the independent object/privilege list is non-empty, we will set ** DB_DBP_INDEP_LIST bit and leave it to psy_dbp_status() to set the ** remaining bits, if appropriate; otherwise we will set DB_DBPGRANT_OK and ** DB_ACTIVE_DBP bits and avoid calling psy_dbp_status() ** ** if creating a system-generated dbproc, mark the dbproc ACTIVE and ** indicate that there is independent object list (there is not one at this ** point, but QEF will insert IIDBDEPENDS tuple recording dependence of the ** dbproc on a constraint (for constraint-enforcing dbprocs) or view (for ** CHECK OPTION-enforcingdbprocs.) */ /* If working on internal dbproc, set DB_IPROC in db_mask[0] */ if (sess_cb->pss_dbp_flags & PSS_IPROC) proctuple.db_mask[0] |= DB_IPROC; if (sess_cb->pss_dbp_flags & PSS_SYSTEM_GENERATED) proctuple.db_mask[0] |= DBP_SYSTEM_GENERATED | DB_ACTIVE_DBP | DB_DBP_INDEP_LIST; else if (empty_indep_list) proctuple.db_mask[0] |= DB_DBPGRANT_OK | DB_ACTIVE_DBP; else proctuple.db_mask[0] |= DB_DBP_INDEP_LIST; if (sess_cb->pss_dbp_flags & PSS_SET_INPUT_PARAM) proctuple.db_mask[0] |= DBP_SETINPUT; if (sess_cb->pss_dbp_flags & PSS_NOT_DROPPABLE) proctuple.db_mask[0] |= DBP_NOT_DROPPABLE; if (sess_cb->pss_dbp_flags & PSS_SUPPORTS_CONS) proctuple.db_mask[0] |= DBP_CONS; if (sess_cb->pss_dbp_flags & PSS_DATA_CHANGE) proctuple.db_mask[0] |= DBP_DATA_CHANGE; if (sess_cb->pss_dbp_flags & PSS_OUT_PARMS) proctuple.db_mask[0] |= DBP_OUT_PARMS; if (sess_cb->pss_dbp_flags & PSS_TX_STMT) proctuple.db_mask[0] |= DBP_TX_STMT; if (sess_cb->pss_dbp_flags & PSS_ROW_PROC) { rowproc = TRUE; proctuple.db_mask[0] |= DBP_ROW_PROC; } else rowproc = FALSE; proctuple.db_mask[1] = 0; /* ** if we were able to determine id of a base table on which this dbproc will ** depend, copy it into proctuple */ proctuple.db_dbp_ubt_id.db_tab_base = sess_cb->pss_dbp_ubt_id.db_tab_base; proctuple.db_dbp_ubt_id.db_tab_index = sess_cb->pss_dbp_ubt_id.db_tab_index; /* db_procid to be filled in by RDF or QEF */ proctuple.db_parameterCount = 0; proctuple.db_recordWidth = 0; proctuple.db_rescolCount = 0; proctuple.db_resrowWidth = 0; if (sess_cb->pss_procparmlist != (QEF_DATA *) NULL) { DB_PROCEDURE_PARAMETER *param_tuple; QEF_DATA *listptr; /* compute count and total width of input and result parameters */ for (listptr = sess_cb->pss_procparmlist; listptr != (QEF_DATA *) NULL; listptr = listptr->dt_next) { param_tuple = (DB_PROCEDURE_PARAMETER *)listptr->dt_data; if (param_tuple->dbpp_flags & DBPP_RESULT_COL) { proctuple.db_rescolCount++; proctuple.db_resrowWidth = param_tuple->dbpp_offset + param_tuple->dbpp_length; } else { proctuple.db_parameterCount++; proctuple.db_recordWidth = param_tuple->dbpp_offset + param_tuple->dbpp_length; } } if (proctuple.db_rescolCount > 0) proctuple.db_resrowWidth -= proctuple.db_recordWidth; } /* Initialize the RDF request block. */ pst_rdfcb_init(&rdf_cb, sess_cb); (VOID) MEcopy((PTR)&psy_cb->psy_tabname[0], sizeof(DB_DBP_NAME), (PTR)&rdf_rb->rdr_name.rdr_prcname); STRUCT_ASSIGN_MACRO(sess_cb->pss_user, rdf_rb->rdr_owner); rdf_rb->rdr_types_mask = RDR_PROCEDURE; rdf_rb->rdr_update_op = RDR_APPEND; rdf_rb->rdr_qrytuple = (PTR) &proctuple; rdf_rb->rdr_l_querytext = textlen; rdf_rb->rdr_querytext = ((char *) qsf_rb.qsf_root) + sizeof(i4); /* ** pass information about objects/privileges on which the new database ** procedure depends to QEF */ sess_cb->pss_indep_objs.psq_grantee = &sess_cb->pss_user; rdf_rb->rdr_indep = (PTR) &sess_cb->pss_indep_objs; /* fill in the description of the procedure's parameters ** (which RDF/QEF will store into iiprocedure_parameter); */ rdf_rb->rdr_proc_param_cnt = proctuple.db_parameterCount + proctuple.db_rescolCount; rdf_rb->rdr_proc_params = sess_cb->pss_procparmlist; /* Create a new procedure in the system catalogs */ status = rdf_call(RDF_UPDATE, (PTR) &rdf_cb); if (DB_FAILURE_MACRO(status)) { if (rdf_cb.rdf_error.err_code == E_RD0137_DUPLICATE_PROCS) { /* Retry */ psy_cb->psy_error.err_code = E_PS0008_RETRY; } else { (VOID) psf_rdf_error(RDF_UPDATE, &rdf_cb.rdf_error, &psy_cb->psy_error); } goto exit; } /* ** if the independent object/privilege list is non-empty, verify that the ** dbproc we are about to create is not abandoned; strictly speaking, as ** long as the independent object list is empty, we are guaranteed that the ** dbproc is not abandoned (user posesses required privileges), but we will ** take an extra step and try to determine whether it is grantable */ if (!empty_indep_list) { MEcopy((PTR) psy_cb->psy_tabname, sizeof(DB_DBP_NAME), (PTR) &dbp_descr.psy_tabnm); dbp_descr.psy_tabid.db_tab_base = proctuple.db_procid.db_tab_base; dbp_descr.psy_tabid.db_tab_index = proctuple.db_procid.db_tab_index; status = psy_dbp_status(&dbp_descr, sess_cb, (PSF_QUEUE *) NULL, (i4) PSQ_CREDBP, dbp_mask, &psy_cb->psy_error); if (DB_FAILURE_MACRO(status)) { goto exit; } } exit: /* ** close the memory stream which was used to allocate descriptors of ** objects/privileges on which the new dbproc depends */ stat = psf_mclose(sess_cb, sess_cb->pss_dependencies_stream, &psy_cb->psy_error); if (DB_FAILURE_MACRO(stat) && stat > status) status = stat; /* ** ensure that no one tries to use the stream that is no longer valid */ sess_cb->pss_dependencies_stream = (PSF_MSTREAM *) NULL; /* Get a lock on the query text from QSF */ qsf_rb.qsf_lk_state = QSO_EXLOCK; stat = qsf_call(QSO_LOCK, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_error(E_PS0A08_CANTLOCK, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (stat > status) status = stat; } else { /* Now destroy the query text */ stat = qsf_call(QSO_DESTROY, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_error(E_PS0A09_CANTDESTROY, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (stat > status) status = stat; } } /* Destroy the procedure QP in QSF if things went wrong. */ if (DB_FAILURE_MACRO(status)) { PSS_DBPALIAS dbpid; DB_CURSOR_ID dbp_curs_id; qsf_rb.qsf_feobj_id.qso_type = QSO_ALIAS_OBJ; qsf_rb.qsf_feobj_id.qso_lname = sizeof(dbpid); /* Identify the object first */ dbp_curs_id.db_cursor_id[0] = dbp_curs_id.db_cursor_id[1] = 0; (VOID) MEcopy((PTR)&psy_cb->psy_tabname[0], DB_TAB_MAXNAME, (PTR)dbp_curs_id.db_cur_name); MEcopy((PTR) &dbp_curs_id, sizeof(DB_CURSOR_ID), (PTR) dbpid); (VOID) MEcopy((PTR) &sess_cb->pss_user, DB_OWN_MAXNAME, (PTR) (dbpid + sizeof(DB_CURSOR_ID))); I4ASSIGN_MACRO(sess_cb->pss_udbid, *(i4 *) (dbpid + sizeof(DB_CURSOR_ID) + DB_OWN_MAXNAME)); (VOID)MEcopy((PTR) dbpid, sizeof(dbpid), (PTR) qsf_rb.qsf_feobj_id.qso_name); /* See if QP for the alias already exists. */ qsf_rb.qsf_lk_state = QSO_SHLOCK; stat = qsf_call(QSO_JUST_TRANS, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { if (qsf_rb.qsf_error.err_code != E_QS0019_UNKNOWN_OBJ) { (VOID) psf_error(E_PS037A_QSF_TRANS_ERR, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (stat > status) status = stat; goto exit1; } else { /* QP disappeared, which is alright. */ goto exit1; } } /* Now destroy the QP object in QSF */ stat = qsf_call(QSO_DESTROY, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_error(E_PS0A09_CANTDESTROY, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (stat > status) status = stat; } } exit1: 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: psq_cbreturn - Clear call and session CB after processing. ** ** Description: ** This routine is a common routine that clears up the call (psq_cb) and ** session CB (sess_cb) after processing a query. The routine ** psq_parseqry still does its own work as it handles various errors ** associated with textual queries. ** ** Inputs: ** psq_cb Pointer to call CB. ** sess_cb Pointer to the session control block ** ** Outputs: ** psq_cb ** .psq_result Result query tree. ** Returns: ** DB_STATUS ** Exceptions: ** None ** ** Side Effects: ** None ** ** History: ** 21-apr-89 (neil) ** Extracted this from psq_parseqry to allow it to be called from ** other routines as well. ** 15-jun-92 (barbara) ** Sybil merge. Pass in sess control block to pst_clrrng. ** 25-may-1993 (rog) ** Moved clean-up/exit code here from psq_parseqry() above, and added ** status argument so that we know whether to execute the good exit ** code or the bad exit code. ** 10-aug-93 (andre) ** fixed cause of a compiler warning ** 27-aug-93 (andre) ** (part of fix for bug 54348) ** moved code responsible for destroying a dbproc QEP into a separate ** function (psq_destr_dbp_qep()) which will be called from ** psq_cbreturn() and from psq_recreate() if an error occurs AFTER the ** dbproc QEP QSF object has been created ** 16-mar-94 (andre) ** use psf_retry() to determine whether we are going to retry parsing ** this query and, therefore, whether we should destroy any QSF objects ** created during the just completed attempt ** 28-jan-2004 (schka24) ** Close partition def memory if open. ** 15-Mar-2006 (kschendel) ** Close function-arg stream if open. ** 28-nov-2007 (dougi) ** Different logic for PSQ_REPDYN (for cached dynamic queries). */ DB_STATUS psq_cbreturn( PSQ_CB *psq_cb, PSS_SESBLK *sess_cb, DB_STATUS ret_val) { DB_STATUS status; i4 err_code; QSF_RCB qsf_rb; 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; /* Tasks that are common to both a normal return and an error return. */ do /* Something to break out of */ { /* Clear any cached stack blocks as we're going to ** trash the memory stream in here anyway. */ sess_cb->pss_stk_freelist = NULL; /* Other stuff that shouldn't be left dangling */ sess_cb->pss_yyvars = NULL; /* ** Clear out the user's range table. */ status = pst_clrrng(sess_cb, &sess_cb->pss_usrrange, &psq_cb->psq_error); if (status == E_DB_FATAL) ret_val = status; /* ** Clear out the auxliary range table. */ status = pst_clrrng(sess_cb, &sess_cb->pss_auxrng, &psq_cb->psq_error); if (status == E_DB_FATAL) ret_val = status; /* ** If the statement has emitted query text, close the stream. */ if (sess_cb->pss_tchain != (PTR) NULL) { status = psq_tclose(sess_cb->pss_tchain, &psq_cb->psq_error); if (status == E_DB_FATAL) ret_val = status; sess_cb->pss_tchain = (PTR) NULL; } if (sess_cb->pss_tchain2 != (PTR) NULL) { status = psq_tclose(sess_cb->pss_tchain2, &psq_cb->psq_error); if (status == E_DB_FATAL) ret_val = status; sess_cb->pss_tchain2 = (PTR) NULL; } /* Close partition definition working memory stream if in use */ if (sess_cb->pss_ses_flag & PSS_PARTDEF_STREAM_OPEN) { status = ulm_closestream(&sess_cb->pss_partdef_stream); /* Toss any error */ sess_cb->pss_ses_flag &= ~PSS_PARTDEF_STREAM_OPEN; } /* Close nested-function-call arg list stack if it was needed */ if (sess_cb->pss_funarg_stream != NULL) { ULM_RCB ulm; ulm.ulm_facility = DB_PSF_ID; ulm.ulm_poolid = Psf_srvblk->psf_poolid; ulm.ulm_streamid_p = &sess_cb->pss_funarg_stream; ulm.ulm_memleft = &sess_cb->pss_memleft; status = ulm_closestream(&ulm); /* Ignore error */ sess_cb->pss_funarg_stream = NULL; } } while (0); for (; ret_val == E_DB_OK; ) /* Something to break out of */ { /* ** This is the path we take for a successful exit. ** If we have a problem here, we break out of this loop and ** fall through to the failure exit code. */ /* ** Set the QSF id of the return object and unlock it, if any. */ if (sess_cb->pss_ostream.psf_mstream.qso_handle != (PTR) NULL) { qsf_rb.qsf_obj_id.qso_handle = sess_cb->pss_ostream.psf_mstream.qso_handle; qsf_rb.qsf_lk_id = sess_cb->pss_ostream.psf_mlock; if (psq_cb->psq_mode == PSQ_REPDYN) { /* If cached dynamic qp already exists, destroy parse ** tree object. */ if (ret_val = qsf_call(QSO_DESTROY, &qsf_rb)) { (VOID) psf_error(E_PS0A09_CANTDESTROY, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psq_cb->psq_error, 0); /* break out to the failure code. */ break; } } else { /* If not cached dynamic, copy object ID and unlock ** parse tree. */ STRUCT_ASSIGN_MACRO(sess_cb->pss_ostream.psf_mstream, psq_cb->psq_result); if (ret_val = qsf_call(QSO_UNLOCK, &qsf_rb)) { (VOID) psf_error(E_PS0B05_CANT_UNLOCK, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psq_cb->psq_error, 0); /* break out to the failure code. */ break; } } sess_cb->pss_ostream.psf_mlock = 0; } /* ** Unlock the text stream if it was used. ** this is unlocked after the text stream is created. ** (Don't know why this was taken out.) */ #ifdef NO if (sess_cb->pss_tstream.psf_mstream.qso_handle != (PTR) NULL) { qsf_rb.qsf_obj_id.qso_handle = sess_cb->pss_tstream.psf_mstream.qso_handle; qsf_rb.qsf_lk_id = sess_cb->pss_tstream.psf_mlock; if (ret_val = qsf_call(QSO_UNLOCK, &qsf_rb)) { (VOID) psf_error(E_PS0B05_CANT_UNLOCK, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psq_cb->psq_error, 0); /* break out to the failure code. */ break; } sess_cb->pss_tstream.psf_mlock = 0; } #endif /* Unlock the control block stream if it was used */ if (sess_cb->pss_cbstream.psf_mstream.qso_handle != (PTR) NULL) { qsf_rb.qsf_obj_id.qso_handle = sess_cb->pss_cbstream.psf_mstream.qso_handle; qsf_rb.qsf_lk_id = sess_cb->pss_cbstream.psf_mlock; if (ret_val = qsf_call(QSO_UNLOCK, &qsf_rb)) { (VOID) psf_error(E_PS0B05_CANT_UNLOCK, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psq_cb->psq_error, 0); /* break out to the failure code. */ break; } sess_cb->pss_cbstream.psf_mlock = 0; } /* ** We need to deallocate memory if the same query is to be tried ** again. This is supposed to fix The 'drop table, nonexistent_table' ** memory leak problem. */ if (psf_retry(sess_cb, psq_cb, ret_val)) { if (sess_cb->pss_ostream.psf_mstream.qso_handle != (PTR) NULL) { (VOID) psf_mclose(sess_cb, &sess_cb->pss_ostream, &psq_cb->psq_error); sess_cb->pss_ostream.psf_mstream.qso_handle = (PTR) NULL; } if (sess_cb->pss_tstream.psf_mstream.qso_handle != (PTR) NULL) { (VOID) psf_mclose(sess_cb, &sess_cb->pss_tstream, &psq_cb->psq_error); sess_cb->pss_tstream.psf_mstream.qso_handle = (PTR) NULL; } if (sess_cb->pss_cbstream.psf_mstream.qso_handle != (PTR) NULL) { (VOID) psf_mclose(sess_cb, &sess_cb->pss_cbstream, &psq_cb->psq_error); sess_cb->pss_cbstream.psf_mstream.qso_handle = (PTR) NULL; } } /* end with no output streams */ sess_cb->pss_ostream.psf_mstream.qso_handle = (PTR) NULL; sess_cb->pss_tstream.psf_mstream.qso_handle = (PTR) NULL; sess_cb->pss_cbstream.psf_mstream.qso_handle = (PTR) NULL; if (ret_val == E_DB_OK) { /* ** this IF statement is here to keep acc from complaining - we would ** never reach this point unless ret_val was E_DB_OK */ return (ret_val); } } /* This is the failure exit code. */ /* ** On an error, the yacc error handling function will call psf_error. ** All that's necessary here is to return a status indicating that ** an error has occurred. If an output object has been allocated, ** and the error caused the statement to fail, deallocate the object ** before returning. Same for miscellaneous objects, like control ** blocks and query text. */ if (ret_val == E_DB_ERROR || ret_val == E_DB_FATAL || ret_val == E_DB_SEVERE) { /* ** In case of CREATE PROCEDURE statement we also need to ** destroy the QEP object in QSF if it was created. */ if (sess_cb->pss_ostream.psf_mstream.qso_handle != (PTR) NULL && psq_cb->psq_mode == PSQ_CREDBP ) { DB_STATUS stat; stat = psq_destr_dbp_qep(sess_cb, sess_cb->pss_ostream.psf_mstream.qso_handle, &psq_cb->psq_error); if (stat > ret_val) ret_val = stat; } if (sess_cb->pss_ostream.psf_mstream.qso_handle != (PTR) NULL) { (VOID) psf_mclose(sess_cb, &sess_cb->pss_ostream, &psq_cb->psq_error); sess_cb->pss_ostream.psf_mstream.qso_handle = (PTR) NULL; } if (sess_cb->pss_tstream.psf_mstream.qso_handle != (PTR) NULL) { (VOID) psf_mclose(sess_cb, &sess_cb->pss_tstream, &psq_cb->psq_error); sess_cb->pss_tstream.psf_mstream.qso_handle = (PTR) NULL; } if (sess_cb->pss_cbstream.psf_mstream.qso_handle != (PTR) NULL) { (VOID) psf_mclose(sess_cb, &sess_cb->pss_cbstream, &psq_cb->psq_error); sess_cb->pss_cbstream.psf_mstream.qso_handle = (PTR) NULL; } } /* ** If the statement was a "define cursor" or a "define repeat cursor", ** we have to deallocate the cursor control block, if it was allocated. */ if (sess_cb->pss_cstream != (PTR) NULL) { status = psq_delcursor(sess_cb->pss_crsr, &sess_cb->pss_curstab, &sess_cb->pss_memleft, &psq_cb->psq_error); if (status == E_DB_FATAL) ret_val = status; } return(ret_val); }
/*{ ** 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_qrymod - Process views, permits, and integrities ** ** Description: ** This function applies the qrymod algorithm to user queries. ** This algorithm is well-described in the specific functions ** psyview.c, psyinteg.c, and psypermit.c. It MUST be applied ** in the order: views, integrities, permits. This is because ** permits are on base tables, not on views, and we don't want ** the extra qualifications stuck on by the permit process to ** affect the integrities. ** ** Rules modifications are applied after all other query mods. ** Rules processing (in psyrules.c) may add statement trees to rule lists ** maintained in sess_cb. ** ** Inputs: ** qtree The query tree to modify ** sess_cb Current session control block ** psq_cb Calling control block ** num_joins ptr to a var containing number of joins ** in the query tree ** ** Outputs: ** qtree Can be modified by views, permits, ** and integrities ** sess_cb All kinds of miscellaneous stuff ** that is too detailed to describe here ** psq_cb ** .err_blk Filled in if an error happens ** ** *num_joins Will be incremented if any of the views ** involved in the query or any permits ** applied use joins. ** ** *resp_mask if the qmode==PSQ_VIEW will contain ** indicators of whether the view appears ** to be updateable and/or grantable view ** ** Returns: ** E_DB_OK Success ** E_DB_FATAL Failure ** Exceptions: ** none ** ** Side Effects: ** Can allocate memory ** Catalog I/O ** ** History: ** 09-jul-86 (jeff) ** Adapted from 4.0 qrymod() function ** 02-sep-86 (seputis) ** used result relation from auxiliary table since RDF ptrs are NULLed ** 18-sep-86 (daved) ** the result relation in the session cb is not always the ** same as the one found in the user range table. this is because ** the one in the range table is accurate only in an append statement. ** thus, we will now change the session cb to point to the correct ** result relation. ** 2-apr-87 (daved) ** set a pss_permit flag in the range table for each used entry. ** psy_permit will clear this flag. Psy_view will set this flag ** in all range entries that a derived from a table that still has ** this flag set, else it will clear this flag for new entries. ** We will then call psy_permit again, to catch all the new entries. ** 24-sep-87 (stec) ** Change error handling - call psf_error. ** 25-oct-87 (stec) ** Remove transaction creation, this is now done by SCF. ** 26-jan-89 (neil) ** Now also calls psy_rules to fill in rule tree. ** 08-mar-89 (neil) ** Calls psy_rules with more arguments. ** 22-jun-89 (andre) ** remove call to psy_protect() before call to psy_view() since ** psy_view() was modified to call psy_protect() before performing ** query modification on SQL views not belonging to the user. ** Additionally, &qtree will not be passed to psy_view(), since there ** is no apparent reason to do so (we no longer attempt to assign to ** the corresponding argument inside of psy_view() ) ** 24-jul-89 (jennifer) ** For each statement, indicate that audit information is NULL. ** 08-sep-89 (andre) ** recieve and pass to psy_view() and psy_permit() ptr to a var ** containing number of joins in the query tree. ** 19-sep-90 (andre) ** as a result of changes in psy_view(), we no longer need to call ** psy_permit() from psy_qrymod(). ** 13-dec-90 (ralph) ** Move reset of pss_audit to before psy_view invocation (b34903) ** 11-mar-91 (andre) ** if qmode is PSQ_VIEW, pass PSQ_RETRIEVE to psy_view(), psy_integ(), ** and psy_rules() + do not try to copy pss_usrrange to pss_auxrng ** since psy_dview() already placed the range table entries into ** pss_auxrng. ** (this is a part of fix for bug 33038) ** 07-aug-91 (andre) ** if qmode is PSQ_VIEW do NOT translate it to PSQ_RETRIEVE + ** if qmode is PSQ_VIEW do not bother calling psy_integ() and ** psy_rules() ** ** Added resp_mask to the argument list. This mask will be used to ** communicate info to the caller of psy_qrymod(). psy_qrymod() itself ** will not contribute anything to the information, instead, it will ** pass it to other functions (initially only to psy_view()) which will ** be responsible for providing the actual information. ** 30-dec-92 (andre) ** when processing a view tree in the course of processing CREATE RULE ** on a view we do NOT want to do any permit, rule, or integrity ** checking ** 04-jan-93 (andre) ** if we are processing DELETE, INSERT, or UPDATE, and a table or a ** view being updated has rules defined on it, set PSS_CHECK_RULES in ** sess_cb->pss_resrng->pss_var_mask to remind psy_view() to call ** psy_rules() ** ** since psy_dview() is going away, and psl_cview() calls psy_qrymod() ** with range variables as they were entered in the grammar, we must ** copy the range table from pss_usrrange to pss_auxrng for define VIEW ** just as we do for all other QUEL statements ** ** In most cases psy_rules() will NO LONGER be called from ** psy_qrymod(). It will be called from psy_view() when processing ** INSERT, DELETE, or UPDATE. This change was necessary because now ** rules can be defined on views, and it seemed reasonable that rule ** checking (just like privilege checking) can be driven from ** psy_view(). The once exception occurs when we are processing UPDATE ** WHERE CURRENT OF - psy_view() will simply return when presented with ** this query mode, but there may be rules that apply to this ** statement. Consequently, psy_rules() will be called from ** psy_qrymod() only when psq_qmode == PSQ_REPCURS ** 08-jan-93 (rblumer) ** set flag to bypass permission checking for dummy set-input ** range variables. ** 30-mar-93 (rblumer) ** set check_perms to false for system-generated procedures ** and for (internal) queries with PSS_NO_CHECKPERMS set. ** 01-apr-93 (andre) ** (fix for bugs 50823, 50825, and 50899) ** do not copy QUEL range table to aux. range table when processing ** REPLACE CURSOR - the entry describing the table/view over ** which the cursor has been defined has already been placed into ** pss_auxrng ** 08-apr-93 (andre) ** do not call psy_rules() explicitly for PSQ_REPCURS. psy_view() will ** now handle PSQ_REPCURS and call psy_rules() when necessary. ** 09-apr-93 (andre) ** set PSS_CHECK_RULES in the pss_var_mask corresponding to the ** table/view being updated via a cursor ** 26-apr-93 (markg) ** If the server is running C2 then initialize each range ** entry to show that it needs to be audited. ** 5-jul-93 (robf) ** If server is C2 then initialize any result range table to show ** it needs to be audited. ** 14-sep-93 (robf) ** Add QUERY_SYSCAT check, disallowing direct querying of ** base catalogs without privilege. This is done here rather than ** lower down in view/permit processing since we want to allow ** access to views on base catalogs (e.g. standard catalogs) ** to go ahead. ** 13-oct-93 (robf) ** Add <st.h> ** 25-oct-93 (stephenb) ** If the server is running C2 then also initialize the result range ** entry to show that it needs to be audited. ** 13-dec-93 (robf) ** When checking for query_syscat privilege, allow for extended ** catalogs. Some catalogs (like iistar_cdbs) have both the ** DMT_CATALOG and DMT_EXTENDED_CAT bits set. In this case we allow ** access since this priv restricts base catalogs only. ** 09-feb-94 (andre) ** fix for bug 59595: ** we also need to forego checking permits if we are parsing a ** system-generated query ** 11-jul-94 (robf) ** Restrict direct queries to table extensions the same way ** as system catalogs, i.e. to those with SELECT_SYSCAT privilege. ** The rationale for this is that info in extensions is really ** part of the base table, and to present an appropriately ** protected view of the data access should be through the base ** table, not directly at the extension. */ DB_STATUS psy_qrymod( PST_QNODE *qtree, PSS_SESBLK *sess_cb, PSQ_CB *psq_cb, PST_J_ID *num_joins, i4 *resp_mask) { PSS_RNGTAB *resrng; PSS_USRRANGE *rngtab = &sess_cb->pss_auxrng; PSF_MSTREAM *mstream = &sess_cb->pss_ostream; DB_ERROR *err_blk = &psq_cb->psq_error; i4 qmode = psq_cb->psq_mode; DB_STATUS status; i4 i; i4 check_perms = TRUE; GLOBALREF PSF_SERVBLK *Psf_srvblk; i4 err_code; bool can_query_syscat=FALSE; *resp_mask = 0L; /* init the response mask */ /* ** avoid permit checking when processing RULE trees, ** or when parsing/reparsing a system-generated procedure, ** or when parsing a system-generated query (e.g. a query to check whether ** constraints hold on existing data) */ if ((psq_cb->psq_mode == PSQ_RULE) || ( (sess_cb->pss_dbp_flags & PSS_DBPROC) && (sess_cb->pss_dbp_flags & PSS_SYSTEM_GENERATED)) || (psq_cb->psq_flag & PSQ_NO_CHECK_PERMS) || ( (psq_cb->psq_info != (PST_INFO *) NULL) && (psq_cb->psq_info->pst_execflags & PST_SYSTEM_GENERATED)) ) { check_perms = FALSE; } /* ** Can user directly query system catalogs ? ** Users with databse privilege QUERY_SYSCAT, or users with ** SECURITY subject privilege may directly query system cats */ if(psy_ckdbpr(psq_cb, DBPR_QUERY_SYSCAT)==E_DB_OK) can_query_syscat=TRUE; else if(sess_cb->pss_ustat&DU_USECURITY) can_query_syscat=TRUE; else can_query_syscat=FALSE; /* ** Reset pss_audit prior to calling psy_view. */ sess_cb->pss_audit = NULL; /* ** We don't want to clobber the user's view of the range table in ** the process of doing qrymod. Therefore, we will copy the user's ** range table to the auxiliary range table, and use that instead. */ /* don't need to copy range table for SQL because there are no range ** table entries that live longer than one statement ** ** REPLACE CURSOR places description of the table/view over which it is ** defined into pss_auxrng */ if ( sess_cb->pss_lang == DB_QUEL && qmode != PSQ_REPCURS) { status = pst_rgcopy(&sess_cb->pss_usrrange, rngtab, &sess_cb->pss_resrng, err_blk); if (DB_FAILURE_MACRO(status)) return (status); } for (i = 0; i < PST_NUMVARS; i++) { rngtab->pss_rngtab[i].pss_rgparent = -1; /* don't want to check permissions on ** set-input temporary tables */ if (rngtab->pss_rngtab[i].pss_rgtype == PST_SETINPUT) rngtab->pss_rngtab[i].pss_permit = FALSE; else rngtab->pss_rngtab[i].pss_permit = check_perms; /* ** See if this is a real base catalog. ** We special case iidbcapabilities since its marked as a base ** catalog, but gets directly queried (and needs to be ** accessed by frontends) */ if(rngtab->pss_rngtab[i].pss_used && rngtab->pss_rngtab[i].pss_rgno != -1 && rngtab->pss_rngtab[i].pss_tabdesc && (( (rngtab->pss_rngtab[i].pss_tabdesc->tbl_2_status_mask&DMT_TEXTENSION) ) || ( (rngtab->pss_rngtab[i].pss_tabdesc->tbl_status_mask&DMT_CATALOG) && !(rngtab->pss_rngtab[i].pss_tabdesc->tbl_status_mask&DMT_VIEW) && !(rngtab->pss_rngtab[i].pss_tabdesc->tbl_status_mask&DMT_EXTENDED_CAT) && STncasecmp((char*)&rngtab->pss_rngtab[i].pss_tabdesc->tbl_name, "iidbcapabilities", GL_MAXNAME)!=0 )) ) { /* ** This is a base catalog. */ if(!can_query_syscat) { /* ** Error, user can't query base catalogs. */ if ( Psf_srvblk->psf_capabilities & PSF_C_C2SECURE ) (VOID)psy_secaudit( FALSE, sess_cb, (char*)&rngtab->pss_rngtab[i].pss_tabdesc->tbl_name, &rngtab->pss_rngtab[i].pss_tabdesc->tbl_owner, DB_MAXNAME, SXF_E_SECURITY, I_SX273B_QUERY_SYSCAT, (SXF_A_SELECT|SXF_A_FAIL), err_blk); (VOID) psf_error(E_PS035A_CANT_QUERY_SYSCAT, 0L, PSF_USERERR, &err_code, err_blk, 1, psf_trmwhite(sizeof(DB_NAME), (char*)&rngtab->pss_rngtab[i].pss_tabdesc->tbl_name), (char*)&rngtab->pss_rngtab[i].pss_tabdesc->tbl_name); return E_DB_ERROR; } } /* ** If this is a C2 server set the PSS_DOAUDIT flag ** for this entry. */ if (Psf_srvblk->psf_capabilities & PSF_C_C2SECURE) rngtab->pss_rngtab[i].pss_var_mask |= PSS_DOAUDIT; } if (resrng = sess_cb->pss_resrng) { /* don't want to check permissions ** on set-input temporary tables */ if (rngtab->pss_rngtab[i].pss_rgtype == PST_SETINPUT) rngtab->pss_rngtab[i].pss_permit = FALSE; else rngtab->pss_rngtab[i].pss_permit = check_perms; /* ** if processing DELETE, UPDATE, INSERT, or UPDATE/REPLACE cursor, and ** there are rules defined on the view/table being updated, set ** PSS_CHECK_RULES in pss_var_mask */ if ( ( qmode == PSQ_APPEND || qmode == PSQ_DELETE || qmode == PSQ_REPLACE || qmode == PSQ_REPCURS ) && resrng->pss_tabdesc->tbl_status_mask & DMT_RULE ) { resrng->pss_var_mask |= PSS_CHECK_RULES; } /* ** If this is a C2 server set the PSS_DOAUDIT flag ** for the result table. */ if (Psf_srvblk->psf_capabilities & PSF_C_C2SECURE) rngtab->pss_rsrng.pss_var_mask |= PSS_DOAUDIT; } /* Apply view processing */ status = psy_view(mstream, qtree, rngtab, qmode, sess_cb, err_blk, num_joins, resp_mask); if (DB_FAILURE_MACRO(status)) { return (status); } /* ** do not check for existing INTEGRITIES when processing trees as a ** part of CREATE/DEFINE VIEW or CREATE RULE processing */ if (qmode != PSQ_VIEW && qmode != PSQ_RULE) { /* Apply integrity processing */ status = psy_integ(mstream, qtree, rngtab, resrng, qmode, sess_cb, &qtree, err_blk); if (DB_FAILURE_MACRO(status)) { return (status); } } 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: psy_gproc - Get a database procedure definition from ** the system catalogs. ** ** Description: ** ** Inputs: ** psq_cb ** psq_cursid ** db_cur_name dbproc name ** sess_cb ** pss_user current user name ** pss_dba dba name ** qsf_rb ** rdf_cb ** dbp_owner name of the owner whose dbproc will be ** looked up iff gproc_mask & PSS_DBP_BY_OWNER. ** gproc_mask mask used to specify the possible owners of ** the dbproc to look for ** PSS_USRDBP look for dbproc owned by the current user ** PSS_DBADBP look for dbproc owned by the DBA ** PSS_INGDBP look for dbproc owned by $INGRES ** PSS_DBP_BY_OWNER look for dbproc owned by the specific user ** ** NOTE: if PSS_DBP_BY_OWNER is set, ** PSS_USRDBP, PSS_DBADBP, and PSS_INGDBP ** will be disregarded ** otherwise, we expect that PSS_USRDBP ** will be always set, while PSS_DBADBP ** and PSS_INGDBP may or may not be set ** ** Outputs: ** alt_user set to point the name of dbproc owner if ** different from cb->pss_user ** psq_cb ** psq_error filled in if an error occurred ** ** ret_flags bits may be set to pass info to the caller ** PSS_MISSING_DBPROC dbproc not found ** ** Exceptions: ** none ** ** Returns ** E_DB_OK, E_DB_ERROR ** ** Side Effects: ** Allocates memory. ** ** History: ** 27-apr-88 (stec) ** Created. ** 04-aug-88 (stec) ** Improve recovery of resources. ** 17-aug-88 (stec) ** Change bad STRUCT_ASSIGN statements. ** 28-sep-88 (stec) ** Do not lock object on cleanup. ** 28-sep-88 (stec) ** Must not unfix RDF entry. ** 16-mar-89 (andre) ** Added a new parameter - alt_user. ** psy_gproc will no longer reset pss_user or set pss_ruset. Instead ** it may set the ptr passed to it to the name of the dbproc owner. ** 16-mar-89 (neil) ** Modified psy_gproc to access the procedure through the specified ** owner (psq_als_owner) if psq_alias_set is on. ** 27-apr-89 (andre) ** Further modify to search for dbprocs owned by $ingres if the search ** by user and DBA failed. The search part of the function has been, ** essentially, rewritten. ** 12-mar-90 (andre) ** set rdr_2types_mask to 0. ** 22-may-90 (teg) ** init rdr_instr to RDF_NO_INSTR ** 01-jun-90 (andre) ** Changed interface to allow caller to explicitly specify the possible ** owners of the dbproc to look for. ** 01-oct-91 (andre) ** Added ret_flags to the interface - this field will be used to pass ** additional info to the caller. in particular, it will allow us to ** signal certain classes of errors (e.g. dbproc not found) without ** setting return status to E_DB_ERROR, thus enabling the caller to ** distinguish such errors from unexpected errors ** 15-oct-1993 (rog) ** We need to use ulm_copy() instead of MEcopy() when copying query ** text that might be more than 64k. ** 12-jan-94 (andre) ** As a part of fix for bug 58048, we must remove the assumption that ** either PSS_USRDBP or PSS_DBP_BY_OWNER bit will always be set in ** gproc_mask. Now we will be prepared to handle PSS_DBP_BY_OWNER or ** one or more of PSS_USRDBP, PSS_DBADBP, and PSS_INGDBP. ** 13-jan-94 (andre) ** if we fail to find a dbproc, we will set PSS_MISSING_DBPROC in ** *ret_flags, but leave it up to the caller to issue an error ** message - this way the caller can decide whether this warrants an ** error message and if so can choose his favourite error message */ DB_STATUS psy_gproc( PSQ_CB *psq_cb, PSS_SESBLK *sess_cb, QSF_RCB *qsf_rb, RDF_CB *rdf_cb, DB_OWN_NAME **alt_user, DB_OWN_NAME *dbp_owner, i4 gproc_mask, i4 *ret_flags) { DB_STATUS status, stat; i4 err_code; RDD_QRYMOD *pinfo; PSQ_QDESC *qdesc; bool leave_loop = TRUE; DB_PROCEDURE *dbp; SXF_ACCESS access; i4 msgid; i4 local_status; *ret_flags = 0; /* First call RDF to retrieve the definition ** of the procedure. */ /* Initialize the RDF control block */ pst_rdfcb_init(rdf_cb, sess_cb); (VOID) MEcopy((PTR) psq_cb->psq_cursid.db_cur_name, sizeof (DB_DBP_NAME), (PTR) &rdf_cb->rdf_rb.rdr_name.rdr_prcname); rdf_cb->rdf_rb.rdr_types_mask = RDR_PROCEDURE | RDR_BY_NAME; /* assume that the dbproc is owned by the current user */ *alt_user = (DB_OWN_NAME *) NULL; do { if (gproc_mask & (PSS_USRDBP | PSS_DBP_BY_OWNER)) { if (gproc_mask & PSS_USRDBP) { STRUCT_ASSIGN_MACRO(sess_cb->pss_user, rdf_cb->rdf_rb.rdr_owner); } else { STRUCT_ASSIGN_MACRO((*dbp_owner), rdf_cb->rdf_rb.rdr_owner); if (MEcmp((PTR)&sess_cb->pss_user, (PTR) dbp_owner, sizeof(DB_OWN_NAME))) { *alt_user = dbp_owner; /* Try someone else */ } } /* Get the text */ status = rdf_call(RDF_GETINFO, (PTR) rdf_cb); /* ** We do not want to continue search if: ** 1) dbproc was found OR ** 2) we got an error other that PROC_NOT_FOUND OR ** 3) caller requested a dbproc owned by a specific user ** (gproc_mask & PSS_DBP_BY_OWNER) OR */ if ( DB_SUCCESS_MACRO(status) || rdf_cb->rdf_error.err_code != E_RD0201_PROC_NOT_FOUND || gproc_mask & PSS_DBP_BY_OWNER) { break; } } /* ** if we were told to check whether a dbproc is owned by the DBA, do so ** unless the DBA is the current user and we have already established ** that the current user does not own a dbproc with this name */ if ( gproc_mask & PSS_DBADBP && ( ~gproc_mask & PSS_USRDBP || MEcmp((PTR) &sess_cb->pss_user, (PTR) &sess_cb->pss_dba.db_tab_own, sizeof(DB_OWN_NAME)) ) ) { STRUCT_ASSIGN_MACRO(sess_cb->pss_dba.db_tab_own, rdf_cb->rdf_rb.rdr_owner); /* ** If we succeed and the DBA is different from the current user, ** the procedure text will have to be parsed in the context of ** dbproc's owner, i.e. DBA. Note that if we were also asked to ** check whether the dbproc is owned by the current user and still ** found ourselves here, DBA must be different from the current user */ if ( gproc_mask & PSS_USRDBP || MEcmp((PTR) &sess_cb->pss_user, (PTR) &sess_cb->pss_dba.db_tab_own, sizeof(DB_OWN_NAME))) { *alt_user = &sess_cb->pss_dba.db_tab_own; } /* Get the text */ status = rdf_call(RDF_GETINFO, (PTR) rdf_cb); /* ** We do not want to continue search if: ** 1) dbproc was found OR ** 2) we got an error other that PROC_NOT_FOUND */ if (DB_SUCCESS_MACRO(status) || rdf_cb->rdf_error.err_code != E_RD0201_PROC_NOT_FOUND) { break; } } if (gproc_mask & PSS_INGDBP) { MEmove(sizeof(*sess_cb->pss_cat_owner), (PTR)sess_cb->pss_cat_owner, ' ', DB_OWN_MAXNAME, (PTR) &rdf_cb->rdf_rb.rdr_owner); } else { /* ** if user has not requested that a dbproc owned by $INGRES be ** looked up, might as well get out of the loop */ break; } /* ** if we were told to check whether a dbproc is owned by $ingres, do so ** unless the $ingres is the current user and we have already ** established that the current user does not own a dbproc with this ** name or $ingres is the DBA and we have already established that the ** DBA does not own a dbproc with this name */ if ( ( ~gproc_mask & PSS_USRDBP || MEcmp((PTR)&sess_cb->pss_user, (PTR) &rdf_cb->rdf_rb.rdr_owner, sizeof(DB_OWN_NAME)) ) && ( ~gproc_mask & PSS_DBADBP || MEcmp((PTR) &sess_cb->pss_dba.db_tab_own, (PTR) &rdf_cb->rdf_rb.rdr_owner, sizeof(DB_OWN_NAME)) ) ) { /* ** If we succeed, the procedure text will have to be parsed in ** the context of the original owner, i.e. $ingres. */ *alt_user = &rdf_cb->rdf_rb.rdr_owner; /* ** If we succeed and $ingres is not the current user, the procedure ** text will have to be parsed in the context of dbproc's owner, ** i.e. $ingres. Note that if we were also asked to check whether ** the dbproc is owned by the current user and still found ourselves ** here, the current user must NOT be $ingres */ if ( gproc_mask & PSS_USRDBP || MEcmp((PTR) &sess_cb->pss_user, (PTR) &rdf_cb->rdf_rb.rdr_owner, sizeof(DB_OWN_NAME))) { *alt_user = &sess_cb->pss_dba.db_tab_own; } /* Get the text */ status = rdf_call(RDF_GETINFO, (PTR) rdf_cb); } /* leave_loop has already been set to TRUE */ } while (!leave_loop); if (DB_FAILURE_MACRO(status)) { if (rdf_cb->rdf_error.err_code == E_RD0201_PROC_NOT_FOUND) { /* ** dbproc was not found - set a bit in ret_flags and reset status to ** E_DB_OK to enable callers to distinguish between this and other ** errors */ status = E_DB_OK; *ret_flags |= PSS_MISSING_DBPROC; } else { (VOID) psf_rdf_error(RDF_GETINFO, &rdf_cb->rdf_error, &psq_cb->psq_error); } return(status); } /* we get here only if the dbproc was found */ /* ** If the procedure has a security label, validate it */ dbp=rdf_cb->rdf_info_blk->rdr_dbp; /* 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_RCB); qsf_rb->qsf_owner = (PTR)DB_PSF_ID; qsf_rb->qsf_sid = sess_cb->pss_sessid; qsf_rb->qsf_obj_id.qso_type = QSO_QTEXT_OBJ; qsf_rb->qsf_obj_id.qso_lname = 0; /* Having retrieved the text place it in QSF ** because the parser expects it to be there. */ status = qsf_call(QSO_CREATE, qsf_rb); if (DB_FAILURE_MACRO(status)) { i4 qerr = qsf_rb->qsf_error.err_code; if (qerr == E_QS0001_NOMEM) { (VOID) psf_error(qerr, qerr, PSF_CALLERR, &err_code, &psq_cb->psq_error, 0); } else { (VOID) psf_error(E_PS0A05_BADMEMREQ, qerr, PSF_INTERR, &err_code, &psq_cb->psq_error, 0); } goto cleanup2; } /* Object is locked exclusively now. */ pinfo = rdf_cb->rdf_rb.rdr_procedure; /* Allocate enough memory for the query descriptor ** plus the length of text. */ qsf_rb->qsf_sz_piece = sizeof(PSQ_QDESC) + pinfo->rdf_l_querytext + 3; /* one space, one null, ** one for safety. */ status = qsf_call(QSO_PALLOC, qsf_rb); if (DB_FAILURE_MACRO(status)) { i4 qerr = qsf_rb->qsf_error.err_code; if (qerr == E_QS0001_NOMEM) { (VOID) psf_error(qerr, qerr, PSF_CALLERR, &err_code, &psq_cb->psq_error, 0); } else { (VOID) psf_error(E_PS0A05_BADMEMREQ, qerr, PSF_INTERR, &err_code, &psq_cb->psq_error, 0); } goto cleanup1; } qdesc = (PSQ_QDESC *) qsf_rb->qsf_piece; /* Initialize query descriptor. */ qdesc->psq_qrysize = pinfo->rdf_l_querytext + 1; /* 1 trailing space */ qdesc->psq_datasize = 0; qdesc->psq_dnum = 0; qdesc->psq_qrytext = (char *)(qdesc + 1); /* Ptr arithmetic, should point ** right after the PSQ_QDESC. */ qdesc->psq_qrydata = (DB_DATA_VALUE **) NULL; /* QSF memory has been allocated now, copy ** the text from RDF. */ ulm_copy((PTR) pinfo->rdf_querytext, (i4) pinfo->rdf_l_querytext, (PTR) qdesc->psq_qrytext); /* Add a space after the text and null terminate. */ { char *p; p = qdesc->psq_qrytext; /* beginning of text */ p += pinfo->rdf_l_querytext; /* 1st char past end */ *p++ = ' '; *p = '\0'; /* 2nd char past end */ } /* Set root for the QSF object */ qsf_rb->qsf_root = qsf_rb->qsf_piece; status = qsf_call(QSO_SETROOT, qsf_rb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_error(E_PS0A05_BADMEMREQ, qsf_rb->qsf_error.err_code, PSF_INTERR, &err_code, &psq_cb->psq_error, 0); goto cleanup1; } status = qsf_call(QSO_UNLOCK, qsf_rb); if (DB_FAILURE_MACRO(status)) { (VOID) psf_error(E_PS0B05_CANT_UNLOCK, qsf_rb->qsf_error.err_code, PSF_INTERR, &err_code, &psq_cb->psq_error, 0); goto cleanup1; } psq_cb->psq_qid = qsf_rb->qsf_obj_id.qso_handle; return (status); cleanup1: /* Destroy the object, it's already locked. */ stat = qsf_call(QSO_DESTROY, qsf_rb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_error(E_PS0A09_CANTDESTROY, qsf_rb->qsf_error.err_code, PSF_INTERR, &err_code, &psq_cb->psq_error, 0); if (stat > status) status = stat; } cleanup2: /* RDF object can be released now. */ stat = rdf_call(RDF_UNFIX, (PTR) rdf_cb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_rdf_error(RDF_UNFIX, &rdf_cb->rdf_error, &psq_cb->psq_error); if (stat > status) status = stat; } return (status); }
/*{ ** Name: psy_kproc - Drop a database procedure definition from ** the system catalogs. ** ** Description: ** ** Inputs: ** ** Outputs: ** Exceptions: ** none ** ** Side Effects: ** Modifies system catalogs. ** ** History: ** 27-apr-88 (stec) ** Created. ** 12-mar-90 (andre) ** set rdr_2types_mask to 0. ** 22-may-90 (teg) ** init rdr_instr to RDF_NO_INSTR ** 12-jun-90 (andre) ** when trying to destroy a dbproc QEP, use "private" alias, which is ** always defined as opposed to the "public" alias which would not be ** defined if the dbproc is not grantable. ** 06-aug-92 (teresa) ** change interface to invalidate procedure by name, owner. ** must set rdr_flags_mask = RDR_PROCEDURE when invalidating the ** procedure cache object. ** 07-nov-92 (andre) ** we will no longer create private aliases for dbproc QPs ** 16-jun-94 (andre) ** it is dangerous to cast a (char *) into a (DB_CURSOR_ID *) and then ** dereference the resulting pointer because the chat ptr may not be ** pointing at memory not aligned on an appopriate boundary. Instead, ** we will allocate a DB_CURSOR_ID structure on the stack, initialize ** it and MEcopy() it into the char array */ DB_STATUS psy_kproc( PSY_CB *psy_cb, PSS_SESBLK *sess_cb) { RDF_CB rdf_cb; register RDR_RB *rdf_rb = &rdf_cb.rdf_rb; DB_STATUS status, stat; i4 err_code; pst_rdfcb_init(&rdf_cb, sess_cb); (VOID) MEcopy((PTR)&psy_cb->psy_tabname[0], sizeof(DB_DBP_NAME), (PTR)&rdf_rb->rdr_name.rdr_prcname); STRUCT_ASSIGN_MACRO(sess_cb->pss_user, rdf_rb->rdr_owner); rdf_rb->rdr_types_mask = RDR_PROCEDURE; rdf_rb->rdr_update_op = RDR_DELETE; status = rdf_call(RDF_UPDATE, (PTR) &rdf_cb); if (DB_FAILURE_MACRO(status)) { if (rdf_cb.rdf_error.err_code == E_RD0013_NO_TUPLE_FOUND) { /* Retry */ psy_cb->psy_error.err_code = E_PS0008_RETRY; } else { (VOID) psf_rdf_error(RDF_UPDATE, &rdf_cb.rdf_error, &psy_cb->psy_error); return (status); } } /* Try to destroy the procedure QP in QSF if things went OK. */ if (DB_SUCCESS_MACRO(status)) { QSF_RCB qsf_rb; PSS_DBPALIAS dbpid; DB_CURSOR_ID dbp_curs_id; /* Initialize the header of 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; qsf_rb.qsf_feobj_id.qso_type = QSO_ALIAS_OBJ; qsf_rb.qsf_feobj_id.qso_lname = sizeof(dbpid); /* Identify the object first */ dbp_curs_id.db_cursor_id[0] = dbp_curs_id.db_cursor_id[1] = 0; (VOID) MEcopy((PTR)&psy_cb->psy_tabname[0], DB_TAB_MAXNAME, (PTR)dbp_curs_id.db_cur_name); MEcopy((PTR) &dbp_curs_id, sizeof(DB_CURSOR_ID), (PTR) dbpid); (VOID) MEcopy((PTR) &sess_cb->pss_user, DB_OWN_MAXNAME, (PTR) (dbpid + sizeof(DB_CURSOR_ID))); I4ASSIGN_MACRO(sess_cb->pss_udbid, *(i4 *) (dbpid + sizeof(DB_CURSOR_ID) + DB_OWN_MAXNAME)); (VOID)MEcopy((PTR) dbpid, sizeof(dbpid), (PTR) qsf_rb.qsf_feobj_id.qso_name); /* See if QEP for the alias already exists. */ qsf_rb.qsf_lk_state = QSO_SHLOCK; stat = qsf_call(QSO_JUST_TRANS, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { if (qsf_rb.qsf_error.err_code != E_QS0019_UNKNOWN_OBJ) { (VOID) psf_error(E_PS037A_QSF_TRANS_ERR, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (stat > status) status = stat; goto exit; } else { /* Nothing to destroy, QP not in QSF */ goto exit; } } /* Now destroy the ALIAS and the QP objects in QSF */ stat = qsf_call(QSO_DESTROY, &qsf_rb); if (DB_FAILURE_MACRO(stat)) { (VOID) psf_error(E_PS0A09_CANTDESTROY, qsf_rb.qsf_error.err_code, PSF_INTERR, &err_code, &psy_cb->psy_error, 0); if (stat > status) status = stat; } } /* Invalidate procedure object from RDF cache */ pst_rdfcb_init(&rdf_cb, sess_cb); (VOID) MEcopy((PTR)&psy_cb->psy_tabname[0], sizeof(DB_DBP_NAME), (PTR)&rdf_rb->rdr_name.rdr_prcname); STRUCT_ASSIGN_MACRO(sess_cb->pss_user, rdf_rb->rdr_owner); rdf_rb->rdr_types_mask = RDR_PROCEDURE; 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: return (status); }
/*{ ** 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; }
/*{ ** 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); }