boolean_t trigger_delete_name(char *trigger_name, uint4 trigger_name_len, uint4 *trig_stats) { sgmnt_addrs *csa; char curr_name[MAX_MIDENT_LEN + 1]; uint4 curr_name_len, orig_name_len; mval mv_curr_nam; char *ptr; char *name_tail_ptr; char save_name[MAX_MIDENT_LEN + 1]; gv_key save_currkey[DBKEYALLOC(MAX_KEY_SZ)]; gd_region *save_gv_cur_region, *lgtrig_reg; gv_namehead *save_gv_target; sgm_info *save_sgm_info_ptr; mval trig_gbl; mval *trigger_count; char trigvn[MAX_MIDENT_LEN + 1]; int trigvn_len; int trig_indx; int badpos; boolean_t wildcard; char utilprefix[1024]; int utilprefixlen; boolean_t first_gtmio; uint4 triggers_deleted; mval trigjrec; boolean_t jnl_format_done; gd_region *reg, *reg_top; char disp_trigvn[MAX_MIDENT_LEN + SPANREG_REGION_LITLEN + MAX_RN_LEN + 1 + 1]; /* SPANREG_REGION_LITLEN for " (region ", MAX_RN_LEN for region name, * 1 for ")" and 1 for trailing '\0'. */ int disp_trigvn_len; int trig_protected_mval_push_count; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; badpos = 0; trigjrec.mvtype = MV_STR; trigjrec.str.len = trigger_name_len--; trigjrec.str.addr = trigger_name++; orig_name_len = trigger_name_len; if ((0 == trigger_name_len) || (trigger_name_len != (badpos = validate_input_trigger_name(trigger_name, trigger_name_len, &wildcard)))) { /* is the input name valid */ CONV_STR_AND_PRINT("Invalid trigger NAME string: ", orig_name_len, trigger_name); /* badpos is the string position where the bad character was found, pretty print it */ trig_stats[STATS_ERROR_TRIGFILE]++; return TRIG_FAILURE; } name_tail_ptr = trigger_name + trigger_name_len - 1; if ((TRIGNAME_SEQ_DELIM == *name_tail_ptr) || wildcard) trigger_name_len--; /* drop the trailing # sign for wildcard */ jnl_format_done = FALSE; lgtrig_reg = NULL; first_gtmio = TRUE; triggers_deleted = 0; assert(trigger_name_len < MAX_MIDENT_LEN); memcpy(save_name, trigger_name, trigger_name_len); save_name[trigger_name_len] = '\0'; utilprefixlen = ARRAYSIZE(utilprefix); trig_protected_mval_push_count = 0; INCR_AND_PUSH_MV_STENT(trigger_count); /* Protect trigger_count from garbage collection */ for (reg = gd_header->regions, reg_top = reg + gd_header->n_regions; reg < reg_top; reg++) { GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(reg); csa = cs_addrs; if (NULL == csa) /* not BG or MM access method */ continue; /* gv_target now points to ^#t in region "reg" */ /* To write the LGTRIG logical jnl record, choose some region that has journaling enabled */ if (!reg->read_only && !jnl_format_done && JNL_WRITE_LOGICAL_RECS(csa)) lgtrig_reg = reg; if (!gv_target->root) continue; memcpy(curr_name, save_name, trigger_name_len); curr_name_len = trigger_name_len; do { /* GVN = $get(^#t("#TNAME",curr_name)) */ BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), curr_name, curr_name_len); if (gvcst_get(&trig_gbl)) { if (reg->read_only) rts_error_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_TRIGMODREGNOTRW, 2, REG_LEN_STR(reg)); SAVE_TRIGGER_REGION_INFO(save_currkey); ptr = trig_gbl.str.addr; trigvn_len = MIN(trig_gbl.str.len, MAX_MIDENT_LEN); STRNLEN(ptr, trigvn_len, trigvn_len); ptr += trigvn_len; if ((trig_gbl.str.len == trigvn_len) || ('\0' != *ptr)) { /* We expect $c(0) in the middle of ptr. If not found, 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(NULL) VARLSTCNT(6) ERR_TRIGNAMBAD, 4, LEN_AND_LIT("\"#TNAME\""), curr_name_len, curr_name); } memcpy(trigvn, trig_gbl.str.addr, trigvn_len); /* the index is just beyond the length of the GVN string */ ptr++; A2I(ptr, trig_gbl.str.addr + trig_gbl.str.len, trig_indx); if (1 > trig_indx) { /* Trigger indexes start from 1 */ 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(NULL) VARLSTCNT(6) ERR_TRIGNAMBAD, 4, LEN_AND_LIT("\"#TNAME\""), curr_name_len, curr_name); } SET_DISP_TRIGVN(reg, disp_trigvn, disp_trigvn_len, trigvn, trigvn_len); /* $get(^#t(GVN,"COUNT") */ BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT)); if (!gvcst_get(trigger_count)) { UTIL_PRINT_PREFIX_IF_NEEDED(first_gtmio, utilprefix, &utilprefixlen); util_out_print_gtmio("Trigger named !AD exists in the lookup table, " "but global ^!AD has no triggers", FLUSH, curr_name_len, curr_name, disp_trigvn_len, disp_trigvn); trig_stats[STATS_ERROR_TRIGFILE]++; RETURN_AND_POP_MVALS(TRIG_FAILURE); } if (!jnl_format_done && JNL_WRITE_LOGICAL_RECS(csa)) { jnl_format(JNL_LGTRIG, NULL, &trigjrec, 0); jnl_format_done = TRUE; } /* kill the target trigger for GVN at index trig_indx */ if (PUT_SUCCESS != (trigger_delete(trigvn, trigvn_len, trigger_count, trig_indx))) { UTIL_PRINT_PREFIX_IF_NEEDED(first_gtmio, utilprefix, &utilprefixlen); util_out_print_gtmio("Trigger named !AD exists in the lookup table for global ^!AD," \ " but was not deleted!", FLUSH, orig_name_len, trigger_name, disp_trigvn_len, disp_trigvn); trig_stats[STATS_ERROR_TRIGFILE]++; RETURN_AND_POP_MVALS(TRIG_FAILURE); } else { csa->incr_db_trigger_cycle = TRUE; trigger_incr_cycle(trigvn, trigvn_len); /* ^#t records changed, increment cycle */ if (dollar_ztrigger_invoked) { /* Increment db_dztrigger_cycle so that next gvcst_put/gvcst_kill in this * transaction, on this region, will re-read triggers. See trigger_update.c * for a comment on why it is okay for db_dztrigger_cycle to be incremented * more than once in the same transaction. */ csa->db_dztrigger_cycle++; } trig_stats[STATS_DELETED]++; if (0 == trig_stats[STATS_ERROR_TRIGFILE]) { UTIL_PRINT_PREFIX_IF_NEEDED(first_gtmio, utilprefix, &utilprefixlen); util_out_print_gtmio("Deleted trigger named '!AD' for global ^!AD", FLUSH, curr_name_len, curr_name, disp_trigvn_len, disp_trigvn); } } trigger_count->mvtype = 0; /* allow stp_gcol to release the current contents if necessary */ RESTORE_TRIGGER_REGION_INFO(save_currkey); triggers_deleted++; } if (!wildcard) /* not a wild card, don't $order for the next match */ break; op_gvorder(&mv_curr_nam); if (0 == mv_curr_nam.str.len) break; assert(mv_curr_nam.str.len < MAX_MIDENT_LEN); memcpy(curr_name, mv_curr_nam.str.addr, mv_curr_nam.str.len); curr_name_len = mv_curr_nam.str.len; if (0 != memcmp(curr_name, save_name, trigger_name_len)) /* stop when gv_order returns a string that no longer starts save_name */ break; } while (TRUE); } DECR_AND_POP_MV_STENT(); if (!jnl_format_done && (NULL != lgtrig_reg)) { /* There was no journaled region that had a ^#t update, but found at least one journaled region * so write a LGTRIG logical jnl record there. */ GVTR_SWITCH_REG_AND_HASHT_BIND_NAME(lgtrig_reg); csa = cs_addrs; /* Attach to jnlpool. Normally SET or KILL of the ^#t records take care of this but in * case this is a NO-OP trigger operation that wont update any ^#t records and we still * want to write a TLGTRIG/ULGTRIG journal record. Hence the need to do this. */ JNLPOOL_INIT_IF_NEEDED(csa, csa->hdr, csa->nl); assert(dollar_tlevel); /* below is needed to set update_trans TRUE on this region even if NO db updates happen to ^#t nodes */ T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_TRIGLOADFAIL); jnl_format(JNL_LGTRIG, NULL, &trigjrec, 0); jnl_format_done = TRUE; } if (wildcard) { UTIL_PRINT_PREFIX_IF_NEEDED(first_gtmio, utilprefix, &utilprefixlen); if (triggers_deleted) { trig_stats[STATS_NOERROR_TRIGFILE]++; util_out_print_gtmio("All existing triggers named !AD (count = !UL) now deleted", FLUSH, orig_name_len, trigger_name, triggers_deleted); } else { trig_stats[STATS_UNCHANGED_TRIGFILE]++; util_out_print_gtmio("No matching triggers of the form !AD found for deletion", FLUSH, orig_name_len, trigger_name); } } else if (triggers_deleted) { /* util_out_print_gtmio of "Deleted trigger named ..." already done so no need to do it again */ trig_stats[STATS_NOERROR_TRIGFILE]++; } else { /* No names match. But treat it as a no-op (i.e. success). */ UTIL_PRINT_PREFIX_IF_NEEDED(first_gtmio, utilprefix, &utilprefixlen); util_out_print_gtmio("Trigger named !AD does not exist", FLUSH, orig_name_len, trigger_name); trig_stats[STATS_UNCHANGED_TRIGFILE]++; } return TRIG_SUCCESS; }
int4 trigger_delete(char *trigvn, int trigvn_len, mval *trigger_count, int index) { int count; mval *mv_cnt_ptr; mval mv_val; mval *mv_val_ptr; int num_len; char *ptr1; int4 result; int4 retval; char save_currkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)]; gv_key *save_gv_currkey; 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; uint4 used_trigvn_len; mval val; char val_str[MAX_DIGITS_IN_INT + 1]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; mv_val_ptr = &mv_val; MV_FORCE_MVAL(&trigger_index, index); count = MV_FORCE_INT(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; 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) { tt_val[sub_indx] = NULL; tt_val_len[sub_indx] = 0; continue; } if (TRIGNAME_SUB == sub_indx) { trig_name_len = trig_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_INT(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_INT(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, 0); } 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'; 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 { /* in PRO this is a nasty case that will result in an access violation * because data that should be present is not. In the next go around * with trigger installation this case should be handled better */ assert(!((TRIGNAME_SUB == sub_indx) || (CMD_SUB == sub_indx) || (CHSET_SUB == sub_indx))); /* these should not be zero length */ trig_len = 0; } if (NUM_SUBS > sub_indx) { tt_val[sub_indx] = ptr1; tt_val_len[sub_indx] = trig_len; if (0 < trig_len) { memcpy(ptr1, trigger_value.str.addr, trig_len); ptr1 += trig_len; } *ptr1++ = '\0'; } } /* $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_INT(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_INT(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(trigvn_len, 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_MVAL(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 */ } trigger_incr_cycle(trigvn, trigvn_len); return PUT_SUCCESS; }