bool gvcmx_order(void) { mval v; gvcmz_doop(CMMS_Q_ORDER, CMMS_R_ORDER, &v); if (MV_FORCE_INT(&v)) return TRUE; else return FALSE; }
void ztrap_save_ctxt(void) { int level; error_def(ERR_STACKOFLOW); error_def(ERR_STACKCRIT); level = dollar_zlevel(); if (level == MV_FORCE_INT(&ztrap_pop2level)) return; PUSH_MV_STENT(MVST_MSAV); mv_chain->mv_st_cont.mvs_msav.v = ztrap_pop2level; mv_chain->mv_st_cont.mvs_msav.addr = &ztrap_pop2level; MV_FORCE_MVAL(&ztrap_pop2level, level); return; }
void coerce(oprtype *a,unsigned short new_type) { mliteral *lit; opctype conv, old_op; triple *ref, *coerc; assert (new_type == OCT_MVAL || new_type == OCT_MINT || new_type == OCT_BOOL); assert (a->oprclass == TRIP_REF); ref = a->oprval.tref; old_op = ref->opcode; if (new_type & oc_tab[old_op].octype) return; if (old_op == OC_COMVAL || old_op == OC_COMINT) { dqdel(ref,exorder); ref = ref->operand[0].oprval.tref; old_op = ref->opcode; if (new_type & oc_tab[old_op].octype) return; } else if (old_op == OC_LIT && new_type == OCT_MINT) { lit = ref->operand[0].oprval.mlit; if (!(++lit->rt_addr)) dqdel(lit, que); ref->opcode = OC_ILIT; ref->operand[0].oprclass = ILIT_REF; ref->operand[0].oprval.ilit = MV_FORCE_INT(&(lit->v)); return; } if (new_type == OCT_BOOL) conv = OC_COBOOL; else if (new_type == OCT_MINT) conv = OC_COMINT; else conv = OC_COMVAL; coerc = newtriple(conv); coerc->operand[0] = put_tref(ref); *a = put_tref(coerc); return; }
int mval2fao( char *message, /* text of message in fao format */ va_list pfao, /* argument list of caller */ UINTPTR_T *outparm, /* array of resulting fao parameters */ int mcount, int fcount, /* mvalcount and faocount */ char *bufbase, char *buftop) /* buffer space for !AC and !AS */ { char *buf; int i, parmcnt, num; mval *fao; fao = va_arg(pfao, mval *); parmcnt = 0; buf = bufbase; for ( ; mcount && parmcnt < fcount; ) { MV_FORCE_DEFINED(fao); while (*message != '!') message++; for (i=0;(*++message > 47) && (*message < 58);i++) /* a length for the fao parameter */ ; switch (*message++) { case '/': case '_': case '^': case '!': break; case 'A': MV_FORCE_STR(fao); switch(*message++) { /* ascii counted string */ case 'C': if ((fao)->str.len > 256 || (fao)->str.len < 0) return -1; if (buf + (fao)->str.len + 1 >= buftop) return -1; *buf++ = (fao)->str.len; memcpy(buf, (fao)->str.addr, (fao)->str.len); buf += (fao)->str.len; break; /* len,addr string, '.' filled */ case 'F': /* len,addr string */ case 'D': if (parmcnt + 2 > fcount) return parmcnt; outparm[parmcnt++] = (unsigned int)(fao)->str.len; outparm[parmcnt++] = (UINTPTR_T)(fao)->str.addr; break; /* ascii string descriptor */ case 'S': if (buf + sizeof(desc_struct) >= buftop) return -1; ((desc_struct *)buf)->len = (fao)->str.len; ((desc_struct *)buf)->addr = (fao)->str.addr; outparm[parmcnt++] = (UINTPTR_T)buf; buf += sizeof(desc_struct); break; default: return -1; } fao = va_arg(pfao, mval *); mcount--; break; /* octal number */ case 'O': /* hex number */ case 'X': /* signed number */ case 'S': num = MV_FORCE_INT(fao); switch(*message++) { case 'B': outparm[parmcnt++] = (UINTPTR_T)num; break; case 'W': outparm[parmcnt++] = (UINTPTR_T)num; break; case 'L': outparm[parmcnt++] = (UINTPTR_T)num; break; default: return -1; } fao = va_arg(pfao, mval *); mcount--; break; /* zero filled num */ case 'Z': /* unsigned num */ case 'U': num = MV_FORCE_INT(fao); switch(*message++) { case 'B': outparm[parmcnt++] = (UINTPTR_T)num; break; case 'W': outparm[parmcnt++] = (UINTPTR_T)num; break; case 'L': outparm[parmcnt++] = (UINTPTR_T)num; break; default: return -1; } fao = va_arg(pfao, mval *); mcount--; break; default: return -1; } } return parmcnt; }
void op_svput(int varnum, mval *v) { int i, ok, state; char *vptr; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; switch (varnum) { case SV_X: MV_FORCE_NUM(v); io_curr_device.out->dollar.x = (short)MV_FORCE_INT(v); if ((short)(io_curr_device.out->dollar.x) < 0) io_curr_device.out->dollar.x = 0; break; case SV_Y: MV_FORCE_NUM(v); io_curr_device.out->dollar.y = (short)MV_FORCE_INT(v); if ((short)(io_curr_device.out->dollar.y) < 0) io_curr_device.out->dollar.y = 0; break; case SV_ZCOMPILE: MV_FORCE_STR(v); if ((TREF(dollar_zcompile)).addr) free ((TREF(dollar_zcompile)).addr); (TREF(dollar_zcompile)).addr = (char *)malloc(v->str.len); memcpy((TREF(dollar_zcompile)).addr, v->str.addr, v->str.len); (TREF(dollar_zcompile)).len = v->str.len; break; case SV_ZSTEP: MV_FORCE_STR(v); op_commarg(v,indir_linetail); op_unwind(); dollar_zstep = *v; break; case SV_ZGBLDIR: MV_FORCE_STR(v); if ((dollar_zgbldir.str.len != v->str.len) || memcmp(dollar_zgbldir.str.addr, v->str.addr, dollar_zgbldir.str.len)) { if (0 == v->str.len) { /* set $zgbldir="" */ dpzgbini(); gd_header = NULL; } else { gd_header = zgbldir(v); /* update the gd_map */ SET_GD_MAP; dollar_zgbldir.str.len = v->str.len; dollar_zgbldir.str.addr = v->str.addr; s2pool(&dollar_zgbldir.str); } if (NULL != gv_currkey) { gv_currkey->base[0] = '\0'; gv_currkey->prev = gv_currkey->end = 0; } else if (NULL != gd_header) gvinit(); if (NULL != gv_target) gv_target->clue.end = 0; } break; case SV_ZMAXTPTIME: dollar_zmaxtptime = mval2i(v); break; case SV_ZROUTINES: MV_FORCE_STR(v); /* The string(v) should be parsed and loaded before setting $zroutines * to retain the old value in case errors occur while loading */ zro_load(&v->str); if ((TREF(dollar_zroutines)).addr) free ((TREF(dollar_zroutines)).addr); (TREF(dollar_zroutines)).addr = (char *)malloc(v->str.len); memcpy((TREF(dollar_zroutines)).addr, v->str.addr, v->str.len); (TREF(dollar_zroutines)).len = v->str.len; break; case SV_ZSOURCE: MV_FORCE_STR(v); dollar_zsource.mvtype = MV_STR; dollar_zsource.str = v->str; break; case SV_ZTRAP: # ifdef GTM_TRIGGER if (0 < gtm_trigger_depth) rts_error(VARLSTCNT(1) ERR_NOZTRAPINTRIG); # endif MV_FORCE_STR(v); if (ztrap_new) op_newintrinsic(SV_ZTRAP); dollar_ztrap.mvtype = MV_STR; dollar_ztrap.str = v->str; /* Setting either $ZTRAP or $ETRAP to empty causes any current error trapping to be canceled */ if (!v->str.len) { dollar_etrap.mvtype = MV_STR; dollar_etrap.str = v->str; ztrap_explicit_null = TRUE; } else /* Ensure that $ETRAP and $ZTRAP are not both active at the same time */ { ztrap_explicit_null = FALSE; if (dollar_etrap.str.len > 0) gtm_newintrinsic(&dollar_etrap); } if (ztrap_form & ZTRAP_POP) ztrap_save_ctxt(); if (tp_timeout_deferred && !dollar_zininterrupt) /* A tp timeout was deferred. Now that $ETRAP is no longer in effect and no job interrupt is in * effect, the timeout need no longer be deferred and can be recognized. */ tptimeout_set(0); break; case SV_ZSTATUS: MV_FORCE_STR(v); dollar_zstatus.mvtype = MV_STR; dollar_zstatus.str = v->str; break; case SV_PROMPT: MV_FORCE_STR(v); MV_FORCE_LEN_STRICT(v); /* Ensure that direct mode prompt will not have BADCHARs, * otherwise the BADCHAR error may fill up the filesystem */ if (v->str.len <= SIZEOF_prombuf) (TREF(gtmprompt)).len = v->str.len; else if (!gtm_utf8_mode) (TREF(gtmprompt)).len = SIZEOF_prombuf; # ifdef UNICODE_SUPPORTED else { UTF8_LEADING_BYTE(v->str.addr + SIZEOF_prombuf, v->str.addr, vptr); (TREF(gtmprompt)).len = INTCAST(vptr - v->str.addr); } # endif memcpy((TREF(gtmprompt)).addr, v->str.addr, (TREF(gtmprompt)).len); break; case SV_ECODE: MV_FORCE_STR(v); if (v->str.len) { /* Format must be like ,Mnnn,Mnnn,Zxxx,Uxxx, * Mnnn are ANSI standard error codes * Zxxx are implementation-specific codes * Uxxx are end-user defined codes * Note that there must be commas at the start and at the end */ for (state = 2, i = 0; (i < v->str.len) && (state <= 2); i++) { switch(state) { case 2: state = (v->str.addr[i] == ',') ? 1 : 101; break; case 1: state = ((v->str.addr[i] == 'M') || (v->str.addr[i] == 'U') || (v->str.addr[i] == 'Z')) ? 0 : 101; break; case 0: state = (v->str.addr[i] == ',') ? 1 : 0; break; } } /* The above check would pass strings like "," * so double-check that there are at least three characters * (starting comma, ending comma, and something in between) */ if ((state != 1) || (v->str.len < 3)) { /* error, ecode = M101 */ rts_error(VARLSTCNT(4) ERR_INVECODEVAL, 2, v->str.len, v->str.addr); } } if (v->str.len > 0) { ecode_add(&v->str); rts_error(VARLSTCNT(2) ERR_SETECODE, 0); } else { NULLIFY_DOLLAR_ECODE; /* reset $ECODE related variables to correspond to $ECODE = NULL state */ NULLIFY_ERROR_FRAME; /* we are no more in error-handling mode */ if (tp_timeout_deferred && !dollar_zininterrupt) /* A tp timeout was deferred. Now that we are clear of error handling and no job interrupt * is in process, allow the timeout to be recognized. */ tptimeout_set(0); } break; case SV_ETRAP: MV_FORCE_STR(v); dollar_etrap.mvtype = MV_STR; dollar_etrap.str = v->str; /* Setting either $ZTRAP or $ETRAP to empty causes any current error trapping to be canceled */ if (!v->str.len) { dollar_ztrap.mvtype = MV_STR; dollar_ztrap.str = v->str; } else if (dollar_ztrap.str.len > 0) { /* Ensure that $ETRAP and $ZTRAP are not both active at the same time */ assert(FALSE == ztrap_explicit_null); gtm_newintrinsic(&dollar_ztrap); } ztrap_explicit_null = FALSE; break; case SV_ZERROR: MV_FORCE_STR(v); dollar_zerror.mvtype = MV_STR; dollar_zerror.str = v->str; break; case SV_ZYERROR: MV_FORCE_STR(v); dollar_zyerror.mvtype = MV_STR; dollar_zyerror.str = v->str; break; case SV_SYSTEM: assert(FALSE); rts_error(VARLSTCNT(4) ERR_SYSTEMVALUE, 2, v->str.len, v->str.addr); break; case SV_ZDIR: setzdir(v, NULL); /* change directory to v */ getzdir(); /* update dollar_zdir with current working directory */ break; case SV_ZINTERRUPT: MV_FORCE_STR(v); dollar_zinterrupt.mvtype = MV_STR; dollar_zinterrupt.str = v->str; break; case SV_ZDATE_FORM: MV_FORCE_NUM(v); TREF(zdate_form) = (short)MV_FORCE_INT(v); break; case SV_ZTEXIT: MV_FORCE_STR(v); dollar_ztexit.mvtype = MV_STR; dollar_ztexit.str = v->str; /* Coercing $ZTEXIT to boolean at SET command is more efficient than coercing before each * rethrow at TR/TRO. Since we want to maintain dollar_ztexit as a string, coercion should * not be performed on dollar_ztext, but on a temporary (i.e. parameter v) */ dollar_ztexit_bool = MV_FORCE_BOOL(v); break; case SV_ZQUIT: dollar_zquit_anyway = MV_FORCE_BOOL(v); break; case SV_ZTVALUE: # ifdef GTM_TRIGGER assert(!dollar_tlevel || (tstart_trigger_depth <= gtm_trigger_depth)); if (!dollar_tlevel || (tstart_trigger_depth == gtm_trigger_depth)) rts_error(VARLSTCNT(4) ERR_SETINTRIGONLY, 2, RTS_ERROR_TEXT("$ZTVALUE")); if (dollar_ztriggerop != &gvtr_cmd_mval[GVTR_CMDTYPE_SET]) rts_error(VARLSTCNT(4) ERR_SETINSETTRIGONLY, 2, RTS_ERROR_TEXT("$ZTVALUE")); assert(0 < gtm_trigger_depth); memcpy(dollar_ztvalue, v, SIZEOF(mval)); dollar_ztvalue->mvtype &= ~MV_ALIASCONT; /* Make sure to shut off alias container flag on copy */ assert(NULL != ztvalue_changed_ptr); *ztvalue_changed_ptr = TRUE; break; # else rts_error(VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZTWORMHOLE: # ifdef GTM_TRIGGER MV_FORCE_STR(v); /* See jnl.h for why MAX_ZTWORMHOLE_SIZE should be less than minimum alignsize */ assert(MAX_ZTWORMHOLE_SIZE < (JNL_MIN_ALIGNSIZE * DISK_BLOCK_SIZE)); if (MAX_ZTWORMHOLE_SIZE < v->str.len) rts_error(VARLSTCNT(4) ERR_ZTWORMHOLE2BIG, 2, v->str.len, MAX_ZTWORMHOLE_SIZE); dollar_ztwormhole.mvtype = MV_STR; dollar_ztwormhole.str = v->str; break; # else rts_error(VARLSTCNT(1) ERR_UNIMPLOP); # endif case SV_ZTSLATE: # ifdef GTM_TRIGGER assert(!dollar_tlevel || (tstart_trigger_depth <= gtm_trigger_depth)); if (!dollar_tlevel || (tstart_trigger_depth == gtm_trigger_depth)) rts_error(VARLSTCNT(4) ERR_SETINTRIGONLY, 2, RTS_ERROR_TEXT("$ZTSLATE")); assert(0 < gtm_trigger_depth); MV_FORCE_DEFINED(v); memcpy((char *)&dollar_ztslate, v, SIZEOF(mval)); dollar_ztslate.mvtype &= ~MV_ALIASCONT; /* Make sure to shut off alias container flag on copy */ break; # else rts_error(VARLSTCNT(1) ERR_UNIMPLOP); # endif default: GTMASSERT; } return; }
void op_svput(int varnum, mval *v) { int i, ok, state; error_def(ERR_UNIMPLOP); error_def(ERR_TEXT); error_def(ERR_INVECODEVAL); error_def(ERR_SETECODE); error_def(ERR_SYSTEMVALUE); switch (varnum) { case SV_X: MV_FORCE_NUM(v); io_curr_device.out->dollar.x = (short)MV_FORCE_INT(v); if ((short)(io_curr_device.out->dollar.x) < 0) io_curr_device.out->dollar.x = 0; break; case SV_Y: MV_FORCE_NUM(v); io_curr_device.out->dollar.y = (short)MV_FORCE_INT(v); if ((short)(io_curr_device.out->dollar.y) < 0) io_curr_device.out->dollar.y = 0; break; case SV_ZCOMPILE: MV_FORCE_STR(v); if (dollar_zcompile.addr) free (dollar_zcompile.addr); dollar_zcompile.addr = (char *)malloc(v->str.len); memcpy (dollar_zcompile.addr, v->str.addr, v->str.len); dollar_zcompile.len = v->str.len; break; case SV_ZSTEP: MV_FORCE_STR(v); op_commarg(v,indir_linetail); op_unwind(); dollar_zstep = *v; break; case SV_ZGBLDIR: MV_FORCE_STR(v); if (!(dollar_zgbldir.str.len == v->str.len && !memcmp(dollar_zgbldir.str.addr, v->str.addr, dollar_zgbldir.str.len))) { if(v->str.len == 0) { /* set $zgbldir="" */ dpzgbini(); gd_header = NULL; } else { gd_header = zgbldir(v); dollar_zgbldir.str.len = v->str.len; dollar_zgbldir.str.addr = v->str.addr; s2pool(&dollar_zgbldir.str); } if (gv_currkey) gv_currkey->base[0] = 0; if (gv_target) gv_target->clue.end = 0; } break; case SV_ZMAXTPTIME: dollar_zmaxtptime = mval2i(v); break; case SV_ZROUTINES: MV_FORCE_STR(v); /* The string(v) should be parsed and loaded before setting $zroutines * to retain the old value in case errors occur while loading */ zro_load(&v->str); if (dollar_zroutines.addr) free (dollar_zroutines.addr); dollar_zroutines.addr = (char *)malloc(v->str.len); memcpy (dollar_zroutines.addr, v->str.addr, v->str.len); dollar_zroutines.len = v->str.len; break; case SV_ZSOURCE: MV_FORCE_STR(v); dollar_zsource = v->str; break; case SV_ZTRAP: MV_FORCE_STR(v); if (ztrap_new) op_newintrinsic(SV_ZTRAP); dollar_ztrap.mvtype = MV_STR; dollar_ztrap.str = v->str; /* Setting either $ZTRAP or $ETRAP to empty causes any current error trapping to be canceled */ if (!v->str.len) { dollar_etrap.mvtype = MV_STR; dollar_etrap.str = v->str; ztrap_explicit_null = TRUE; } else /* Ensure that $ETRAP and $ZTRAP are not both active at the same time */ { ztrap_explicit_null = FALSE; if (dollar_etrap.str.len > 0) gtm_newintrinsic(&dollar_etrap); } if (ztrap_form & ZTRAP_POP) ztrap_save_ctxt(); break; case SV_ZSTATUS: MV_FORCE_STR(v); dollar_zstatus.mvtype = MV_STR; dollar_zstatus.str = v->str; break; case SV_PROMPT: MV_FORCE_STR(v); gtmprompt.len = v->str.len < sizeof(prombuf) ? v->str.len : sizeof(prombuf); memcpy(gtmprompt.addr,v->str.addr,gtmprompt.len); break; case SV_ECODE: MV_FORCE_STR(v); if (v->str.len) { /* Format must be like ,Mnnn,Mnnn,Zxxx,Uxxx, * Mnnn are ANSI standard error codes * Zxxx are implementation-specific codes * Uxxx are end-user defined codes * Note that there must be commas at the start and at the end */ for (state = 2, i = 0; (i < v->str.len) && (state <= 2); i++) { switch(state) { case 2: state = (v->str.addr[i] == ',') ? 1 : 101; break; case 1: state = ((v->str.addr[i] == 'M') || (v->str.addr[i] == 'U') || (v->str.addr[i] == 'Z')) ? 0 : 101; break; case 0: state = (v->str.addr[i] == ',') ? 1 : 0; break; } } /* The above check would pass strings like "," * so double-check that there are at least three characters * (starting comma, ending comma, and something in between) */ if ((state != 1) || (v->str.len < 3)) { /* error, ecode = M101 */ rts_error(VARLSTCNT(4) ERR_INVECODEVAL, 2, v->str.len, v->str.addr); } } if (v->str.len > 0) { ecode_add(&v->str); rts_error(VARLSTCNT(2) ERR_SETECODE, 0); } else { NULLIFY_DOLLAR_ECODE; /* reset $ECODE related variables to correspond to $ECODE = NULL state */ NULLIFY_ERROR_FRAME; /* we are no more in error-handling mode */ } break; case SV_ETRAP: MV_FORCE_STR(v); dollar_etrap.mvtype = MV_STR; dollar_etrap.str = v->str; /* Setting either $ZTRAP or $ETRAP to empty causes any current error trapping to be canceled */ if (!v->str.len) { dollar_ztrap.mvtype = MV_STR; dollar_ztrap.str = v->str; } else if (dollar_ztrap.str.len > 0) { /* Ensure that $ETRAP and $ZTRAP are not both active at the same time */ assert(FALSE == ztrap_explicit_null); gtm_newintrinsic(&dollar_ztrap); } ztrap_explicit_null = FALSE; break; case SV_ZERROR: MV_FORCE_STR(v); dollar_zerror.mvtype = MV_STR; dollar_zerror.str = v->str; break; case SV_ZYERROR: MV_FORCE_STR(v); dollar_zyerror.mvtype = MV_STR; dollar_zyerror.str = v->str; break; case SV_SYSTEM: ok = 1; if (!(v->mvtype & MV_STR)) ok = 0; if (ok && v->str.addr[0] != '4') ok = 0; if (ok && v->str.addr[1] != '7') ok = 0; if ((' ' != v->str.addr[2]) && !ispunct(v->str.addr[2])) ok = 0; if (ok) dollar_system.str = v->str; else rts_error(VARLSTCNT(4) ERR_SYSTEMVALUE, 2, v->str.len, v->str.addr); break; case SV_ZDIR: setzdir(v, NULL); /* change directory to v */ getzdir(); /* update dollar_zdir with current working directory */ break; case SV_ZINTERRUPT: MV_FORCE_STR(v); dollar_zinterrupt.mvtype = MV_STR; dollar_zinterrupt.str = v->str; break; case SV_ZDATE_FORM: MV_FORCE_NUM(v); zdate_form = (short)MV_FORCE_INT(v); break; case SV_ZTEXIT: MV_FORCE_STR(v); dollar_ztexit.mvtype = MV_STR; dollar_ztexit.str = v->str; /* Coercing $ZTEXIT to boolean at SET command is more efficient than coercing before each * rethrow at TR/TRO. Since we want to maintain dollar_ztexit as a string, coercion should * not be performed on dollar_ztext, but on a temporary (i.e. parameter v) */ dollar_ztexit_bool = MV_FORCE_BOOL(v); break; default: GTMASSERT; } return; }
void trigger_delete_all(void) { int count; char count_str[MAX_DIGITS_IN_INT + 1]; sgmnt_addrs *csa; mval curr_gbl_name; int cycle; mstr gbl_name; mname_entry gvent; gv_namehead *hasht_tree, *gvt; mval *mv_count_ptr; mval *mv_cycle_ptr; mval mv_indx; gd_region *reg; int reg_indx; int4 result; char save_currkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)]; gv_key *save_gv_currkey; gd_region *save_gv_cur_region; gv_namehead *save_gv_target; sgm_info *save_sgm_info_ptr; int trig_indx; mval trigger_cycle; mval trigger_count; mval val; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(0 < dollar_tlevel); /* Before we delete any triggers, verify that none of the triggers have been fired in this transaction. If they have, * this creates an un-commitable transaction that will end in a TPFAIL error. Since that error indicates database * damage, we'd rather detect this avoidable condition and give a descriptive error instead (TRIGMODINTP). */ for (gvt = gv_target_list; NULL != gvt; gvt = gvt->next_gvnh) { if (gvt->trig_local_tn == local_tn) rts_error(VARLSTCNT(1) ERR_TRIGMODINTP); } SWITCH_TO_DEFAULT_REGION; INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; if (0 != gv_target->root) { /* kill ^#t("#TRHASH") */ BUILD_HASHT_SUB_CURRKEY(LITERAL_HASHTRHASH, STRLEN(LITERAL_HASHTRHASH)); gvcst_kill(TRUE); /* kill ^#t("#TNAME") */ BUILD_HASHT_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME)); gvcst_kill(TRUE); } for (reg_indx = 0, reg = gd_header->regions; reg_indx < gd_header->n_regions; reg_indx++, reg++) { if (!reg->open) gv_init_reg(reg); if (!reg->read_only) { gv_cur_region = reg; change_reg(); csa = cs_addrs; SETUP_TRIGGER_GLOBAL; INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED; /* There might not be any ^#t in this region, so check */ if (0 != gv_target->root) { /* Kill all descendents of ^#t(trigvn, indx) where trigvn is any global with a trigger, * but skip the "#XYZ" entries. setup ^#t(trigvn,"$") as the PREV key for op_gvorder */ BUILD_HASHT_SUB_CURRKEY(LITERAL_MAXHASHVAL, STRLEN(LITERAL_MAXHASHVAL)); TREF(gv_last_subsc_null) = FALSE; /* We know its not null, but prior state is unreliable */ while (TRUE) { op_gvorder(&curr_gbl_name); /* quit:$length(curr_gbl_name)=0 */ if (0 == curr_gbl_name.str.len) break; /* $get(^#t(curr_gbl_name,#COUNT)) */ BUILD_HASHT_SUB_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT)); if (gvcst_get(&trigger_count)) { mv_count_ptr = &trigger_count; count = MV_FORCE_INT(mv_count_ptr); /* $get(^#t(curr_gbl_name,#CYCLE)) */ BUILD_HASHT_SUB_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len, LITERAL_HASHCYCLE, STRLEN(LITERAL_HASHCYCLE)); if (!gvcst_get(&trigger_cycle)) assert(FALSE); /* Found #COUNT, there must be #CYCLE */ mv_cycle_ptr = &trigger_cycle; cycle = MV_FORCE_INT(mv_cycle_ptr); /* kill ^#t(curr_gbl_name) */ BUILD_HASHT_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len); gvcst_kill(TRUE); cycle++; MV_FORCE_MVAL(&trigger_cycle, cycle); /* set ^#t(curr_gbl_name,#CYCLE)=trigger_cycle */ SET_TRIGGER_GLOBAL_SUB_SUB_MVAL(curr_gbl_name.str.addr, curr_gbl_name.str.len, LITERAL_HASHCYCLE, STRLEN(LITERAL_HASHCYCLE), trigger_cycle, result); assert(PUT_SUCCESS == result); } /* else there is no #COUNT, then no triggers, leave #CYCLE alone */ /* get ready for op_gvorder() call for next trigger under ^#t */ BUILD_HASHT_SUB_CURRKEY(curr_gbl_name.str.addr, curr_gbl_name.str.len); } csa->incr_db_trigger_cycle = TRUE; if (dollar_ztrigger_invoked) { /* increment db_dztrigger_cycle so that next gvcst_put/gvcst_kill in this transaction, * on this region, will re-read. See trigger_update.c for a comment on why it is okay * for db_dztrigger_cycle to be incremented more than once in the same transaction */ csa->db_dztrigger_cycle++; } } } } util_out_print_gtmio("All existing triggers deleted", FLUSH); }
int4 trigger_delete(char *trigvn, int trigvn_len, mval *trigger_count, int index) { int count; mval *mv_cnt_ptr; mval mv_val; mval *mv_val_ptr; int num_len; char *ptr1; int4 result; int4 retval; char save_currkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)]; gv_key *save_gv_currkey; stringkey kill_hash, set_hash; int sub_indx; char tmp_trig_str[MAX_BUFF_SIZE]; int4 trig_len; char trig_name[MAX_TRIGNAME_LEN]; int trig_name_len; int tmp_len; char *tt_val[NUM_SUBS]; uint4 tt_val_len[NUM_SUBS]; mval trigger_value; mval trigger_index; mval xecute_index; uint4 xecute_idx; uint4 used_trigvn_len; mval val; char val_str[MAX_DIGITS_IN_INT + 1]; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; mv_val_ptr = &mv_val; MV_FORCE_MVAL(&trigger_index, index); count = MV_FORCE_INT(trigger_count); /* build up array of values - needed for comparison in hash stuff */ ptr1 = tmp_trig_str; memcpy(ptr1, trigvn, trigvn_len); ptr1 += trigvn_len; *ptr1++ = '\0'; tmp_len = trigvn_len + 1; for (sub_indx = 0; sub_indx < NUM_SUBS; sub_indx++) { BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigger_index, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx])); trig_len = gvcst_get(&trigger_value) ? trigger_value.str.len : 0; if (0 == trig_len) { tt_val[sub_indx] = NULL; tt_val_len[sub_indx] = 0; continue; } if (TRIGNAME_SUB == sub_indx) { trig_name_len = trig_len; assert(MAX_TRIGNAME_LEN >= trig_len); memcpy(trig_name, trigger_value.str.addr, trig_name_len); tt_val[sub_indx] = NULL; tt_val_len[sub_indx] = 0; continue; } tt_val[sub_indx] = ptr1; tt_val_len[sub_indx] = trig_len; tmp_len += trig_len; if (0 < trig_len) { if (MAX_BUFF_SIZE <= tmp_len) return VAL_TOO_LONG; memcpy(ptr1, trigger_value.str.addr, trig_len); ptr1 += trig_len; } *ptr1++ = '\0'; tmp_len++; } /* Get trigger name, set hash value, and kill hash values from trigger before we delete it. * The values will be used in clean ups associated with the deletion */ /* $get(^#t(GVN,trigger_index,"LHASH") for deletion in cleanup_trigger_hash */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigger_index, trigger_subs[LHASH_SUB], STRLEN(trigger_subs[LHASH_SUB])); if (gvcst_get(mv_val_ptr)) kill_hash.hash_code = (uint4)MV_FORCE_INT(mv_val_ptr); else { util_out_print_gtmio("The LHASH for global ^!AD does not exist", FLUSH, trigvn_len, trigvn); kill_hash.hash_code = 0; } /* $get(^#t(GVN,trigger_index,"BHASH") for deletion in cleanup_trigger_hash */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, trigger_index, trigger_subs[BHASH_SUB], STRLEN(trigger_subs[BHASH_SUB])); if (gvcst_get(mv_val_ptr)) set_hash.hash_code = (uint4)MV_FORCE_INT(mv_val_ptr); else { util_out_print_gtmio("The BHASH for global ^!AD does not exist", FLUSH, trigvn_len, trigvn); set_hash.hash_code = 0; } /* kill ^#t(GVN,trigger_index) */ BUILD_HASHT_SUB_MSUB_CURRKEY(trigvn, trigvn_len, trigger_index); gvcst_kill(TRUE); assert(0 == gvcst_data()); if (1 == count) { /* This is the last trigger for "trigvn" - clean up trigger name, remove #LABEL and #COUNT */ assert(1 == index); BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHLABEL, STRLEN(LITERAL_HASHLABEL)); gvcst_kill(TRUE); BUILD_HASHT_SUB_SUB_CURRKEY(trigvn, trigvn_len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT)); gvcst_kill(TRUE); cleanup_trigger_name(trigvn, trigvn_len, trig_name, trig_name_len); cleanup_trigger_hash(trigvn, trigvn_len, tt_val, tt_val_len, &set_hash, &kill_hash, TRUE, 0); } else { cleanup_trigger_hash(trigvn, trigvn_len, tt_val, tt_val_len, &set_hash, &kill_hash, TRUE, index); cleanup_trigger_name(trigvn, trigvn_len, trig_name, trig_name_len); if (index != count) { /* Shift the last trigger (index is #COUNT value) to the just deleted trigger's index. * This way count is always accurate and can still be used as the index for new triggers. * Note - there is no dependence on the trigger order, or this technique wouldn't work. */ ptr1 = tmp_trig_str; memcpy(ptr1, trigvn, trigvn_len); ptr1 += trigvn_len; *ptr1++ = '\0'; for (sub_indx = 0; sub_indx < NUM_TOTAL_SUBS; sub_indx++) { /* $get(^#t(GVN,trigger_count,sub_indx) */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, *trigger_count, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx])); if (gvcst_get(&trigger_value)) { trig_len = trigger_value.str.len; /* set ^#t(GVN,trigger_index,sub_indx)=^#t(GVN,trigger_count,sub_indx) */ SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MVAL(trigvn, trigvn_len, trigger_index, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), trigger_value, result); assert(PUT_SUCCESS == result); } else if (XECUTE_SUB == sub_indx) { /* multi line trigger broken up because it exceeds record size */ for (xecute_idx = 0; ; xecute_idx++) { i2mval(&xecute_index, xecute_idx); BUILD_HASHT_SUB_MSUB_SUB_MSUB_CURRKEY(trigvn, trigvn_len, *trigger_count, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), xecute_index); if (!gvcst_get(&trigger_value)) break; SET_TRIGGER_GLOBAL_SUB_MSUB_SUB_MSUB_MVAL(trigvn, trigvn_len, trigger_index, trigger_subs[sub_indx], STRLEN(trigger_subs[sub_indx]), xecute_index, trigger_value, result); assert(PUT_SUCCESS == result); } assert (xecute_idx >= 2); /* multi-line trigger, indices 0, 1 and 2 MUST be defined */ } else { /* in PRO this is a nasty case that will result in an access violation * because data that should be present is not. In the next go around * with trigger installation this case should be handled better */ assert(!((TRIGNAME_SUB == sub_indx) || (CMD_SUB == sub_indx) || (CHSET_SUB == sub_indx))); /* these should not be zero length */ trig_len = 0; } if (NUM_SUBS > sub_indx) { tt_val[sub_indx] = ptr1; tt_val_len[sub_indx] = trig_len; if (0 < trig_len) { memcpy(ptr1, trigger_value.str.addr, trig_len); ptr1 += trig_len; } *ptr1++ = '\0'; } } /* $get(^#t(GVN,trigger_count,"LHASH") for update_trigger_hash_value */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, *trigger_count, trigger_subs[LHASH_SUB], STRLEN(trigger_subs[LHASH_SUB])); if (!gvcst_get(mv_val_ptr)) return PUT_SUCCESS; kill_hash.hash_code = (uint4)MV_FORCE_INT(mv_val_ptr); /* $get(^#t(GVN,trigger_count,"BHASH") for update_trigger_hash_value */ BUILD_HASHT_SUB_MSUB_SUB_CURRKEY(trigvn, trigvn_len, *trigger_count, trigger_subs[BHASH_SUB], STRLEN(trigger_subs[BHASH_SUB])); if (!gvcst_get(mv_val_ptr)) return PUT_SUCCESS; set_hash.hash_code = (uint4)MV_FORCE_INT(mv_val_ptr); /* update hash values from above */ if (VAL_TOO_LONG == (retval = update_trigger_hash_value(trigvn, trigvn_len, tt_val, tt_val_len, &set_hash, &kill_hash, count, index))) return VAL_TOO_LONG; /* fix the value ^#t("#TNAME",^#t(GVN,index,"#TRIGNAME")) to point to the correct "index" */ if (VAL_TOO_LONG == (retval = update_trigger_name_value(trigvn_len, tt_val[TRIGNAME_SUB], tt_val_len[TRIGNAME_SUB], index))) return VAL_TOO_LONG; /* kill ^#t(GVN,COUNT) which was just shifted to trigger_index */ BUILD_HASHT_SUB_MSUB_CURRKEY(trigvn, trigvn_len, *trigger_count); gvcst_kill(TRUE); } /* Update #COUNT */ count--; MV_FORCE_MVAL(trigger_count, count); SET_TRIGGER_GLOBAL_SUB_SUB_MVAL(trigvn, trigvn_len, LITERAL_HASHCOUNT, STRLEN(LITERAL_HASHCOUNT), *trigger_count, result); assert(PUT_SUCCESS == result); /* Size of count can only get shorter or stay the same */ } trigger_incr_cycle(trigvn, trigvn_len); return PUT_SUCCESS; }
STATICFNDEF void cleanup_trigger_name(char *trigvn, int trigvn_len, char *trigger_name, int trigger_name_len) { sgmnt_addrs *csa; mname_entry gvent; gv_namehead *hasht_tree; int4 result; char save_currkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)]; char save_altkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)]; gv_key *save_gv_altkey; gv_key *save_gv_currkey; gd_region *save_gv_cur_region; gv_namehead *save_gv_target; gv_namehead *save_gvtarget; sgm_info *save_sgm_info_ptr; char trunc_name[MAX_TRIGNAME_LEN + 1]; uint4 used_trigvn_len; mval val; mval *val_ptr; char val_str[MAX_DIGITS_IN_INT + 1]; int var_count; boolean_t is_auto_name; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* assume user defined name or auto gen name whose GVN < 21 chars */ is_auto_name = FALSE; if (!trigger_user_name(trigger_name, trigger_name_len)) { /* auto gen name uses #TNCOUNT and #SEQNO under #TNAME */ is_auto_name = TRUE; used_trigvn_len = MIN(trigvn_len, MAX_AUTO_TRIGNAME_LEN); memcpy(trunc_name, trigvn, used_trigvn_len); } SAVE_TRIGGER_REGION_INFO; SWITCH_TO_DEFAULT_REGION; if (0 != gv_target->root) { if (is_auto_name) { /* $get(^#t("#TNAME",<trunc_name>,"#TNCOUNT")) */ BUILD_HASHT_SUB_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trunc_name, used_trigvn_len, LITERAL_HASHTNCOUNT, STRLEN(LITERAL_HASHTNCOUNT)); if (gvcst_get(&val)) { /* only long autogenerated names have a #TNCOUNT entry */ val_ptr = &val; var_count = MV_FORCE_INT(val_ptr); if (1 == var_count) { /* kill ^#t("#TNAME",<trunc_name>) to kill #TNCOUNT and #SEQNO */ BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trunc_name, used_trigvn_len); gvcst_kill(TRUE); } else { var_count--; MV_FORCE_MVAL(&val, var_count); /* set ^#t("#TNAME",GVN,"#TNCOUNT")=var_count */ SET_TRIGGER_GLOBAL_SUB_SUB_SUB_MVAL(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trunc_name, used_trigvn_len, LITERAL_HASHTNCOUNT, STRLEN(LITERAL_HASHTNCOUNT), val, result); assert(PUT_SUCCESS == result); /* The count size can only decrease */ } } } /* kill ^#t("#TNAME",<trigger_name>,:) or zkill ^#t("#TNAME",<trigger_name>) if is_auto_name==FALSE */ BUILD_HASHT_SUB_SUB_CURRKEY(LITERAL_HASHTNAME, STRLEN(LITERAL_HASHTNAME), trigger_name, trigger_name_len - 1); gvcst_kill(is_auto_name); } RESTORE_TRIGGER_REGION_INFO; }
void op_zprevious(mval *v) { int4 n; int min_reg_index, reg_index, res; mname_entry gvname; mval tmpmval, *datamval; enum db_acc_method acc_meth; boolean_t found, ok_to_change_currkey; gd_binding *gd_map_start, *map, *prev_map; gd_addr *gd_targ; gvnh_reg_t *gvnh_reg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(gv_currkey->prev || !TREF(gv_last_subsc_null)); if (gv_currkey->prev) { /* If last subscript is a NULL subscript, modify gv_currkey such that a gvcst_search of the resulting gv_currkey * will find the last available subscript. But in case of dba_usr, (the custom implementation of $ZPREVIOUS which * is overloaded for DDP now but could be more in the future) it is better to hand over gv_currkey as it is so * the custom implementation can decide what to do with it. */ acc_meth = REG_ACC_METH(gv_cur_region); ok_to_change_currkey = (dba_usr != acc_meth); if (TREF(gv_last_subsc_null) && ok_to_change_currkey) { /* Replace the last subscript with the highest possible subscript value i.e. the byte sequence * 0xFF (STR_SUB_MAXVAL), 0xFF, 0xFF ... as much as possible i.e. until gv_currkey->top permits. * This subscript is guaranteed to be NOT present in the database since a user who tried to set this * exact subscripted global would have gotten a GVSUBOFLOW error (because GT.M sets aside a few bytes * of padding space). And yet this is guaranteed to collate AFTER any existing subscript. Therefore we * can safely do a gvcst_zprevious on this key to get at the last existing key in the database. * * With standard null collation, the last subscript will be 0x01 * Without standard null collation, the last subscript will be 0xFF * Assert that is indeed the case as this will be used to restore the replaced subscript at the end. */ assert(gv_cur_region->std_null_coll || (STR_SUB_PREFIX == gv_currkey->base[gv_currkey->prev])); assert(!gv_cur_region->std_null_coll || (SUBSCRIPT_STDCOL_NULL == gv_currkey->base[gv_currkey->prev])); assert(KEY_DELIMITER == gv_currkey->base[gv_currkey->prev + 1]); assert(gv_currkey->end == gv_currkey->prev + 2); assert(gv_currkey->end < gv_currkey->top); /* need "<" (not "<=") to account for terminating 0x00 */ GVZPREVIOUS_APPEND_MAX_SUBS_KEY(gv_currkey, gv_target); } if ((dba_bg == acc_meth) || (dba_mm == acc_meth)) { gvnh_reg = TREF(gd_targ_gvnh_reg); if (NULL == gvnh_reg) found = (gv_target->root ? gvcst_zprevious() : FALSE); else INVOKE_GVCST_SPR_XXX(gvnh_reg, found = gvcst_spr_zprevious()); } else if (dba_cm == acc_meth) found = gvcmx_zprevious(); else found = gvusr_zprevious(); v->mvtype = 0; /* so stp_gcol (if invoked below) can free up space currently occupied (BYPASSOK) * by this to-be-overwritten mval */ if (found) { gv_altkey->prev = gv_currkey->prev; if (!IS_STP_SPACE_AVAILABLE(MAX_KEY_SZ)) { if ((0xFF != gv_altkey->base[gv_altkey->prev]) && (SUBSCRIPT_STDCOL_NULL != gv_altkey->base[gv_altkey->prev])) n = MAX_FORM_NUM_SUBLEN; else { n = gv_altkey->end - gv_altkey->prev; assert(n > 0); } v->str.len = 0; /* so stp_gcol (if invoked) can free up space currently occupied by this (BYPASSOK) * to-be-overwritten mval */ ENSURE_STP_FREE_SPACE(n); } v->str.addr = (char *)stringpool.free; v->str.len = MAX_KEY_SZ; stringpool.free = gvsub2str(&gv_altkey->base[gv_altkey->prev], &(v->str), FALSE); v->str.len = INTCAST((char *)stringpool.free - v->str.addr); assert(v->str.addr < (char *)stringpool.top && v->str.addr >= (char *)stringpool.base); assert(v->str.addr + v->str.len <= (char *)stringpool.top && v->str.addr + v->str.len >= (char *)stringpool.base); } else v->str.len = 0; v->mvtype = MV_STR; /* initialize mvtype now that mval has been otherwise completely set up */ if (TREF(gv_last_subsc_null) && ok_to_change_currkey) { /* Restore gv_currkey to what it was at function entry time */ gv_currkey->base[gv_currkey->prev + 1] = KEY_DELIMITER; if (gv_cur_region->std_null_coll) gv_currkey->base[gv_currkey->prev] = SUBSCRIPT_STDCOL_NULL; assert(gv_cur_region->std_null_coll || (STR_SUB_PREFIX == gv_currkey->base[gv_currkey->prev])); gv_currkey->end = gv_currkey->prev + 2; gv_currkey->base[gv_currkey->end] = KEY_DELIMITER; } assert(KEY_DELIMITER == gv_currkey->base[gv_currkey->end]); } else { /* the following section is for $ZPREVIOUS(^gname) */ assert(2 <= gv_currkey->end); assert(gv_currkey->end < (MAX_MIDENT_LEN + 2)); /* until names are not in midents */ assert(KEY_DELIMITER == gv_currkey->base[gv_currkey->end]); assert(KEY_DELIMITER == gv_currkey->base[gv_currkey->end - 1]); gd_targ = TREF(gd_targ_addr); gd_map_start = gd_targ->maps; map = gv_srch_map(gd_targ, (char *)&gv_currkey->base[0], gv_currkey->end - 1); assert(map > (gd_map_start + 1)); /* If ^gname starts at "map" start search from map-1 since $ZPREVIOUS(^gname) is sought */ BACK_OFF_ONE_MAP_ENTRY_IF_EDGECASE(gv_currkey->base, gv_currkey->end - 1, map); found = FALSE; /* The first map entry corresponds to local locks. The second map entry does not contain any globals. * Therefore, any search for globals needs to only look after these maps. Hence the "gd_map_start + 1" below. */ for ( ; map > gd_map_start + 1; map = prev_map) { prev_map = map - 1; gv_cur_region = map->reg.addr; if (!gv_cur_region->open) gv_init_reg(gv_cur_region); change_reg(); acc_meth = REG_ACC_METH(gv_cur_region); /* search region, entries in directory tree could have empty GVT in which case move on to previous entry */ for ( ; ; ) { assert(0 == gv_currkey->prev); /* or else gvcst_zprevious could get confused */ if ((dba_bg == acc_meth) || (dba_mm == acc_meth)) { gv_target = cs_addrs->dir_tree; found = gvcst_zprevious(); } else if (dba_cm == acc_meth) found = gvcmx_zprevious(); else found = gvusr_zprevious(); if ('#' == gv_altkey->base[0]) /* don't want to give any hidden ^#* global, e.g "^#t" */ found = FALSE; if (!found) break; assert(1 < gv_altkey->end); assert(gv_altkey->end < (MAX_MIDENT_LEN + 2)); /* until names are not in midents */ res = memcmp(gv_altkey->base, prev_map->gvkey.addr, gv_altkey->end); assert((0 != res) || (gv_altkey->end <= prev_map->gvkey_len)); if (0 > res) { /* The global name we found is less than the maximum value in the previous map * so this name is not part of the current map for sure. Move on to previous map. */ found = FALSE; break; } gvname.var_name.addr = (char *)gv_altkey->base; gvname.var_name.len = gv_altkey->end - 1; if (dba_cm == acc_meth) break; COMPUTE_HASH_MNAME(&gvname); GV_BIND_NAME_AND_ROOT_SEARCH(gd_targ, &gvname, gvnh_reg); /* updates "gv_currkey" */ assert((NULL != gvnh_reg->gvspan) || (gv_cur_region == map->reg.addr)); if (NULL != gvnh_reg->gvspan) { /* gv_target would NOT have been initialized by GV_BIND_NAME in this case. * So finish that initialization. */ datamval = &tmpmval; /* The below macro finishes the task of GV_BIND_NAME_AND_ROOT_SEARCH * (e.g. setting gv_cur_region for spanning globals) */ GV_BIND_SUBSNAME_IF_GVSPAN(gvnh_reg, gd_targ, gv_currkey, gvnh_reg->gd_reg); op_gvdata(datamval); if (MV_FORCE_INT(datamval)) break; } else { /* else gv_target->root would have been initialized by GV_BIND_NAME_AND_ROOT_SEARCH */ if ((0 != gv_target->root) && (0 != gvcst_data())) break; } } if (found) break; /* If previous map corresponding to a spanning global, then do not update gv_currkey as that would * effectively cause the spanning global to be skipped. If gvkey_len == gvname_len + 1 it is NOT * a spanning global map entry. */ assert(prev_map->gvkey_len >= (prev_map->gvname_len + 1)); if ((prev_map > (gd_map_start + 1)) && (prev_map->gvkey_len == (prev_map->gvname_len + 1))) { assert(strlen(prev_map->gvkey.addr) == prev_map->gvname_len); gv_currkey->end = prev_map->gvname_len + 1; assert(gv_currkey->end <= (MAX_MIDENT_LEN + 1)); memcpy(gv_currkey->base, prev_map->gvkey.addr, gv_currkey->end); assert(KEY_DELIMITER == gv_currkey->base[gv_currkey->end - 1]); gv_currkey->base[gv_currkey->end] = KEY_DELIMITER; assert(gv_currkey->top > gv_currkey->end); /* ensure we are within allocated bounds */ } } /* Reset gv_currkey as we have potentially skipped one or more regions so we no * longer can expect gv_currkey/gv_cur_region/gv_target to match each other. */ gv_currkey->end = 0; gv_currkey->base[0] = KEY_DELIMITER; v->mvtype = 0; /* so stp_gcol (if invoked below) can free up space currently occupied (BYPASSOK) * by this to-be-overwritten mval */ if (found) { if (!IS_STP_SPACE_AVAILABLE(gvname.var_name.len + 1)) { v->str.len = 0; /* so stp_gcol ignores otherwise incompletely setup mval (BYPASSOK) */ INVOKE_STP_GCOL(gvname.var_name.len + 1); } v->str.addr = (char *)stringpool.free; *stringpool.free++ = '^'; memcpy(stringpool.free, gvname.var_name.addr, gvname.var_name.len); stringpool.free += gvname.var_name.len; v->str.len = gvname.var_name.len + 1; assert(v->str.addr < (char *)stringpool.top && v->str.addr >= (char *)stringpool.base); assert(v->str.addr + v->str.len <= (char *)stringpool.top && v->str.addr + v->str.len >= (char *)stringpool.base); } else v->str.len = 0; v->mvtype = MV_STR; /* initialize mvtype now that mval has been otherwise completely set up */ /* No need to restore gv_currkey (to what it was at function entry) as it is already set to NULL */ } return; }
void op_fnorder(lv_val *src, mval *key, mval *dst) { int cur_subscr; mval tmp_sbs; int length; sbs_blk *num, *str; boolean_t found, is_neg; int4 i; lv_val **lv; lv_sbs_tbl *tbl; sbs_search_status status; boolean_t is_fnnext; is_fnnext = in_op_fnnext; in_op_fnnext = FALSE; found = FALSE; if (src) { if (tbl = src->ptrs.val_ent.children) { MV_FORCE_DEFINED(key); num = tbl->num; str = tbl->str; assert(tbl->ident == MV_SBS); if ((MV_IS_STRING(key) && key->str.len == 0) || (is_fnnext && MV_IS_INT(key) && key->m[1] == MINUS_ONE)) { /* With GT.M collation , if last subscript is null, $o returns the first subscript in that level */ if (tbl->int_flag) { assert(num); for (i = 0, lv = &num->ptr.lv[0]; i < SBS_NUM_INT_ELE; i++, lv++) { if (*lv) { MV_FORCE_MVAL(dst,i); found = TRUE; break; } } } else if (num) { assert(num->cnt); MV_ASGN_FLT2MVAL((*dst),num->ptr.sbs_flt[0].flt); found = TRUE; } } else { if (MV_IS_CANONICAL(key)) { MV_FORCE_NUM(key); if (tbl->int_flag) { assert(num); is_neg = (key->mvtype & MV_INT) ? key->m[1] < 0 : key->sgn; if (is_neg) i = 0; else { if (!is_fnnext && (1 == numcmp(key, (mval *)&SBS_MVAL_INT_ELE))) i = SBS_NUM_INT_ELE; else { i = MV_FORCE_INT(key); i++; } } for (lv = &num->ptr.lv[i]; i < SBS_NUM_INT_ELE; i++, lv++) { if (*lv) { MV_FORCE_MVAL(dst,i); found = TRUE; break; } } } else if (num && lv_nxt_num_inx(num, key, &status)) { MV_ASGN_FLT2MVAL((*dst),((sbs_flt_struct*)status.ptr)->flt); found = TRUE; } } else { if (local_collseq) { ALLOC_XFORM_BUFF(&key->str); tmp_sbs.mvtype = MV_STR; tmp_sbs.str.len = max_lcl_coll_xform_bufsiz; assert(NULL != lcl_coll_xform_buff); tmp_sbs.str.addr = lcl_coll_xform_buff; do_xform(local_collseq, XFORM, &key->str, &tmp_sbs.str, &length); tmp_sbs.str.len = length; s2pool(&(tmp_sbs.str)); key = &tmp_sbs; } if (str && lv_nxt_str_inx(str, &key->str, &status)) { dst->mvtype = MV_STR; dst->str = ((sbs_str_struct *)status.ptr)->str; } else { if (!is_fnnext) { dst->mvtype = MV_STR; dst->str.len = 0; } else MV_FORCE_MVAL(dst, -1); } found = TRUE; } } if (!found && str) { /* We are here because * a. key is "" and there is no numeric subscript, OR * b. key is numeric and it is >= the largest numeric subscript at this level implying a switch from * numeric to string subscripts * Either case, return the first string subscript. However, for STDNULLCOLL, skip to the next * subscript should the first subscript be "" */ assert(str->cnt); dst->mvtype = MV_STR; dst->str = str->ptr.sbs_str[0].str; found = TRUE; if (local_collseq_stdnull && 0 == dst->str.len) { assert(lv_null_subs); if (lv_nxt_str_inx(str, &dst->str, &status)) { dst->str = ((sbs_str_struct*)status.ptr)->str; } else found = FALSE; } } } } if (!found) { if (!is_fnnext) { dst->mvtype = MV_STR; dst->str.len = 0; } else MV_FORCE_MVAL(dst, -1); } else if (dst->mvtype == MV_STR && local_collseq) { ALLOC_XFORM_BUFF(&dst->str); assert(NULL != lcl_coll_xform_buff); tmp_sbs.str.addr = lcl_coll_xform_buff; tmp_sbs.str.len = max_lcl_coll_xform_bufsiz; do_xform(local_collseq, XBACK, &dst->str, &tmp_sbs.str, &length); tmp_sbs.str.len = length; s2pool(&(tmp_sbs.str)); dst->str = tmp_sbs.str; } }
void op_merge(void) { boolean_t found, check_for_null_subs, is_base_var; lv_val *dst_lv; mval *mkey, *value, *subsc; int org_glvn1_keysz, org_glvn2_keysz, delta2, dollardata_src, dollardata_dst, sbs_depth; unsigned char *ptr, *ptr2; unsigned char buff[MAX_ZWR_KEY_SZ]; unsigned char nullcoll_src, nullcoll_dst; zshow_out output; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; assert(MAX_STRLEN >= MAX_ZWR_KEY_SZ); assert ((merge_args == (MARG1_LCL | MARG2_LCL)) || (merge_args == (MARG1_LCL | MARG2_GBL)) || (merge_args == (MARG1_GBL | MARG2_LCL)) || (merge_args == (MARG1_GBL | MARG2_GBL))); assert(!lvzwrite_block || 0 == lvzwrite_block->curr_subsc); /* Need to protect value from stpgcol */ PUSH_MV_STENT(MVST_MVAL); value = &mv_chain->mv_st_cont.mvs_mval; value->mvtype = 0; /* initialize mval in the M-stack in case stp_gcol gets called before value gets initialized below */ if (MARG2_IS_GBL(merge_args)) { /* Need to protect mkey returned from gvcst_queryget from stpgcol */ PUSH_MV_STENT(MVST_MVAL); mkey = &mv_chain->mv_st_cont.mvs_mval; mkey->mvtype = 0; /* initialize mval in M-stack in case stp_gcol gets called before mkey gets initialized below */ gvname_env_restore(mglvnp->gblp[IND2]); /* now $DATA will be done for gvn2. op_gvdata input parameters are set in the form of some GBLREF */ op_gvdata(value); dollardata_src = MV_FORCE_INT(value); if (0 == dollardata_src) { /* nothing in source global */ UNDO_ACTIVE_LV; POP_MV_STENT(); /* value */ POP_MV_STENT(); /* mkey */ if (MARG1_IS_GBL(merge_args)) gvname_env_restore(mglvnp->gblp[IND1]); /* store destination as naked indicator in gv_currkey */ merge_args = 0; /* Must reset to zero to reuse the Global */ return; } if (NULL == TREF(gv_mergekey2)) { /* We need to initialize gvn2 (right hand side). */ GVKEY_INIT(TREF(gv_mergekey2), DBKEYSIZE(MAX_KEY_SZ)); } org_glvn1_keysz = mglvnp->gblp[IND1]->s_gv_currkey->end + 1; org_glvn2_keysz = gv_currkey->end + 1; (TREF(gv_mergekey2))->end = gv_currkey->end; (TREF(gv_mergekey2))->prev = gv_currkey->prev; memcpy((TREF(gv_mergekey2))->base, gv_currkey->base, gv_currkey->end + 1); if (MARG1_IS_GBL(merge_args)) { /*==================== MERGE ^gvn1=^gvn2 =====================*/ if (mglvnp->gblp[IND2]->s_gv_target->nct != mglvnp->gblp[IND1]->s_gv_target->nct) rts_error(VARLSTCNT(1) ERR_NCTCOLLDIFF); /* if self merge then NOOP*/ if (!merge_desc_check()) /* will not proceed if one is descendant of another */ { gvname_env_restore(mglvnp->gblp[IND1]); /* store destination as naked indicator in gv_currkey */ POP_MV_STENT(); /* value */ merge_args = 0; /* Must reset to zero to reuse the Global */ return; } nullcoll_src = mglvnp->gblp[IND2]->s_gv_cur_region->std_null_coll; nullcoll_dst = mglvnp->gblp[IND1]->s_gv_cur_region->std_null_coll; if (1 == dollardata_src || 11 == dollardata_src) { found = op_gvget(value); /* value of ^glvn2 */ if (found) { /* SET ^gvn1=^gvn2 */ gvname_env_restore(mglvnp->gblp[IND1]); op_gvput(value); /* Note: If ^gvn1's null_sub=ALLOWEXISTING and say ^gvn1("")=^gvn, * this will give NULL_SUBC error */ } } check_for_null_subs = (NEVER != mglvnp->gblp[IND2]->s_gv_cur_region->null_subs) && (ALWAYS != mglvnp->gblp[IND1]->s_gv_cur_region->null_subs); /* Traverse descendant of ^gvn2 and copy into ^gvn1 */ for (; ;) { if (outofband) { gvname_env_restore(mglvnp->gblp[IND1]); /* naked indicator is restored into gv_currkey */ outofband_action(FALSE); } /* Restore last key under ^gvn2 we worked */ gvname_env_restore(mglvnp->gblp[IND2]); assert(0 == gv_currkey->base[gv_currkey->end - 1] && 0 == gv_currkey->base[gv_currkey->end]); /* following is an attempt to find immidiate right sibling */ gv_currkey->base[gv_currkey->end] = 1; gv_currkey->base[gv_currkey->end + 1] = 0; gv_currkey->base[gv_currkey->end + 2] = 0; gv_currkey->end += 2; /* Do atomic $QUERY and $GET of current glvn2: * mkey is a mstr which contains $QUERY result in database format (So no conversion necessary) * value is a mstr which contains $GET result */ if (!op_gvqueryget(mkey, value)) break; assert(MV_IS_STRING(mkey)); if (mkey->str.len < org_glvn2_keysz) break; if (0 != *((unsigned char *)mkey->str.addr + (TREF(gv_mergekey2))->end - 1) || memcmp(mkey->str.addr, (TREF(gv_mergekey2))->base, (TREF(gv_mergekey2))->end - 1)) break; /* mkey is not under the sub-tree */ delta2 = mkey->str.len - org_glvn2_keysz; /* length increase of source key */ assert (0 < delta2); /* Save the new source key for next iteration */ memcpy(mglvnp->gblp[IND2]->s_gv_currkey->base + org_glvn2_keysz - 2, mkey->str.addr + org_glvn2_keysz - 2, delta2 + 2); mglvnp->gblp[IND2]->s_gv_currkey->end = mkey->str.len - 1; /* Create the destination key for this iteration (under ^glvn1) */ gvname_env_restore(mglvnp->gblp[IND1]); if (gv_cur_region->max_key_size < org_glvn1_keysz + delta2) ISSUE_GVSUBOFLOW_ERROR(gv_currkey); assert(gv_currkey->end == org_glvn1_keysz - 1); memcpy(gv_currkey->base + org_glvn1_keysz - 2, mkey->str.addr + org_glvn2_keysz - 2, delta2 + 2); gv_currkey->end = org_glvn1_keysz + delta2 - 1; if (nullcoll_src != nullcoll_dst) { if (0 == nullcoll_dst) { /* Standard to GTM null subscript conversion*/ STD2GTMNULLCOLL((unsigned char *)gv_currkey->base + org_glvn1_keysz - 1, delta2 - 1); } else { /* GTM to standard null subscript conversion */ GTM2STDNULLCOLL((unsigned char *)gv_currkey->base + org_glvn1_keysz - 1, delta2 - 1); } } /* check null subscripts in destination key, note that we have already restored, destination global * and curresponding region, key information */ if (check_for_null_subs) { ptr2 = gv_currkey->base + gv_currkey->end - 1; for (ptr = gv_currkey->base + org_glvn1_keysz - 2; ptr < ptr2; ) { if (KEY_DELIMITER == *ptr++ && KEY_DELIMITER == *(ptr + 1) && (0 == gv_cur_region->std_null_coll ? (STR_SUB_PREFIX == *ptr) : (SUBSCRIPT_STDCOL_NULL == *ptr))) /* Note: For sgnl_gvnulsubsc/rts_error * we do not restore proper naked indicator. * The standard states that the effect of a MERGE command * on the naked indicator is that the naked indicator will be changed * as if a specific SET command would have been executed. * The standard also states that the effect on the naked indicator * will only take be visible after the MERGE command has completed. * So, if there is an error during the execution of a MERGE command, * the standard allows the naked indicator to reflect any intermediate * state. This provision was made intentionally, otherwise it would * have become nearly impossible to create a fully standard * implementation. : From Ed de Moel : 2/1/2 */ sgnl_gvnulsubsc(); } } /* Now put value of ^glvn2 descendant into corresponding descendant under ^glvn1 */ op_gvput(value); } gvname_env_restore(mglvnp->gblp[IND1]); /* store destination as naked indicator in gv_currkey */ } else { /*==================== MERGE lvn1=^gvn2 =====================*/ assert(MARG1_IS_LCL(merge_args)); assert(mglvnp->lclp[IND1]); /* Need to protect subsc created from global variable subscripts from stpgcol */ PUSH_MV_STENT(MVST_MVAL); subsc = &mv_chain->mv_st_cont.mvs_mval; /* Restore ^gvn2 we will work */ gvname_env_save(mglvnp->gblp[IND2]); if (1 == dollardata_src || 11 == dollardata_src) { /* SET lvn1=^gvn2 */ found = op_gvget(value); if (found) mglvnp->lclp[IND1]->v = *value; } for (; ;) { if (outofband) { gvname_env_restore(mglvnp->gblp[IND2]); /* naked indicator is restored into gv_currkey */ outofband_action(FALSE); } assert(0 == gv_currkey->base[gv_currkey->end - 1] && 0 == gv_currkey->base[gv_currkey->end]); /* following is an attempt to find immidiate right sibling */ gv_currkey->base[gv_currkey->end] = 1; gv_currkey->base[gv_currkey->end + 1] = 0; gv_currkey->base[gv_currkey->end + 2] = 0; gv_currkey->end += 2; /* Do $QUERY and $GET of current glvn2. Result will be in mkey and value respectively. * mkey->str contains data as database format. So no conversion necessary */ if (!op_gvqueryget(mkey, value)) break; if (mkey->str.len < (TREF(gv_mergekey2))->end + 1) break; ptr = (unsigned char *)mkey->str.addr + (TREF(gv_mergekey2))->end - 1; if (0 != *ptr || memcmp(mkey->str.addr, (TREF(gv_mergekey2))->base, (TREF(gv_mergekey2))->end - 1)) break; assert(MV_IS_STRING(mkey)); delta2 = mkey->str.len - org_glvn2_keysz; /* length increase of key */ assert (0 < delta2); /* Create next key for ^glvn2 */ memcpy(gv_currkey->base + org_glvn2_keysz - 2, mkey->str.addr + org_glvn2_keysz - 2, delta2 + 2); gv_currkey->end = mkey->str.len - 1; /* Now add subscripts to create the entire key */ dst_lv = mglvnp->lclp[IND1]; is_base_var = LV_IS_BASE_VAR(dst_lv); ptr = (unsigned char *)gv_currkey->base + org_glvn2_keysz - 1; assert(*ptr); do { LV_SBS_DEPTH(dst_lv, is_base_var, sbs_depth); if (MAX_LVSUBSCRIPTS <= sbs_depth) rts_error(VARLSTCNT(3) ERR_MERGEINCOMPL, 0, ERR_MAXNRSUBSCRIPTS); ptr2 = gvsub2str(ptr, buff, FALSE); subsc->mvtype = MV_STR; subsc->str.addr = (char *)buff; subsc->str.len = INTCAST(ptr2 - buff); s2pool(&subsc->str); dst_lv = op_putindx(VARLSTCNT(2) dst_lv, subsc); while (*ptr++); /* skip to start of next subscript */ is_base_var = FALSE; } while (*ptr); /* We created the key. Pre-process the node in case a container is being replaced, * then assign the value directly. Note there is no need to worry about MV_ALIASCONT * propagation since the source in this case is a global var. */ DECR_AC_REF(dst_lv, TRUE); dst_lv->v = *value; } gvname_env_restore(mglvnp->gblp[IND2]); /* naked indicator is restored into gv_currkey */ POP_MV_STENT(); /* subsc */ } POP_MV_STENT(); /* mkey */ } else { /* source is local */ op_fndata(mglvnp->lclp[IND2], value); dollardata_src = MV_FORCE_INT(value); if (0 == dollardata_src) { UNDO_ACTIVE_LV; POP_MV_STENT(); /* value */ if (MARG1_IS_GBL(merge_args)) gvname_env_restore(mglvnp->gblp[IND1]); /* store destination as naked indicator in gv_currkey */ merge_args = 0; /* Must reset to zero to reuse the Global */ return; } /* not memsetting output to 0 here can cause garbage value of output.out_var.lv.child which in turn can * cause a premature return from lvzwr_var resulting in op_merge() returning without having done the merge. */ memset(&output, 0, SIZEOF(output)); if (MARG1_IS_LCL(merge_args)) { /*==================== MERGE lvn1=lvn2 =====================*/ assert(mglvnp->lclp[IND1]); /* if self merge then NOOP */ if (!merge_desc_check()) /* will not proceed if one is descendant of another */ { POP_MV_STENT(); /* value */ merge_args = 0; /* Must reset to zero to reuse the Global */ return; } output.buff = (char *)buff; output.ptr = output.buff; output.out_var.lv.lvar = mglvnp->lclp[IND1]; zwr_output = &output; lvzwr_init(zwr_patrn_mident, &mglvnp->lclp[IND2]->v); lvzwr_arg(ZWRITE_ASTERISK, 0, 0); lvzwr_var(mglvnp->lclp[IND2], 0); /* assert that destination got all data of the source and its descendants */ DEBUG_ONLY(op_fndata(mglvnp->lclp[IND1], value)); DEBUG_ONLY(dollardata_dst = MV_FORCE_INT(value)); assert((dollardata_src & dollardata_dst) == dollardata_src); } else { /*==================== MERGE ^gvn1=lvn2 =====================*/ assert(MARG1_IS_GBL(merge_args) && MARG2_IS_LCL(merge_args)); gvname_env_save(mglvnp->gblp[IND1]); output.buff = (char *)buff; output.ptr = output.buff; output.out_var.gv.end = gv_currkey->end; output.out_var.gv.prev = gv_currkey->prev; zwr_output = &output; lvzwr_init(zwr_patrn_mident, &mglvnp->lclp[IND2]->v); lvzwr_arg(ZWRITE_ASTERISK, 0, 0); lvzwr_var(mglvnp->lclp[IND2], 0); gvname_env_restore(mglvnp->gblp[IND1]); /* store destination as naked indicator in gv_currkey */ } } POP_MV_STENT(); /* value */ merge_args = 0; /* Must reset to zero to reuse the Global */ }
int one_job_param (char **parptr) { boolean_t neg; int x, len, num; error_def (ERR_JOBPARUNK); error_def (ERR_JOBPARNOVAL); error_def (ERR_JOBPARVALREQ); error_def (ERR_JOBPARNUM); error_def (ERR_JOBPARSTR); if ((window_token != TK_IDENT) || ((x = namelook (job_param_index, job_param_names, window_ident.c)) < 0)) { stx_error (ERR_JOBPARUNK); return FALSE; } advancewindow (); *(*parptr)++ = job_param_data[x]; if (job_param_datatypes[job_param_data[x]] != jpdt_nul) { if (window_token != TK_EQUAL) { stx_error (ERR_JOBPARVALREQ); return FALSE; } advancewindow (); switch (job_param_datatypes[job_param_data[x]]) { case jpdt_num: neg = FALSE; if (window_token == TK_MINUS && director_token == TK_INTLIT) { advancewindow(); neg = TRUE; } if (window_token != TK_INTLIT) { stx_error (ERR_JOBPARNUM); return FALSE; } num = MV_FORCE_INT(&window_mval); *((int4 *) (*parptr)) = (neg ? -num : num); *parptr += sizeof(int4); break; case jpdt_str: if (window_token != TK_STRLIT) { stx_error (ERR_JOBPARSTR); return FALSE; } len = window_mval.str.len; *(*parptr)++ = len; memcpy (*parptr, window_mval.str.addr, len); *parptr += len; break; default: GTMASSERT; } advancewindow (); } else if (window_token == TK_EQUAL) { stx_error (ERR_JOBPARNOVAL); return FALSE; } return TRUE; }