1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
|
/****************************************************************
* *
* Copyright (c) 2010-2023 Fidelity National Information *
* Services, Inc. and/or its subsidiaries. All rights reserved. *
* *
* 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_string.h"
#include "gtm_stdlib.h"
#include "gtm_stdio.h"
#include "cdb_sc.h"
#include "gdsroot.h"
#include "gdskill.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "gdscc.h"
#include "min_max.h" /* needed for gdsblkops.h */
#include "gdsblkops.h"
#include "jnl.h"
#include "copy.h"
#include "buddy_list.h" /* needed for tp.h */
#include "tp.h"
#include "repl_msg.h"
#include "gtmsource.h"
#include "interlock.h"
#include <rtnhdr.h>
#include "stack_frame.h"
#include "gv_trigger.h"
#include "gtm_trigger.h"
#include "gv_trigger_protos.h"
#include "mv_stent.h"
#include "stringpool.h"
#include "trigger.h"
#include "gtm_trigger_trc.h"
#include "tp_frame.h"
#include "tp_restart.h"
#include "t_end.h"
#include "t_retry.h"
#include "t_begin.h"
#include "rc_cpt_ops.h"
#include "add_inter.h"
#include "sleep_cnt.h"
#include "wcs_sleep.h"
#include "util.h"
#include "op.h" /* for op_tstart prototype */
#include "format_targ_key.h" /* for format_targ_key prototype */
#include "tp_set_sgm.h" /* for tp_set_sgm prototype */
#include "op_tcommit.h" /* for op_tcommit prototype */
#include "have_crit.h"
#include "gvcst_protos.h"
#include "gtmimagename.h"
#include "is_file_identical.h"
#include "anticipatory_freeze.h"
#include "gvt_inline.h"
LITREF mval literal_null;
#ifdef DEBUG
GBLREF char *update_array, *update_array_ptr;
GBLREF uint4 update_array_size; /* needed for the ENSURE_UPDATE_ARRAY_SPACE macro */
#endif
GBLREF gd_region *gv_cur_region;
GBLREF gv_key *gv_currkey, *gv_altkey;
GBLREF int4 gv_keysize;
GBLREF gv_namehead *gv_target;
GBLREF jnl_fence_control jnl_fence_ctl;
GBLREF uint4 dollar_tlevel;
GBLREF sgmnt_addrs *cs_addrs;
GBLREF sgmnt_data_ptr_t cs_data;
GBLREF sgm_info *sgm_info_ptr;
GBLREF unsigned char cw_set_depth;
GBLREF unsigned int t_tries;
GBLREF unsigned char t_fail_hist[CDB_MAX_TRIES];
GBLREF boolean_t need_kip_incr;
GBLREF uint4 update_trans;
GBLREF jnlpool_addrs_ptr_t jnlpool;
GBLREF sgmnt_addrs *kip_csa;
GBLREF boolean_t skip_dbtriggers; /* see gbldefs.c for description of this global */
GBLREF int tprestart_state;
GBLREF stack_frame *frame_pointer;
#ifdef GTM_TRIGGER
GBLREF int4 gtm_trigger_depth;
GBLREF int4 tstart_trigger_depth;
GBLREF boolean_t skip_INVOKE_RESTART;
GBLREF boolean_t ztwormhole_used; /* TRUE if $ztwormhole was used by trigger code */
GBLREF mval dollar_ztwormhole;
#endif
error_def(ERR_DBROLLEDBACK);
error_def(ERR_GVZTRIGFAIL);
error_def(ERR_TPRETRY);
error_def(ERR_ZTRIGNOTRW);
error_def(ERR_REMOTEDBNOTRIG);
#ifndef GTM_TRIGGER
error_def(ERR_UNIMPLOP);
void op_ztrigger(void)
{
RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_UNIMPLOP);
}
#else
void op_ztrigger(void)
{
node_local_ptr_t cnl;
sgmnt_addrs *csa;
sgmnt_data_ptr_t csd;
enum cdb_sc cdb_status;
jnl_format_buffer *jfb = NULL, *ztworm_jfb;
uint4 nodeflags;
boolean_t write_logical_jnlrecs, jnl_format_done;
boolean_t is_tpwrap;
boolean_t lcl_implicit_tstart; /* local copy of the global variable "implicit_tstart" */
boolean_t want_root_search = FALSE;
uint4 lcl_onln_rlbkd_cycle;
gtm_trigger_parms trigparms;
gvt_trigger_t *gvt_trigger;
gvtr_invoke_parms_t gvtr_parms;
int gtm_trig_status = 0, rc;
unsigned int idx;
unsigned char *save_msp;
mv_stent *save_mv_chain = NULL;
# ifdef DEBUG
boolean_t is_mm;
GTMTRIG_ONLY(enum cdb_sc save_cdb_status;)
# endif
DCL_THREADGBL_ACCESS;
if (gv_cur_region->read_only)
RTS_ERROR_CSA_ABT(cs_addrs, VARLSTCNT(4) ERR_ZTRIGNOTRW, 2, REG_LEN_STR(gv_cur_region));
SETUP_THREADGBL_ACCESS;
csa = cs_addrs;
assert(('\0' != gv_currkey->base[0]) && gv_currkey->end);
assert(MAX_MIDENT_LEN >= strlen((char *)gv_currkey->base)); /* For Veracode to not flag strlen() below */
if (NULL == csa) /* Remote region */
RTS_ERROR_CSA_ABT(csa, VARLSTCNT(6) ERR_REMOTEDBNOTRIG, 4, strlen((char *)gv_currkey->base),
(char *)gv_currkey->base, REG_LEN_STR(gv_cur_region));
csd = csa->hdr;
cnl = csa->nl;
save_msp = NULL;
DEBUG_ONLY(is_mm = (dba_mm == csd->acc_meth));
TRIG_CHECK_REPLSTATE_MATCHES_EXPLICIT_UPDATE(gv_cur_region, csa);
if (IS_EXPLICIT_UPDATE)
{ /* This is an explicit update. Set ztwormhole_used to FALSE. Note that we initialize this only at the
* beginning of the transaction and not at the beginning of each try/retry. If the application used
* $ztwormhole in any retsarting try of the transaction, we consider it necessary to write the
* TZTWORM/UZTWORM record even though it was not used in the succeeding/committing try.
*/
ztwormhole_used = FALSE;
}
JNLPOOL_INIT_IF_NEEDED(csa, csd, cnl, SCNDDBNOUPD_CHECK_TRUE);
DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC(CHECK_CSA_TRUE);
T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_GVZTRIGFAIL);
lcl_implicit_tstart = FALSE;
assert(NULL != update_array);
assert(NULL != update_array_ptr);
assert(0 != update_array_size);
assert(update_array + update_array_size >= update_array_ptr);
for (;;)
{
assert(csd == cs_data); /* assert csd is in sync with cs_data even if there were MM db file extensions */
assert(csd == csa->hdr);
jnl_format_done = FALSE;
write_logical_jnlrecs = JNL_WRITE_LOGICAL_RECS(csa);
gvtr_parms.num_triggers_invoked = 0; /* clear any leftover value */
is_tpwrap = FALSE;
if (!skip_dbtriggers) /* No trigger init needed if skip_dbtriggers is TRUE (e.g. mupip load etc.) */
{
GVTR_INIT_AND_TPWRAP_IF_NEEDED(csa, csd, gv_target, gvt_trigger, lcl_implicit_tstart, is_tpwrap,
ERR_GVZTRIGFAIL);
assert(0 < dollar_tlevel);
assert(gvt_trigger == gv_target->gvt_trigger);
sgm_info_ptr->update_trans |= UPDTRNS_ZTRIGGER_MASK;
if (NULL != gvt_trigger)
{
/* In other trigger types, a PUSH_ZTOLDMVAL_ON_M_STACK is used here to save ztoldval but since
* this trigger type doesn't have one, pushing that extra mval on the stack makes no sense.
* Instead, we just do the basics that the PUSH_ZTOLDMVAL_ON_M_STACK does to save a marker of
* msp and mv_chain to easily restore later rather than having to pop the values (more
* expensively)
*/
save_msp = msp;
save_mv_chain = mv_chain;
/* Invoke relevant trigger(s) regardless whether data exists or not (we don't even check) */
JNL_FORMAT_ZTWORM_IF_NEEDED(csa, write_logical_jnlrecs,
JNL_ZTRIG, gv_currkey, NULL, ztworm_jfb, jfb, jnl_format_done);
/* Initialize trigger parms that dont depend on the context of the matching trigger. All of
* these parms are initialized to NULL. This causes op_svget to report them as NULL strings
* whichi s all we need for ZTRIGGER. Note $ztvalue is not not updateable for this type
* of trigger.
*/
trigparms.ztoldval_new = NULL;
trigparms.ztdata_new = NULL;
trigparms.ztvalue_new = NULL;
gvtr_parms.gvtr_cmd = GVTR_CMDTYPE_ZTRIGGER;
gvtr_parms.gvt_trigger = gvt_trigger;
/* Now that we have filled in minimal information, let "gvtr_match_n_invoke" do the rest */
gtm_trig_status = gvtr_match_n_invoke(&trigparms, &gvtr_parms);
INCR_GVSTATS_COUNTER(csa, csa->nl, n_ztrigger_fired, gvtr_parms.num_triggers_invoked);
assert((0 == gtm_trig_status) || (ERR_TPRETRY == gtm_trig_status));
if (ERR_TPRETRY == gtm_trig_status)
{ /* A restart has been signaled that we need to handle or complete the handling of.
* This restart could have occurred reading the trigger in which case no
* tp_restart() has yet been done or it could have occurred in trigger code in
* which case we need to finish the incomplete tp_restart. In both cases this
* must be an implicitly TP wrapped transaction. Our action is to complete the
* necessary tp_restart() logic (t_retry is already completed so should be skipped)
* and then re-do the op_ztrigger logic.
*/
assert(lcl_implicit_tstart);
assert(CDB_STAGNATE >= t_tries);
cdb_status = cdb_sc_normal; /* signal "retry:" to avoid t_retry call */
goto retry;
}
REMOVE_ZTWORM_JFB_IF_NEEDED(ztworm_jfb, jfb, sgm_info_ptr);
/* Instead of POP_MVALS_FROM_M_STACK_IF_NEEDED, do a stripped-down version since we don't
* do anything with $ztoldval. This usually pops off 3 or more mvals saved by trigger processing.
*/
if (save_msp > msp)
UNW_MV_STENT_TO(save_msp, save_mv_chain);
}
}
/* finish off any pending root search from previous retry */
REDO_ROOT_SEARCH_IF_NEEDED(want_root_search, cdb_status);
if (cdb_sc_normal != cdb_status)
{ /* gvcst_root_search invoked from REDO_ROOT_SEARCH_IF_NEEDED ended up with a restart situation but did not
* actually invoke t_retry. Instead, it returned control back to us asking us to restart.
*/
goto retry;
}
if (write_logical_jnlrecs)
{ /* Only write jnl recs if we are in fact journaling..
* skip_dbtriggers is set to TRUE for trigger unsupporting platforms. So, nodeflags will be set to skip
* triggers on secondary. This ensures that updates happening in primary (trigger unsupporting platform)
* is treated in the same order in the secondary (trigger supporting platform) irrespective of whether
* the secondary has defined triggers or not for the global that is being updated.
*/
assert(dollar_tlevel);
if (!jnl_format_done)
{
nodeflags = 0;
if (skip_dbtriggers)
nodeflags |= JS_SKIP_TRIGGERS_MASK;
/* Do not replicate implicit updates */
assert(tstart_trigger_depth <= gtm_trigger_depth);
if (gtm_trigger_depth > tstart_trigger_depth)
{
/* Ensure that JS_SKIP_TRIGGERS_MASK and JS_NOT_REPLICATED_MASK are mutually exclusive. */
assert(!(nodeflags & JS_SKIP_TRIGGERS_MASK));
nodeflags |= JS_NOT_REPLICATED_MASK;
}
/* Write ZTRIGGER journal record */
jfb = jnl_format(JNL_ZTRIG, gv_currkey, NULL, nodeflags);
assert(NULL != jfb);
}
}
/* If we started this transaction, finish it now verifying it completed successfully */
if (lcl_implicit_tstart)
{
GVTR_OP_TCOMMIT(cdb_status);
if (cdb_sc_normal != cdb_status)
goto retry;
}
INCR_GVSTATS_COUNTER(cs_addrs, cs_addrs->nl, n_ztrigger, 1);
return;
retry:
if (lcl_implicit_tstart)
{
assert(!skip_dbtriggers);
assert(!skip_INVOKE_RESTART);
assert((cdb_sc_normal != cdb_status) || (ERR_TPRETRY == gtm_trig_status));
if (cdb_sc_normal != cdb_status)
skip_INVOKE_RESTART = TRUE; /* causes t_retry to invoke only tp_restart * without any rts_error */
/* else: t_retry has already been done by gtm_trigger so no need to do it again for this try */
}
assert((cdb_sc_normal != cdb_status) || lcl_implicit_tstart);
if (cdb_sc_normal != cdb_status)
{ /* See comment above about POP_MVALS_FROM_M_STACK_IF_NEEDED */
if ((NULL != save_msp) && (save_msp > msp))
{
assert(save_mv_chain);
UNW_MV_STENT_TO(save_msp, save_mv_chain);
}
t_retry(cdb_status);
skip_INVOKE_RESTART = FALSE;
} else
{ /* else: t_retry has already been done so no need to do that again but need to still invoke tp_restart
* to complete pending "tprestart_state" related work.
*/
assert(ERR_TPRETRY == gtm_trig_status);
TRIGGER_BASE_FRAME_UNWIND_IF_NOMANSLAND;
/* See comment above about POP_MVALS_FROM_M_STACK_IF_NEEDED */
if ((NULL != save_msp) && (save_msp > msp))
{
assert(save_mv_chain);
UNW_MV_STENT_TO(save_msp, save_mv_chain);
}
rc = tp_restart(1, !TP_RESTART_HANDLES_ERRORS);
assert(0 == rc && TPRESTART_STATE_NORMAL == tprestart_state);
}
assert(0 < t_tries);
if (lcl_implicit_tstart)
{
SET_WANT_ROOT_SEARCH(cdb_status, want_root_search);
assert(!skip_INVOKE_RESTART); /* if set to TRUE above, should have been reset by t_retry */
}
/* At this point, we can be in TP only if we implicitly did a tstart in op_ztrigger trying to drive a trigger.
* Assert that. So reinvoke the T_BEGIN call only in case of TP. For non-TP, update_trans is unaffected by
* t_retry.
*/
assert(!dollar_tlevel || lcl_implicit_tstart);
if (dollar_tlevel)
{ /* gvcst_kill has similar code and should be maintained in parallel */
tp_set_sgm(); /* set sgm_info_ptr & first_sgm_info for TP start */
T_BEGIN_SETORKILL_NONTP_OR_TP(ERR_GVZTRIGFAIL);
}
/* In case this is MM and t_retry() remapped an extended database, reset csd */
assert(is_mm || (csd == cs_data));
csd = cs_data;
}
}
#endif /* #ifdef GTM_TRIGGER */
|