void cleanup_trigger_name(char *trigvn, int trigvn_len, char *trigger_name, int trigger_name_len) { int4 result; char trunc_name[MAX_TRIGNAME_LEN + 1]; uint4 used_trigvn_len; mval val; mval *val_ptr; int var_count; boolean_t is_auto_name; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!gv_cur_region->read_only); /* caller should have already checked this */ assert(cs_addrs->hasht_tree == gv_target); /* should have been set up by caller */ assert(gv_target->root); /* should have been ensured by caller */ /* assume user defined name or auto gen name whose GVN < 21 chars */ if (!trigger_user_name(trigger_name, trigger_name_len)) { /* auto gen name uses #TNCOUNT and #SEQNO under #TNAME */ is_auto_name = TRUE; used_trigvn_len = MIN(trigvn_len, MAX_AUTO_TRIGNAME_LEN); memcpy(trunc_name, trigvn, used_trigvn_len); if (is_auto_name) { /* $get(^#t("#TNAME",<trunc_name>,"#TNCOUNT")) */ BUILD_HASHT_SUB_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trunc_name, used_trigvn_len, LITERAL_HASHTNCOUNT, STRLEN(LITERAL_HASHTNCOUNT)); if (gvcst_get(&val)) { /* each autogenerated name has a #TNCOUNT entry */ val_ptr = &val; var_count = MV_FORCE_UINT(val_ptr); if (1 == var_count) { /* kill ^#t("#TNAME",<trunc_name>) to kill #TNCOUNT and #SEQNO */ BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trunc_name, used_trigvn_len); gvcst_kill(TRUE); } else { var_count--; MV_FORCE_UMVAL(&val, var_count); /* set ^#t("#TNAME",GVN,"#TNCOUNT")=var_count */ SET_TRIGGER_GLOBAL_SUB_SUB_SUB_MVAL(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trunc_name, used_trigvn_len, LITERAL_HASHTNCOUNT, STRLEN(LITERAL_HASHTNCOUNT), val, result); assert(PUT_SUCCESS == result); /* The count size can only decrease */ } } } } else is_auto_name = FALSE; /* kill ^#t("#TNAME",<trigger_name>,:) or zkill ^#t("#TNAME",<trigger_name>) if is_auto_name==FALSE */ BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trigger_name, trigger_name_len - 1); gvcst_kill(is_auto_name); }
/* Set ^#t(<gbl>,"#TRHASH",hash_code,nnn)=<gbl>_$c(0)_trigindx where gv_currkey is ^#t(<gbl>). * Note: This routine has code very similar to that in "add_trigger_hash_entry". There is just * not enough commonality to justify merging the two. */ STATICFNDEF void gvtr_set_hashtrhash(char *trigvn, int trigvn_len, uint4 hash_code, int trigindx) { uint4 curend; mval mv_indx, *mv_indx_ptr; mval mv_hash; int hash_indx, num_len; char name_and_index[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT]; char *ptr; char indx_str[MAX_DIGITS_IN_INT]; uint4 len; int4 result; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; curend = gv_currkey->end; /* note down gv_currkey->end before changing it so we can restore it before function returns */ assert(KEY_DELIMITER == gv_currkey->base[curend]); assert(gv_target->gd_csa == cs_addrs); /* Set ^#t(<gvn>,"#TRHASH",kill_hash_code,i). To do that, first determine the * highest i such that ^#t(<gvn>,"#TRHASH",kill_hash_code,i) exists. Set * ^#t(<gvn>,"#TRHASH",kill_hash_code,i+1)=kill_hash_code in that case. */ MV_FORCE_UMVAL(&mv_hash, hash_code); BUILD_HASHT_SUB_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, "", 0); op_zprevious(&mv_indx); mv_indx_ptr = &mv_indx; hash_indx = (0 == mv_indx.str.len) ? 1 : (mval2i(mv_indx_ptr) + 1); i2mval(mv_indx_ptr, hash_indx); MV_FORCE_STR(mv_indx_ptr); /* Prepare the value of the SET */ num_len = 0; I2A(indx_str, num_len, trigindx); assert(MAX_MIDENT_LEN >= trigvn_len); memcpy(name_and_index, trigvn, trigvn_len); ptr = name_and_index + trigvn_len; *ptr++ = '\0'; memcpy(ptr, indx_str, num_len); len = trigvn_len + 1 + num_len; /* Do the SET */ SET_TRIGGER_GLOBAL_SUB_SUB_MSUB_MSUB_STR(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_indx, name_and_index, len, result); assert(PUT_SUCCESS == result); gv_currkey->end = curend; /* reset gv_currkey->end to what it was at function entry */ gv_currkey->base[curend] = KEY_DELIMITER; /* restore terminator for entire key so key is well-formed */ return; }
/* Upgrade ^#t global in "reg" region */ void trigger_upgrade(gd_region *reg) { boolean_t est_first_pass, do_upgrade, is_defined; boolean_t was_null = FALSE, is_null = FALSE; int seq_num, trig_seq_num; int currlabel; mval tmpmval, xecuteimval, *gvname, *tmpmv, *tmpmv2; int4 result, tmpint4; uint4 curend, gvname_prev, xecute_curend; uint4 hash_code, kill_hash_code; int count, i, xecutei, tncount; char *trigname, *trigindex, *ptr; char name_and_index[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT]; char trigvn[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT], nullbyte[1]; uint4 trigname_len, name_index_len; int ilen; sgmnt_addrs *csa; jnl_private_control *jpc; uint4 sts; int close_res; hash128_state_t hash_state, kill_hash_state; uint4 hash_totlen, kill_hash_totlen; int trig_protected_mval_push_count; # ifdef DEBUG int save_dollar_tlevel; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(gv_cur_region == reg); assert(!dollar_tlevel); /* caller should have ensured this. this is needed as otherwise things get complicated. */ assert(!is_replicator); /* caller should have ensured this. this is needed so we dont bump jnl_seqno (if replicating) */ csa = &FILE_INFO(reg)->s_addrs; assert(csa->hdr->hasht_upgrade_needed); /* If before-image journaling is turned on in this region (does not matter if replication is turned on or not), * once this transaction is done, we need to switch to new journal file and cut the back link because * otherwise it is possible for backward journal recovery (or rollback) or source server to encounter * the journal records generated in this ^#t-upgrade-transaction in which case they dont know to handle * it properly (e.g. rollback or backward recovery does not know to restore csa->hdr->hasht_upgrade_needed * if it rolls back this transaction). To achieve this, we set hold_onto_crit to TRUE and do the jnl link * cut AFTER the transaction commits but before anyone else can sneak in to do any more updates. * Since most often we expect databases to be journaled, we do this hold_onto_crit even for the non-journaled case. */ grab_crit(reg); csa->hold_onto_crit = TRUE; DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); assert(!donot_INVOKE_MUMTSTART); DEBUG_ONLY(donot_INVOKE_MUMTSTART = TRUE); op_tstart(IMPLICIT_TSTART, TRUE, &literal_batch, 0); /* 0 ==> save no locals but RESTART OK */ ESTABLISH_NORET(trigger_upgrade_ch, est_first_pass); /* On a TP restart anywhere down below, this line is where the restart resumes execution from */ assert(donot_INVOKE_MUMTSTART); /* Make sure still set for every try/retry of TP transaction */ change_reg(); /* TP_CHANGE_REG wont work as we need to set sgm_info_ptr */ assert(NULL != cs_addrs); assert(csa == cs_addrs); SET_GVTARGET_TO_HASHT_GBL(csa); /* sets up gv_target */ assert(NULL != gv_target); INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; /* Needed to do every retry in case restart was due to an online rollback. * This also sets up gv_currkey */ /* Do actual upgrade of ^#t global. * * Below is a sample layout of the label 2 ^#t global * ------------------------------------------------------- * ^#t("#TNAME","x")="a"_$C(0)_"1" (present in DEFAULT only) * ^#t("#TRHASH",89771515,1)="a"_$C(0)_"1" (present in DEFAULT only) * ^#t("#TRHASH",106937755,1)="a"_$C(0)_"1" (present in DEFAULT only) * ^#t("a",1,"BHASH")="106937755" * ^#t("a",1,"CHSET")="M" * ^#t("a",1,"CMD")="S" * ^#t("a",1,"LHASH")="89771515" * ^#t("a",1,"TRIGNAME")="x#" * ^#t("a",1,"XECUTE")=" do ^twork" * ^#t("a","#COUNT")="1" * ^#t("a","#CYCLE")="1" * ^#t("a","#LABEL")="2" * * Below is a sample layout of the label 3 ^#t global * ------------------------------------------------------- * ^#t("#LABEL")="3" (present only after upgrade, not regular trigger load) * ^#t("#TNAME","x")="a"_$C(0)_"1" (present in CURRENT region) * ^#t("a",1,"BHASH")="71945627" * ^#t("a",1,"CHSET")="M" * ^#t("a",1,"CMD")="S" * ^#t("a",1,"LHASH")="71945627" * ^#t("a",1,"TRIGNAME")="x#" * ^#t("a",1,"XECUTE")=" do ^twork" * ^#t("a","#COUNT")="1" * ^#t("a","#CYCLE")="2" * ^#t("a","#LABEL")="3" * ^#t("a","#TRHASH",71945627,1)="a"_$C(0)_"1" * * Key aspects of the format change * ---------------------------------- * 1) New ^#t("#LABEL")="3" to indicate the format of the ^#t global. This is in addition to * ^#t("a","#LABEL") etc. which is already there. This way we have a #LABEL for not just the installed * triggers but also for the name information stored in the #TNAME nodes. * 2) In the BHASH and LHASH fields. The hash computation is different so there are more chances of BHASH and LHASH * matching in which case we store only one #TRHASH entry (instead of two). So thre is fewer ^#t records in the new * format in most cases. * 3) ^#t("a","#LABEL") bumps from 2 to 3. Similarly ^#t("a","#CYCLE") bumps by one (to make sure triggers for this * global get re-read if and when we implement an -ONLINE upgrade). * 4) DEFAULT used to have ^#t("#TNAME",...) nodes corresponding to triggers across ALL regions in the gbldir and * other regions used to have NO ^#t("#TNAME",...) nodes whereas after the upgrade every region have * ^#t("#TNAME",...) nodes corresponding to triggers installed in that region. So it is safer to kill ^#t("#TNAME") * nodes and add them as needed. * 5) #TRHASH has moved from ^#t() to ^#t(<gbl>). So it is safer to kill ^#t("#TRHASH") nodes and add them as needed. * * Below is a sample layout of the label 4 ^#t global * ------------------------------------------------------- * ^#t("#TNAME","x")="a"_$C(0)_"1" (present in CURRENT region) * ^#t("a",1,"BHASH")="71945627" * ^#t("a",1,"CHSET")="M" * ^#t("a",1,"CMD")="S" * ^#t("a",1,"LHASH")="71945627" * ^#t("a",1,"TRIGNAME")="x#" * ^#t("a",1,"XECUTE")=" do ^twork" * ^#t("a","#COUNT")="1" * ^#t("a","#CYCLE")="2" * ^#t("a","#LABEL")="4" * ^#t("a","#TRHASH",71945627,1)="a"_$C(0)_"1" * * Key aspects of the format change * ---------------------------------- * 1) Removed ^#t("#LABEL") as it is redundant information and trigger load does not include it * 2) Multiline triggers were incorrectly processed resulting in incorrect BHASH and LHASH values. Upgrade fixes this * 3) ^#t("a","#LABEL") bumps from 3 to 4. Similarly ^#t("a","#CYCLE") bumps by one (to make sure * triggers for this global get re-read if and when we implement an -ONLINE upgrade). */ tmpmv = &tmpmval; /* At all points maintain this relationship. The two are used interchangeably below */ if (gv_target->root) do_upgrade = TRUE; /* The below logic assumes ^#t global does not have any integrity errors */ assert(do_upgrade); /* caller should have not invoked us otherwise */ if (do_upgrade) { /* kill ^#t("#TRHASH"), ^#t("#TNAME") and ^#t("#LABEL") first. Regenerate each again as we process ^#t(<gbl>,...) */ csa->incr_db_trigger_cycle = TRUE; /* so that we increment csd->db_trigger_cycle at commit time. * this forces concurrent processes to read upgraded triggers. */ if (JNL_WRITE_LOGICAL_RECS(csa)) { /* Note that the ^#t upgrade is a physical layout change. But it has no logical change (i.e. users * see the same MUPIP TRIGGER -SELECT output as before). So write only a dummy LGTRIG journal * record for this operation. Hence write a string that starts with a trigger comment character ";". */ assert(!gv_cur_region->read_only); jnl_format(JNL_LGTRIG, NULL, (mval *)&literal_trigjnlrec, 0); } /* KILL ^#t("#LABEL") unconditionally */ BUILD_HASHT_SUB_CURRKEY(LITERAL_HASHLABEL, STRLEN(LITERAL_HASHLABEL)); if (0 != gvcst_data()) gvcst_kill(TRUE); /* KILL ^#t("#TNAME") unconditionally and regenerate */ BUILD_HASHT_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME)); if (0 != gvcst_data()) gvcst_kill(TRUE); /* KILL ^#t("#TRHASH") unconditionally and regenerate */ BUILD_HASHT_SUB_CURRKEY(LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH)); if (0 != gvcst_data()) gvcst_kill(TRUE); /* Loop through all global names for which ^#t(<gvn>) exists. The only first-level subscripts of ^#t starting * with # are #TNAME and #TRHASH in collation order. So after #TRHASH we expect to find subscripts that are * global names. Hence the HASHTRHASH code is placed AFTER the HASHTNAME code above. */ TREF(gd_targ_gvnh_reg) = NULL; /* needed so op_gvorder below goes through gvcst_order (i.e. focuses only * on the current region) and NOT through gvcst_spr_order (which does not * apply anyways in the case of ^#t). */ nullbyte[0] = '\0'; trig_protected_mval_push_count = 0; INCR_AND_PUSH_MV_STENT(gvname); /* Protect gvname from garbage collection */ do { op_gvorder(gvname); if (0 == gvname->str.len) break; assert(ARRAYSIZE(trigvn) > gvname->str.len); memcpy(&trigvn[0], gvname->str.addr, gvname->str.len); gvname->str.addr = &trigvn[0]; /* point away from stringpool to avoid stp_gcol issues */ /* Save gv_currkey->prev so it is restored before next call to op_gvorder (which cares about this field). * gv_currkey->prev gets tampered with in the for loop below (e.g. BUILD_HASHT_SUB_CURRKEY macro). * No need to do this for gv_currkey->end since the body of the for loop takes care of restoring it. */ gvname_prev = gv_currkey->prev; BUILD_HASHT_SUB_CURRKEY(gvname->str.addr, gvname->str.len); /* At this point, gv_currkey is ^#t(<gvn>) */ /* Increment ^#t(<gvn>,"#CYCLE") */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_hashcycle, tmpmv); assert(is_defined); tmpint4 = mval2i(tmpmv); tmpint4++; i2mval(tmpmv, tmpint4); gvtr_set_hasht_gblsubs((mval *)&literal_hashcycle, tmpmv); /* Read ^#t(<gvn>,"#COUNT") */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_hashcount, tmpmv); if (is_defined) { tmpint4 = mval2i(tmpmv); count = tmpint4; /* Get ^#t(<gvn>,"#LABEL"), error out for invalid values. Upgrade disallowed for label 1 triggers */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_hashlabel, tmpmv); assert(is_defined); currlabel = mval2i(tmpmv); if ((V19_HASHT_GBL_LABEL_INT >= currlabel) || (HASHT_GBL_CURLABEL_INT <= currlabel)) rts_error_csa(CSA_ARG(csa) VARLSTCNT(8) ERR_TRIGUPBADLABEL, 6, currlabel, HASHT_GBL_CURLABEL_INT, gvname->str.len, gvname->str.addr, REG_LEN_STR(reg)); /* Set ^#t(<gvn>,"#LABEL")=HASHT_GBL_CURLABEL */ gvtr_set_hasht_gblsubs((mval *)&literal_hashlabel, (mval *)&literal_curlabel); } else count = 0; /* Kill ^#t(<gvn>,"#TRHASH") unconditionally and regenerate */ gvtr_kill_hasht_gblsubs((mval *)&literal_hashtrhash, TRUE); /* At this point, gv_currkey is ^#t(<gvn>) */ for (i = 1; i <= count; i++) { /* At this point, gv_currkey is ^#t(<gvn>) */ curend = gv_currkey->end; /* note gv_currkey->end before changing it so we can restore it later */ assert(KEY_DELIMITER == gv_currkey->base[curend]); assert(gv_target->gd_csa == cs_addrs); i2mval(tmpmv, i); COPY_SUBS_TO_GVCURRKEY(tmpmv, gv_cur_region, gv_currkey, was_null, is_null); /* At this point, gv_currkey is ^#t(<gvn>,i) */ /* Compute new LHASH and BHASH hash values. * LHASH uses : GVSUBS, XECUTE * BHASH uses : GVSUBS, DELIM, ZDELIM, PIECES, XECUTE * So reach each of these pieces and compute hash along the way. */ STR_PHASH_INIT(hash_state, hash_totlen); STR_PHASH_PROCESS(hash_state, hash_totlen, gvname->str.addr, gvname->str.len); STR_PHASH_PROCESS(hash_state, hash_totlen, nullbyte, 1); /* Read in ^#t(<gvn>,i,"GVSUBS") */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_gvsubs, tmpmv); if (is_defined) { STR_PHASH_PROCESS(hash_state, hash_totlen, tmpmval.str.addr, tmpmval.str.len); STR_PHASH_PROCESS(hash_state, hash_totlen, nullbyte, 1); } /* Copy over SET hash state (2-tuple <state,totlen>) to KILL hash state before adding * the PIECES, DELIM, ZDELIM portions (those are only part of the SET hash). */ kill_hash_state = hash_state; kill_hash_totlen = hash_totlen; /* Read in ^#t(<gvn>,i,"PIECES") */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_pieces, tmpmv); if (is_defined) { STR_PHASH_PROCESS(hash_state, hash_totlen, tmpmval.str.addr, tmpmval.str.len); STR_PHASH_PROCESS(hash_state, hash_totlen, nullbyte, 1); } /* Read in ^#t(<gvn>,i,"DELIM") */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_delim, tmpmv); if (is_defined) { STR_PHASH_PROCESS(hash_state, hash_totlen, tmpmval.str.addr, tmpmval.str.len); STR_PHASH_PROCESS(hash_state, hash_totlen, nullbyte, 1); } /* Read in ^#t(<gvn>,i,"ZDELIM") */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_zdelim, tmpmv); if (is_defined) { STR_PHASH_PROCESS(hash_state, hash_totlen, tmpmval.str.addr, tmpmval.str.len); STR_PHASH_PROCESS(hash_state, hash_totlen, nullbyte, 1); } /* Read in ^#t(<gvn>,i,"XECUTE"). * Note: The XECUTE portion of the trigger definition is used in SET and KILL hash. * But since we have started maintaining "hash_state" and "kill_hash_state" separately * (due to PIECES, DELIM, ZDELIM) we need to update the hash for both using same input string. */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_xecute, tmpmv); if (is_defined) { STR_PHASH_PROCESS(hash_state, hash_totlen, tmpmval.str.addr, tmpmval.str.len); STR_PHASH_PROCESS(kill_hash_state, kill_hash_totlen, tmpmval.str.addr, tmpmval.str.len); } else { /* Multi-record XECUTE string */ /* At this point, gv_currkey is ^#t(<gvn>,i) */ xecute_curend = gv_currkey->end; /* note gv_currkey->end so we can restore it later */ assert(KEY_DELIMITER == gv_currkey->base[xecute_curend]); tmpmv2 = (mval *)&literal_xecute; COPY_SUBS_TO_GVCURRKEY(tmpmv2, gv_cur_region, gv_currkey, was_null, is_null); xecutei = 1; do { i2mval(&xecuteimval, xecutei); is_defined = gvtr_get_hasht_gblsubs(&xecuteimval, tmpmv); if (!is_defined) break; STR_PHASH_PROCESS(hash_state, hash_totlen, tmpmval.str.addr, tmpmval.str.len); STR_PHASH_PROCESS(kill_hash_state, kill_hash_totlen, tmpmval.str.addr, tmpmval.str.len); xecutei++; } while (TRUE); /* Restore gv_currkey to ^#t(<gvn>,i) */ gv_currkey->end = xecute_curend; gv_currkey->base[xecute_curend] = KEY_DELIMITER; } STR_PHASH_RESULT(hash_state, hash_totlen, hash_code); STR_PHASH_RESULT(kill_hash_state, kill_hash_totlen, kill_hash_code); /* Set ^#t(<gvn>,i,"LHASH") */ MV_FORCE_UMVAL(tmpmv, kill_hash_code); gvtr_set_hasht_gblsubs((mval *)&literal_lhash, tmpmv); /* Set ^#t(<gvn>,i,"BHASH") */ MV_FORCE_UMVAL(tmpmv, hash_code); gvtr_set_hasht_gblsubs((mval *)&literal_bhash, tmpmv); /* Read in ^#t(<gvn>,i,"TRIGNAME") to determine if #SEQNUM/#TNCOUNT needs to be maintained */ is_defined = gvtr_get_hasht_gblsubs((mval *)&literal_trigname, tmpmv); assert(is_defined); assert('#' == tmpmval.str.addr[tmpmval.str.len - 1]); tmpmval.str.len--; if ((tmpmval.str.len <= ARRAYSIZE(name_and_index)) && (NULL != (ptr = memchr(tmpmval.str.addr, '#', tmpmval.str.len)))) { /* Auto-generated name. Need to maintain #SEQNUM/#TNCOUNT */ /* Take copy of trigger name into non-stringpool location to avoid stp_gcol issues */ trigname_len = ptr - tmpmval.str.addr; ptr++; name_index_len = (tmpmval.str.addr + tmpmval.str.len) - ptr; assert(ARRAYSIZE(name_and_index) >= (trigname_len + 1 + name_index_len)); trigname = &name_and_index[0]; trigindex = ptr; memcpy(trigname, tmpmval.str.addr, tmpmval.str.len); A2I(ptr, ptr + name_index_len, trig_seq_num); /* At this point, gv_currkey is ^#t(<gvn>,i) */ /* $get(^#t("#TNAME",<trigger name>,"#SEQNUM")) */ BUILD_HASHT_SUB_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trigname, trigname_len, LITERAL_HASHSEQNUM, STR_LIT_LEN(LITERAL_HASHSEQNUM)); seq_num = gvcst_get(tmpmv) ? mval2i(tmpmv) : 0; if (trig_seq_num > seq_num) { /* Set ^#t("#TNAME",<trigger name>,"#SEQNUM") = trig_seq_num */ SET_TRIGGER_GLOBAL_SUB_SUB_SUB_STR(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trigname, trigname_len, LITERAL_HASHSEQNUM, STR_LIT_LEN(LITERAL_HASHSEQNUM), trigindex, name_index_len, result); assert(PUT_SUCCESS == result); } /* set ^#t("#TNAME",<trigger name>,"#TNCOUNT")++ */ BUILD_HASHT_SUB_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trigname, trigname_len, LITERAL_HASHTNCOUNT, STR_LIT_LEN(LITERAL_HASHTNCOUNT)); tncount = gvcst_get(tmpmv) ? mval2i(tmpmv) + 1 : 1; i2mval(tmpmv, tncount); SET_TRIGGER_GLOBAL_SUB_SUB_SUB_MVAL(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trigname, trigname_len, LITERAL_HASHTNCOUNT, STR_LIT_LEN(LITERAL_HASHTNCOUNT), tmpmval, result); trigname_len += 1 + name_index_len; /* in preparation for ^#t("#TNAME") set below */ assert(PUT_SUCCESS == result); BUILD_HASHT_SUB_CURRKEY(gvname->str.addr, gvname->str.len); /* At this point, gv_currkey is ^#t(<gvn>) */ } else { /* Take copy of trigger name into non-stringpool location to avoid stp_gcol issues */ trigname = &name_and_index[0]; /* in preparation for ^#t("#TNAME") set below */ trigname_len = MIN(tmpmval.str.len, ARRAYSIZE(name_and_index)); assert(ARRAYSIZE(name_and_index) >= trigname_len); memcpy(trigname, tmpmval.str.addr, trigname_len); /* Restore gv_currkey to what it was at beginning of for loop iteration */ gv_currkey->end = curend; gv_currkey->base[curend] = KEY_DELIMITER; } /* At this point, gv_currkey is ^#t(<gvn>) */ if (kill_hash_code != hash_code) gvtr_set_hashtrhash(gvname->str.addr, gvname->str.len, kill_hash_code, i); /* Set ^#t(<gvn>,"#TRHASH",hash_code,i) */ gvtr_set_hashtrhash(gvname->str.addr, gvname->str.len, hash_code, i); /* Set ^#t("#TNAME",<trigname>)=<gvn>_$c(0)_<trigindx> */ /* The upgrade assumes that the region does not contain two triggers with the same name. * V62000 and before could potentially have this out of design case. Once implemented * the trigger integrity check will warn users of this edge case */ ptr = &trigvn[gvname->str.len]; *ptr++ = '\0'; ilen = 0; I2A(ptr, ilen, i); ptr += ilen; assert(ptr <= ARRAYTOP(trigvn)); SET_TRIGGER_GLOBAL_SUB_SUB_STR(LITERAL_HASHTNAME, STR_LIT_LEN(LITERAL_HASHTNAME), trigname, trigname_len, trigvn, ptr - gvname->str.addr, result); assert(PUT_SUCCESS == result); BUILD_HASHT_SUB_CURRKEY(gvname->str.addr, gvname->str.len); /* At this point, gv_currkey is ^#t(<gvn>) */ } /* At this point, gv_currkey is ^#t(<gvn>) i.e. gv_currkey->end is correct but gv_currkey->prev * might have been tampered with. Restore it to proper value first. */ gv_currkey->prev = gvname_prev; gvname->mvtype = 0; /* can now be garbage collected in the next iteration */ } while (TRUE); } op_tcommit(); REVERT; /* remove our condition handler */ DEBUG_ONLY(donot_INVOKE_MUMTSTART = FALSE;) if (csa->hold_onto_crit)
int4 trigger_delete(char *trigvn, int trigvn_len, mval *trigger_count, int index) { int count; mval mv_val; mval *mv_val_ptr; char *ptr1; int4 result; int4 retval; stringkey kill_hash, set_hash; int sub_indx; char tmp_trig_str[MAX_BUFF_SIZE]; int4 trig_len; char trig_name[MAX_TRIGNAME_LEN]; int trig_name_len; int tmp_len; char *tt_val[NUM_SUBS]; uint4 tt_val_len[NUM_SUBS]; mval trigger_value; mval trigger_index; mval xecute_index; uint4 xecute_idx; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!gv_cur_region->read_only); /* caller should have already checked this */ assert(cs_addrs->hasht_tree == gv_target); /* should have been set up by caller */ assert(gv_target->root); /* should have been ensured by caller */ mv_val_ptr = &mv_val; MV_FORCE_UMVAL(&trigger_index, index); count = MV_FORCE_UINT(trigger_count); /* build up array of values - needed for comparison in hash stuff */ ptr1 = tmp_trig_str; memcpy(ptr1, trigvn, trigvn_len); ptr1 += trigvn_len; *ptr1++ = '\0'; tmp_len = trigvn_len + 1; /* Assert that BHASH and LHASH are not part of NUM_SUBS calculation (confirms the -2 done in the #define of NUM_SUBS) */ assert(BHASH_SUB == NUM_SUBS); assert(LHASH_SUB == (NUM_SUBS + 1)); for (sub_indx = 0; sub_indx < NUM_SUBS; sub_indx++) { BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigger_index, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx])); trig_len = gvcst_get(&trigger_value) ? trigger_value.str.len : 0; if (0 == trig_len) { if (((TRIGNAME_SUB == sub_indx) || (CMD_SUB == sub_indx) || (CHSET_SUB == sub_indx))) { /* CMD, NAME and CHSET cannot be zero length */ if (CDB_STAGNATE > t_tries) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); rts_error_csa(CSA_ARG(REG2CSA(gv_cur_region)) VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, trigvn_len, trigvn, STRLEN(trigger_subs[sub_indx]), trigger_subs[sub_indx]); } tt_val[sub_indx] = NULL; tt_val_len[sub_indx] = 0; continue; } if (TRIGNAME_SUB == sub_indx) { trig_name_len = MIN(trig_len, MAX_TRIGNAME_LEN); assert(MAX_TRIGNAME_LEN >= trig_len); memcpy(trig_name, trigger_value.str.addr, trig_name_len); tt_val[sub_indx] = NULL; tt_val_len[sub_indx] = 0; continue; } tt_val[sub_indx] = ptr1; tt_val_len[sub_indx] = trig_len; tmp_len += trig_len; if (0 < trig_len) { if (MAX_BUFF_SIZE <= tmp_len) return VAL_TOO_LONG; memcpy(ptr1, trigger_value.str.addr, trig_len); ptr1 += trig_len; } *ptr1++ = '\0'; tmp_len++; } /* Get trigger name, set hash value, and kill hash values from trigger before we delete it. * The values will be used in clean ups associated with the deletion */ /* $get(^#t(GVN,trigger_index,"LHASH") for deletion in cleanup_trigger_hash */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigger_index, trigger_subs[LHASH_SUB], STRLEN(trigger_subs[LHASH_SUB])); if (gvcst_get(mv_val_ptr)) kill_hash.hash_code = (uint4)MV_FORCE_UINT(mv_val_ptr); else { util_out_print_gtmio("The LHASH for global ^!AD does not exist", FLUSH, trigvn_len, trigvn); kill_hash.hash_code = 0; } /* $get(^#t(GVN,trigger_index,"BHASH") for deletion in cleanup_trigger_hash */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigger_index, trigger_subs[BHASH_SUB], STRLEN(trigger_subs[BHASH_SUB])); if (gvcst_get(mv_val_ptr)) set_hash.hash_code = (uint4)MV_FORCE_UINT(mv_val_ptr); else { util_out_print_gtmio("The BHASH for global ^!AD does not exist", FLUSH, trigvn_len, trigvn); set_hash.hash_code = 0; } /* kill ^#t(GVN,trigger_index) */ BUILD_HASHT_SUB_MSUB_CURRKEY(trigvn, trigvn_len, trigger_index); gvcst_kill(TRUE); assert(0 == gvcst_data()); if (1 == count) { /* This is the last trigger for "trigvn" - clean up trigger name, remove #LABEL and #COUNT */ assert(1 == index); BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHLABEL, STRLEN(LITERAL_HASHLABEL)); gvcst_kill(TRUE); BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT)); gvcst_kill(TRUE); cleanup_trigger_name(trigvn, trigvn_len, trig_name, trig_name_len); cleanup_trigger_hash(trigvn, trigvn_len, tt_val, tt_val_len, &set_hash, &kill_hash, TRUE, index); } else { cleanup_trigger_hash(trigvn, trigvn_len, tt_val, tt_val_len, &set_hash, &kill_hash, TRUE, index); cleanup_trigger_name(trigvn, trigvn_len, trig_name, trig_name_len); if (index != count) { /* Shift the last trigger (index is #COUNT value) to the just deleted trigger's index. * This way count is always accurate and can still be used as the index for new triggers. * Note - there is no dependence on the trigger order, or this technique wouldn't work. */ ptr1 = tmp_trig_str; memcpy(ptr1, trigvn, trigvn_len); ptr1 += trigvn_len; *ptr1++ = '\0'; tmp_len = trigvn_len + 1; for (sub_indx = 0; sub_indx < NUM_TOTAL_SUBS; sub_indx++) { /* $get(^#t(GVN,trigger_count,sub_indx) */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, *trigger_count, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx])); if (gvcst_get(&trigger_value)) { trig_len = trigger_value.str.len; /* set ^#t(GVN,trigger_index,sub_indx)=^#t(GVN,trigger_count,sub_indx) */ SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MVAL(trigvn, trigvn_len, trigger_index, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), trigger_value, result); assert(PUT_SUCCESS == result); } else if (XECUTE_SUB == sub_indx) { /* multi line trigger broken up because it exceeds record size */ for (xecute_idx = 0; ; xecute_idx++) { i2mval(&xecute_index, xecute_idx); BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY(trigvn, trigvn_len, *trigger_count, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), xecute_index); if (!gvcst_get(&trigger_value)) break; SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MSUB_MVAL(trigvn, trigvn_len, trigger_index, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), xecute_index, trigger_value, result); assert(PUT_SUCCESS == result); } assert (xecute_idx >= 2); /* multi-line trigger, indices 0, 1 and 2 MUST be defined */ } else { if (((TRIGNAME_SUB == sub_indx) || (CMD_SUB == sub_indx) || (CHSET_SUB == sub_indx))) { /* CMD, NAME and CHSET cannot be zero length */ if (CDB_STAGNATE > t_tries) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); rts_error_csa(CSA_ARG(REG2CSA(gv_cur_region)) VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, trigvn_len, trigvn, STRLEN(trigger_subs[sub_indx]), trigger_subs[sub_indx]); } /* OPTIONS, PIECES and DELIM can be zero */ trig_len = 0; } if (NUM_SUBS > sub_indx) { tt_val[sub_indx] = ptr1; tt_val_len[sub_indx] = trig_len; tmp_len += trig_len; if (0 < trig_len) { if (MAX_BUFF_SIZE <= tmp_len) { /* Exceeding the temporary buffer is impossible, restart*/ if (CDB_STAGNATE > t_tries) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); rts_error_csa(CSA_ARG(REG2CSA(gv_cur_region)) VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, trigvn_len, trigvn, STRLEN(trigger_subs[sub_indx]), trigger_subs[sub_indx]); } memcpy(ptr1, trigger_value.str.addr, trig_len); ptr1 += trig_len; } *ptr1++ = '\0'; tmp_len++; } } /* $get(^#t(GVN,trigger_count,"LHASH") for update_trigger_hash_value */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, *trigger_count, trigger_subs[LHASH_SUB], STRLEN(trigger_subs[LHASH_SUB])); if (!gvcst_get(mv_val_ptr)) return PUT_SUCCESS; kill_hash.hash_code = (uint4)MV_FORCE_UINT(mv_val_ptr); /* $get(^#t(GVN,trigger_count,"BHASH") for update_trigger_hash_value */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, *trigger_count, trigger_subs[BHASH_SUB], STRLEN(trigger_subs[BHASH_SUB])); if (!gvcst_get(mv_val_ptr)) return PUT_SUCCESS; set_hash.hash_code = (uint4)MV_FORCE_UINT(mv_val_ptr); /* update hash values from above */ if (VAL_TOO_LONG == (retval = update_trigger_hash_value(trigvn, trigvn_len, tt_val, tt_val_len, &set_hash, &kill_hash, count, index))) return VAL_TOO_LONG; /* fix the value ^#t("#TNAME",^#t(GVN,index,"#TRIGNAME")) to point to the correct "index" */ if (VAL_TOO_LONG == (retval = update_trigger_name_value(tt_val[TRIGNAME_SUB], tt_val_len[TRIGNAME_SUB], index))) return VAL_TOO_LONG; /* kill ^#t(GVN,COUNT) which was just shifted to trigger_index */ BUILD_HASHT_SUB_MSUB_CURRKEY(trigvn, trigvn_len, *trigger_count); gvcst_kill(TRUE); } /* Update #COUNT */ count--; MV_FORCE_UMVAL(trigger_count, count); SET_TRIGGER_GLOBAL_SUB_SUB_MVAL(trigvn, trigvn_len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT), *trigger_count, result); assert(PUT_SUCCESS == result); /* Size of count can only get shorter or stay the same */ } return PUT_SUCCESS; }
STATICFNDEF int4 update_trigger_hash_value(char *trigvn, int trigvn_len, char **values, uint4 *value_len, stringkey *set_hash, stringkey *kill_hash, int old_trig_index, int new_trig_index) { sgmnt_addrs *csa; int hash_index; mval key_val; uint4 len; mval mv_hash; mval mv_hash_indx; int num_len; char *ptr; int4 result; char tmp_str[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(!gv_cur_region->read_only); /* caller should have already checked this */ assert(cs_addrs->hasht_tree == gv_target); /* should have been set up by caller */ assert(gv_target->root); /* should have been ensured by caller */ csa = cs_addrs; if ((NULL != strchr(values[CMD_SUB], 'S')) && (set_hash->hash_code != kill_hash->hash_code)) { if (!search_trigger_hash(trigvn, trigvn_len, set_hash, old_trig_index, &hash_index)) { /* There has to be an entry with the old hash value, we just looked it up */ TRHASH_DEFINITION_RETRY_OR_ERROR(set_hash, csa); } MV_FORCE_UMVAL(&mv_hash, set_hash->hash_code); MV_FORCE_UMVAL(&mv_hash_indx, hash_index); BUILD_HASHT_SUB_SUB_MSUB_MSUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_hash_indx); if (!gvcst_get(&key_val)) { /* There has to be a #TRHASH entry */ TRHASH_DEFINITION_RETRY_OR_ERROR(set_hash, csa); } assert((MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT) >= key_val.str.len); ptr = key_val.str.addr; len = MIN(key_val.str.len, MAX_MIDENT_LEN); STRNLEN(ptr, len, len); ptr += len; if ((key_val.str.len == len) || ('\0' != *ptr)) { /* We expect $c(0) in the middle of ptr. If we dont find it, this is a restartable situation */ if (CDB_STAGNATE > t_tries) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); rts_error_csa(CSA_ARG(REG2CSA(gv_cur_region)) VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, LEN_AND_LIT("\"#BHASH\""), mv_hash.str.len, mv_hash.str.addr); } memcpy(tmp_str, key_val.str.addr, len); ptr = tmp_str + len; *ptr++ = '\0'; num_len = 0; I2A(ptr, num_len, new_trig_index); len += num_len + 1; SET_TRIGGER_GLOBAL_SUB_SUB_MSUB_MSUB_STR(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_hash_indx, tmp_str, len, result); if (PUT_SUCCESS != result) return result; } if (!search_trigger_hash(trigvn, trigvn_len, kill_hash, old_trig_index, &hash_index)) { /* There has to be an entry with the old hash value, we just looked it up */ TRHASH_DEFINITION_RETRY_OR_ERROR(kill_hash, csa); } MV_FORCE_UMVAL(&mv_hash, kill_hash->hash_code); MV_FORCE_UMVAL(&mv_hash_indx, hash_index); BUILD_HASHT_SUB_SUB_MSUB_MSUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_hash_indx); if (!gvcst_get(&key_val)) { /* There has to be a #TRHASH entry */ TRHASH_DEFINITION_RETRY_OR_ERROR(kill_hash, csa); } assert((MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT) >= key_val.str.len); ptr = key_val.str.addr; len = MIN(key_val.str.len, MAX_MIDENT_LEN); STRNLEN(ptr, len, len); ptr += len; if ((key_val.str.len == len) || ('\0' != *ptr)) { /* We expect $c(0) in the middle of ptr. If we dont find it, this is a restartable situation */ if (CDB_STAGNATE > t_tries) t_retry(cdb_sc_triggermod); assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); rts_error_csa(CSA_ARG(REG2CSA(gv_cur_region)) VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, LEN_AND_LIT("\"#LHASH\""), mv_hash.str.len, mv_hash.str.addr); } memcpy(tmp_str, key_val.str.addr, len); ptr = tmp_str + len; *ptr++ = '\0'; num_len = 0; I2A(ptr, num_len, new_trig_index); len += num_len + 1; SET_TRIGGER_GLOBAL_SUB_SUB_MSUB_MSUB_STR(trigvn, trigvn_len, LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_hash_indx, tmp_str, len, result); return result; }
STATICFNDEF int4 update_trigger_hash_value(char *trigvn, int trigvn_len, char **values, uint4 *value_len, stringkey *set_hash, stringkey *kill_hash, int old_trig_index, int new_trig_index) { sgmnt_addrs *csa; int hash_index; mval key_val; uint4 len; mval mv_hash; mval mv_hash_indx; int num_len; char *ptr; int4 result; char save_currkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)]; gv_key *save_gv_currkey; gd_region *save_gv_cur_region; gv_namehead *save_gv_target; sgm_info *save_sgm_info_ptr; char tmp_str[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; SAVE_TRIGGER_REGION_INFO; SWITCH_TO_DEFAULT_REGION; assert(0 != gv_target->root); if (NULL != strchr(values[CMD_SUB], 'S')) { if (search_trigger_hash(trigvn, trigvn_len, set_hash, old_trig_index, &hash_index)) { MV_FORCE_UMVAL(&mv_hash, set_hash->hash_code); MV_FORCE_MVAL(&mv_hash_indx, hash_index); BUILD_HASHT_SUB_MSUB_MSUB_CURRKEY(LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_hash_indx); if (!gvcst_get(&key_val)) { /* There has to be a #TRHASH entry */ assert(FALSE); rts_error(VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, LEN_AND_LIT("\"#TRHASH\""), set_hash->str.len, set_hash->str.addr); } assert((MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT) >= key_val.str.len); len = STRLEN(key_val.str.addr); memcpy(tmp_str, key_val.str.addr, len); ptr = tmp_str + len; *ptr++ = '\0'; num_len = 0; I2A(ptr, num_len, new_trig_index); len += num_len + 1; SET_TRIGGER_GLOBAL_SUB_MSUB_MSUB_STR(LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_hash_indx, tmp_str, len, result); if (PUT_SUCCESS != result) { RESTORE_TRIGGER_REGION_INFO; return result; } } else { /* There has to be an entry with the old hash value, we just looked it up */ assert(FALSE); rts_error(VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, LEN_AND_LIT("\"#TRHASH\""), set_hash->str.len, set_hash->str.addr); } } if (search_trigger_hash(trigvn, trigvn_len, kill_hash, old_trig_index, &hash_index)) { MV_FORCE_UMVAL(&mv_hash, kill_hash->hash_code); MV_FORCE_MVAL(&mv_hash_indx, hash_index); BUILD_HASHT_SUB_MSUB_MSUB_CURRKEY(LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_hash_indx); if (!gvcst_get(&key_val)) { /* There has to be a #TRHASH entry */ assert(FALSE); rts_error(VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, LEN_AND_LIT("\"#TRHASH\""), kill_hash->str.len, kill_hash->str.addr); } assert((MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT) >= key_val.str.len); len = STRLEN(key_val.str.addr); memcpy(tmp_str, key_val.str.addr, len); ptr = tmp_str + len; *ptr++ = '\0'; num_len = 0; I2A(ptr, num_len, new_trig_index); len += num_len + 1; SET_TRIGGER_GLOBAL_SUB_MSUB_MSUB_STR(LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_hash_indx, tmp_str, len, result); if (PUT_SUCCESS != result) { RESTORE_TRIGGER_REGION_INFO; return result; } } else { /* There has to be an entry with the old hash value, we just looked it up */ assert(FALSE); rts_error(VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, LEN_AND_LIT("\"#TRHASH\""), kill_hash->str.len, kill_hash->str.addr); } RESTORE_TRIGGER_REGION_INFO; return PUT_SUCCESS; }
void op_svget(int varnum, mval *v) { io_log_name *tl; int count; gtm_uint64_t ucount; char *c1, *c2; mval *mvp; # ifdef UNIX d_rm_struct *d_rm; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # if defined(UNIX) && defined(DEBUG) if (gtm_white_box_test_case_enabled && (WBTEST_HUGE_ALLOC == gtm_white_box_test_case_number)) { if (1 == gtm_white_box_test_case_count) totalAlloc = totalAllocGta = totalRmalloc = totalRallocGta = totalUsed = totalUsedGta = 0xffff; else if (2 == gtm_white_box_test_case_count) totalAlloc = totalAllocGta = totalRmalloc = totalRallocGta = totalUsed = totalUsedGta = 0xfffffff; else if (3 == gtm_white_box_test_case_count) { # ifdef GTM64 if (8 == SIZEOF(size_t)) totalAlloc = totalAllocGta = totalRmalloc = totalRallocGta = totalUsed = totalUsedGta = 0xfffffffffffffff; else # endif totalAlloc = totalAllocGta = totalRmalloc = totalRallocGta = totalUsed = totalUsedGta = 0xfffffff; } else totalAlloc = totalAllocGta = totalRmalloc = totalRallocGta = totalUsed = totalUsedGta = GTM64_ONLY(SIZEOF(size_t)) NON_GTM64_ONLY(SIZEOF(size_t) > 4 ? 4 : SIZEOF(size_t)); } # endif switch (varnum) { case SV_HOROLOG: op_horolog(v); break; case SV_ZGBLDIR: v->mvtype = MV_STR; v->str = dollar_zgbldir.str; break; case SV_PRINCIPAL: tl = dollar_principal ? dollar_principal : io_root_log_name->iod->trans_name; v->str.addr = tl->dollar_io; v->str.len = tl->len; /*** The following should be in the I/O code ***/ if (ESC == *v->str.addr) { if (5 > v->str.len) v->str.len = 0; else { v->str.addr += ESC_OFFSET; v->str.len -= ESC_OFFSET; } } s2pool(&(v->str)); v->mvtype = MV_STR; break; case SV_ZIO: v->mvtype = MV_STR; /* NOTE: This is **NOT** equivalent to : * io_curr_log_name->dollar_io */ v->str.addr = io_curr_device.in->trans_name->dollar_io; v->str.len = io_curr_device.in->trans_name->len; if (ESC == *v->str.addr) { if (5 > v->str.len) v->str.len = 0; else { v->str.addr += ESC_OFFSET; v->str.len -= ESC_OFFSET; } } s2pool(&(v->str)); break; case SV_JOB: *v = dollar_job; break; case SV_REFERENCE: get_reference(v); break; case SV_SYSTEM: *v = dollar_system; break; case SV_STORAGE: /* double2mval(v, getstorage()); Causes issues with unaligned stack on x86_64 - remove until fixed */ i2mval(v, getstorage()); break; case SV_TLEVEL: count = (int)dollar_tlevel; MV_FORCE_MVAL(v, count); break; case SV_TRESTART: MV_FORCE_MVAL(v, (int)((MAX_VISIBLE_TRESTART < dollar_trestart) ? MAX_VISIBLE_TRESTART : dollar_trestart)); break; case SV_X: count = (int)io_curr_device.out->dollar.x; MV_FORCE_MVAL(v, count); break; case SV_Y: count = (int)io_curr_device.out->dollar.y; MV_FORCE_MVAL(v, count); break; case SV_ZA: count = (int)io_curr_device.in->dollar.za; MV_FORCE_MVAL(v, count); break; case SV_ZB: c1 = (char *)io_curr_device.in->dollar.zb; c2 = c1 + SIZEOF(io_curr_device.in->dollar.zb); ENSURE_STP_FREE_SPACE(SIZEOF(io_curr_device.in->dollar.zb)); v->mvtype = MV_STR; v->str.addr = (char *)stringpool.free; while (c1 < c2 && *c1) *stringpool.free++ = *c1++; v->str.len = INTCAST((char *)stringpool.free - v->str.addr); break; case SV_ZC: /****THESE ARE DUMMY VALUES ONLY!!!!!!!!!!!!!!!!!****/ MV_FORCE_MVAL(v, 0); break; case SV_ZCMDLINE: get_command_line(v, TRUE); /* TRUE to indicate we want $ZCMDLINE (i.e. processed not actual command line) */ break; case SV_ZEOF: # ifdef UNIX if (rm == io_curr_device.in->type) { d_rm = (d_rm_struct *)io_curr_device.in->dev_sp; if (RM_READ != d_rm->lastop) { *v = literal_zero; break; } } # endif *v = io_curr_device.in->dollar.zeof ? literal_one : literal_zero; break; case SV_ZQUIT: *v = dollar_zquit_anyway ? literal_one : literal_zero; break; case SV_IO: v->str.addr = io_curr_device.in->name->dollar_io; v->str.len = io_curr_device.in->name->len; /*** The following should be in the I/O code ***/ if (ESC == *v->str.addr) { if (5 > v->str.len) v->str.len = 0; else { v->str.addr += ESC_OFFSET; v->str.len -= ESC_OFFSET; } } s2pool(&(v->str)); v->mvtype = MV_STR; break; case SV_PROMPT: v->mvtype = MV_STR; v->str.addr = (TREF(gtmprompt)).addr; v->str.len = (TREF(gtmprompt)).len; s2pool(&v->str); break; case SV_ZCOMPILE: v->mvtype = MV_STR; v->str = TREF(dollar_zcompile); s2pool(&(v->str)); break; case SV_ZDIR: setzdir(NULL, v); if (v->str.len != dollar_zdir.str.len || 0 != memcmp(v->str.addr, dollar_zdir.str.addr, v->str.len)) gtm_putmsg_csa(CSA_ARG(NULL) VARLSTCNT(6) ERR_ZDIROUTOFSYNC, 4, v->str.len, v->str.addr, dollar_zdir.str.len, dollar_zdir.str.addr); SKIP_DEVICE_IF_NOT_NEEDED(v); s2pool(&(v->str)); break; case SV_ZSTEP: *v = dollar_zstep; break; case SV_ZMODE: *v = TREF(dollar_zmode); break; case SV_ZMAXTPTIME: i2mval(v, TREF(dollar_zmaxtptime)); break; case SV_ZPOS: getzposition(v); break; case SV_ZPROC: getzprocess(); *v = dollar_zproc; break; case SV_ZLEVEL: count = dollar_zlevel(); MV_FORCE_MVAL(v, count); break; case SV_ZROUTINES: if (!TREF(zro_root)) zro_init(); v->mvtype = MV_STR; v->str = TREF(dollar_zroutines); s2pool(&(v->str)); break; case SV_ZSOURCE: v->mvtype = MV_STR; v->str = dollar_zsource.str; break; case SV_ZSTATUS: *v = dollar_zstatus; s2pool(&(v->str)); break; case SV_ZTRAP: v->mvtype = MV_STR; v->str = dollar_ztrap.str; assert(!v->str.len || !ztrap_explicit_null); s2pool(&(v->str)); break; case SV_DEVICE: get_dlr_device(v); break; case SV_KEY: get_dlr_key(v); break; case SV_ZVERSION: v->mvtype = MV_STR; v->str.addr = (char *)gtm_release_name; v->str.len = gtm_release_name_len; break; case SV_ZSYSTEM: MV_FORCE_MVAL(v, dollar_zsystem); break; case SV_ZCSTATUS: /* Maintain the external $ZCSTATUS == 1 for SUCCESS on UNIX while internal good is 0 */ MV_FORCE_MVAL(v, UNIX_ONLY((0 == TREF(dollar_zcstatus)) ? 1 : ) TREF(dollar_zcstatus)); break; case SV_ZEDITOR: MV_FORCE_MVAL(v, dollar_zeditor); break; case SV_QUIT: MV_FORCE_MVAL(v, dollar_quit()); break; case SV_ECODE: ecode_get(-1, v); break; case SV_ESTACK: count = (dollar_zlevel() - 1) - dollar_estack_delta.m[0]; MV_FORCE_MVAL(v, count); break; case SV_ETRAP: v->mvtype = MV_STR; v->str = dollar_etrap.str; assert(!v->str.len || !ztrap_explicit_null); s2pool(&(v->str)); break; case SV_STACK: count = (dollar_zlevel() - 1); MV_FORCE_MVAL(v, count); break; case SV_ZERROR: v->mvtype = MV_STR; v->str = dollar_zerror.str; s2pool(&(v->str)); break; case SV_ZYERROR: v->mvtype = MV_STR; v->str = dollar_zyerror.str; s2pool(&(v->str)); break; case SV_ZINTERRUPT: v->mvtype = MV_STR; v->str = dollar_zinterrupt.str; s2pool(&(v->str)); break; case SV_ZININTERRUPT: MV_FORCE_MVAL(v, dollar_zininterrupt); break; case SV_ZJOB: MV_FORCE_UMVAL(v, dollar_zjob); break; case SV_ZDATE_FORM: MV_FORCE_MVAL(v, TREF(zdate_form)); break; case SV_ZTEXIT: *v = dollar_ztexit; break; case SV_ZALLOCSTOR: ucount = (gtm_uint64_t)totalAlloc + (gtm_uint64_t)totalAllocGta; ui82mval(v, ucount); break; case SV_ZREALSTOR: ucount = (gtm_uint64_t)totalRmalloc + (gtm_uint64_t)totalRallocGta; ui82mval(v, ucount); break; case SV_ZUSEDSTOR: ucount = (gtm_uint64_t)totalUsed + (gtm_uint64_t)totalUsedGta; ui82mval(v, ucount); break; case SV_ZCHSET: v->mvtype = MV_STR; v->str = dollar_zchset; break; case SV_ZPATNUMERIC: v->mvtype = MV_STR; v->str = dollar_zpatnumeric; break; case SV_ZTNAME: case SV_ZTCODE: /* deprecated */ # ifdef GTM_TRIGGER if (NULL == dollar_ztname) memcpy(v, &literal_null, SIZEOF(mval)); else { v->mvtype = MV_STR; v->str.addr = dollar_ztname->addr; v->str.len = dollar_ztname->len; } break; # else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZTDATA: # ifdef GTM_TRIGGER /* Value comes from GT.M, but it might be numeric and need conversion to a string */ assert(!dollar_ztdata || MV_DEFINED(dollar_ztdata)); if (NULL != dollar_ztdata) MV_FORCE_STR(dollar_ztdata); memcpy(v, (NULL != dollar_ztdata) ? dollar_ztdata : &literal_null, SIZEOF(mval)); break; # else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZTDELIM: # ifdef GTM_TRIGGER assert(!dollar_ztdelim || MV_DEFINED(dollar_ztdelim)); if (NULL == dollar_ztdelim || !(MV_STR & dollar_ztdelim->mvtype) || (0 == dollar_ztdelim->str.len)) memcpy(v, &literal_null, SIZEOF(mval)); else memcpy(v, dollar_ztdelim, SIZEOF(mval)); break; # else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZTOLDVAL: # ifdef GTM_TRIGGER /* Value comes from GT.M, but it might be numeric and need conversion to a string */ assert(!dollar_ztoldval || MV_DEFINED(dollar_ztoldval)); if (NULL != dollar_ztoldval) MV_FORCE_STR(dollar_ztoldval); memcpy(v, (NULL != dollar_ztoldval) ? dollar_ztoldval : &literal_null, SIZEOF(mval)); break; # else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZTRIGGEROP: # ifdef GTM_TRIGGER /* Value comes from GT.M, but assert it's a string */ assert(!dollar_ztriggerop || (MV_STR & dollar_ztriggerop->mvtype)); memcpy(v, (NULL != dollar_ztriggerop) ? dollar_ztriggerop : &literal_null, SIZEOF(mval)); break; # else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZTUPDATE: # ifdef GTM_TRIGGER /* Value comes from GT.M, but if there were no delims involved, the value will be undefined, and * we return a "literal_null". */ memcpy(v, ((NULL != dollar_ztupdate && (MV_STR & dollar_ztupdate->mvtype)) ? dollar_ztupdate : &literal_null), SIZEOF(mval)); break; # else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZTVALUE: # ifdef GTM_TRIGGER /* Value comes from user-land so make sure things are proper */ assert(!dollar_ztvalue || MV_DEFINED(dollar_ztvalue)); if (NULL != dollar_ztvalue) MV_FORCE_STR(dollar_ztvalue); memcpy(v, (NULL != dollar_ztvalue) ? dollar_ztvalue : &literal_null, SIZEOF(mval)); break; # else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZTWORMHOLE: # ifdef GTM_TRIGGER /* Value comes from user-land so make sure things are proper */ mvp = &dollar_ztwormhole; if (MV_DEFINED(mvp)) { MV_FORCE_STR(mvp); memcpy(v, mvp, SIZEOF(mval)); } else memcpy(v, &literal_null, SIZEOF(mval)); ztwormhole_used = TRUE; break; # else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZTSLATE: # ifdef GTM_TRIGGER /* Value comes from user-land so make sure things are proper */ assert(MV_DEFINED((&dollar_ztslate))); mvp = &dollar_ztslate; MV_FORCE_STR(mvp); memcpy(v, mvp, SIZEOF(mval)); break; # else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZTLEVEL: # ifdef GTM_TRIGGER MV_FORCE_MVAL(v, gtm_trigger_depth); break; # else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZONLNRLBK: # ifdef UNIX count = TREF(dollar_zonlnrlbk); MV_FORCE_MVAL(v, count); break; # else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZCLOSE: # ifdef UNIX count = TREF(dollar_zclose); MV_FORCE_MVAL(v, count); break; # else rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZKEY: get_dlr_zkey(v); break; default: assertpro(FALSE); } }