/*{ ** 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_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); }