1678 lines
62 KiB
C
1678 lines
62 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2010, 2012 Fidelity Information Services, Inc *
|
|
* *
|
|
* This source code contains the intellectual property *
|
|
* of its copyright holder(s), and is made available *
|
|
* under a license. If you do not know the terms of *
|
|
* the license, please stop and do not read further. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#include "mdef.h"
|
|
|
|
#ifdef GTM_TRIGGER
|
|
#include "gdsroot.h" /* for gdsfhead.h */
|
|
#include "gdsbt.h" /* for gdsfhead.h */
|
|
#include "gdsfhead.h" /* For gvcst_protos.h */
|
|
#include "dpgbldir.h"
|
|
#include "gvcst_protos.h"
|
|
#include "rtnhdr.h"
|
|
#include "io.h"
|
|
#include "iormdef.h"
|
|
#include "hashtab_str.h"
|
|
#include "wbox_test_init.h"
|
|
#include "gv_trigger.h"
|
|
#include "trigger_delete_protos.h"
|
|
#include "trigger.h"
|
|
#include "trigger_incr_cycle.h"
|
|
#include "trigger_parse_protos.h"
|
|
#include "trigger_update_protos.h"
|
|
#include "trigger_compare_protos.h"
|
|
#include "trigger_user_name.h"
|
|
#include "gtm_string.h"
|
|
#include "mv_stent.h" /* for COPY_SUBS_TO_GVCURRKEY macro */
|
|
#include "gvsub2str.h" /* for COPY_SUBS_TO_GVCURRKEY */
|
|
#include "format_targ_key.h" /* for COPY_SUBS_TO_GVCURRKEY */
|
|
#include "targ_alloc.h" /* for SETUP_TRIGGER_GLOBAL & SWITCH_TO_DEFAULT_REGION */
|
|
#include "gdsblk.h"
|
|
#include "gdscc.h" /* needed for tp.h */
|
|
#include "gdskill.h" /* needed for tp.h */
|
|
#include "buddy_list.h" /* needed for tp.h */
|
|
#include "hashtab_int4.h" /* needed for tp.h */
|
|
#include "filestruct.h" /* needed for jnl.h */
|
|
#include "jnl.h" /* needed for tp.h */
|
|
#include "tp.h"
|
|
#include "min_max.h" /* Needed for MIN */
|
|
#include "mvalconv.h" /* Needed for MV_FORCE_* */
|
|
#include "op.h"
|
|
#include "util.h"
|
|
#include "op_tcommit.h"
|
|
#include "tp_restart.h"
|
|
#include "error.h"
|
|
#include "file_input.h"
|
|
#include "stack_frame.h"
|
|
#include "tp_frame.h"
|
|
#include "t_retry.h"
|
|
#include "gtmimagename.h"
|
|
|
|
GBLREF sgmnt_data_ptr_t cs_data;
|
|
GBLREF sgm_info *first_sgm_info;
|
|
GBLREF gd_region *gv_cur_region;
|
|
GBLREF gv_key *gv_currkey;
|
|
GBLREF gd_addr *gd_header;
|
|
GBLREF io_pair io_curr_device;
|
|
GBLREF sgm_info *sgm_info_ptr;
|
|
GBLREF int tprestart_state;
|
|
GBLREF gv_namehead *reset_gv_target;
|
|
GBLREF uint4 dollar_tlevel;
|
|
GBLREF boolean_t dollar_ztrigger_invoked;
|
|
GBLREF trans_num local_tn;
|
|
GBLREF unsigned int t_tries;
|
|
GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES];
|
|
#ifdef DEBUG
|
|
GBLREF boolean_t donot_INVOKE_MUMTSTART;
|
|
#endif
|
|
|
|
error_def(ERR_DBROLLEDBACK);
|
|
error_def(ERR_TEXT);
|
|
error_def(ERR_TPRETRY);
|
|
error_def(ERR_TPRETRY);
|
|
error_def(ERR_TRIGDEFBAD);
|
|
error_def(ERR_TRIGMODINTP);
|
|
error_def(ERR_TRIGMODREGNOTRW);
|
|
|
|
LITREF mval gvtr_cmd_mval[GVTR_CMDTYPES];
|
|
LITREF int4 gvtr_cmd_mask[GVTR_CMDTYPES];
|
|
LITREF mval literal_hasht;
|
|
LITREF mval literal_one;
|
|
LITREF char *trigger_subs[];
|
|
|
|
#define MAX_COMMANDS_LEN 32 /* Need room for S,K,ZK,ZTK + room for expansion */
|
|
#define MAX_OPTIONS_LEN 32 /* Need room for NOI,NOC + room for expansion */
|
|
#define MAX_TRIGNAME_SEQ_NUM 999999
|
|
#define LITERAL_M "M"
|
|
#define OPTIONS_I 1
|
|
#define OPTIONS_NOI 2
|
|
#define OPTIONS_C 4
|
|
#define OPTIONS_NOC 8
|
|
|
|
#define ADD_UPDATE_NOCHANGE 0x00
|
|
#define ADD_UPDATE_NAME 0x01
|
|
#define ADD_UPDATE_CMDS 0x02
|
|
#define ADD_UPDATE_OPTIONS 0x04
|
|
#define SUB_UPDATE_NAME 0x10
|
|
#define SUB_UPDATE_CMDS 0x20
|
|
#define SUB_UPDATE_OPTIONS 0x40
|
|
#define SUB_UPDATE_NOCHANGE 0x00
|
|
#define DELETE_REC 0x80
|
|
|
|
#define BUILD_COMMAND_BITMAP(BITMAP, COMMANDS) \
|
|
{ \
|
|
char lcl_cmds[MAX_COMMANDS_LEN + 1]; \
|
|
char *lcl_ptr; \
|
|
\
|
|
memcpy(lcl_cmds, COMMANDS, STRLEN(COMMANDS) + 1); \
|
|
BITMAP = 0; \
|
|
lcl_ptr = strtok(lcl_cmds, ","); \
|
|
do \
|
|
{ \
|
|
switch (*lcl_ptr) \
|
|
{ \
|
|
case 'S': \
|
|
BITMAP |= gvtr_cmd_mask[GVTR_CMDTYPE_SET]; \
|
|
break; \
|
|
case 'K': \
|
|
BITMAP |= gvtr_cmd_mask[GVTR_CMDTYPE_KILL]; \
|
|
break; \
|
|
case 'Z': \
|
|
switch (*(lcl_ptr + 1)) \
|
|
{ \
|
|
case 'K': \
|
|
BITMAP |= gvtr_cmd_mask[GVTR_CMDTYPE_ZKILL]; \
|
|
break; \
|
|
case 'T': \
|
|
switch(*(lcl_ptr + 2)) \
|
|
{ \
|
|
case 'K': \
|
|
BITMAP |= gvtr_cmd_mask[GVTR_CMDTYPE_ZTKILL]; \
|
|
break; \
|
|
case 'R': \
|
|
BITMAP |= gvtr_cmd_mask[GVTR_CMDTYPE_ZTRIGGER]; \
|
|
break; \
|
|
default: \
|
|
GTMASSERT; \
|
|
break; \
|
|
} \
|
|
break; \
|
|
default: \
|
|
GTMASSERT; /* Parsing should have found invalid command */ \
|
|
break; \
|
|
} \
|
|
break; \
|
|
default: \
|
|
GTMASSERT; /* Parsing should have found invalid command */ \
|
|
break; \
|
|
} \
|
|
} while (lcl_ptr = strtok(NULL, ",")); \
|
|
}
|
|
|
|
#define COMMAND_BITMAP_TO_STR(COMMANDS, BITMAP, LEN) \
|
|
{ \
|
|
int count, cmdtype, lcl_len; \
|
|
char *lcl_ptr; \
|
|
\
|
|
count = 0; \
|
|
lcl_ptr = COMMANDS; \
|
|
lcl_len = LEN; \
|
|
for (cmdtype = 0; cmdtype < GVTR_CMDTYPES; cmdtype++) \
|
|
{ \
|
|
if (gvtr_cmd_mask[cmdtype] & (BITMAP)) \
|
|
{ \
|
|
ADD_COMMA_IF_NEEDED(count, lcl_ptr, lcl_len); \
|
|
ADD_STRING(count, lcl_ptr, gvtr_cmd_mval[cmdtype].str.len, gvtr_cmd_mval[cmdtype].str.addr, lcl_len); \
|
|
} \
|
|
} \
|
|
*lcl_ptr = '\0'; \
|
|
LEN = STRLEN(COMMANDS); \
|
|
}
|
|
|
|
#define BUILD_OPTION_BITMAP(BITMAP, OPTIONS) \
|
|
{ \
|
|
char lcl_options[MAX_OPTIONS_LEN + 1]; \
|
|
char *lcl_ptr; \
|
|
\
|
|
memcpy(lcl_options, OPTIONS, STRLEN(OPTIONS) + 1); \
|
|
BITMAP = 0; \
|
|
lcl_ptr = strtok(lcl_options, ","); \
|
|
if (NULL != lcl_ptr) \
|
|
do \
|
|
{ \
|
|
switch (*lcl_ptr) \
|
|
{ \
|
|
case 'C': \
|
|
BITMAP |= OPTIONS_C; \
|
|
break; \
|
|
case 'I': \
|
|
BITMAP |= OPTIONS_I; \
|
|
break; \
|
|
case 'N': \
|
|
assert('O' == *(lcl_ptr + 1)); \
|
|
switch (*(lcl_ptr + 2)) \
|
|
{ \
|
|
case 'C': \
|
|
BITMAP |= OPTIONS_NOC; \
|
|
break; \
|
|
case 'I': \
|
|
BITMAP |= OPTIONS_NOI; \
|
|
break; \
|
|
default: \
|
|
GTMASSERT; /* Parsing should have found invalid command */ \
|
|
break; \
|
|
} \
|
|
break; \
|
|
default: \
|
|
GTMASSERT; /* Parsing should have found invalid command */ \
|
|
break; \
|
|
} \
|
|
} while (lcl_ptr = strtok(NULL, ",")); \
|
|
}
|
|
|
|
#define OPTION_BITMAP_TO_STR(OPTIONS, BITMAP, LEN) \
|
|
{ \
|
|
int count, optiontype, lcl_len; \
|
|
char *lcl_ptr; \
|
|
\
|
|
count = 0; \
|
|
lcl_len = LEN; \
|
|
lcl_ptr = OPTIONS; \
|
|
if (OPTIONS_I & BITMAP) \
|
|
{ \
|
|
ADD_COMMA_IF_NEEDED(count, lcl_ptr, lcl_len); \
|
|
ADD_STRING(count, lcl_ptr, STRLEN(HASHT_OPT_ISOLATION), HASHT_OPT_ISOLATION, lcl_len); \
|
|
} \
|
|
if (OPTIONS_NOI & BITMAP) \
|
|
{ \
|
|
ADD_COMMA_IF_NEEDED(count, lcl_ptr, lcl_len); \
|
|
ADD_STRING(count, lcl_ptr, STRLEN(HASHT_OPT_NOISOLATION), HASHT_OPT_NOISOLATION, lcl_len); \
|
|
} \
|
|
if (OPTIONS_C & BITMAP) \
|
|
{ \
|
|
ADD_COMMA_IF_NEEDED(count, lcl_ptr, lcl_len); \
|
|
ADD_STRING(count, lcl_ptr, STRLEN(HASHT_OPT_CONSISTENCY), HASHT_OPT_CONSISTENCY, lcl_len); \
|
|
} \
|
|
if (OPTIONS_NOC & BITMAP) \
|
|
{ \
|
|
ADD_COMMA_IF_NEEDED(count, lcl_ptr, lcl_len); \
|
|
ADD_STRING(count, lcl_ptr, STRLEN(HASHT_OPT_NOCONSISTENCY), HASHT_OPT_NOCONSISTENCY, lcl_len); \
|
|
} \
|
|
*lcl_ptr = '\0'; \
|
|
LEN = STRLEN(OPTIONS); \
|
|
}
|
|
|
|
#define TOO_LONG_REC_KEY_ERROR_MSG \
|
|
{ \
|
|
trig_stats[STATS_ERROR]++; \
|
|
if (KEY_TOO_LONG == result) \
|
|
util_out_print_gtmio("^!AD trigger - key larger than max key size", FLUSH, trigvn_len, trigvn); \
|
|
else \
|
|
util_out_print_gtmio("^!AD trigger - value larger than max record size", FLUSH, trigvn_len, trigvn); \
|
|
}
|
|
|
|
#define IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(RESULT) \
|
|
{ \
|
|
if (PUT_SUCCESS != RESULT) \
|
|
{ \
|
|
TOO_LONG_REC_KEY_ERROR_MSG; \
|
|
return TRIG_FAILURE; \
|
|
} \
|
|
}
|
|
|
|
#define TRIGGER_SAME_NAME_EXISTS_ERROR \
|
|
{ \
|
|
trig_stats[STATS_ERROR]++; \
|
|
util_out_print_gtmio("a trigger named !AD already exists", FLUSH, value_len[TRIGNAME_SUB], values[TRIGNAME_SUB]); \
|
|
return TRIG_FAILURE; \
|
|
}
|
|
|
|
/* This error macro is used for all definition errors where the target is ^#t(GVN,<index>,<required subscript>) */
|
|
#define HASHT_GVN_DEFINITION_RETRY_OR_ERROR(INDEX,SUBSCRIPT) \
|
|
{ \
|
|
if (CDB_STAGNATE > t_tries) \
|
|
t_retry(cdb_sc_triggermod); \
|
|
else \
|
|
{ \
|
|
assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); \
|
|
/* format "INDEX,SUBSCRIPT" of ^#t(GVN,INDEX,SUBSCRIPT) in the error message */ \
|
|
SET_PARAM_STRING(util_buff, util_len, INDEX, SUBSCRIPT); \
|
|
rts_error(VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, \
|
|
trigvn_len, trigvn, util_len, util_buff); \
|
|
} \
|
|
}
|
|
|
|
/* This error macro is used for all definition errors where the target is ^#t(GVN,<#LABEL|#COUNT|#CYCLE>) */
|
|
#define HASHT_DEFINITION_RETRY_OR_ERROR(SUBSCRIPT,MOREINFO) \
|
|
{ \
|
|
if (CDB_STAGNATE > t_tries) \
|
|
t_retry(cdb_sc_triggermod); \
|
|
else \
|
|
{ \
|
|
HASHT_DEFINITION_ERROR(SUBSCRIPT,MOREINFO); \
|
|
} \
|
|
}
|
|
|
|
#define HASHT_DEFINITION_ERROR(SUBSCRIPT,MOREINFO) \
|
|
{ \
|
|
assert(WBTEST_HELPOUT_TRIGDEFBAD == gtm_white_box_test_case_number); \
|
|
rts_error(VARLSTCNT(12) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, \
|
|
trigvn_len, trigvn, LEN_AND_LIT(SUBSCRIPT), \
|
|
ERR_TEXT, 2, RTS_ERROR_TEXT(MOREINFO)); \
|
|
}
|
|
|
|
STATICFNDEF boolean_t validate_label(char *trigvn, int trigvn_len)
|
|
{
|
|
mval trigger_label;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHLABEL, STRLEN(LITERAL_HASHLABEL));
|
|
if (!gvcst_get(&trigger_label)) /* There has to be a #LABEL */
|
|
HASHT_DEFINITION_RETRY_OR_ERROR("\"#LABEL\"","#LABEL was not found")
|
|
return ((trigger_label.str.len == STRLEN(HASHT_GBL_CURLABEL))
|
|
&& (0 == memcmp(trigger_label.str.addr, HASHT_GBL_CURLABEL, trigger_label.str.len)));
|
|
}
|
|
|
|
STATICFNDEF int4 update_commands(char *trigvn, int trigvn_len, int trigger_index, char *new_trig_cmds, char *orig_db_cmds)
|
|
{
|
|
mval mv_trig_indx;
|
|
uint4 orig_cmd_bm, new_cmd_bm;
|
|
int4 result;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
if (!validate_label(trigvn, trigvn_len))
|
|
return INVALID_LABEL;
|
|
BUILD_COMMAND_BITMAP(orig_cmd_bm, orig_db_cmds);
|
|
BUILD_COMMAND_BITMAP(new_cmd_bm, new_trig_cmds);
|
|
i2mval(&mv_trig_indx, trigger_index);
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[CMD_SUB], STRLEN(trigger_subs[CMD_SUB]),
|
|
new_trig_cmds, STRLEN(new_trig_cmds), result);
|
|
if (PUT_SUCCESS != result)
|
|
return result;
|
|
if ((0 != (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & orig_cmd_bm)) && (0 == (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & new_cmd_bm)))
|
|
{ /* SET was removed from the commands, so delete the SET specific attributes */
|
|
BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, mv_trig_indx, LITERAL_DELIM, LITERAL_DELIM_LEN);
|
|
gvcst_kill(TRUE);
|
|
BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, mv_trig_indx, LITERAL_ZDELIM, LITERAL_ZDELIM_LEN);
|
|
gvcst_kill(TRUE);
|
|
BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, mv_trig_indx, LITERAL_PIECES, LITERAL_PIECES_LEN);
|
|
gvcst_kill(TRUE);
|
|
}
|
|
trigger_incr_cycle(trigvn, trigvn_len);
|
|
return SUB_UPDATE_CMDS;
|
|
}
|
|
|
|
STATICFNDEF int4 update_options(char *trigvn, int trigvn_len, int trigger_index, char *trig_options, char *option_value)
|
|
{
|
|
mval mv_trig_indx;
|
|
int4 result;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
if (!validate_label(trigvn, trigvn_len))
|
|
return INVALID_LABEL;
|
|
i2mval(&mv_trig_indx, trigger_index);
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[OPTIONS_SUB],
|
|
STRLEN(trigger_subs[OPTIONS_SUB]), trig_options, STRLEN(trig_options), result);
|
|
if (PUT_SUCCESS != result)
|
|
return result;
|
|
trigger_incr_cycle(trigvn, trigvn_len);
|
|
return SUB_UPDATE_OPTIONS;
|
|
}
|
|
|
|
STATICFNDEF int4 update_trigger_name(char *trigvn, int trigvn_len, int trigger_index, char *db_trig_name, char *tf_trig_name,
|
|
uint4 tf_trig_name_len)
|
|
{
|
|
mval mv_trig_indx;
|
|
int4 result;
|
|
uint4 retval;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
retval = NO_NAME_CHANGE;
|
|
if ((0 != tf_trig_name_len) && (tf_trig_name_len != STRLEN(db_trig_name) - 1)
|
|
|| (0 != memcmp(tf_trig_name, db_trig_name, tf_trig_name_len)))
|
|
{
|
|
if (!validate_label(trigvn, trigvn_len))
|
|
return INVALID_LABEL;
|
|
i2mval(&mv_trig_indx, trigger_index);
|
|
tf_trig_name[tf_trig_name_len++] = TRIGNAME_SEQ_DELIM;
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, LITERAL_TRIGNAME, STRLEN(LITERAL_TRIGNAME),
|
|
tf_trig_name, tf_trig_name_len, result);
|
|
if (PUT_SUCCESS != result)
|
|
return result;
|
|
cleanup_trigger_name(trigvn, trigvn_len, db_trig_name, STRLEN(db_trig_name));
|
|
trigger_incr_cycle(trigvn, trigvn_len);
|
|
retval = ADD_UPDATE_NAME;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
STATICFNDEF boolean_t check_unique_trigger_name(char *trigvn, int trigvn_len, char *trigger_name, uint4 trigger_name_len)
|
|
{
|
|
sgmnt_addrs *csa;
|
|
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;
|
|
boolean_t status;
|
|
mval val;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
/* We only check user supplied names for uniqueness (since autogenerated names are unique). */
|
|
if (0 == trigger_name_len)
|
|
return TRUE;
|
|
SAVE_TRIGGER_REGION_INFO;
|
|
SWITCH_TO_DEFAULT_REGION;
|
|
INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED;
|
|
if (0 == gv_target->root)
|
|
{
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
return TRUE;
|
|
}
|
|
assert((MAX_HASH_INDEX_LEN + 1 + MAX_DIGITS_IN_INT) > gv_cur_region->max_rec_size);
|
|
/* $get(^#t("#TNAME",trigger_name) */
|
|
BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trigger_name, trigger_name_len);
|
|
status = !gvcst_get(&val);
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
return status;
|
|
}
|
|
|
|
STATICFNDEF int4 add_trigger_hash_entry(char *trigvn, int trigvn_len, char *cmd_value, int trigindx, boolean_t add_kill_hash,
|
|
stringkey *kill_hash, stringkey *set_hash)
|
|
{
|
|
sgmnt_addrs *csa;
|
|
int hash_indx;
|
|
char indx_str[MAX_DIGITS_IN_INT];
|
|
uint4 len;
|
|
mval mv_hash;
|
|
mval mv_indx, *mv_indx_ptr;
|
|
char name_and_index[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT];
|
|
int num_len;
|
|
char *ptr;
|
|
int4 result;
|
|
char save_currkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)];
|
|
gv_key *save_gv_currkey;
|
|
gd_region *save_gv_cur_region;
|
|
gv_namehead *save_gv_target;
|
|
sgm_info *save_sgm_info_ptr;
|
|
boolean_t set_cmp;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
SAVE_TRIGGER_REGION_INFO;
|
|
SWITCH_TO_DEFAULT_REGION;
|
|
if (gv_cur_region->read_only)
|
|
rts_error(VARLSTCNT(4) ERR_TRIGMODREGNOTRW, 2, REG_LEN_STR(gv_cur_region));
|
|
INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED;
|
|
set_cmp = (NULL != strchr(cmd_value, 'S'));
|
|
mv_indx_ptr = &mv_indx;
|
|
num_len = 0;
|
|
I2A(indx_str, num_len, trigindx);
|
|
assert(MAX_MIDENT_LEN >= trigvn_len);
|
|
memcpy(name_and_index, trigvn, trigvn_len);
|
|
ptr = name_and_index + trigvn_len;
|
|
*ptr++ = '\0';
|
|
memcpy(ptr, indx_str, num_len);
|
|
len = trigvn_len + 1 + num_len;
|
|
if (set_cmp)
|
|
{
|
|
MV_FORCE_UMVAL(&mv_hash, set_hash->hash_code);
|
|
if (0 != gv_target->root)
|
|
{
|
|
BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, "", 0);
|
|
op_zprevious(&mv_indx);
|
|
hash_indx = (0 == mv_indx.str.len) ? 1 : (mval2i(mv_indx_ptr) + 1);
|
|
} else
|
|
hash_indx = 1;
|
|
i2mval(&mv_indx, hash_indx);
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_MSUB_STR(LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_indx,
|
|
name_and_index, len, result);
|
|
if (PUT_SUCCESS != result)
|
|
{
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
return result;
|
|
}
|
|
} else
|
|
set_hash->hash_code = 0;
|
|
if (add_kill_hash)
|
|
{
|
|
MV_FORCE_UMVAL(&mv_hash, kill_hash->hash_code);
|
|
if (0 != gv_target->root)
|
|
{
|
|
BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, "", 0);
|
|
op_zprevious(&mv_indx);
|
|
hash_indx = (0 == mv_indx.str.len) ? 1 : (mval2i(mv_indx_ptr) + 1);
|
|
} else
|
|
hash_indx = 1;
|
|
i2mval(&mv_indx, hash_indx);
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_MSUB_STR(LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH), mv_hash, mv_indx,
|
|
name_and_index, len, result);
|
|
if (PUT_SUCCESS != result)
|
|
{
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
return result;
|
|
}
|
|
} else
|
|
kill_hash->hash_code = 0;
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
return PUT_SUCCESS;
|
|
}
|
|
|
|
STATICFNDEF boolean_t trigger_already_exists(char *trigvn, int trigvn_len, char **values, uint4 *value_len, int *set_index,
|
|
int *kill_index, boolean_t *set_cmp_result, boolean_t *kill_cmp_result,
|
|
boolean_t *full_match, stringkey *set_trigger_hash, stringkey *kill_trigger_hash,
|
|
mval *setname, mval *killname)
|
|
{
|
|
sgmnt_addrs *csa;
|
|
boolean_t db_has_K;
|
|
boolean_t db_has_S;
|
|
char *ptr;
|
|
int hash_indx;
|
|
boolean_t kill_cmp, kill_found;
|
|
int kill_indx;
|
|
boolean_t name_match;
|
|
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;
|
|
boolean_t set_cmp, set_found, set_name_match, kill_name_match;
|
|
int set_indx;
|
|
mval trigindx;
|
|
unsigned char util_buff[MAX_TRIG_UTIL_LEN];
|
|
int4 util_len;
|
|
mval val;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
SAVE_TRIGGER_REGION_INFO;
|
|
SWITCH_TO_DEFAULT_REGION;
|
|
INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED;
|
|
/* test with SET and/or KILL hash */
|
|
set_cmp = (NULL != strchr(values[CMD_SUB], 'S'));
|
|
kill_cmp = ((NULL != strchr(values[CMD_SUB], 'K')) || (NULL != strchr(values[CMD_SUB], 'R')));
|
|
set_found = kill_found = set_name_match = kill_name_match = FALSE;
|
|
if (set_cmp)
|
|
{ /* test for SET hash match if SET command specified */
|
|
set_found = search_triggers(trigvn, trigvn_len, values, value_len, set_trigger_hash, &hash_indx, &set_indx, 0,
|
|
TRUE);
|
|
if (set_found)
|
|
{
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
i2mval(&trigindx, set_indx);
|
|
BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigindx, trigger_subs[TRIGNAME_SUB],
|
|
STRLEN(trigger_subs[TRIGNAME_SUB]));
|
|
if (!gvcst_get(setname)) /* There has to be a name value */
|
|
HASHT_GVN_DEFINITION_RETRY_OR_ERROR(set_indx, ",\"TRIGNAME\"");
|
|
set_name_match = ((value_len[TRIGNAME_SUB] == (setname->str.len - 1))
|
|
&& (0 == memcmp(setname->str.addr, values[TRIGNAME_SUB], value_len[TRIGNAME_SUB])));
|
|
}
|
|
}
|
|
*set_cmp_result = set_found;
|
|
if (kill_cmp || !set_found)
|
|
{ /* if SET is not found OR KILL is specified in commands, test for KILL hash match */
|
|
kill_found = search_triggers(trigvn, trigvn_len, values, value_len, kill_trigger_hash, &hash_indx, &kill_indx, 0,
|
|
FALSE);
|
|
if (kill_found)
|
|
{
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
i2mval(&trigindx, kill_indx);
|
|
BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigindx, trigger_subs[CMD_SUB],
|
|
STRLEN(trigger_subs[CMD_SUB]));
|
|
if (!gvcst_get(&val)) /* There has to be a command string */
|
|
HASHT_GVN_DEFINITION_RETRY_OR_ERROR(kill_indx, ",\"CMD\"");
|
|
db_has_S = (NULL != memchr(val.str.addr, 'S', val.str.len));
|
|
db_has_K = ((NULL != memchr(val.str.addr, 'K', val.str.len)) ||
|
|
(NULL != memchr(val.str.addr, 'R', val.str.len)));
|
|
/* Below means
|
|
* NOT ( Matched trigger has SET && New trigger has SET &&
|
|
* NOT ( Matched trigger has SET + KILL && New trigger has SET + KILL ) )
|
|
*
|
|
* KILL is found if:
|
|
* The matched trigger does not have a SET || The new trigger does not have a SET
|
|
* But not if the matched trigger has a SET or KILL && the new trigger does not have a SET or KILL
|
|
*/
|
|
kill_found = !(db_has_S && set_cmp && !(db_has_S && db_has_K && set_cmp && kill_cmp));
|
|
/* $get(^#t(trigvn,trigindx,"TRIGNAME") */
|
|
BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigindx, trigger_subs[TRIGNAME_SUB],
|
|
STRLEN(trigger_subs[TRIGNAME_SUB]));
|
|
if (!gvcst_get(killname)) /* There has to be a name string */
|
|
HASHT_GVN_DEFINITION_RETRY_OR_ERROR(kill_indx, ",\"TRIGNAME\"");
|
|
kill_name_match = ((value_len[TRIGNAME_SUB] == (killname->str.len - 1))
|
|
&& (0 == memcmp(killname->str.addr, values[TRIGNAME_SUB], value_len[TRIGNAME_SUB])));
|
|
}
|
|
}
|
|
/* Starting from the beginning:
|
|
* Matching both set and kill, but for different records -- don't update the kill record, hence the FALSE
|
|
* Matching a set implies matching a kill -- hence the ||
|
|
*/
|
|
if (set_found && kill_found && (set_indx != kill_indx))
|
|
{
|
|
*kill_cmp_result = FALSE;
|
|
*kill_index = kill_indx;
|
|
} else
|
|
{
|
|
*kill_cmp_result = (kill_found || set_found);
|
|
if (!set_found)
|
|
{
|
|
setname->mvtype = MV_STR;
|
|
setname->str.addr = killname->str.addr;
|
|
setname->str.len = killname->str.len;
|
|
}
|
|
}
|
|
*set_index = (set_found) ? set_indx : (kill_found) ? kill_indx : 0;
|
|
/* If there is both a set and a kill and the set components don't match, there is no name match no matter if the kill
|
|
* components match or not. If there is no set, then the name match is only based on the kill components.
|
|
*/
|
|
*full_match = (set_cmp) ? set_name_match : kill_name_match;
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
return (set_found || kill_found);
|
|
}
|
|
|
|
STATICFNDEF int4 add_trigger_cmd_attributes(char *trigvn, int trigvn_len, int trigger_index, char *trig_cmds, char **values,
|
|
uint4 *value_len, boolean_t set_compare, boolean_t kill_compare, stringkey *kill_hash, stringkey *set_hash)
|
|
{
|
|
char cmd_str[MAX_COMMANDS_LEN];
|
|
int cmd_str_len;
|
|
uint4 db_cmd_bm;
|
|
mval mv_hash;
|
|
mval mv_trig_indx;
|
|
int4 result;
|
|
uint4 tf_cmd_bm;
|
|
uint4 tmp_bm;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
if (!validate_label(trigvn, trigvn_len))
|
|
return INVALID_LABEL;
|
|
BUILD_COMMAND_BITMAP(db_cmd_bm, trig_cmds);
|
|
BUILD_COMMAND_BITMAP(tf_cmd_bm, values[CMD_SUB]);
|
|
/* If the trigger file command string is contained in the database command and either
|
|
* 1. the trigger file command has no SET components or
|
|
* 2. the trigger file command matched a database SET component or
|
|
* then the trigger file command is already in the database, so return.
|
|
*/
|
|
if ((tf_cmd_bm == (db_cmd_bm & tf_cmd_bm))
|
|
&& ((0 == (tf_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_SET])) || set_compare))
|
|
return CMDS_PRESENT;
|
|
/* If the database command string is contained in the trigger file command and the database is only a "SET"
|
|
* and the trigger file SET matched the database, but not the KILL (which doesn't make sense until you realize that
|
|
* trigger_already_exists() returns kill_compare as FALSE when the trigger file record matches both SET and KILL, but
|
|
* the matches are with two different triggers, then the trigger file command is already in the database so return.
|
|
*/
|
|
if ((db_cmd_bm == (db_cmd_bm & tf_cmd_bm))
|
|
&& ((db_cmd_bm == (db_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_SET])) && set_compare && !kill_compare))
|
|
return CMDS_PRESENT;
|
|
/* If merge would combine K and ZTK, it's an error */
|
|
if (((0 != (db_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_KILL])) && (0 != (tf_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_ZTKILL])))
|
|
|| ((0 != (db_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_ZTKILL])) && (0 != (tf_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_KILL]))))
|
|
return K_ZTK_CONFLICT;
|
|
if (!set_compare && kill_compare
|
|
&& (0 != (tf_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_SET])) && (0 != (db_cmd_bm & gvtr_cmd_mask[GVTR_CMDTYPE_SET])))
|
|
{ /* Subtract common (between triggerfile and DB) "non-S" from tf_cmd_bm */
|
|
tmp_bm = gvtr_cmd_mask[GVTR_CMDTYPE_SET];
|
|
COMMAND_BITMAP_TO_STR(values[CMD_SUB], tmp_bm, value_len[CMD_SUB]);
|
|
/* since the KILL matches, update the corresponding trigger's KILLs */
|
|
tmp_bm = db_cmd_bm | (tf_cmd_bm ^ (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & tf_cmd_bm));
|
|
cmd_str_len = ARRAYSIZE(cmd_str);
|
|
COMMAND_BITMAP_TO_STR(cmd_str, tmp_bm, cmd_str_len);
|
|
i2mval(&mv_trig_indx, trigger_index);
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[CMD_SUB],
|
|
STRLEN(trigger_subs[CMD_SUB]), cmd_str, cmd_str_len, result);
|
|
assert(result == PUT_SUCCESS);
|
|
return (result == PUT_SUCCESS) ? ADD_NEW_TRIGGER : result;
|
|
}
|
|
cmd_str_len = ARRAYSIZE(cmd_str);
|
|
COMMAND_BITMAP_TO_STR(cmd_str, db_cmd_bm | tf_cmd_bm, cmd_str_len);
|
|
i2mval(&mv_trig_indx, trigger_index);
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[CMD_SUB], STRLEN(trigger_subs[CMD_SUB]),
|
|
cmd_str, cmd_str_len, result);
|
|
if (PUT_SUCCESS != result)
|
|
return result;
|
|
strcpy(trig_cmds, cmd_str);
|
|
if ((0 != (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & tf_cmd_bm)) && (0 == (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & db_cmd_bm)))
|
|
{ /* need to add SET attributes */
|
|
if (0 < value_len[DELIM_SUB])
|
|
{
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[DELIM_SUB],
|
|
STRLEN(trigger_subs[DELIM_SUB]), values[DELIM_SUB], value_len[DELIM_SUB], result);
|
|
if (PUT_SUCCESS != result)
|
|
return result;
|
|
}
|
|
if (0 < value_len[ZDELIM_SUB])
|
|
{
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[ZDELIM_SUB],
|
|
STRLEN(trigger_subs[ZDELIM_SUB]), values[ZDELIM_SUB], value_len[ZDELIM_SUB], result);
|
|
if (PUT_SUCCESS != result)
|
|
return result;
|
|
}
|
|
if (0 < value_len[PIECES_SUB])
|
|
{
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[PIECES_SUB],
|
|
STRLEN(trigger_subs[PIECES_SUB]), values[PIECES_SUB], value_len[PIECES_SUB], result);
|
|
if (PUT_SUCCESS != result)
|
|
return result;
|
|
}
|
|
if ((0 == (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & db_cmd_bm))
|
|
&& (0 != (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & tf_cmd_bm)))
|
|
{ /* We gained an "S" so we need to add the set hash value */
|
|
result = add_trigger_hash_entry(trigvn, trigvn_len, values[CMD_SUB], trigger_index, FALSE, kill_hash,
|
|
set_hash);
|
|
if (PUT_SUCCESS != result)
|
|
return result;
|
|
MV_FORCE_UMVAL(&mv_hash, set_hash->hash_code);
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MVAL(trigvn, trigvn_len, mv_trig_indx, trigger_subs[BHASH_SUB],
|
|
STRLEN(trigger_subs[BHASH_SUB]), mv_hash, result);
|
|
if (PUT_SUCCESS != result)
|
|
return result;
|
|
}
|
|
}
|
|
trigger_incr_cycle(trigvn, trigvn_len);
|
|
return ADD_UPDATE_CMDS;
|
|
}
|
|
|
|
STATICFNDEF int4 add_trigger_options_attributes(char *trigvn, int trigvn_len, int trigger_index, char *trig_options, char **values,
|
|
uint4 *value_len)
|
|
{
|
|
uint4 db_option_bm;
|
|
mval mv_trig_indx;
|
|
char option_str[MAX_OPTIONS_LEN];
|
|
int option_str_len;
|
|
int4 result;
|
|
uint4 tf_option_bm;
|
|
uint4 tmp_bm;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
BUILD_OPTION_BITMAP(db_option_bm, trig_options);
|
|
BUILD_OPTION_BITMAP(tf_option_bm, values[OPTIONS_SUB]);
|
|
if (tf_option_bm == (db_option_bm & tf_option_bm))
|
|
/* If trigger file OPTIONS is contained in the DB OPTIONS, then trigger file entry is already in DB, just return */
|
|
return OPTIONS_PRESENT;
|
|
tmp_bm = db_option_bm | tf_option_bm;
|
|
if (((0 != (OPTIONS_C & tmp_bm)) && (0 != (OPTIONS_NOC & tmp_bm)))
|
|
|| ((0 != (OPTIONS_I & tmp_bm)) && (0 != (OPTIONS_NOI & tmp_bm))))
|
|
/* Can't combine incompatible options, so triggers are different */
|
|
return OPTION_CONFLICT;
|
|
if (!validate_label(trigvn, trigvn_len))
|
|
return INVALID_LABEL;
|
|
option_str_len = ARRAYSIZE(option_str);
|
|
OPTION_BITMAP_TO_STR(option_str, tmp_bm, option_str_len);
|
|
i2mval(&mv_trig_indx, trigger_index);
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, mv_trig_indx, trigger_subs[OPTIONS_SUB],
|
|
STRLEN(trigger_subs[OPTIONS_SUB]), option_str, option_str_len, result);
|
|
if (PUT_SUCCESS != result)
|
|
return result;
|
|
strcpy(trig_options, option_str);
|
|
trigger_incr_cycle(trigvn, trigvn_len);
|
|
return ADD_UPDATE_OPTIONS;
|
|
}
|
|
|
|
STATICFNDEF boolean_t subtract_trigger_cmd_attributes(char *trigvn, int trigvn_len, char *trig_cmds, char **values,
|
|
uint4 *value_len, boolean_t set_cmp, stringkey *kill_hash, stringkey *set_hash)
|
|
{
|
|
char cmd_str[MAX_COMMANDS_LEN];
|
|
int cmd_str_len;
|
|
uint4 db_cmd_bm;
|
|
uint4 len;
|
|
uint4 tf_cmd_bm;
|
|
uint4 restore_set = 0;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
BUILD_COMMAND_BITMAP(db_cmd_bm, trig_cmds);
|
|
BUILD_COMMAND_BITMAP(tf_cmd_bm, values[CMD_SUB]);
|
|
if (!set_cmp && (0 != (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & tf_cmd_bm)))
|
|
{ /* If the set compare failed, we don't want to consider the SET */
|
|
restore_set = gvtr_cmd_mask[GVTR_CMDTYPE_SET];
|
|
tf_cmd_bm &= ~restore_set;
|
|
}
|
|
if (0 == (db_cmd_bm & tf_cmd_bm))
|
|
/* If trigger file CMD does NOT overlap with the DB CMD, then no match. So no delete. Just return */
|
|
return 0;
|
|
cmd_str_len = ARRAYSIZE(cmd_str);
|
|
if (db_cmd_bm != (db_cmd_bm & tf_cmd_bm))
|
|
{ /* combine cmds - subtract trigger file attributes from db attributes */
|
|
COMMAND_BITMAP_TO_STR(cmd_str, (db_cmd_bm & tf_cmd_bm) ^ db_cmd_bm, cmd_str_len);
|
|
strcpy(trig_cmds, cmd_str);
|
|
if ((0 != (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & db_cmd_bm))
|
|
&& (0 == (gvtr_cmd_mask[GVTR_CMDTYPE_SET] & ((db_cmd_bm & tf_cmd_bm) ^ db_cmd_bm))))
|
|
/* We lost the "S" so we need to delete the set hash value */
|
|
cleanup_trigger_hash(trigvn, trigvn_len, values, value_len, set_hash, kill_hash, FALSE, 0);
|
|
} else
|
|
{ /* Both cmds are the same - candidate for delete */
|
|
trig_cmds[0] = '\0';
|
|
db_cmd_bm |= restore_set;
|
|
COMMAND_BITMAP_TO_STR(cmd_str, db_cmd_bm, cmd_str_len);
|
|
value_len[CMD_SUB] = cmd_str_len;
|
|
strcpy(values[CMD_SUB], cmd_str);
|
|
|
|
}
|
|
return SUB_UPDATE_CMDS;
|
|
}
|
|
|
|
STATICFNDEF boolean_t subtract_trigger_options_attributes(char *trigvn, int trigvn_len, char *trig_options, char *option_value)
|
|
{
|
|
uint4 db_option_bm;
|
|
char option_str[MAX_OPTIONS_LEN];
|
|
int option_str_len;
|
|
uint4 tf_option_bm;
|
|
uint4 tmp_bm;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
BUILD_OPTION_BITMAP(db_option_bm, trig_options);
|
|
BUILD_OPTION_BITMAP(tf_option_bm, option_value);
|
|
if (((0 != db_option_bm) && (0 != tf_option_bm)) && (0 == (db_option_bm & tf_option_bm)))
|
|
/* If trigger file OPTIONS does NOT overlap with the DB OPTIONS, then no match. So no delete. Just return */
|
|
return 0;
|
|
if (db_option_bm != (db_option_bm & tf_option_bm))
|
|
{
|
|
/* combine options - subtract trigger file attributes from db attributes */
|
|
tmp_bm = (db_option_bm & tf_option_bm) ^ db_option_bm;
|
|
assert((0 == (OPTIONS_C & tmp_bm)) || (0 == (OPTIONS_NOC & tmp_bm)));
|
|
assert((0 == (OPTIONS_I & tmp_bm)) || (0 == (OPTIONS_NOI & tmp_bm)));
|
|
option_str_len = ARRAYSIZE(option_str);
|
|
OPTION_BITMAP_TO_STR(option_str, tmp_bm, option_str_len);
|
|
strcpy(trig_options, option_str);
|
|
} else
|
|
/* Both options are the same - candidate to delete */
|
|
trig_options[0] = '\0';
|
|
return SUB_UPDATE_OPTIONS;
|
|
}
|
|
|
|
STATICFNDEF int4 modify_record(char *trigvn, int trigvn_len, char add_delete, int trigger_index, char **values, uint4 *value_len,
|
|
mval *trigger_count, boolean_t set_compare, boolean_t kill_compare, stringkey *kill_hash, stringkey *set_hash)
|
|
{
|
|
char db_cmds[MAX_COMMANDS_LEN + 1];
|
|
boolean_t name_matches;
|
|
int4 result;
|
|
uint4 retval;
|
|
mval trigindx;
|
|
char trig_cmds[MAX_COMMANDS_LEN + 1];
|
|
char trig_name[MAX_USER_TRIGNAME_LEN + 2]; /* One spot for # delimiter and one for trailing 0 */
|
|
char trig_options[MAX_OPTIONS_LEN + 1];
|
|
unsigned char util_buff[MAX_TRIG_UTIL_LEN];
|
|
int4 util_len;
|
|
mval val;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
retval = 0;
|
|
i2mval(&trigindx, trigger_index);
|
|
BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigindx, trigger_subs[CMD_SUB], STRLEN(trigger_subs[CMD_SUB]));
|
|
if (!gvcst_get(&val)) /* There has to be a command string */
|
|
HASHT_GVN_DEFINITION_RETRY_OR_ERROR(trigger_index, ",\"CMD\"");
|
|
memcpy(trig_cmds, val.str.addr, val.str.len);
|
|
trig_cmds[val.str.len] = '\0';
|
|
/* get(^#t(GVN,trigindx,"OPTIONS") */
|
|
BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigindx, trigger_subs[OPTIONS_SUB],
|
|
STRLEN(trigger_subs[OPTIONS_SUB]));
|
|
if (gvcst_get(&val))
|
|
memcpy(trig_options, val.str.addr, val.str.len);
|
|
else
|
|
val.str.len = 0;
|
|
trig_options[val.str.len] = '\0';
|
|
/* get(^#t(GVN,trigindx,"TRIGNAME") */
|
|
BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigindx, trigger_subs[TRIGNAME_SUB],
|
|
STRLEN(trigger_subs[TRIGNAME_SUB]));
|
|
if (gvcst_get(&val))
|
|
memcpy(trig_name, val.str.addr, val.str.len);
|
|
else
|
|
val.str.len = 0;
|
|
trig_name[val.str.len] = '\0';
|
|
if ('+' == add_delete)
|
|
{
|
|
result = add_trigger_cmd_attributes(trigvn, trigvn_len, trigger_index, trig_cmds, values, value_len,
|
|
set_compare, kill_compare, kill_hash, set_hash);
|
|
switch (result)
|
|
{
|
|
case K_ZTK_CONFLICT:
|
|
case INVALID_LABEL:
|
|
case ADD_NEW_TRIGGER:
|
|
case VAL_TOO_LONG:
|
|
case KEY_TOO_LONG:
|
|
return result;
|
|
default:
|
|
retval = result;
|
|
}
|
|
result = update_trigger_name(trigvn, trigvn_len, trigger_index, trig_name, values[TRIGNAME_SUB],
|
|
value_len[TRIGNAME_SUB]);
|
|
if ((INVALID_LABEL == result) || (VAL_TOO_LONG == result) || (KEY_TOO_LONG == result))
|
|
return result;
|
|
retval |= result;
|
|
result = add_trigger_options_attributes(trigvn, trigvn_len, trigger_index, trig_options, values, value_len);
|
|
if ((INVALID_LABEL == result) || (VAL_TOO_LONG == result) || (KEY_TOO_LONG == result))
|
|
return result;
|
|
retval |= result;
|
|
} else
|
|
{
|
|
name_matches = (0 == value_len[TRIGNAME_SUB])
|
|
|| ((value_len[TRIGNAME_SUB] == (STRLEN(trig_name) - 1))
|
|
&& (0 == memcmp(values[TRIGNAME_SUB], trig_name, value_len[TRIGNAME_SUB])));
|
|
if (name_matches)
|
|
{
|
|
retval = SUB_UPDATE_NAME;
|
|
memcpy(db_cmds, trig_cmds, SIZEOF(trig_cmds));
|
|
retval |= subtract_trigger_cmd_attributes(trigvn, trigvn_len, trig_cmds, values, value_len, set_compare,
|
|
kill_hash, set_hash);
|
|
retval |= subtract_trigger_options_attributes(trigvn, trigvn_len, trig_options, values[OPTIONS_SUB]);
|
|
}
|
|
if ((0 != (retval & SUB_UPDATE_NAME)) && (0 != (retval & SUB_UPDATE_OPTIONS)) && (0 != (retval & SUB_UPDATE_CMDS)))
|
|
{
|
|
if ((0 == trig_cmds[0]) && (0 == trig_options[0]))
|
|
{
|
|
result = trigger_delete(trigvn, trigvn_len, trigger_count, trigger_index);
|
|
if ((VAL_TOO_LONG == result) || (KEY_TOO_LONG == result))
|
|
return result;
|
|
retval = DELETE_REC;
|
|
} else
|
|
{
|
|
retval = 0;
|
|
if (0 != trig_cmds[0])
|
|
{
|
|
result = update_commands(trigvn, trigvn_len, trigger_index, trig_cmds, db_cmds);
|
|
if (SUB_UPDATE_CMDS != result)
|
|
return result;
|
|
retval |= result;
|
|
}
|
|
if (0 != trig_options[0])
|
|
{
|
|
result = update_options(trigvn, trigvn_len, trigger_index, trig_options,
|
|
values[OPTIONS_SUB]);
|
|
if (SUB_UPDATE_OPTIONS != result)
|
|
return result;
|
|
if (0 != value_len[OPTIONS_SUB])
|
|
retval |= result;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
retval = SUB_UPDATE_NOCHANGE;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
STATICFNDEF int4 gen_trigname_sequence(char *trigvn, int trigvn_len, mval *trigger_count, char *user_trigname_str,
|
|
uint4 user_trigname_len)
|
|
{
|
|
sgmnt_addrs *csa;
|
|
char name_and_index[MAX_MIDENT_LEN + 1 + MAX_DIGITS_IN_INT];
|
|
char *ptr1;
|
|
int rndm_int;
|
|
int seq_num;
|
|
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;
|
|
int4 result;
|
|
char *seq_ptr, *uniq_ptr;
|
|
char trig_name[MAX_USER_TRIGNAME_LEN + 1];
|
|
uint4 trigname_len;
|
|
char unique_seq_str[NUM_TRIGNAME_SEQ_CHARS + 1];
|
|
mval val, *val_ptr;
|
|
char val_str[MAX_DIGITS_IN_INT + 1];
|
|
int var_count;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
assert(MAX_USER_TRIGNAME_LEN >= user_trigname_len);
|
|
uniq_ptr = unique_seq_str;
|
|
seq_num = 1;
|
|
if (0 == user_trigname_len)
|
|
{ /* autogenerated name -- might be long */
|
|
trigname_len = MIN(trigvn_len, MAX_AUTO_TRIGNAME_LEN);
|
|
strncpy(trig_name, trigvn, trigname_len);
|
|
} else
|
|
{ /* user supplied name */
|
|
trigname_len = user_trigname_len;
|
|
strncpy(trig_name, user_trigname_str, user_trigname_len);
|
|
}
|
|
SAVE_TRIGGER_REGION_INFO;
|
|
SWITCH_TO_DEFAULT_REGION;
|
|
if (gv_cur_region->read_only)
|
|
rts_error(VARLSTCNT(4) ERR_TRIGMODREGNOTRW, 2, REG_LEN_STR(gv_cur_region));
|
|
INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED;
|
|
if (0 == user_trigname_len)
|
|
{ /* autogenerated name */
|
|
if (0 != gv_target->root)
|
|
{
|
|
val_ptr = &val;
|
|
/* $get(^#t("#TNAME",GVN,"#SEQCOUNT")) */
|
|
BUILD_HASHT_SUB_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trig_name, trigname_len,
|
|
LITERAL_HASHSEQNUM, STRLEN(LITERAL_HASHSEQNUM));
|
|
seq_num = gvcst_get(val_ptr) ? mval2i(val_ptr) + 1 : 1;
|
|
if (MAX_TRIGNAME_SEQ_NUM < seq_num)
|
|
{
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
return TOO_MANY_TRIGGERS;
|
|
}
|
|
}
|
|
INT2STR(seq_num, uniq_ptr);
|
|
/* set ^#t("#TNAME",GVN,"#SEQCOUNT")++ via unique_seq_str which came from seq_num*/
|
|
SET_TRIGGER_GLOBAL_SUB_SUB_SUB_STR(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trig_name, trigname_len,
|
|
LITERAL_HASHSEQNUM, STRLEN(LITERAL_HASHSEQNUM), unique_seq_str, STRLEN(unique_seq_str), result);
|
|
if (PUT_SUCCESS != result)
|
|
{
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
return result;
|
|
}
|
|
/* set ^#t("#TNAME",GVN,"#TNCOUNT")++ */
|
|
BUILD_HASHT_SUB_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trig_name, trigname_len,
|
|
LITERAL_HASHTNCOUNT, STRLEN(LITERAL_HASHTNCOUNT));
|
|
var_count = gvcst_get(val_ptr) ? mval2i(val_ptr) + 1 : 1;
|
|
i2mval(&val, var_count);
|
|
SET_TRIGGER_GLOBAL_SUB_SUB_SUB_MVAL(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trig_name, trigname_len,
|
|
LITERAL_HASHTNCOUNT, STRLEN(LITERAL_HASHTNCOUNT), val, result);
|
|
if (PUT_SUCCESS != result)
|
|
{
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
return result;
|
|
}
|
|
} else
|
|
*uniq_ptr = '\0'; /* user supplied name */
|
|
seq_ptr = user_trigname_str;
|
|
memcpy(seq_ptr, trig_name, trigname_len);
|
|
seq_ptr += trigname_len;
|
|
if (0 == user_trigname_len)
|
|
{ /* Autogenerated */
|
|
*seq_ptr++ = TRIGNAME_SEQ_DELIM;
|
|
memcpy(seq_ptr, unique_seq_str, STRLEN(unique_seq_str));
|
|
seq_ptr += STRLEN(unique_seq_str);
|
|
}
|
|
*seq_ptr = '\0';
|
|
ptr1 = name_and_index;
|
|
memcpy(ptr1, trigvn, trigvn_len);
|
|
ptr1 += trigvn_len;
|
|
*ptr1++ = '\0';
|
|
MV_FORCE_STR(trigger_count);
|
|
memcpy(ptr1, trigger_count->str.addr, trigger_count->str.len);
|
|
SET_TRIGGER_GLOBAL_SUB_SUB_STR(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), user_trigname_str, STRLEN(user_trigname_str),
|
|
name_and_index, trigvn_len + 1 + trigger_count->str.len, result);
|
|
if (PUT_SUCCESS != result)
|
|
{
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
return result;
|
|
}
|
|
*seq_ptr++ = TRIGNAME_SEQ_DELIM;
|
|
*seq_ptr = '\0';
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
return SEQ_SUCCESS;
|
|
}
|
|
|
|
boolean_t trigger_update_rec(char *trigger_rec, uint4 len, boolean_t noprompt, uint4 *trig_stats, io_pair *trigfile_device,
|
|
int4 *record_num)
|
|
{
|
|
char add_delete;
|
|
char ans[2];
|
|
sgmnt_addrs *csa;
|
|
boolean_t cmd_modified;
|
|
char db_trig_name[MAX_USER_TRIGNAME_LEN + 1];
|
|
boolean_t full_match;
|
|
mstr gbl_name, var_name;
|
|
mname_entry gvent;
|
|
gv_namehead *hasht_tree;
|
|
boolean_t kill_cmp;
|
|
int4 max_len;
|
|
boolean_t multi_line, multi_line_xecute;
|
|
mval mv_hash;
|
|
boolean_t new_name;
|
|
int num;
|
|
int4 offset;
|
|
char *ptr1;
|
|
int4 rec_len;
|
|
int4 rec_num;
|
|
boolean_t result;
|
|
char save_currkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)];
|
|
char save_altkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)];
|
|
gv_key *save_gv_currkey;
|
|
gv_key *save_gv_altkey;
|
|
gv_namehead *save_gvtarget;
|
|
boolean_t set_cmp;
|
|
boolean_t skip_set_trigger;
|
|
boolean_t status;
|
|
int sub_indx;
|
|
char tcount[MAX_DIGITS_IN_INT];
|
|
char tfile_rec_val[MAX_BUFF_SIZE];
|
|
char trig_cmds[MAX_COMMANDS_LEN + 1];
|
|
mval *trig_cnt_ptr;
|
|
char trig_name[MAX_USER_TRIGNAME_LEN + 2]; /* One spot for '#' delimiter and one for trailing '\0' */
|
|
char trig_options[MAX_OPTIONS_LEN + 1];
|
|
char trigvn[MAX_MIDENT_LEN + 1];
|
|
mval trigger_count;
|
|
int trigvn_len;
|
|
int trigindx, kill_index = -1;
|
|
int4 updates = 0;
|
|
char *values[NUM_SUBS];
|
|
uint4 value_len[NUM_SUBS];
|
|
stringkey kill_trigger_hash, set_trigger_hash;
|
|
char tmp_str[MAX_HASH_INDEX_LEN + 1 + MAX_DIGITS_IN_INT];
|
|
char xecute_buffer[MAX_XECUTE_LEN];
|
|
mval xecute_index, xecute_size;
|
|
mval reportname, reportnamealt;
|
|
io_pair io_save_device;
|
|
int4 max_xecute_size;
|
|
boolean_t no_error;
|
|
boolean_t newtrigger;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
assert(0 > memcmp(LITERAL_HASHLABEL, LITERAL_MAXHASHVAL, MIN(STRLEN(LITERAL_HASHLABEL), STRLEN(LITERAL_MAXHASHVAL))));
|
|
assert(0 > memcmp(LITERAL_HASHCOUNT, LITERAL_MAXHASHVAL, MIN(STRLEN(LITERAL_HASHCOUNT), STRLEN(LITERAL_MAXHASHVAL))));
|
|
assert(0 > memcmp(LITERAL_HASHCYCLE, LITERAL_MAXHASHVAL, MIN(STRLEN(LITERAL_HASHCYCLE), STRLEN(LITERAL_MAXHASHVAL))));
|
|
assert(0 > memcmp(LITERAL_HASHTNAME, LITERAL_MAXHASHVAL, MIN(STRLEN(LITERAL_HASHTNAME), STRLEN(LITERAL_MAXHASHVAL))));
|
|
assert(0 > memcmp(LITERAL_HASHTNCOUNT, LITERAL_MAXHASHVAL, MIN(STRLEN(LITERAL_HASHTNCOUNT), STRLEN(LITERAL_MAXHASHVAL))));
|
|
rec_num = (NULL == record_num) ? 0 : *record_num;
|
|
gvinit();
|
|
if ((0 == len) || (COMMENT_LITERAL == *trigger_rec))
|
|
return TRIG_SUCCESS;
|
|
if (('-' != *trigger_rec) && ('+' != *trigger_rec))
|
|
{
|
|
trig_stats[STATS_ERROR]++;
|
|
util_out_print_gtmio("missing +/- at start of line: !AD", FLUSH, len, trigger_rec);
|
|
return TRIG_FAILURE;
|
|
}
|
|
add_delete = *trigger_rec++;
|
|
len--;
|
|
if ('-' == add_delete)
|
|
{
|
|
if ((1 == len) && ('*' == *trigger_rec))
|
|
{
|
|
if (!noprompt)
|
|
{
|
|
util_out_print("This operation will delete all triggers.", FLUSH);
|
|
util_out_print("Proceed? [y/n]: ", FLUSH);
|
|
SCANF("%1s", ans); /* We only need one char, any more would overflow our buffer */
|
|
if ('y' != ans[0] && 'Y' != ans[0])
|
|
{
|
|
util_out_print_gtmio("Triggers NOT deleted", FLUSH);
|
|
return TRIG_SUCCESS;
|
|
}
|
|
}
|
|
trigger_delete_all();
|
|
return TRIG_SUCCESS;
|
|
} else if ((0 == len) || ('^' != *trigger_rec))
|
|
{ /* if the length < 0 let trigger_delete_name respond with the error message */
|
|
if (TRIG_FAILURE == (status = trigger_delete_name(trigger_rec, len, trig_stats)))
|
|
trig_stats[STATS_ERROR]++;
|
|
return status;
|
|
}
|
|
}
|
|
values[GVSUBS_SUB] = tfile_rec_val; /* GVSUBS will be the first entry set so initialize it */
|
|
max_len = (int4)SIZEOF(tfile_rec_val);
|
|
multi_line_xecute = FALSE;
|
|
no_error = TRUE;
|
|
if (!trigger_parse(trigger_rec, len, trigvn, values, value_len, &max_len, &multi_line_xecute))
|
|
{
|
|
if (multi_line_xecute)
|
|
no_error = FALSE;
|
|
else
|
|
{
|
|
trig_stats[STATS_ERROR]++;
|
|
return TRIG_FAILURE;
|
|
}
|
|
}
|
|
trigvn_len = STRLEN(trigvn);
|
|
set_trigger_hash.str.addr = tmp_str;
|
|
set_trigger_hash.str.len = SIZEOF(tmp_str);
|
|
build_set_cmp_str(trigvn, trigvn_len, values, value_len, &set_trigger_hash.str, multi_line_xecute);
|
|
COMPUTE_HASH_STR(&set_trigger_hash);
|
|
kill_trigger_hash.str.addr = tmp_str;
|
|
kill_trigger_hash.str.len = SIZEOF(tmp_str);
|
|
build_kill_cmp_str(trigvn, trigvn_len, values, value_len, &kill_trigger_hash.str, multi_line_xecute);
|
|
COMPUTE_HASH_STR(&kill_trigger_hash);
|
|
if (multi_line_xecute)
|
|
{
|
|
if (NULL == trigfile_device)
|
|
{
|
|
util_out_print_gtmio("Cannot use multi-line xecute in $ztrigger ITEM", FLUSH);
|
|
return TRIG_FAILURE;
|
|
}
|
|
io_save_device = io_curr_device;
|
|
io_curr_device = *trigfile_device;
|
|
values[XECUTE_SUB] = xecute_buffer;
|
|
value_len[XECUTE_SUB] = 0;
|
|
max_xecute_size = SIZEOF(xecute_buffer);
|
|
multi_line = multi_line_xecute;
|
|
while (multi_line && (0 <= (rec_len = file_input_get(&trigger_rec))))
|
|
{
|
|
rec_num++;
|
|
io_curr_device = io_save_device; /* In case we have to write an error message */
|
|
no_error &= trigger_parse(trigger_rec, (uint4)rec_len, trigvn, values, value_len, &max_xecute_size,
|
|
&multi_line);
|
|
io_curr_device = *trigfile_device;
|
|
}
|
|
if (NULL != record_num)
|
|
*record_num = rec_num;
|
|
if (!no_error)
|
|
{
|
|
io_curr_device = io_save_device;
|
|
trig_stats[STATS_ERROR]++;
|
|
return TRIG_FAILURE;
|
|
}
|
|
if (0 > rec_len)
|
|
{
|
|
io_curr_device = io_save_device;
|
|
util_out_print_gtmio("Multi-line trigger -XECUTE is not properly terminated", FLUSH);
|
|
trig_stats[STATS_ERROR]++;
|
|
return TRIG_FAILURE;
|
|
}
|
|
STR_HASH(values[XECUTE_SUB], value_len[XECUTE_SUB], set_trigger_hash.hash_code, set_trigger_hash.hash_code);
|
|
STR_HASH(values[XECUTE_SUB], value_len[XECUTE_SUB], kill_trigger_hash.hash_code, kill_trigger_hash.hash_code);
|
|
io_curr_device = io_save_device;
|
|
}
|
|
gbl_name.addr = trigvn;
|
|
gbl_name.len = trigvn_len;
|
|
GV_BIND_NAME_ONLY(gd_header, &gbl_name);
|
|
if (gv_cur_region->read_only)
|
|
rts_error(VARLSTCNT(4) ERR_TRIGMODREGNOTRW, 2, REG_LEN_STR(gv_cur_region));
|
|
csa = gv_target->gd_csa;
|
|
/* Now that the gv_target of the global the trigger refers to is setup, check if we are attempting to modify/delete a
|
|
* trigger for a global that has already had a trigger fire in this transaction. For these single-region (at a time)
|
|
* checks, we can do them all the time as they are cheap.
|
|
*/
|
|
if (gv_target->trig_local_tn == local_tn)
|
|
rts_error(VARLSTCNT(1) ERR_TRIGMODINTP);
|
|
csa->incr_db_trigger_cycle = TRUE; /* so that we increment csd->db_trigger_cycle at commit time */
|
|
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. Note that the below increment happens for every record added. So, even if a single trigger file loaded
|
|
* multiple triggers on the same region, db_dztrigger_cycle will be incremented more than one for same transaction.
|
|
* This is considered okay since we only need db_dztrigger_cycle to be equal to a different value than
|
|
* gvt->db_dztrigger_cycle
|
|
*/
|
|
csa->db_dztrigger_cycle++;
|
|
}
|
|
SETUP_TRIGGER_GLOBAL;
|
|
INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED;
|
|
new_name = check_unique_trigger_name(trigvn, trigvn_len, values[TRIGNAME_SUB], value_len[TRIGNAME_SUB]);
|
|
cmd_modified = skip_set_trigger = newtrigger = FALSE;
|
|
trigindx = 1;
|
|
assert(('+' == add_delete) || ('-' == add_delete)); /* Has to be + or - */
|
|
if (0 != gv_target->root)
|
|
{
|
|
BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT));
|
|
if (gvcst_get(&trigger_count))
|
|
{
|
|
if (trigger_already_exists(trigvn, trigvn_len, values, value_len, &trigindx, &kill_index, &set_cmp,
|
|
&kill_cmp, &full_match, &set_trigger_hash, &kill_trigger_hash,
|
|
&reportname, &reportnamealt))
|
|
{
|
|
if (!new_name && ('+' == add_delete) && (!full_match))
|
|
{
|
|
TRIGGER_SAME_NAME_EXISTS_ERROR;
|
|
}
|
|
if (-1 != kill_index)
|
|
{
|
|
if (0 != value_len[TRIGNAME_SUB])
|
|
{ /* can't match two different trigger with a user defined name */
|
|
trig_stats[STATS_ERROR]++;
|
|
util_out_print_gtmio("Conflicting trigger definition for global ^!AD. Definition " \
|
|
"matches trigger named !AD and attempts to create a new trigger named !AD",
|
|
FLUSH, trigvn_len, trigvn, reportnamealt.str.len, reportnamealt.str.addr,
|
|
value_len[TRIGNAME_SUB], values[TRIGNAME_SUB]);
|
|
return TRIG_FAILURE;
|
|
}
|
|
updates = modify_record(trigvn, trigvn_len, add_delete, kill_index, values, value_len,
|
|
&trigger_count, FALSE, TRUE, &kill_trigger_hash, &set_trigger_hash);
|
|
switch (updates)
|
|
{
|
|
case INVALID_LABEL:
|
|
trig_stats[STATS_ERROR]++;
|
|
util_out_print_gtmio("Current trigger format not compatible to update " \
|
|
"the trigger on ^!AD named !AD", FLUSH, trigvn_len, trigvn,
|
|
reportnamealt.str.len, reportnamealt.str.addr);
|
|
return TRIG_FAILURE;
|
|
case VAL_TOO_LONG:
|
|
trig_stats[STATS_ERROR]++;
|
|
util_out_print_gtmio("^!AD trigger - value larger than record size", FLUSH,
|
|
trigvn_len, trigvn);
|
|
return TRIG_FAILURE;
|
|
case K_ZTK_CONFLICT:
|
|
trig_stats[STATS_ERROR]++;
|
|
util_out_print_gtmio("Command options !AD incompatible with trigger on " \
|
|
"^!AD named !AD", FLUSH, value_len[CMD_SUB], values[CMD_SUB],
|
|
trigvn_len, trigvn, reportnamealt.str.len, reportnamealt.str.addr);
|
|
return TRIG_FAILURE;
|
|
default:
|
|
if ((0 != (updates & ADD_UPDATE_CMDS)) ||
|
|
(0 != (updates & SUB_UPDATE_CMDS)))
|
|
{
|
|
if (0 == trig_stats[STATS_ERROR])
|
|
util_out_print_gtmio("Updated trigger on ^!AD named " \
|
|
"!AD and ", NOFLUSH, trigvn_len,
|
|
trigvn, reportnamealt.str.len,
|
|
reportnamealt.str.addr);
|
|
trig_stats[STATS_MODIFIED]++;
|
|
} else if ((0 != (updates & ADD_UPDATE_NAME)) ||
|
|
(0 != (updates & SUB_UPDATE_NAME)) ||
|
|
(0 != (updates & SUB_UPDATE_OPTIONS)) ||
|
|
(0 != (updates & ADD_UPDATE_OPTIONS)))
|
|
{ /* NAME and OPTIONS cannot change on K-type match */
|
|
assertpro(FALSE);
|
|
} else if (0 != (updates & DELETE_REC))
|
|
{
|
|
if (0 == trig_stats[STATS_ERROR])
|
|
util_out_print_gtmio("Deleted trigger on ^!AD named "\
|
|
"!AD and ", NOFLUSH, trigvn_len,
|
|
trigvn, reportnamealt.str.len,
|
|
reportnamealt.str.addr);
|
|
trig_stats[STATS_DELETED]++;
|
|
/* if trigger deleted, search for possible new SET trigger index */
|
|
if(kill_index < trigindx &&
|
|
!(trigger_already_exists(trigvn, trigvn_len, values, value_len,
|
|
&trigindx, &kill_index,
|
|
&set_cmp, &kill_cmp, &full_match,
|
|
&set_trigger_hash, &kill_trigger_hash,
|
|
&reportname, &reportnamealt)))
|
|
{ /* SET trigger found previously is not found again */
|
|
if (CDB_STAGNATE > t_tries)
|
|
t_retry(cdb_sc_triggermod);
|
|
else
|
|
{
|
|
assert(WBTEST_HELPOUT_TRIGDEFBAD == \
|
|
gtm_white_box_test_case_number);
|
|
trig_stats[STATS_ERROR]++;
|
|
util_out_print_gtmio("Previously found trigger on" \
|
|
"^!AD ,named !AD but cannot " \
|
|
"find it again",
|
|
FLUSH, trigvn_len, trigvn,
|
|
reportnamealt.str.len,
|
|
reportnamealt.str.addr);
|
|
}
|
|
return TRIG_FAILURE;
|
|
}
|
|
} else
|
|
{
|
|
util_out_print_gtmio("Trigger on ^!AD already present " \
|
|
"-- same as trigger named !AD and ",
|
|
NOFLUSH, trigvn_len, trigvn,
|
|
reportname.str.len, reportname.str.addr);
|
|
trig_stats[STATS_UNCHANGED]++;
|
|
}
|
|
}
|
|
}
|
|
updates = modify_record(trigvn, trigvn_len, add_delete, trigindx, values, value_len,
|
|
&trigger_count, set_cmp, kill_cmp, &kill_trigger_hash, &set_trigger_hash);
|
|
switch (updates)
|
|
{
|
|
case ADD_NEW_TRIGGER:
|
|
if (0 != value_len[TRIGNAME_SUB])
|
|
{ /* can't add a new trigger when you already matched on a name */
|
|
trig_stats[STATS_ERROR]++;
|
|
util_out_print_gtmio("Conflicting trigger definition for global ^!AD." \
|
|
" Definition matches trigger named !AD and attempts" \
|
|
" to create a new trigger named !AD", FLUSH,
|
|
trigvn_len, trigvn,
|
|
reportname.str.len, reportname.str.addr,
|
|
value_len[TRIGNAME_SUB], values[TRIGNAME_SUB]);
|
|
return TRIG_FAILURE;
|
|
}
|
|
cmd_modified = TRUE;
|
|
trig_cnt_ptr = &trigger_count;
|
|
num = mval2i(trig_cnt_ptr);
|
|
trigindx = ++num;
|
|
i2mval(&trigger_count, num);
|
|
break;
|
|
case INVALID_LABEL:
|
|
trig_stats[STATS_ERROR]++;
|
|
util_out_print_gtmio("Current trigger format not compatible to update " \
|
|
"the trigger on ^!AD named !AD", FLUSH, trigvn_len, trigvn,
|
|
reportname.str.len, reportname.str.addr);
|
|
return TRIG_FAILURE;
|
|
case VAL_TOO_LONG:
|
|
trig_stats[STATS_ERROR]++;
|
|
util_out_print_gtmio("^!AD trigger - value larger than record size", FLUSH,
|
|
trigvn_len, trigvn);
|
|
return TRIG_FAILURE;
|
|
case K_ZTK_CONFLICT:
|
|
trig_stats[STATS_ERROR]++;
|
|
util_out_print_gtmio("Command options !AD incompatible with trigger on " \
|
|
"^!AD named !AD", FLUSH, value_len[CMD_SUB], values[CMD_SUB],
|
|
trigvn_len, trigvn, reportname.str.len, reportname.str.addr);
|
|
return TRIG_FAILURE;
|
|
default:
|
|
skip_set_trigger = TRUE;
|
|
if ((0 != (updates & ADD_UPDATE_NAME)) || (0 != (updates & ADD_UPDATE_CMDS))
|
|
|| (0 != (updates & ADD_UPDATE_OPTIONS)))
|
|
{
|
|
i2mval(&trigger_count, trigindx);
|
|
trig_stats[STATS_MODIFIED]++;
|
|
if (0 == trig_stats[STATS_ERROR])
|
|
util_out_print_gtmio("Updated trigger on ^!AD named !AD", FLUSH,
|
|
trigvn_len, trigvn, reportname.str.len,
|
|
reportname.str.addr);
|
|
} else if (0 != (updates & DELETE_REC))
|
|
{
|
|
trig_stats[STATS_DELETED]++;
|
|
if (0 == trig_stats[STATS_ERROR])
|
|
util_out_print_gtmio("Deleted trigger on ^!AD named !AD",
|
|
FLUSH, trigvn_len, trigvn,
|
|
reportname.str.len, reportname.str.addr);
|
|
} else if ((0 != (updates & SUB_UPDATE_NAME)) || (0 != (updates & SUB_UPDATE_CMDS))
|
|
|| (0 != (updates & SUB_UPDATE_OPTIONS)))
|
|
{
|
|
trig_stats[STATS_MODIFIED]++;
|
|
if (0 == trig_stats[STATS_ERROR])
|
|
util_out_print_gtmio("Updated trigger on ^!AD named !AD", FLUSH,
|
|
trigvn_len, trigvn, reportname.str.len,
|
|
reportname.str.addr);
|
|
} else if ('+' == add_delete)
|
|
{
|
|
if (0 == trig_stats[STATS_ERROR])
|
|
{
|
|
util_out_print_gtmio("Trigger on ^!AD already present -- same" \
|
|
" as trigger named !AD", FLUSH, trigvn_len, trigvn,
|
|
reportname.str.len, reportname.str.addr);
|
|
trig_stats[STATS_UNCHANGED]++;
|
|
}
|
|
} else
|
|
{
|
|
if (0 == trig_stats[STATS_ERROR])
|
|
{
|
|
util_out_print_gtmio("Trigger on ^!AD does not exist - " \
|
|
"no action taken", FLUSH, trigvn_len, trigvn);
|
|
trig_stats[STATS_UNCHANGED]++;
|
|
}
|
|
}
|
|
}
|
|
} else if ('+' == add_delete)
|
|
{
|
|
assert(0 == trigindx);
|
|
if (!new_name)
|
|
{
|
|
TRIGGER_SAME_NAME_EXISTS_ERROR;
|
|
}
|
|
trig_cnt_ptr = &trigger_count;
|
|
num = mval2i(trig_cnt_ptr);
|
|
trigindx = ++num;
|
|
i2mval(&trigger_count, num);
|
|
} else
|
|
{ /* '-' == add_delete */
|
|
if (0 == trig_stats[STATS_ERROR])
|
|
{
|
|
trig_stats[STATS_UNCHANGED]++;
|
|
util_out_print_gtmio("Trigger on ^!AD does not exist - no action taken", FLUSH,
|
|
trigvn_len, trigvn);
|
|
}
|
|
skip_set_trigger = TRUE;
|
|
}
|
|
} else
|
|
newtrigger = TRUE;
|
|
} else
|
|
newtrigger = TRUE;
|
|
if (newtrigger)
|
|
{
|
|
if ('-' == add_delete)
|
|
{
|
|
if (0 == trig_stats[STATS_ERROR])
|
|
util_out_print_gtmio("Trigger on ^!AD does not exist - no action taken",
|
|
FLUSH, trigvn_len, trigvn);
|
|
else
|
|
trig_stats[STATS_DELETED]++;
|
|
skip_set_trigger = TRUE;
|
|
} else
|
|
{
|
|
if (!new_name)
|
|
{
|
|
TRIGGER_SAME_NAME_EXISTS_ERROR;
|
|
}
|
|
trigger_count = literal_one;
|
|
}
|
|
}
|
|
/* Since a specified trigger name will grow by 1, copy it to a long enough array */
|
|
if (((0 != (updates & ADD_UPDATE_NAME)) && ('-' != add_delete)) || !skip_set_trigger)
|
|
{
|
|
memcpy(trig_name, values[TRIGNAME_SUB], value_len[TRIGNAME_SUB] + 1);
|
|
values[TRIGNAME_SUB] = trig_name;
|
|
result = gen_trigname_sequence(trigvn, trigvn_len, &trigger_count, values[TRIGNAME_SUB],
|
|
value_len[TRIGNAME_SUB]);
|
|
if (SEQ_SUCCESS != result)
|
|
{
|
|
if (TOO_MANY_TRIGGERS == result)
|
|
util_out_print_gtmio("^!AD trigger - Too many triggers", FLUSH, trigvn_len, trigvn);
|
|
else
|
|
{
|
|
TOO_LONG_REC_KEY_ERROR_MSG;
|
|
}
|
|
trig_stats[STATS_ERROR]++;
|
|
return TRIG_FAILURE;
|
|
}
|
|
}
|
|
if (!skip_set_trigger && (0 == trig_stats[STATS_ERROR]))
|
|
{
|
|
value_len[TRIGNAME_SUB] = STRLEN(values[TRIGNAME_SUB]);
|
|
values[CHSET_SUB] = (gtm_utf8_mode) ? UTF8_NAME : LITERAL_M;
|
|
value_len[CHSET_SUB] = STRLEN(values[CHSET_SUB]);
|
|
/* set ^#t(GVN,"#LABEL") = "2" */
|
|
SET_TRIGGER_GLOBAL_SUB_SUB_STR(trigvn, trigvn_len, LITERAL_HASHLABEL, STRLEN(LITERAL_HASHLABEL),
|
|
HASHT_GBL_CURLABEL, STRLEN(HASHT_GBL_CURLABEL), result);
|
|
IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result);
|
|
trigger_incr_cycle(trigvn, trigvn_len);
|
|
/* set ^#t(GVN,"#COUNT") = trigger_count */
|
|
SET_TRIGGER_GLOBAL_SUB_SUB_MVAL(trigvn, trigvn_len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT),
|
|
trigger_count, result);
|
|
IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result);
|
|
for (sub_indx = 0; sub_indx < NUM_SUBS; sub_indx++)
|
|
{
|
|
if (0 >= value_len[sub_indx]) /* subscript index length is zero (no longer used), skip it */
|
|
continue;
|
|
/* set ^#t(GVN,trigger_count,values[sub_indx]) = xecute string */
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_STR(trigvn, trigvn_len, trigger_count,
|
|
trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), values[sub_indx],
|
|
value_len[sub_indx], result);
|
|
if (XECUTE_SUB != sub_indx)
|
|
{
|
|
IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result);
|
|
} else
|
|
{ /* XECUTE_SUB == sub_indx */
|
|
max_len = value_len[XECUTE_SUB];
|
|
assert(0 < max_len);
|
|
if (PUT_SUCCESS != result)
|
|
{ /* xecute string does not fit in one record, break it up */
|
|
i2mval(&xecute_size, max_len);
|
|
num = 0;
|
|
ptr1 = values[XECUTE_SUB];
|
|
i2mval(&xecute_index, num);
|
|
/* set ^#t(GVN,trigger_count,"XECUTE",0) = xecute string length */
|
|
BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY(trigvn, trigvn_len, trigger_count,
|
|
trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), xecute_index);
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MSUB_MVAL(trigvn, trigvn_len, trigger_count,
|
|
trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), xecute_index,
|
|
xecute_size, result);
|
|
IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result);
|
|
while (0 < max_len)
|
|
{
|
|
i2mval(&xecute_index, ++num);
|
|
BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY(trigvn, trigvn_len, trigger_count,
|
|
trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), xecute_index);
|
|
offset = MIN(gv_cur_region->max_rec_size - (gv_currkey->end + 1 + SIZEOF(rec_hdr)),
|
|
max_len);
|
|
/* set ^#t(GVN,trigger_count,"XECUTE",num) = xecute string[offset] */
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MSUB_STR(trigvn, trigvn_len, trigger_count,
|
|
trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), xecute_index,
|
|
ptr1, offset, result);
|
|
IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result);
|
|
ptr1 += offset;
|
|
max_len -= offset;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
result = add_trigger_hash_entry(trigvn, trigvn_len, values[CMD_SUB], trigindx, TRUE, &kill_trigger_hash,
|
|
&set_trigger_hash);
|
|
IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result);
|
|
MV_FORCE_UMVAL(&mv_hash, kill_trigger_hash.hash_code);
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MVAL(trigvn, trigvn_len, trigger_count,
|
|
trigger_subs[LHASH_SUB], STRLEN(trigger_subs[LHASH_SUB]), mv_hash, result);
|
|
IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result);
|
|
MV_FORCE_UMVAL(&mv_hash, set_trigger_hash.hash_code);
|
|
SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MVAL(trigvn, trigvn_len, trigger_count, trigger_subs[BHASH_SUB],
|
|
STRLEN(trigger_subs[BHASH_SUB]), mv_hash, result);
|
|
IF_ERROR_THEN_TOO_LONG_ERROR_MSG_AND_RETURN_FAILURE(result);
|
|
trig_stats[STATS_ADDED]++;
|
|
if (cmd_modified)
|
|
util_out_print_gtmio("Modified commands of the trigger on ^!AD named !AD", FLUSH, trigvn_len, trigvn,
|
|
value_len[TRIGNAME_SUB], values[TRIGNAME_SUB]);
|
|
else
|
|
util_out_print_gtmio("Added trigger on ^!AD named !AD", FLUSH, trigvn_len, trigvn,
|
|
value_len[TRIGNAME_SUB], values[TRIGNAME_SUB]);
|
|
} else if (0 != trig_stats[STATS_ERROR])
|
|
{
|
|
if ('+' == add_delete)
|
|
trig_stats[STATS_ADDED]++;
|
|
util_out_print_gtmio("No errors", FLUSH);
|
|
}
|
|
return TRIG_SUCCESS;
|
|
}
|
|
|
|
STATICFNDEF boolean_t trigger_update_rec_helper(char *trigger_rec, uint4 len, boolean_t noprompt, uint4 *trig_stats)
|
|
{
|
|
enum cdb_sc cdb_status;
|
|
boolean_t trigger_status;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
ESTABLISH_RET(trigger_tpwrap_ch, TRIG_FAILURE);
|
|
trigger_status = trigger_update_rec(trigger_rec, len, TRUE, trig_stats, NULL, NULL);
|
|
if (TRIG_SUCCESS == trigger_status)
|
|
{
|
|
GVTR_OP_TCOMMIT(cdb_status);
|
|
if (cdb_sc_normal != cdb_status)
|
|
t_retry(cdb_status); /* won't return */
|
|
} else
|
|
{ /* Record cannot be committed - undo everything */
|
|
assert(donot_INVOKE_MUMTSTART);
|
|
DEBUG_ONLY(donot_INVOKE_MUMTSTART = FALSE);
|
|
OP_TROLLBACK(-1); /* returns but kills implicit transaction */
|
|
}
|
|
REVERT;
|
|
return trigger_status;
|
|
}
|
|
|
|
boolean_t trigger_update(char *trigger_rec, uint4 len)
|
|
{
|
|
uint4 i;
|
|
uint4 trig_stats[NUM_STATS];
|
|
boolean_t trigger_status = TRIG_FAILURE;
|
|
mval ts_mv;
|
|
int loopcnt;
|
|
DEBUG_ONLY(unsigned int lcl_t_tries;)
|
|
enum cdb_sc failure;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
|
|
for (i = 0; NUM_STATS > i; i++)
|
|
trig_stats[i] = 0;
|
|
ts_mv.mvtype = MV_STR;
|
|
ts_mv.str.len = 0;
|
|
ts_mv.str.addr = NULL;
|
|
if (0 == dollar_tlevel)
|
|
{
|
|
assert(!donot_INVOKE_MUMTSTART);
|
|
DEBUG_ONLY(donot_INVOKE_MUMTSTART = TRUE);
|
|
/* Note down dollar_tlevel before op_tstart. This is needed to determine if we need to break from the for-loop
|
|
* below after a successful op_tcommit of the $ZTRIGGER operation. We cannot check that dollar_tlevel is zero
|
|
* since the op_tstart done below can be a nested sub-transaction
|
|
*/
|
|
op_tstart((IMPLICIT_TSTART + IMPLICIT_TRIGGER_TSTART), TRUE, &ts_mv, 0); /* 0 ==> save no locals but RESTART OK */
|
|
/* The following for loop structure is similar to that in module trigger_trgfile.c (function
|
|
* "trigger_trgfile_tpwrap") and module gv_trigger.c (function gvtr_db_tpwrap) so any changes here
|
|
* might need to be reflected there as well.
|
|
*/
|
|
for (loopcnt = 0; ; loopcnt++)
|
|
{
|
|
assert(donot_INVOKE_MUMTSTART); /* Make sure still set */
|
|
DEBUG_ONLY(lcl_t_tries = t_tries);
|
|
trigger_status = trigger_update_rec_helper(trigger_rec, len, TRUE, trig_stats);
|
|
if (0 == dollar_tlevel)
|
|
break;
|
|
assert(0 < t_tries);
|
|
assert((CDB_STAGNATE == t_tries) || (lcl_t_tries == t_tries - 1));
|
|
failure = t_fail_hist[t_tries - 1];
|
|
assert(((cdb_sc_onln_rlbk1 != failure) && (cdb_sc_onln_rlbk2 != failure))
|
|
|| !gv_target || !gv_target->root);
|
|
assert(((cdb_sc_onln_rlbk1 != failure) && (cdb_sc_onln_rlbk2 != failure))
|
|
|| !IS_MCODE_RUNNING || TREF(dollar_zonlnrlbk));
|
|
if (cdb_sc_onln_rlbk2 == failure)
|
|
rts_error(VARLSTCNT(1) ERR_DBROLLEDBACK);
|
|
/* else if (cdb_sc_onln_rlbk1 == status) we don't need to do anything other than trying again. Since this
|
|
* is ^#t global, we don't need to GVCST_ROOT_SEARCH before continuing with the next restart because the
|
|
* trigger load logic already takes care of doing INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED before doing the
|
|
* actual trigger load
|
|
*/
|
|
util_out_print_gtmio("RESTART has invalidated this transaction's previous output. New output follows.",
|
|
FLUSH);
|
|
/* We expect the above function to return with either op_tcommit or a tp_restart invoked.
|
|
* In the case of op_tcommit, we expect dollar_tlevel to be 0 and if so we break out of the loop.
|
|
* In the tp_restart case, we expect a maximum of 4 tries/retries and much lesser usually.
|
|
* Additionally we also want to avoid an infinite loop so limit the loop to what is considered
|
|
* a huge iteration count and GTMASSERT if that is reached as it suggests an out-of-design situation.
|
|
*/
|
|
if (TPWRAP_HELPER_MAX_ATTEMPTS < loopcnt)
|
|
GTMASSERT;
|
|
}
|
|
} else
|
|
{
|
|
trigger_status = trigger_update_rec(trigger_rec, len, TRUE, trig_stats, NULL, NULL);
|
|
assert(0 < dollar_tlevel);
|
|
}
|
|
return (TRIG_FAILURE == trigger_status);
|
|
}
|
|
#endif /* GTM_TRIGGER */
|