247 lines
9.6 KiB
C
247 lines
9.6 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"
|
|
|
|
#include "gtm_stat.h"
|
|
#include "gtm_string.h"
|
|
|
|
#ifdef GTM_TRIGGER
|
|
#include "gdsroot.h" /* for gdsfhead.h */
|
|
#include "gdsbt.h" /* for gdsfhead.h */
|
|
#include "gdsfhead.h" /* For gvcst_protos.h */
|
|
#include <rtnhdr.h>
|
|
#include "gv_trigger.h"
|
|
#include "io.h"
|
|
#include "hashtab_str.h"
|
|
#include "trigger_trgfile_protos.h"
|
|
#include "trigger_delete_protos.h"
|
|
#include "trigger_update_protos.h"
|
|
#include "file_input.h"
|
|
#include "trigger.h"
|
|
#include "op.h" /* for op_tstart */
|
|
#include "op_tcommit.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 "error.h"
|
|
#include "tp_restart.h"
|
|
#include "mupip_exit.h"
|
|
#include "util.h"
|
|
#include "stack_frame.h"
|
|
#include "mv_stent.h"
|
|
#include "tp_frame.h"
|
|
#include "t_retry.h"
|
|
#include "gtmimagename.h"
|
|
|
|
#define TRIG_ERROR_RETURN \
|
|
{ \
|
|
if (lcl_implicit_tpwrap) \
|
|
{ /* only if we were implicitly wrapped */ \
|
|
assert(dollar_tlevel); \
|
|
assert(donot_INVOKE_MUMTSTART); \
|
|
DEBUG_ONLY(donot_INVOKE_MUMTSTART = FALSE); \
|
|
OP_TROLLBACK(-1); /* Unroll implicit TP */ \
|
|
REVERT; \
|
|
} \
|
|
return TRIG_FAILURE; \
|
|
}
|
|
|
|
GBLREF sgm_info *first_sgm_info;
|
|
GBLREF bool mupip_error_occurred;
|
|
GBLREF gv_key *gv_currkey;
|
|
GBLREF sgm_info *sgm_info_ptr;
|
|
GBLREF gd_addr *gd_header;
|
|
GBLREF sgmnt_data_ptr_t cs_data;
|
|
GBLREF int tprestart_state;
|
|
GBLREF io_pair io_curr_device;
|
|
GBLREF gv_namehead *reset_gv_target;
|
|
GBLREF uint4 dollar_tlevel;
|
|
GBLREF unsigned int t_tries;
|
|
GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES];
|
|
#ifdef DEBUG
|
|
GBLREF boolean_t donot_INVOKE_MUMTSTART;
|
|
#endif
|
|
|
|
LITREF mval literal_hasht;
|
|
|
|
error_def(ERR_DBROLLEDBACK);
|
|
error_def(ERR_ZFILNMBAD);
|
|
|
|
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;
|
|
}
|
|
|
|
boolean_t trigger_trgfile_tpwrap(char *trigger_filename, uint4 trigger_filename_len, boolean_t noprompt)
|
|
{
|
|
boolean_t trigger_status = TRIG_FAILURE;
|
|
mval ts_mv;
|
|
int loopcnt;
|
|
struct stat statbuf;
|
|
DEBUG_ONLY(unsigned int lcl_t_tries;)
|
|
enum cdb_sc failure;
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
|
|
ts_mv.mvtype = MV_STR;
|
|
ts_mv.str.len = 0;
|
|
ts_mv.str.addr = NULL;
|
|
/* Do sanity checks on the filename and the file's accessibility. */
|
|
assert('\0' == trigger_filename[trigger_filename_len]); /* should have been made sure by caller */
|
|
if (-1 == Stat(trigger_filename, &statbuf))
|
|
{
|
|
util_out_print_gtmio("Invalid file name: !AD: !AZ", FLUSH, trigger_filename_len, trigger_filename, STRERROR(errno));
|
|
return TRUE; /* Failure */
|
|
} else if (!S_ISREG(statbuf.st_mode))
|
|
{
|
|
util_out_print_gtmio("Invalid file name: !AD: Not a proper input file", FLUSH, trigger_filename_len,
|
|
trigger_filename);
|
|
return TRUE; /* Failure */
|
|
}
|
|
if (0 == dollar_tlevel)
|
|
{ /* If not already wrapped in TP, wrap it now implicitly */
|
|
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_update.c (function "trigger_update")
|
|
* 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_trgfile_tpwrap_helper(trigger_filename, trigger_filename_len, noprompt, TRUE);
|
|
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_trgfile_tpwrap_helper(trigger_filename, trigger_filename_len, noprompt, FALSE);
|
|
assert(0 < dollar_tlevel);
|
|
}
|
|
return (TRIG_FAILURE == trigger_status);
|
|
}
|
|
#endif /* GTM_TRIGGER */
|