File: op_ztrigger.c

package info (click to toggle)
fis-gtm 7.1-006-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 32,908 kB
  • sloc: ansic: 344,906; asm: 5,184; csh: 4,859; sh: 2,000; awk: 294; makefile: 73; sed: 13
file content (334 lines) | stat: -rw-r--r-- 12,975 bytes parent folder | download | duplicates (2)
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 */