fis-gtm/sr_unix/trigger_fill_xecute_buffer.c

198 lines
8.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"
#ifdef GTM_TRIGGER
#include "gdsroot.h" /* for gdsfhead.h */
#include "gdsbt.h" /* for gdsfhead.h */
#include "gdsfhead.h"
#include "gvcst_protos.h"
#include <rtnhdr.h>
#include "gv_trigger.h"
#include "trigger.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 "filestruct.h" /* for INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED (FILE_INFO) */
#include "mvalconv.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 "jnl.h" /* needed for tp.h */
#include "tp.h" /* for sgm_info */
#include "tp_frame.h"
#include "tp_restart.h"
#include "tp_set_sgm.h"
#include "t_retry.h"
#include "op.h"
#include "op_tcommit.h"
#include "memcoherency.h"
#include "gtmimagename.h"
#include "trigger_fill_xecute_buffer.h"
#include "trigger_gbl_fill_xecute_buffer.h"
GBLREF sgmnt_data_ptr_t cs_data;
GBLREF sgmnt_addrs *cs_addrs;
GBLREF gd_addr *gd_header;
GBLREF gv_key *gv_currkey;
GBLREF gd_region *gv_cur_region;
GBLREF sgm_info *sgm_info_ptr;
GBLREF gv_namehead *gv_target;
GBLREF boolean_t skip_INVOKE_RESTART;
GBLREF int tprestart_state;
GBLREF int4 tstart_trigger_depth;
GBLREF int4 gtm_trigger_depth;
GBLREF tp_frame *tp_pointer;
LITREF mval literal_hasht;
error_def(ERR_TRIGNAMBAD);
error_def(ERR_TPRETRY);
STATICFNDCL CONDITION_HANDLER(trigger_fill_xecute_buffer_ch);
STATICFNDCL void trigger_fill_xecute_buffer_read_trigger_source(gv_trigger_t *trigdsc);
/* Similar condition handler to above without the tp-restart - just unwind and let caller do the restart */
CONDITION_HANDLER(trigger_fill_xecute_buffer_ch)
{
START_CH;
if ((int)ERR_TPRETRY == SIGNAL)
{
UNWIND(NULL, NULL);
}
NEXTCH;
}
int trigger_fill_xecute_buffer(gv_trigger_t *trigdsc)
{
int src_fetch_status;
assert(!dollar_tlevel || (tstart_trigger_depth <= gtm_trigger_depth));
/* We have 3 cases to consider - all of which REQUIRE a TP fence to already be in effect. The reason for this is, if we
* detect a restartable condition, we are going to cause this region's triggers to be unloaded which destroys the block
* our parameter is pointing to so the restart logic MUST take place outside of this routine.
*
* 1. We have an active transaction due to an IMPLICIT TSTART done by trigger handling but the trigger level has not yet
* been created. We don't need another TP wrapper in this case but we do need a condition handler to trap the thrown
* retry to again prevent C stack unwind and return to the caller in the same shape that gtm_trigger would return.
* 2. We have an active transaction due to an EXPLICIT M-code TSTART command. For this case, the trigger loads proceed
* as normal with restarts handled in the regular automatic fashion. Note this case also covers the tp restarts done
* by both the update process and mupip recover forward since those functions have their own way of intercepting and
* dealing with restarts. To cover those cases, tp->implicit_tstart can be TRUE but tp_implicit_trigger MUST be
* FALSE.
* 3. We have an active transaction due to an IMPLICIT TSTART done by trigger handling and one or more triggers are
* running. This becomes like case 2 since the restart will be handled by gtm_trigger and the proper thing will
* be done.
*
* An extra note about case 3. Case 3 can be the identified case if in a nested trigger we are in trigger-no-mans-land
* with a base frame for the nested trigger (having driven one of a set of parallel nested triggers) but no actual trigger
* execution frame yet exists. This is really a case 1 situation with a nested trigger but it turns out that dealing with
* like case 3 does the right thing because if/when mdb_condition_handler catches a thrown TPRETRY error, mdb_condition
* handler will peal the nested trigger frame off before doing the restart which works for us and avoids issues of
* multi-level implicit restarts we would otherwise have to handle.
*
* Note, this routine is for loading trigger source when the trigger is to be driven. The trigger_source_read_andor_verify()
* routine should be used when fetching trigger source for reasons other than driving the triggers. This routine is lighter
* weight but has a dependence on the restartability of the trigger-drive logic for getting the triggers reloaded as
* necessary.
*/
if (0 < dollar_tlevel)
{
if (!tp_pointer->implicit_trigger /* Case 2 */
|| (tp_pointer->implicit_tstart && tp_pointer->implicit_trigger
&& (tstart_trigger_depth != gtm_trigger_depth))) /* Case 3 */
{ /* Test for Case 3/4 where we get to do very little: */
assert((!tp_pointer->implicit_trigger) || (0 < gtm_trigger_depth));
trigger_fill_xecute_buffer_read_trigger_source(trigdsc);
} else
{ /* Test for Case 1 where we only need a condition handler */
assert(tp_pointer->implicit_tstart && tp_pointer->implicit_trigger);
assert(tstart_trigger_depth == gtm_trigger_depth);
ESTABLISH_RET(trigger_fill_xecute_buffer_ch, SIGNAL);
trigger_fill_xecute_buffer_read_trigger_source(trigdsc);
REVERT;
}
} else
GTMASSERT;
/* return our bounty to caller */
trigdsc->xecute_str.mvtype = MV_STR;
return 0; /* Could return ERR_TPRETRY if return is via our condition handler */
}
/* Workhorse of fetching source for given trigger.
*/
STATICFNDEF void trigger_fill_xecute_buffer_read_trigger_source(gv_trigger_t *trigdsc)
{
mname_entry gvent;
enum cdb_sc cdb_status;
int4 index;
mstr gbl, xecute_buff;
mval trig_index;
sgmnt_addrs *csa;
sgmnt_data_ptr_t csd;
gvt_trigger_t *gvt_trigger;
gv_namehead *gvt;
gv_namehead *hasht_tree, *save_gv_target;
gv_key *save_gv_currkey;
gd_region *save_gv_cur_region;
sgm_info *save_sgm_info_ptr;
char save_currkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)];
assert(0 < dollar_tlevel);
assert(NULL != trigdsc);
SAVE_TRIGGER_REGION_INFO;
gvt_trigger = trigdsc->gvt_trigger; /* We now know our base block now */
index = trigdsc - gvt_trigger->gv_trig_array + 1; /* We now know our trigger index value */
i2mval(&trig_index, index);
gvt = gv_target = gvt_trigger->gv_target; /* gv_target contains global name */
gbl.addr = gvt->gvname.var_name.addr;
gbl.len = gvt->gvname.var_name.len;
/* Our situation is that while our desired gv_target has csa information, we don't know specifically
* which global directory was in use so we can't run gv_bind_name() lest we find the given global
* name in the wrong global directory thus running the wrong triggers. But we know this target is
* properly formed since it had to be when it was recorded when the triggers were loaded. Because of
* that, we can get the correct csa and gv_target and csa-region will point us to a region that will
* work even if it isn't exactly the one we used to get to this trigger.
*/
TP_CHANGE_REG_IF_NEEDED(gvt->gd_csa->region);
csa = cs_addrs;
csd = csa->hdr;
assert(csd == cs_data);
tp_set_sgm();
/* See if we need to reload our triggers */
if ((csa->db_trigger_cycle != gvt->db_trigger_cycle)
|| (csa->db_dztrigger_cycle && (gvt->db_dztrigger_cycle != csa->db_dztrigger_cycle)))
{ /* The process' view of the triggers could be potentially stale. Restart to be safe.
* Triggers can be invoked only by GT.M and Update process. Out of these, we expect only
* GT.M to see restarts due to concurrent trigger changes. Update process is the only
* updater on the secondary so we dont expect it to see any concurrent trigger changes
* Assert accordingly.
*/
assert(CDB_STAGNATE > t_tries);
assert(IS_GTM_IMAGE);
t_retry(cdb_sc_triggermod);
}
SETUP_TRIGGER_GLOBAL;
INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED;
assert(0 == trigdsc->xecute_str.str.len); /* Make sure not replacing/losing a buffer */
xecute_buff.addr = trigger_gbl_fill_xecute_buffer(gbl.addr, gbl.len, &trig_index, NULL, (int4 *)&xecute_buff.len);
trigdsc->xecute_str.str = xecute_buff;
/* Restore gv_target/gv_currkey which need to be kept in sync */
RESTORE_TRIGGER_REGION_INFO;
return;
}
#endif /* GTM_TRIGGER */