/**************************************************************** * * * Copyright 2010, 2011 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 "mv_stent.h" /* for COPY_SUBS_TO_GVCURRKEY macro (within BUILD_HASHT_...) */ #include "gvsub2str.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "format_targ_key.h" /* for COPY_SUBS_TO_GVCURRKEY */ #include "op.h" #include "trigger.h" #include "trigger_gbl_fill_xecute_buffer.h" #include "mvalconv.h" #include "memcoherency.h" #include "t_retry.h" #include "gtmimagename.h" LITREF mval literal_ten; error_def(ERR_TRIGDEFBAD); error_def(ERR_GTMASSERT); error_def(ERR_ASSERT); error_def(ERR_GTMCHECK); error_def(ERR_MEMORY); error_def(ERR_OUTOFSPACE); GBLREF sgmnt_data_ptr_t cs_data; GBLREF sgmnt_addrs *cs_addrs; GBLREF gd_region *gv_cur_region; GBLREF gv_key *gv_currkey; GBLREF uint4 dollar_tlevel; GBLREF unsigned int t_tries; STATICDEF char *xecute_buff; STATICFNDCL CONDITION_HANDLER(trigger_gbl_fill_xecute_buffer_ch); /* The trigger_gbl_fill_xecute_buffer() routine below malloc()s storage for our trigger buffer buffer below but any one * of the various database calls can cause a restart that "loses" the buffer. So we wrap the call will this condition * handler to release the buffer if one was allocated before moving on to the next handler. */ STATICFNDEF CONDITION_HANDLER(trigger_gbl_fill_xecute_buffer_ch) { START_CH; if (!DUMPABLE && (NULL != xecute_buff)) free(xecute_buff); NEXTCH; } char *trigger_gbl_fill_xecute_buffer(char *trigvn, int trigvn_len, mval *trig_index, mval *first_rec, int4 *xecute_len) { mval data_val; boolean_t have_value; mval index, key_val, *val_ptr; int4 len, xecute_buff_len; int4 num; int4 trgindx; unsigned char util_buff[MAX_TRIG_UTIL_LEN]; int4 util_len; char *xecute_buff_ptr; mval xecute_index; DEBUG_ONLY(int gvt_cycle;) DEBUG_ONLY(int csd_cycle;) DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* assert(0 < dollar_tlevel); Too be added later when it stops breaking MUPIP SELECT & $ZTRIGGER("SELECT"..) */ xecute_buff = NULL; ESTABLISH_RET(trigger_gbl_fill_xecute_buffer_ch, NULL); index = *trig_index; if (NULL != first_rec) { xecute_buff_len = first_rec->str.len; assert(MAX_XECUTE_LEN >= xecute_buff_len); xecute_buff = malloc(xecute_buff_len); memcpy(xecute_buff, first_rec->str.addr, xecute_buff_len); } else { /* First check for a single record xecute string */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, index, LITERAL_XECUTE, LITERAL_XECUTE_LEN); if (gvcst_get(&data_val)) { xecute_buff_len = data_val.str.len; assert(MAX_XECUTE_LEN >= xecute_buff_len); xecute_buff = malloc(xecute_buff_len); memcpy(xecute_buff, data_val.str.addr, xecute_buff_len); *xecute_len = xecute_buff_len; REVERT; return xecute_buff; } else { /* No single line trigger exists. See if multi-line trigger exists. The form is ^#t(gbl,indx,XECUTE,n) * so can be easily tested for with $DATA(). */ op_gvdata(&data_val); if ((literal_ten.m[0] != data_val.m[0]) || (literal_ten.m[1] != data_val.m[1])) { /* The process' view of the triggers is likely 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. Note similar asserts occur in t_end.c and tp_tend.c. */ assert(CDB_STAGNATE > t_tries); assert(IS_GTM_IMAGE); /* Assert that the cycle has changed but in order to properly do the assert, we need a memory * barrier since cs_data->db_trigger_cycle could be stale in our cache. */ DEBUG_ONLY(SHM_READ_MEMORY_BARRIER); /* Vars in locals so can look at them in the core instead of at constantly changing numbers */ DEBUG_ONLY(gvt_cycle = gv_target->db_trigger_cycle); DEBUG_ONLY(csd_cycle = cs_data->db_trigger_cycle); assert(csd_cycle > gvt_cycle); t_retry(cdb_sc_triggermod); } } /* Multi-line triggers exist */ num = 0; i2mval(&xecute_index, num); BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY(trigvn, trigvn_len, index, LITERAL_XECUTE, LITERAL_XECUTE_LEN, xecute_index); if (!gvcst_get(&key_val)) { /* There has to be an XECUTE string */ assert(FALSE); trgindx = mval2i(&index); SET_PARAM_STRING(util_buff, util_len, trgindx, ",\"XECUTE\""); rts_error(VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, trigvn_len, trigvn, util_len, util_buff); } val_ptr = &key_val; xecute_buff_len = mval2i(val_ptr); assert(MAX_XECUTE_LEN >= xecute_buff_len); xecute_buff_ptr = xecute_buff = malloc(xecute_buff_len); len = 0; while (len < xecute_buff_len) { i2mval(&xecute_index, ++num); BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY(trigvn, trigvn_len, index, LITERAL_XECUTE, LITERAL_XECUTE_LEN, xecute_index); if (!gvcst_get(&key_val)) break; if (xecute_buff_len < (len + key_val.str.len)) { /* The DB string total is longer than the length stored at index 0 -- something is wrong */ free(xecute_buff); assert(FALSE); SET_PARAM_STRING(util_buff, util_len, num, ",\"XECUTE\""); rts_error(VARLSTCNT(8) ERR_TRIGDEFBAD, 6, trigvn_len, trigvn, trigvn_len, trigvn, util_len, util_buff); } memcpy(xecute_buff_ptr, key_val.str.addr, key_val.str.len); xecute_buff_ptr += key_val.str.len; len += key_val.str.len; } } *xecute_len = xecute_buff_len; REVERT; return xecute_buff; } #endif