boolean_t check_trigger_name(char *name_str, uint4 *name_len) { uint4 len, lcl_name_len; unsigned char *ptr; /* To conform to the other uses of the check_* functions and their use in the GET_CLI_STR_AND_CHECK macro * name_len is passed in as a uint4 *, but in this case a uint4 would suffice. To mitigate the dereferencing * issues, a local copy is used. */ lcl_name_len = *name_len; if (MAX_USER_TRIGNAME_LEN < lcl_name_len) { util_out_print_gtmio("Trigger name length [!UL] exceeds maximum supported length of [!UL]", FLUSH, lcl_name_len, MAX_USER_TRIGNAME_LEN); return FALSE; } ptr = (unsigned char *)name_str; if (('%' != *ptr) && !ISALPHA_ASCII(*ptr)) { util_out_print_gtmio("Trigger name does not begin with an ascii alphabet or %", FLUSH); return FALSE; } for (len = lcl_name_len - 1, ptr++; (0 < len) && ISALNUM_ASCII(*ptr); ptr++, len--) ; if (len) util_out_print_gtmio("Trigger name has non-alphanumeric character", FLUSH); return (0 == len); }
STATICFNDEF boolean_t process_options(char *option_str, uint4 option_len, boolean_t *isolation, boolean_t *noisolation, boolean_t *consistency, boolean_t *noconsistency) { char local_options[MAX_OPTIONS_LEN]; char *ptr, *strtokptr; if (MAX_OPTIONS_LEN < option_len) { util_out_print_gtmio("Too many options", FLUSH); return FALSE; } memcpy(local_options, option_str, option_len); *isolation = *noisolation = *consistency = *noconsistency = FALSE; ptr = local_options; for ( ; 0 < option_len; ptr++, option_len--) *ptr = TOUPPER(*ptr); ptr = STRTOK_R(local_options, ",", &strtokptr); do { switch (*ptr) { case 'C': if (1 == STRLEN(ptr)) *consistency = TRUE; else { assert(0 == MEMCMP_LIT(ptr, HASHT_OPT_CONSISTENCY)); *consistency = TRUE; } break; case 'I': if (1 == STRLEN(ptr)) *isolation = TRUE; else { assert(0 == MEMCMP_LIT(ptr, HASHT_OPT_ISOLATION)); *isolation = TRUE; } break; case 'N': assert('O' == *(ptr + 1)); if ('C' == *(ptr + 2)) { assert(0 == MEMCMP_LIT(ptr, HASHT_OPT_NOCONSISTENCY)); *noconsistency = TRUE; } else { assert('I' == *(ptr + 2)); assert(0 == MEMCMP_LIT(ptr, HASHT_OPT_NOISOLATION)); *noisolation = TRUE; } break; default: assert(FALSE); /* Parsing should have found invalid command */ break; } } while (ptr = STRTOK_R(NULL, ",", &strtokptr)); return !((*isolation && *noisolation) || (*consistency && *noconsistency)); }
STATICFNDEF char *scan_to_end_quote(char *ptr, int len, int max_len) { int str_len = 0; if ((1 >= len) || ('"' != *ptr)) return NULL; /* Invalid string - it needs at least "" */ if (max_len < ++str_len) { util_out_print_gtmio("String too long", FLUSH); return NULL; } ptr++; len--; for ( ; 0 < len; len--, ptr++) { /* Scan until the closing quote */ if ('"' == *ptr) { if (1 == len) break; if ('"' == *(ptr + 1)) { if (max_len < ++str_len) { util_out_print_gtmio("String too long", FLUSH); return NULL; } ptr++; len--; } else break; } if (max_len < ++str_len) { util_out_print_gtmio("String too long", FLUSH); return NULL; } } return (('"' == *ptr) ? ptr + 1 : NULL); }
STATICFNDEF boolean_t process_subscripts(char *subscr_str, uint4 *subscr_len, char **next_str, char *out_str, int4 *out_max) { boolean_t alternation; char dst[MAX_GVSUBS_LEN]; int dst_len; char *dst_ptr; int len; boolean_t have_pattern; boolean_t have_range; boolean_t have_star; boolean_t have_dot; int i; int lvn_count; uint4 lvn_len[MAX_LVN_COUNT]; char *lvn_start; char *lvn_str[MAX_LVN_COUNT]; int multiplier; boolean_t newsub; int num1; int num2; char *ptr; char *ptr1; char *save_dst_ptr; int save_len; char *start_dst_ptr; uint4 start_len; uint4 subsc_count; uint4 tmp_len; boolean_t valid_sub; have_pattern = have_range = valid_sub = have_star = FALSE; ptr = subscr_str; start_len = len = *subscr_len; dst_len = 0; newsub = TRUE; subsc_count = lvn_count = 0; start_dst_ptr = dst_ptr = dst; while ((0 < len) && (')' != *ptr)) { if (ISDIGIT_ASCII(*ptr) || ('-' == *ptr)) { PROCESS_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); newsub = FALSE; valid_sub = TRUE; } else if (ISALPHA_ASCII(*ptr) || ('%' == *ptr)) { if (!newsub) { util_out_print_gtmio("Invalid subscript", FLUSH); return FALSE; } lvn_start = ptr; tmp_len = 0; do { if (MAX_MIDENT_LEN < ++tmp_len) { util_out_print_gtmio("Variable name too long", FLUSH); return FALSE; } if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = *ptr++; len--; } while (ISALNUM_ASCII(*ptr)); if ('=' != *ptr) { util_out_print_gtmio("Invalid variable name in subscript", FLUSH); return FALSE; } for (i = 0; i < lvn_count; i++) { if ((lvn_len[i] == tmp_len) && (0 == strncmp(lvn_str[i], lvn_start, tmp_len))) { util_out_print_gtmio("Duplicate variable name in subscript", FLUSH); return FALSE; } } lvn_str[lvn_count] = lvn_start; lvn_len[lvn_count] = tmp_len; lvn_count++; if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = *ptr++; /* move past = */ start_dst_ptr = dst_ptr; len--; if ((0 < len) && ((',' == *ptr) || (')' == *ptr))) { util_out_print_gtmio("Variable name not followed by valid subscript", FLUSH); return FALSE; } continue; } else { switch (*ptr) { case '"': PROCESS_STRING(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); valid_sub = TRUE; newsub = FALSE; break; case '$': if (!process_dollar_char(&ptr, &len, have_star, &dst_ptr, &dst_len)) { util_out_print_gtmio("Invalid subscript", FLUSH); return FALSE; } newsub = FALSE; valid_sub = TRUE; break; case '?': if (have_range) { util_out_print_gtmio("Range and pattern match not valid in same subscript", FLUSH); return FALSE; } UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); ptr1 = ptr; alternation = FALSE; while ((0 < len) && ((',' != *ptr) || alternation) && ((')' != *ptr) || alternation) && (';' != *ptr)) { num1 = num2 = -1; have_dot = FALSE; if (ISDIGIT_ASCII(*ptr)) { PROCESS_AND_GET_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, num1, MAX_GVSUBS_LEN); } if ('.' == *ptr) { have_dot = TRUE; if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = *ptr++; len--; if (ISDIGIT_ASCII(*ptr)) { PROCESS_AND_GET_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, num2, MAX_GVSUBS_LEN); } if (-1 == num1) num1 = 0; } switch (*ptr) { case '(': if (!alternation && (0 <= num1)) { UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); alternation = TRUE; continue; } break; case ',': if (alternation && (-1 == num1) && (-1 == num2)) { UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); continue; } break; case ')': if (alternation && (-1 == num1) && (-1 == num2)) { UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); alternation = FALSE; continue; } break; default: break; } if ((0 <= num1) && (0 <= num2) && (num2 < num1)) { util_out_print_gtmio("Invalid pattern match range", FLUSH); return FALSE; } switch (TOUPPER(*ptr)) { case 'E': if (have_dot && (0 >= num1) && (-1 == num2)) { /* Treat ?.E the same as * */ have_star = TRUE; dst_ptr = start_dst_ptr; if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = '*'; } /* Note: we're dropping into the following case */ case 'A': case 'C': case 'K': case 'L': case 'N': case 'P': case 'U': case 'V': UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); break; case '"': PROCESS_STRING(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); break; /* Note: we're dropping into the default/error case */ default: CONV_CH_AND_PRINT("Unexpected character !AD in pattern code", ptr); return FALSE; } } have_pattern = TRUE; valid_sub = TRUE; newsub = FALSE; break; case ':': if (have_range) { util_out_print_gtmio("Range within a range not allowed", FLUSH); return FALSE; } if (have_pattern) { util_out_print_gtmio("Pattern not allowed as a range", FLUSH); return FALSE; } UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); if (ISDIGIT_ASCII(*ptr) || ('-' == *ptr)) { PROCESS_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); } else if ('"' == *ptr) { PROCESS_STRING(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); } else if ('$' == *ptr) { if (!process_dollar_char(&ptr, &len, have_star, &dst_ptr, &dst_len)) { util_out_print_gtmio("Invalid range value", FLUSH); return FALSE; } } else if ((0 < len) && (',' != *ptr) && (';' != *ptr) && (')' != *ptr)) { util_out_print_gtmio("Invalid string range", FLUSH); return FALSE; } else { /* A range with no lower end - just scan the numeric or string */ ptr1 = ptr; if (ISDIGIT_ASCII(*ptr) || ('-' == *ptr)) { PROCESS_NUMERIC(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); } else if ('"' == *ptr1) { PROCESS_STRING(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); } else if ((0 < len) && ((',' != *ptr) && (';' != *ptr) && (')' != *ptr))) { util_out_print_gtmio("Range value must be integer or string", FLUSH); return FALSE; } else if (!valid_sub) { /* this is a single ":" - treat it just like a * */ have_star = TRUE; dst_ptr = start_dst_ptr; if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = '*'; } } valid_sub = TRUE; newsub = FALSE; have_range = TRUE; break; case '*': if (!have_star) { have_star = TRUE; dst_ptr = start_dst_ptr; if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = '*'; } ptr++; len--; if ((0 < len) && (',' != *ptr) && (';' != *ptr) && (')' != *ptr)) { util_out_print_gtmio("Invalid use of *", FLUSH); return FALSE; } else valid_sub = TRUE; newsub = FALSE; break; case ';': UPDATE_DST(ptr, len, have_star, dst_ptr, dst_len, MAX_GVSUBS_LEN); /* Delete extraneous ; in the subscript */ if ((!have_star) && (newsub || ((0 < len) && ((',' == *ptr) || (';' == *ptr) || ISALPHA_ASCII(*ptr) || ('%' == *ptr) || (')' == *ptr))))) dst_ptr--; valid_sub = FALSE; have_pattern = have_range = FALSE; break; case ',': if (newsub) { util_out_print_gtmio("Empty subscript not allowed", FLUSH); return FALSE; } if (MAX_GVSUBSCRIPTS <= ++subsc_count) { util_out_print_gtmio("Too many subscripts", FLUSH); return FALSE; } if (MAX_GVSUBS_LEN < ++dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *dst_ptr++ = *ptr++; len--; start_dst_ptr = dst_ptr; newsub = TRUE; have_range = have_pattern = valid_sub = have_star = FALSE; break; default: CONV_CH_AND_PRINT("Invalid character !AD in subscript", ptr); return FALSE; } } } if ((0 == len) && (')' != *ptr)) { util_out_print_gtmio("Missing \")\" after global subscript", FLUSH); return FALSE; } if ((start_len == len) || newsub) { util_out_print_gtmio("Empty subscript not allowed", FLUSH); return FALSE; } if ((')' == *ptr) && (MAX_GVSUBSCRIPTS <= ++subsc_count)) { util_out_print_gtmio("Too many subscripts", FLUSH); return FALSE; } CHECK_FOR_ROOM_IN_OUTPUT_AND_COPY(dst, out_str, (int)(dst_ptr - dst), *out_max); *subscr_len = (uint4)(dst_ptr - dst); *next_str = ptr + 1; return TRUE; }
STATICFNDEF boolean_t process_delim(char *delim_str, uint4 *delim_len) { int char_count; mstr dst; int dst_len; char *dst_ptr; char dst_string[MAX_DELIM_LEN + 1]; uint4 len; char *ptr; char *ptr1; int q_len; char src_string[MAX_DELIM_LEN + 1]; mstr src; char *src_ptr; if (MAX_DELIM_LEN < *delim_len) { util_out_print_gtmio("Delimiter too long", FLUSH); return FALSE; } ptr = delim_str; len = *delim_len; src_ptr = src_string; dst_len = 0; /* If ", scan to end quote * If $, look for char --> c or zchar --> zch * If _, leave it */ while (0 < len) { switch (*ptr) { case '"': PROCESS_STRING(ptr, len, FALSE, src_ptr, dst_len, MAX_DELIM_LEN); break; case '$': UPDATE_DST(ptr, len, FALSE, src_ptr, dst_len, MAX_DELIM_LEN); if (0 == len) { util_out_print_gtmio("Invalid entry in delimiter", FLUSH); return FALSE; } if ((3 < len) && ('C' == lower_to_upper_table[*ptr]) && ('H' == lower_to_upper_table[*(ptr + 1)]) && ('A' == lower_to_upper_table[*(ptr + 2)]) && ('R' == lower_to_upper_table[*(ptr + 3)])) { if (MAX_DELIM_LEN < ++dst_len) { util_out_print_gtmio("Trigger definition too long", FLUSH); return FALSE; } *src_ptr++ = 'C'; ptr += 4; len -= 4; } else if ((4 < len) && ('Z' == lower_to_upper_table[*ptr]) && ('C' == lower_to_upper_table[*(ptr + 1)]) && ('H' == lower_to_upper_table[*(ptr + 2)]) && ('A' == lower_to_upper_table[*(ptr + 3)]) && ('R' == lower_to_upper_table[*(ptr + 4)])) { if (MAX_DELIM_LEN < (dst_len + 3)) { util_out_print_gtmio("Trigger definition too long", FLUSH); return FALSE; } MEMCPY_LIT(src_ptr, "ZCH"); src_ptr += 3; dst_len += 3; ptr += 5; len -= 5; } else { UPDATE_DST(ptr, len, FALSE, src_ptr, dst_len, MAX_DELIM_LEN); } break; default: UPDATE_DST(ptr, len, FALSE, src_ptr, dst_len, MAX_DELIM_LEN); break; } } *src_ptr = '\0'; src.addr = src_string; src.len = (mstr_len_t)(src_ptr - src_string); dst.addr = dst_string; dst.len = 0; if (!zwr2format(&src, &dst)) { util_out_print_gtmio("Invalid delimiter", FLUSH); return FALSE; } if (MAX_DELIM_LEN < dst.len) { util_out_print_gtmio("Delimiter too long", FLUSH); return FALSE; } memcpy(delim_str, dst_string, dst.len); *delim_len = dst.len; return TRUE; }
STATICFNDEF boolean_t process_dollar_char(char **src_ptr, int *src_len, boolean_t have_star, char **d_ptr, int *dst_len) { int char_count; char *char_ptr; int len; char *dst_ptr; char dst_string[MAX_DCHAR_LEN]; int lcl_dst_len; mstr m_dst; mstr m_src; char *ptr; int q_len; char *tmp_dst_ptr; tmp_dst_ptr = dst_ptr = *d_ptr; ptr = *src_ptr; len = *src_len; lcl_dst_len = *dst_len; assert('$' == *ptr); UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN); if (0 == len) return FALSE; switch (*ptr) { case 'c': case 'C': UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN); if ((0 < len) && ('(' == *ptr)) break; else if ((3 < len) && ('H' == lower_to_upper_table[*ptr]) && ('A' == lower_to_upper_table[*(ptr + 1)]) && ('R' == lower_to_upper_table[*(ptr + 2)]) && ('(' == *(ptr + 3))) { ptr += 3; len -= 3; break; } else return FALSE; break; case 'z': case 'Z': UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN); if ((2 < len) && ('C' == lower_to_upper_table[*ptr]) && ('H' == lower_to_upper_table[*(ptr + 1)]) && ('(' == *(ptr + 2))) { ptr += 2; len -= 2; } else if ((4 < len) && ('C' == lower_to_upper_table[*ptr]) && ('H' == lower_to_upper_table[*(ptr + 1)]) && ('A' == lower_to_upper_table[*(ptr + 2)]) && ('R' == lower_to_upper_table[*(ptr + 3)]) && ('(' == *(ptr + 4))) { ptr += 4; len -= 4; } else return FALSE; if (MAX_GVSUBS_LEN < lcl_dst_len + 2) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } MEMCPY_LIT(dst_ptr, "CH"); dst_ptr += 2; lcl_dst_len += 2; break; default: return FALSE; } assert('(' == *ptr); UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN); while ((0 < len) && (')' != *ptr)) { UPDATE_DST(ptr, len, have_star, dst_ptr, lcl_dst_len, MAX_GVSUBS_LEN); } q_len = 0; if (!have_star) { if (MAX_GVSUBS_LEN < ++lcl_dst_len) { util_out_print_gtmio("$[Z]CHAR expression too long", FLUSH); return FALSE; } *dst_ptr++ = *ptr++; *dst_ptr = '\0'; m_src.addr = tmp_dst_ptr; m_src.len = (mstr_len_t)(dst_ptr - tmp_dst_ptr); m_dst.addr = dst_string; m_dst.len = 0; if (!zwr2format(&m_src, &m_dst)) return FALSE; lcl_dst_len = *dst_len; /* Reset length because we're creating the final version now */ if (MAX_GVSUBS_LEN < ++lcl_dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *tmp_dst_ptr++ = '"'; char_ptr = m_dst.addr; if (MAX_GVSUBS_LEN < (lcl_dst_len + m_dst.len)) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } for (char_count = 0; m_dst.len > char_count; char_count++) { if ('"' == *char_ptr) { if (MAX_GVSUBS_LEN < ++lcl_dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *tmp_dst_ptr++ = '"'; q_len++; } *tmp_dst_ptr++ = *char_ptr++; lcl_dst_len++; } if (MAX_GVSUBS_LEN < ++lcl_dst_len) { util_out_print_gtmio("Subscript too long", FLUSH); return FALSE; } *tmp_dst_ptr++ = '"'; dst_ptr = tmp_dst_ptr; } else ptr++; assert(!have_star || ((dst_ptr == *d_ptr) && (lcl_dst_len == *dst_len))); *src_ptr = ptr; *src_len = len + 2 + q_len; /* Allow for the open and close quotes and any internal quotes */ *d_ptr = dst_ptr; *dst_len = lcl_dst_len; return TRUE; }
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; }
void trigger_delete_all(char *trigger_rec, uint4 len, uint4 *trig_stats) { int count; sgmnt_addrs *csa; mval curr_gbl_name; int cycle; mval *mv_count_ptr; mval *mv_cycle_ptr; gd_region *reg, *reg_top; int4 result; gd_region *lgtrig_reg; mval trigger_cycle; mval trigger_count; boolean_t this_db_updated; uint4 triggers_deleted; mval trigjrec; boolean_t jnl_format_done; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(0 < dollar_tlevel); jnl_format_done = FALSE; lgtrig_reg = NULL; trigjrec.mvtype = MV_STR; trigjrec.str.len = len; trigjrec.str.addr = trigger_rec; triggers_deleted = 0; 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; /* kill ^#t("#TNAME") */ BUILD_HASHT_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME)); if (0 != gvcst_data()) { /* Issue error if we dont have permissions to touch ^#t global */ if (reg->read_only) rts_error_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_TRIGMODREGNOTRW, 2, REG_LEN_STR(reg)); gvcst_kill(TRUE); } /* Kill all descendents of ^#t(trigvn, ...) where trigvn is any global with a trigger, * but skip the ^#t("#...",...) entries. Setup ^#t("$") as the key for op_gvorder */ BUILD_HASHT_SUB_CURRKEY(LITERAL_MAXHASHVAL, STRLEN(LITERAL_MAXHASHVAL)); TREF(gv_last_subsc_null) = FALSE; /* We know its not null, but prior state is unreliable */ this_db_updated = FALSE; while (TRUE) { op_gvorder(&curr_gbl_name); /* quit:$length(curr_gbl_name)=0 */ if (0 == curr_gbl_name.str.len) break; /* $get(^#t(curr_gbl_name,#COUNT)) */ BUILD_HASHT_SUB_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT)); if (gvcst_get(&trigger_count)) { /* Now that we know there is something to kill, check if we have permissions to touch ^#t global */ if (reg->read_only) rts_error_csa(CSA_ARG(csa) VARLSTCNT(4) ERR_TRIGMODREGNOTRW, 2, REG_LEN_STR(reg)); mv_count_ptr = &trigger_count; count = MV_FORCE_UINT(mv_count_ptr); /* $get(^#t(curr_gbl_name,#CYCLE)) */ BUILD_HASHT_SUB_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len, LITERAL_HASHCYCLE, STRLEN(LITERAL_HASHCYCLE)); if (!gvcst_get(&trigger_cycle)) { /* Found #COUNT, there must be #CYCLE */ 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(csa) VARLSTCNT(12) ERR_TRIGDEFBAD, 6, curr_gbl_name.str.len, curr_gbl_name.str.addr, curr_gbl_name.str.len, curr_gbl_name.str.addr, LEN_AND_LIT("\"#CYCLE\""), ERR_TEXT, 2, RTS_ERROR_TEXT("#CYCLE field is missing")); } mv_cycle_ptr = &trigger_cycle; cycle = MV_FORCE_UINT(mv_cycle_ptr); if (!jnl_format_done && JNL_WRITE_LOGICAL_RECS(csa)) { jnl_format(JNL_LGTRIG, NULL, &trigjrec, 0); jnl_format_done = TRUE; } /* kill ^#t(curr_gbl_name) */ BUILD_HASHT_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len); gvcst_kill(TRUE); /* Note : ^#t(curr_gbl_name,"#TRHASH") is also killed as part of the above */ cycle++; MV_FORCE_MVAL(&trigger_cycle, cycle); /* set ^#t(curr_gbl_name,#CYCLE)=trigger_cycle */ SET_TRIGGER_GLOBAL_SUB_SUB_MVAL(curr_gbl_name.str.addr, curr_gbl_name.str.len, LITERAL_HASHCYCLE, STRLEN(LITERAL_HASHCYCLE), trigger_cycle, result); assert(PUT_SUCCESS == result); this_db_updated = TRUE; triggers_deleted += count; } /* else there is no #COUNT, then no triggers, leave #CYCLE alone */ /* get ready for op_gvorder() call for next trigger under ^#t */ BUILD_HASHT_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len); } if (this_db_updated) { csa->incr_db_trigger_cycle = TRUE; if (dollar_ztrigger_invoked) { /* increment db_dztrigger_cycle so that next gvcst_put/gvcst_kill in this transaction, * on this region, will re-read. 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++; } } } 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; JNLPOOL_INIT_IF_NEEDED(csa, csa->hdr, csa->nl); /* see previous usage for comment on why it is needed */ assert(dollar_tlevel); T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_TRIGLOADFAIL); /* needed to set update_trans TRUE on this region * even if NO db updates happen to ^#t nodes. */ jnl_format(JNL_LGTRIG, NULL, &trigjrec, 0); jnl_format_done = TRUE; } if (triggers_deleted) { util_out_print_gtmio("All existing triggers (count = !UL) deleted", FLUSH, triggers_deleted); trig_stats[STATS_DELETED] += triggers_deleted; trig_stats[STATS_NOERROR_TRIGFILE]++; } else { util_out_print_gtmio("No matching triggers found for deletion", FLUSH); trig_stats[STATS_UNCHANGED_TRIGFILE]++; } }
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; }
void trigger_delete_all(void) { int count; char count_str[MAX_DIGITS_IN_INT + 1]; sgmnt_addrs *csa; mval curr_gbl_name; int cycle; mstr gbl_name; mname_entry gvent; gv_namehead *hasht_tree, *gvt; mval *mv_count_ptr; mval *mv_cycle_ptr; mval mv_indx; gd_region *reg; int reg_indx; 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; int trig_indx; mval trigger_cycle; mval trigger_count; mval val; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(0 < dollar_tlevel); /* Before we delete any triggers, verify that none of the triggers have been fired in this transaction. If they have, * this creates an un-commitable transaction that will end in a TPFAIL error. Since that error indicates database * damage, we'd rather detect this avoidable condition and give a descriptive error instead (TRIGMODINTP). */ for (gvt = gv_target_list; NULL != gvt; gvt = gvt->next_gvnh) { if (gvt->trig_local_tn == local_tn) rts_error(VARLSTCNT(1) ERR_TRIGMODINTP); } SWITCH_TO_DEFAULT_REGION; INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; if (0 != gv_target->root) { /* kill ^#t("#TRHASH") */ BUILD_HASHT_SUB_CURRKEY(LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH)); gvcst_kill(TRUE); /* kill ^#t("#TNAME") */ BUILD_HASHT_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME)); gvcst_kill(TRUE); } for (reg_indx = 0, reg = gd_header->regions; reg_indx < gd_header->n_regions; reg_indx++, reg++) { if (!reg->open) gv_init_reg(reg); if (!reg->read_only) { gv_cur_region = reg; change_reg(); csa = cs_addrs; SETUP_TRIGGER_GLOBAL; INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; /* There might not be any ^#t in this region, so check */ if (0 != gv_target->root) { /* Kill all descendents of ^#t(trigvn, indx) where trigvn is any global with a trigger, * but skip the "#XYZ" entries. setup ^#t(trigvn,"$") as the PREV key for op_gvorder */ BUILD_HASHT_SUB_CURRKEY(LITERAL_MAXHASHVAL, STRLEN(LITERAL_MAXHASHVAL)); TREF(gv_last_subsc_null) = FALSE; /* We know its not null, but prior state is unreliable */ while (TRUE) { op_gvorder(&curr_gbl_name); /* quit:$length(curr_gbl_name)=0 */ if (0 == curr_gbl_name.str.len) break; /* $get(^#t(curr_gbl_name,#COUNT)) */ BUILD_HASHT_SUB_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT)); if (gvcst_get(&trigger_count)) { mv_count_ptr = &trigger_count; count = MV_FORCE_INT(mv_count_ptr); /* $get(^#t(curr_gbl_name,#CYCLE)) */ BUILD_HASHT_SUB_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len, LITERAL_HASHCYCLE, STRLEN(LITERAL_HASHCYCLE)); if (!gvcst_get(&trigger_cycle)) assert(FALSE); /* Found #COUNT, there must be #CYCLE */ mv_cycle_ptr = &trigger_cycle; cycle = MV_FORCE_INT(mv_cycle_ptr); /* kill ^#t(curr_gbl_name) */ BUILD_HASHT_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len); gvcst_kill(TRUE); cycle++; MV_FORCE_MVAL(&trigger_cycle, cycle); /* set ^#t(curr_gbl_name,#CYCLE)=trigger_cycle */ SET_TRIGGER_GLOBAL_SUB_SUB_MVAL(curr_gbl_name.str.addr, curr_gbl_name.str.len, LITERAL_HASHCYCLE, STRLEN(LITERAL_HASHCYCLE), trigger_cycle, result); assert(PUT_SUCCESS == result); } /* else there is no #COUNT, then no triggers, leave #CYCLE alone */ /* get ready for op_gvorder() call for next trigger under ^#t */ BUILD_HASHT_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len); } csa->incr_db_trigger_cycle = TRUE; if (dollar_ztrigger_invoked) { /* increment db_dztrigger_cycle so that next gvcst_put/gvcst_kill in this transaction, * on this region, will re-read. 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++; } } } } util_out_print_gtmio("All existing triggers deleted", FLUSH); }
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; }
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; mstr gbl_name; mname_entry gvent; gv_namehead *hasht_tree; int len; mval mv_curr_nam; boolean_t name_found; char *ptr; char *name_tail_ptr; 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; char save_name[MAX_MIDENT_LEN + 1]; sgm_info *save_sgm_info_ptr; mval trig_gbl; mval trig_value; mval trigger_count; char trigvn[MAX_MIDENT_LEN + 1]; int trigvn_len; int trig_indx; int badpos; boolean_t wildcard; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; badpos = 0; 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 */ return TRIG_FAILURE; } name_tail_ptr = trigger_name + trigger_name_len - 1; if (TRIGNAME_SEQ_DELIM == *name_tail_ptr || wildcard ) /* drop the trailing # sign or wildcard */ trigger_name_len--; /* $data(^#t) */ SWITCH_TO_DEFAULT_REGION; INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; if (0 == gv_target->root) { util_out_print_gtmio("Trigger named !AD does not exist", FLUSH, orig_name_len, trigger_name); return TRIG_FAILURE; } name_found = FALSE; assert(trigger_name_len < MAX_MIDENT_LEN); memcpy(save_name, trigger_name, trigger_name_len); save_name[trigger_name_len] = '\0'; memcpy(curr_name, save_name, trigger_name_len); curr_name_len = trigger_name_len; STR2MVAL(mv_curr_nam, trigger_name, 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)) { SAVE_TRIGGER_REGION_INFO; ptr = trig_gbl.str.addr; trigvn_len = STRLEN(trig_gbl.str.addr); assert(MAX_MIDENT_LEN >= trigvn_len); memcpy(trigvn, ptr, trigvn_len); ptr += trigvn_len + 1; /* the index is just beyon the length of the GVN string */ A2I(ptr, trig_gbl.str.addr + trig_gbl.str.len, trig_indx); gbl_name.addr = trigvn; gbl_name.len = trigvn_len; GV_BIND_NAME_ONLY(gd_header, &gbl_name); csa = gv_target->gd_csa; SETUP_TRIGGER_GLOBAL; INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; /* $get(^#t(GVN,"COUNT") */ BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT)); /* if it does not exist, return false */ if (!gvcst_get(&trigger_count)) { util_out_print_gtmio("Trigger named !AD exists in the lookup table, " "but global ^!AD has no triggers", FLUSH, curr_name_len, curr_name, trigvn_len, trigvn); return TRIG_FAILURE; } /* kill the target trigger for GVN at index trig_indx */ if (PUT_SUCCESS != (trigger_delete(trigvn, trigvn_len, &trigger_count, trig_indx))) { util_out_print_gtmio("Trigger named !AD exists in the lookup table, but was not deleted!", FLUSH, orig_name_len, trigger_name); } else { csa->incr_db_trigger_cycle = TRUE; 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]) util_out_print_gtmio("Deleted trigger named '!AD' for global ^!AD", FLUSH, curr_name_len, curr_name, trigvn_len, trigvn); } RESTORE_TRIGGER_REGION_INFO; name_found = TRUE; } else { /* no names match, if !wildcard report an error */ if (!wildcard) { util_out_print_gtmio("Trigger named !AD does not exist", FLUSH, orig_name_len, trigger_name); return TRIG_FAILURE; } } 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 (wildcard); if (name_found) return TRIG_SUCCESS; util_out_print_gtmio("Trigger named !AD does not exist", FLUSH, orig_name_len, trigger_name); return TRIG_FAILURE; }
STATICFNDEF boolean_t trigger_trgfile_tpwrap_helper(char *trigger_filename, uint4 trigger_filename_len, boolean_t noprompt, boolean_t lcl_implicit_tpwrap) { boolean_t all_triggers_error; uint4 i; io_pair io_save_device; io_pair io_trigfile_device; int len; int4 record_num; boolean_t trigger_status; enum cdb_sc cdb_status; uint4 trig_stats[NUM_STATS]; char *trigger_rec; char *values[NUM_SUBS]; unsigned short value_len[NUM_SUBS]; all_triggers_error = FALSE; if (lcl_implicit_tpwrap) ESTABLISH_RET(trigger_tpwrap_ch, TRIG_FAILURE); /* Return through here is a failure */ io_save_device = io_curr_device; file_input_init(trigger_filename, trigger_filename_len); if (mupip_error_occurred) TRIG_ERROR_RETURN; io_trigfile_device = io_curr_device; record_num = 0; for (i = 0; NUM_STATS > i; i++) trig_stats[i] = 0; while ((0 == io_curr_device.in->dollar.zeof) && (0 <= (len = file_input_get(&trigger_rec)))) { io_curr_device = io_save_device; record_num++; if ((0 != len) && (COMMENT_LITERAL != trigger_rec[0])) util_out_print_gtmio("File !AD, Line !UL: ", NOFLUSH, trigger_filename_len, trigger_filename, record_num); trigger_status = trigger_update_rec(trigger_rec, (uint4)len, noprompt, trig_stats, &io_trigfile_device, &record_num); all_triggers_error |= (TRIG_FAILURE == trigger_status); io_curr_device = io_trigfile_device; } if ((-1 == len) && (!io_curr_device.in->dollar.zeof)) { io_curr_device = io_save_device; util_out_print_gtmio("File !AD, Line !UL: Line too long", FLUSH, trigger_filename_len, trigger_filename, ++record_num); } file_input_close(); io_curr_device = io_save_device; if (all_triggers_error) { util_out_print_gtmio("=========================================", FLUSH); util_out_print_gtmio("!UL trigger file entries matched existing triggers", FLUSH, trig_stats[STATS_UNCHANGED]); util_out_print_gtmio("!UL trigger file entries have errors", FLUSH, trig_stats[STATS_ERROR]); util_out_print_gtmio("!UL trigger file entries have no errors", FLUSH, trig_stats[STATS_ADDED] + trig_stats[STATS_DELETED] + trig_stats[STATS_MODIFIED]); util_out_print_gtmio("=========================================", FLUSH); TRIG_ERROR_RETURN; } if (lcl_implicit_tpwrap) { GVTR_OP_TCOMMIT(cdb_status); if (cdb_sc_normal != cdb_status) t_retry(cdb_status); /* won't return */ REVERT; } if ((0 == trig_stats[STATS_ERROR]) && (0 != (trig_stats[STATS_ADDED] + trig_stats[STATS_DELETED] + trig_stats[STATS_UNCHANGED] + trig_stats[STATS_MODIFIED]))) { util_out_print_gtmio("=========================================", FLUSH); util_out_print_gtmio("!UL triggers added", FLUSH, trig_stats[STATS_ADDED]); util_out_print_gtmio("!UL triggers deleted", FLUSH, trig_stats[STATS_DELETED]); util_out_print_gtmio("!UL trigger file entries not changed", FLUSH, trig_stats[STATS_UNCHANGED]); util_out_print_gtmio("!UL triggers modified", FLUSH, trig_stats[STATS_MODIFIED]); util_out_print_gtmio("=========================================", FLUSH); } else if (0 != trig_stats[STATS_ERROR]) { util_out_print_gtmio("=========================================", FLUSH); util_out_print_gtmio("!UL trigger file entries matched existing triggers", FLUSH, trig_stats[STATS_UNCHANGED]); util_out_print_gtmio("!UL trigger file entries have errors", FLUSH, trig_stats[STATS_ERROR]); util_out_print_gtmio("!UL trigger file entries have no errors", FLUSH, trig_stats[STATS_ADDED] + trig_stats[STATS_DELETED] + trig_stats[STATS_MODIFIED]); util_out_print_gtmio("=========================================", FLUSH); } return TRIG_SUCCESS; }