boolean_t gvcst_query(void) { /* Similar to gvcst_order and gvcst_zprevious. In each case we skip over hidden subscripts as needed. * * 1 2 3 NULL <--- order/zprev... * 1 2 3 NULL NULL * 1 2 3 NULL NULL NULL <--- query from here... * 1 2 3 NULL NULL NULL hidden * 1 2 3 NULL NULL hidden * 1 2 3 NULL hidden * 1 2 3 hidden <--- ... skip this guy and go to bottom/top, respectively * 1 2 3 7 <--- ... needs to end up here */ boolean_t found, is_hidden, sn_tpwrapped; boolean_t est_first_pass; gv_key save_currkey[DBKEYALLOC(MAX_KEY_SZ)]; int i; int save_dollar_tlevel; DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); found = gvcst_query2(); # ifdef UNIX assert(save_dollar_tlevel == dollar_tlevel); CHECK_HIDDEN_SUBSCRIPT_AND_RETURN(found, gv_altkey, is_hidden); IF_SN_DISALLOWED_AND_NO_SPAN_IN_DB(return found); assert(found && is_hidden); SAVE_GV_CURRKEY(save_currkey); if (!dollar_tlevel) { sn_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_query_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); } else sn_tpwrapped = FALSE; for (i = 0; i <= MAX_GVSUBSCRIPTS; i++) { INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_query, (gtm_uint64_t) -1); found = gvcst_query2(); CHECK_HIDDEN_SUBSCRIPT_AND_BREAK(found, gv_altkey, is_hidden); assert(found && is_hidden); /* Replace last subscript to be the highest possible hidden subscript so another * gvcst_query2 will give us the next non-hidden subscript. */ REPLACE_HIDDEN_SUB_TO_HIGHEST(gv_altkey, gv_currkey); /* uses gv_altkey to modify gv_currkey */ } if (sn_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } RESTORE_GV_CURRKEY(save_currkey); assert(save_dollar_tlevel == dollar_tlevel); # endif return found; }
boolean_t gvcst_order(void) { /* See gvcst_query.c */ boolean_t found, is_hidden, sn_tpwrapped; boolean_t est_first_pass; gv_key save_currkey[DBKEYALLOC(MAX_KEY_SZ)]; int end, prev, oldend; int save_dollar_tlevel; DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); found = gvcst_order2(); # ifdef UNIX assert(save_dollar_tlevel == dollar_tlevel); CHECK_HIDDEN_SUBSCRIPT_AND_RETURN(found, gv_altkey, is_hidden); assert(found && is_hidden); IF_SN_DISALLOWED_AND_NO_SPAN_IN_DB(return found); SAVE_GV_CURRKEY_LAST_SUBSCRIPT(save_currkey, prev, oldend); if (!dollar_tlevel) { sn_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_order_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_order, (gtm_uint64_t) -1); found = gvcst_order2(); } else sn_tpwrapped = FALSE; if (found) { CHECK_HIDDEN_SUBSCRIPT(gv_altkey, is_hidden); if (is_hidden) { /* Replace last subscript to be the highest possible hidden subscript so another * gvcst_order2 will give us the next non-hidden subscript. */ REPLACE_HIDDEN_SUB_TO_HIGHEST(gv_altkey, gv_currkey); /* uses gv_altkey to modify gv_currkey */ /* fix up since it should only be externally counted as one $order */ INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_order, (gtm_uint64_t) -1); found = gvcst_order2(); } } if (sn_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } RESTORE_GV_CURRKEY_LAST_SUBSCRIPT(save_currkey, prev, oldend); assert(save_dollar_tlevel == dollar_tlevel); # endif return found; }
boolean_t gvcst_queryget(mval *val) { bool found, is_hidden, is_dummy = FALSE, sn_tpwrapped; boolean_t est_first_pass; char save_currkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)]; gv_key *save_gv_currkey; int save_dollar_tlevel; DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); found = gvcst_queryget2(val, NULL); # ifdef UNIX assert(save_dollar_tlevel == dollar_tlevel); CHECK_HIDDEN_SUBSCRIPT(gv_altkey, is_hidden); if (found && IS_SN_DUMMY(val->str.len, val->str.addr)) is_dummy = TRUE; if (!found || (!is_dummy && !is_hidden)) return found; IF_SN_DISALLOWED_AND_NO_SPAN_IN_DB(return found); SAVE_GV_CURRKEY; if (!dollar_tlevel) { sn_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_queryget_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); } else sn_tpwrapped = FALSE; found = gvcst_query(); COPY_KEY(gv_currkey, gv_altkey); /* set gv_currkey to gv_altkey */ found = gvcst_get(val); INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_get, (gtm_uint64_t) -1); /* only counted externally as one get */ INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_query, (gtm_uint64_t) -1); if (sn_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } RESTORE_GV_CURRKEY; assert(save_dollar_tlevel == dollar_tlevel); # endif return found; }
/* 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)
void gvcst_spr_kill(void) { boolean_t spr_tpwrapped; boolean_t est_first_pass; int reg_index; gd_binding *start_map, *end_map, *map; gd_region *reg, *gd_reg_start; gd_addr *addr; gv_namehead *start_map_gvt; gvnh_reg_t *gvnh_reg; trans_num gd_targ_tn, *tn_array; # ifdef DEBUG int save_dollar_tlevel; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; start_map = TREF(gd_targ_map); /* set up by op_gvname/op_gvnaked/op_gvextnam done just before invoking op_gvkill */ start_map_gvt = gv_target; /* save gv_target corresponding to start_map so we can restore at end */ /* Find out if the next (in terms of $order) key maps to same map as currkey. If so, no spanning activity needed */ GVKEY_INCREMENT_ORDER(gv_currkey); end_map = gv_srch_map_linear(start_map, (char *)&gv_currkey->base[0], gv_currkey->end - 1); BACK_OFF_ONE_MAP_ENTRY_IF_EDGECASE(gv_currkey->base, gv_currkey->end - 1, end_map); GVKEY_UNDO_INCREMENT_ORDER(gv_currkey); if (start_map == end_map) { assert(gv_target == start_map_gvt); if (IS_OK_TO_INVOKE_GVCST_KILL(start_map_gvt)) gvcst_kill(TRUE); return; } /* Do any initialization that is independent of retries BEFORE the op_tstart */ addr = TREF(gd_targ_addr); assert(NULL != addr); gd_reg_start = &addr->regions[0]; tn_array = TREF(gd_targ_reg_array); gvnh_reg = TREF(gd_targ_gvnh_reg); assert(NULL != gvnh_reg); assert(NULL != gvnh_reg->gvspan); /* Now that we know the keyrange maps to more than one region, go through each of them and do the kill. * Since multiple regions are potentially involved, need a TP fence. */ DEBUG_ONLY(save_dollar_tlevel = dollar_tlevel); if (!dollar_tlevel) { spr_tpwrapped = TRUE; op_tstart((IMPLICIT_TSTART), TRUE, &literal_batch, 0); ESTABLISH_NORET(gvcst_spr_kill_ch, est_first_pass); GVCST_ROOT_SEARCH_AND_PREP(est_first_pass); } else spr_tpwrapped = FALSE; assert(gv_cur_region == start_map->reg.addr); DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); /* Do any initialization that is dependent on retries AFTER the op_tstart */ map = start_map; INCREMENT_GD_TARG_TN(gd_targ_tn); /* takes a copy of incremented "TREF(gd_targ_tn)" into local variable "gd_targ_tn" */ /* Verify that initializations that happened before op_tstart are still unchanged */ assert(addr == TREF(gd_targ_addr)); assert(tn_array == TREF(gd_targ_reg_array)); assert(gvnh_reg == TREF(gd_targ_gvnh_reg)); for ( ; map <= end_map; map++) { reg = map->reg.addr; GET_REG_INDEX(addr, gd_reg_start, reg, reg_index); /* sets "reg_index" */ assert((map != start_map) || (tn_array[reg_index] != gd_targ_tn)); assert(TREF(gd_targ_reg_array_size) > reg_index); if (tn_array[reg_index] == gd_targ_tn) continue; if (map != start_map) GV_BIND_SUBSREG(addr, reg, gvnh_reg); /* sets gv_target/gv_cur_region/cs_addrs */ assert(reg->open); if (IS_OK_TO_INVOKE_GVCST_KILL(gv_target)) gvcst_kill(TRUE); tn_array[reg_index] = gd_targ_tn; } if (gv_target != start_map_gvt) { /* Restore gv_cur_region/gv_target etc. */ gv_target = start_map_gvt; gv_cur_region = start_map->reg.addr; change_reg(); } DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE); if (spr_tpwrapped) { op_tcommit(); REVERT; /* remove our condition handler */ } assert(save_dollar_tlevel == dollar_tlevel); return; }