/* This code is very similar to op_fngvget.c except, if the gvn is undefined, this returns an undefined value as a signal to * op_fnget2, which, in turn, returns a specified "default" value; that slight of hand deals with order of evaluation issues * Any changes to this routine most likely have to be made in op_fngvget.c as well. */ void op_fngvget1(mval *dst) { boolean_t gotit; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(gv_last_subsc_null) && NEVER == gv_cur_region->null_subs) sgnl_gvnulsubsc(); switch (gv_cur_region->dyn.addr->acc_meth) { case dba_bg : case dba_mm : gotit = gv_target->root ? gvcst_get(dst) : FALSE; break; case dba_cm : gotit = gvcmx_get(dst); break; default : if (gotit = gvusr_get(dst)) /* NOTE: assignment */ s2pool(&dst->str); break; } if (!gotit) dst->mvtype = 0; assert(0 == (dst->mvtype & MV_ALIASCONT)); /* Should be no alias container flag */ return; }
/* This code is very similar to "op_fngvget1.c" except that this one returns the default value * (while op_fngvget1 returns an undefined value) if the global variable that we are trying to get is undefined. * * Any changes to this routine most likely have to be made in op_fngvget1.c as well. */ void op_fngvget(mval *v, mval *def) { boolean_t gotit; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(gv_last_subsc_null) && NEVER == gv_cur_region->null_subs) sgnl_gvnulsubsc(); switch (gv_cur_region->dyn.addr->acc_meth) { case dba_bg : case dba_mm : if (gv_target->root) gotit = gvcst_get(v); else gotit = FALSE; break; case dba_cm : gotit = gvcmx_get(v); break; default : gotit = gvusr_get(v); if (gotit) s2pool(&v->str); break; } if (!gotit) { *v = *def; v->mvtype &= ~MV_ALIASCONT; /* Make sure do not allow alias container property to pass */ } return; }
boolean_t op_gvqueryget(mval *key, mval *val) { boolean_t gotit; gv_key *save_key; gvnh_reg_t *gvnh_reg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(gv_last_subsc_null) && NEVER == gv_cur_region->null_subs) sgnl_gvnulsubsc(); switch (REG_ACC_METH(gv_cur_region)) { case dba_bg: case dba_mm: gvnh_reg = TREF(gd_targ_gvnh_reg); if (NULL == gvnh_reg) gotit = ((0 != gv_target->root) ? gvcst_queryget(val) : FALSE); /* global does not exist if root is 0 */ else INVOKE_GVCST_SPR_XXX(gvnh_reg, gotit = gvcst_spr_queryget(val)); break; case dba_cm: gotit = gvcmx_query(val); break; case dba_usr: save_key = gv_currkey; gv_currkey = gv_altkey; /* We rely on the fact that *gv_altkey area is not modified by gvusr_queryget, and don't change gv_altkey. * If and when *gv_altkey area is modified by gvusr_queryget, we have to set up a spare key area * (apart from gv_altkey and gv_currkey), and make gv_altkey point the spare area before calling gvusr_queryget */ memcpy(gv_currkey, save_key, SIZEOF(*save_key) + save_key->end); gotit = gvusr_queryget(val); gv_altkey = gv_currkey; gv_currkey = save_key; break; default: assertpro(FALSE && REG_ACC_METH(gv_cur_region)); } if (gotit) { key->mvtype = MV_STR; key->str.addr = (char *)gv_altkey->base; key->str.len = gv_altkey->end + 1; s2pool(&key->str); } else { *key = literal_null; *val = literal_null; } return gotit; }
boolean_t op_gvqueryget(mval *key, mval *val) { boolean_t gotit; gv_key *save_key; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(gv_last_subsc_null) && NEVER == gv_cur_region->null_subs) sgnl_gvnulsubsc(); switch (gv_cur_region->dyn.addr->acc_meth) { case dba_bg: case dba_mm: gotit = (0 != gv_target->root) ? gvcst_queryget(val) : FALSE; break; case dba_cm: gotit = gvcmx_query(val); break; case dba_usr: save_key = gv_currkey; gv_currkey = gv_altkey; /* We rely on the fact that *gv_altkey area is not modified by gvusr_queryget, and don't change gv_altkey. * If and when *gv_altkey area is modified by gvusr_queryget, we have to set up a spare key area * (apart from gv_altkey and gv_currkey), and make gv_altkey point the spare area before calling gvusr_queryget */ memcpy(gv_currkey, save_key, SIZEOF(*save_key) + save_key->end); gotit = gvusr_queryget(val); gv_altkey = gv_currkey; gv_currkey = save_key; break; default: GTMASSERT; } if (gotit) { key->mvtype = MV_STR; key->str.addr = (char *)gv_altkey->base; key->str.len = gv_altkey->end + 1; s2pool(&key->str); } else { *key = literal_null; *val = literal_null; } return gotit; }
void op_gvkill(void) { gd_region *reg; error_def(ERR_DBPRIVERR); if (gv_cur_region->read_only) rts_error(VARLSTCNT(4) ERR_DBPRIVERR, 2, DB_LEN_STR(gv_cur_region)); if (gv_curr_subsc_null && gv_cur_region->null_subs == FALSE) sgnl_gvnulsubsc(); if (gv_cur_region->dyn.addr->acc_meth == dba_bg || gv_cur_region->dyn.addr->acc_meth == dba_mm) { if (gv_target->root) { gvcst_kill(TRUE); } } else if (gv_cur_region->dyn.addr->acc_meth == dba_cm) { gvcmx_kill(TRUE); }else { gvusr_kill(TRUE); } if (gv_cur_region->dyn.addr->repl_list) { gv_replication_error = gv_replopen_error; gv_replopen_error = FALSE; reg = gv_cur_region; while (gv_cur_region = gv_cur_region->dyn.addr->repl_list) /* set replicated segments */ { if (gv_cur_region->open) { change_reg(); kill_var(); } else gv_replication_error = TRUE; } gv_cur_region = reg; change_reg(); if (gv_replication_error) sgnl_gvreplerr(); } }
void op_gvdata(mval *v) { mint x; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(gv_last_subsc_null) && NEVER == gv_cur_region->null_subs) sgnl_gvnulsubsc(); x = 0; if (gv_cur_region->dyn.addr->acc_meth == dba_bg || gv_cur_region->dyn.addr->acc_meth == dba_mm) { if (gv_target->root) x = gvcst_data(); } else if (gv_cur_region->dyn.addr->acc_meth == dba_cm) x = gvcmx_data(); else x = gvusr_data(); *v = *fndata_table[x / 10][x & 1]; }
/* From the generated code's point of view, this is a void function. * The defined state isn't looked at by MUMPS code. This routine, * however, is used by some servers who would dearly like to know * the status of the get, though they don't want to cause any * errors. */ boolean_t op_gvget(mval *v) { boolean_t gotit; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(gv_last_subsc_null) && NEVER == gv_cur_region->null_subs) sgnl_gvnulsubsc(); if (IS_REG_BG_OR_MM(gv_cur_region)) { if (0 == gv_target->root) /* global does not exist */ { /* Assert that if gtm_gvundef_fatal is non-zero, then we better not be about to signal a GVUNDEF */ assert(!TREF(gtm_gvundef_fatal)); gotit = FALSE; } else { DEBUG_ONLY(TREF(in_op_gvget) = TRUE;) gotit = gvcst_get(v); assert(FALSE == TREF(in_op_gvget)); /* gvcst_get should have reset it right away */ } } else if (dba_cm == gv_cur_region->dyn.addr->acc_meth)
void op_gvincr(mval *increment, mval *result) { unsigned char buff[MAX_ZWR_KEY_SZ], *end; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* If specified var name is global ^%Y*, the name is illegal to use in a SET or KILL command, only GETs are allowed */ if ((RESERVED_NAMESPACE_LEN <= gv_currkey->end) && (0 == MEMCMP_LIT(gv_currkey->base, RESERVED_NAMESPACE))) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_PCTYRESERVED); if (gv_cur_region->read_only) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_DBPRIVERR, 2, DB_LEN_STR(gv_cur_region)); if ((TREF(gv_last_subsc_null) || TREF(gv_some_subsc_null)) && (ALWAYS != gv_cur_region->null_subs)) sgnl_gvnulsubsc(); assert(gv_currkey->end + 1 <= gv_cur_region->max_key_size); MV_FORCE_NUM(increment); switch (gv_cur_region->dyn.addr->acc_meth) { case dba_bg: case dba_mm: gvcst_incr(increment, result); break; case dba_cm: gvcmx_increment(increment, result); break; case dba_usr: /* $INCR not supported for DDP/USR access method */ if (0 == (end = format_targ_key(buff, MAX_ZWR_KEY_SZ, gv_currkey, TRUE))) end = &buff[MAX_ZWR_KEY_SZ - 1]; rts_error_csa(CSA_ARG(NULL) VARLSTCNT(10) ERR_UNIMPLOP, 0, ERR_TEXT, 2, LEN_AND_LIT("GTCM DDP server does not support $INCREMENT"), ERR_GVIS, 2, end - buff, buff, ERR_TEXT, 2, REG_LEN_STR(gv_cur_region)); break; default: assertpro(FALSE); } assert(MV_DEFINED(result)); }
void op_gvkill(void) { gd_region *reg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (gv_cur_region->read_only) rts_error(VARLSTCNT(4) ERR_DBPRIVERR, 2, DB_LEN_STR(gv_cur_region)); if (TREF(gv_last_subsc_null) && NEVER == gv_cur_region->null_subs) sgnl_gvnulsubsc(); if (gv_cur_region->dyn.addr->acc_meth == dba_bg || gv_cur_region->dyn.addr->acc_meth == dba_mm) { if (IS_OK_TO_INVOKE_GVCST_KILL(gv_target)) gvcst_kill(TRUE); } else if (gv_cur_region->dyn.addr->acc_meth == dba_cm) gvcmx_kill(TRUE); else gvusr_kill(TRUE); if (gv_cur_region->dyn.addr->repl_list) { gv_replication_error = gv_replopen_error; gv_replopen_error = FALSE; reg = gv_cur_region; while (gv_cur_region = gv_cur_region->dyn.addr->repl_list) /* set replicated segments */ { if (gv_cur_region->open) { change_reg(); kill_var(); } else gv_replication_error = TRUE; } gv_cur_region = reg; change_reg(); if (gv_replication_error) sgnl_gvreplerr(); } }
void op_gvdata(mval *v) { mint x; gvnh_reg_t *gvnh_reg; enum db_acc_method acc_meth; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (TREF(gv_last_subsc_null) && NEVER == gv_cur_region->null_subs) sgnl_gvnulsubsc(); acc_meth = REG_ACC_METH(gv_cur_region); if (IS_ACC_METH_BG_OR_MM(acc_meth)) { gvnh_reg = TREF(gd_targ_gvnh_reg); if (NULL == gvnh_reg) x = (gv_target->root ? gvcst_data() : 0); else INVOKE_GVCST_SPR_XXX(gvnh_reg, x = gvcst_spr_data()); } else if (REG_ACC_METH(gv_cur_region) == dba_cm) x = gvcmx_data(); else x = gvusr_data(); *v = *fndata_table[x / 10][x & 1]; }
void op_fnzqgblmod(mval *v) { bool gblmod; if (gv_curr_subsc_null && NEVER == gv_cur_region->null_subs) sgnl_gvnulsubsc(); gblmod = TRUE; if (NULL != gv_cur_region) { if (gv_cur_region->dyn.addr->acc_meth == dba_bg || gv_cur_region->dyn.addr->acc_meth == dba_mm) { if (gv_target->root) gblmod = gvcst_gblmod(v); else gblmod = FALSE; } else assert(FALSE); } *v = *fnzqgblmod_table[gblmod]; }
void lvzwr_out(lv_val *lvp) { char buff; uchar_ptr_t lastc; int n, nsubs, sbs_depth; lv_val *dst_lv, *res_lv, *lvpc; mstr one; mval *subscp, *val, outindx; ht_ent_addr *tabent_addr; ht_ent_mname *tabent_mname; boolean_t htent_added, dump_container; zwr_alias_var *newzav, *zav; mident_fixed zwrt_varname; lvzwrite_datablk *newzwrb; gparam_list param_list; /* for op_putindx call through callg */ gvnh_reg_t *gvnh_reg; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; val = &lvp->v; assert(lvzwrite_block); if (!merge_args) { /* The cases that exist here are: * 1) This is a container variable. If the lv_val it refers to has been printed, show that association. * Else, "create" a $ZWRTACxxx var/index that will define the value. Then before returning, cause * that container var to be dumped with the appropriate $ZWRTACxxx index as the var name. * 2) This is an alias base variable. If first time seen, we print normally but record it and put a * ";#" tag on the end to signify it is an alias var (doesn't affect value). If we look it up and it * is not the first time this lv_val has been printed, then we instead print the statement needed to * alias it to the first seen var. * 3) This is just a normal var needing to be printed normally. */ htent_added = FALSE; one.addr = &buff; one.len = 1; lvzwrite_block->zav_added = FALSE; if (lvp->v.mvtype & MV_ALIASCONT) { /* Case 1 -- have an alias container */ assert(curr_symval->alias_activity); assert(!LV_IS_BASE_VAR(lvp)); /* verify is subscripted var */ lvpc = (lv_val *)lvp->v.str.addr; assert(lvpc); assert(LV_IS_BASE_VAR(lvpc)); /* Verify base var lv_val */ if (tabent_addr = (ht_ent_addr *)lookup_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lvpc)) { /* The value was found, we have a reference we can print now */ assert(HTENT_VALID_ADDR(tabent_addr, zwr_alias_var, zav)); *one.addr = '*'; zshow_output(zwr_output, &one); lvzwr_out_targkey(&one); *one.addr = '='; zshow_output(zwr_output, &one); zav = (zwr_alias_var *)tabent_addr->value; assert(0 < zav->zwr_var.len); zwr_output->flush = TRUE; zshow_output(zwr_output, (const mstr *)&zav->zwr_var); return; } /* This lv_val isn't known to us yet. Scan the hash curr_symval hash table to see if it is known as a * base variable as we could have a "forward reference" here. */ tabent_mname = als_lookup_base_lvval(lvpc); /* note even though both paths below add a zav, not bothering to set zav_added because that flag is * really only (currently) cared about in reference to processing a basevar so we wouldn't * be in this code path anyway. Comment here to record potential usage if that changes. */ if (tabent_mname) { /* Found a base var it can reference -- create a zwrhtab entry for it */ assert(tabent_mname->key.var_name.len); newzav = als_getzavslot(); newzav->zwr_var = tabent_mname->key.var_name; htent_added = add_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lvpc, newzav, &tabent_addr); assert(htent_added); dump_container = FALSE; } else { /* Unable to find lv_val .. must be "orphaned" so we generate a new $ZWRTAC var for it. The first * check however is if this is the first $ZWRTAC var being generated for this $ZWR. If yes, generate * a $ZWRTAC="" line to preceed it. This will be a flag to load to clear out all existing $ZWRTAC * temp vars so there is no pollution between loads of ZWRitten data. */ if (0 == zwrtacindx++) { /* Put out "dummy" statement that will clear all the $ZWRTAC vars for a clean slate */ zwr_output->flush = TRUE; zshow_output(zwr_output, &dzwrtac_clean); } MEMCPY_LIT(zwrt_varname.c, DOLLAR_ZWRTAC); lastc = i2asc((uchar_ptr_t)zwrt_varname.c + STR_LIT_LEN(DOLLAR_ZWRTAC), zwrtacindx); newzav = als_getzavslot(); newzav->zwr_var.addr = zwrt_varname.c; newzav->zwr_var.len = INTCAST(((char *)lastc - &zwrt_varname.c[0])); s2pool(&newzav->zwr_var); htent_added = add_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lvpc, newzav, &tabent_addr); assert(htent_added); dump_container = TRUE; } /* Note value_printed flag in newzav not set since we are NOT dumping the value at this point * but only the association. Since the flag is not set, we *will* dump it when we get to that * actual variable. */ *one.addr = '*'; zshow_output(zwr_output, &one); lvzwr_out_targkey(&one); *one.addr = '='; zshow_output(zwr_output, &one); zwr_output->flush = TRUE; zshow_output(zwr_output, (const mstr *)&newzav->zwr_var); if (dump_container) { /* We want to dump the entire container variable but the name doesn't match the var we are * currently dumping so push a new lvzwrite_block onto the stack, fill it in for the current var * and call lvzwr_var() to handle it. When done, dismantle the temp lvzwrite_block. */ newzwrb = (lvzwrite_datablk *)malloc(SIZEOF(lvzwrite_datablk)); memset(newzwrb, 0, SIZEOF(lvzwrite_datablk)); newzwrb->sub = (zwr_sub_lst *)malloc(SIZEOF(zwr_sub_lst) * MAX_LVSUBSCRIPTS); newzwrb->curr_name = &newzav->zwr_var; newzwrb->prev = lvzwrite_block; lvzwrite_block = newzwrb; lvzwr_var(lvpc, 0); assert(newzav->value_printed); assert(newzwrb == lvzwrite_block); free(newzwrb->sub); lvzwrite_block = newzwrb->prev; free(newzwrb); } return; } else if (LV_IS_BASE_VAR(lvp) && IS_ALIASLV(lvp)) { /* Case 2 -- alias base variable (only base vars have reference counts). Note this can occur with * TP save/restore vars since we increment both trefcnt and crefcnt for these hidden copied references. * Because of that, we can't assert alias_activity but otherwise it shouldn't affect processing. */ if (!(htent_added = add_hashtab_addr(&zwrhtab->h_zwrtab, (char **)&lvp, NULL, &tabent_addr))) { /* Entry already existed -- need to output association rather than values */ assert(tabent_addr); zav = (zwr_alias_var *)tabent_addr->value; assert(zav); if (zav->value_printed) { /* Value has already been output -- print association this time */ *one.addr = '*'; /* Flag as creating an alias */ zshow_output(zwr_output, &one); /* Now for (new) variable name */ zshow_output(zwr_output, lvzwrite_block->curr_name); *one.addr = '='; zshow_output(zwr_output, &one); /* .. and the var name aliasing to (the first seen with this lv_val) */ assert(zav->zwr_var.len); zwr_output->flush = TRUE; zshow_output(zwr_output, &zav->zwr_var); return; } /* Else the value for this entry has not yet been printed so let us fall into case 3 * and get that done. Also set the flag so we mark it as an alias. Note this can happen if * a container value for a name is encountered before the base var it points to. We will * properly resolve the entry but its value won't have been printed until we actually encounter * it in the tree. */ htent_added = TRUE; /* to force the ;# tag at end of value printing */ zav->value_printed = TRUE; /* value will be output shortly below */ } else { /* Entry was added so is first appearance -- give it a value to hold onto and print it */ newzav = als_getzavslot(); newzav->zwr_var = *lvzwrite_block->curr_name; newzav->value_printed = TRUE; /* or rather it will be shortly.. */ tabent_addr->value = (void *)newzav; lvzwrite_block->zav_added = TRUE; /* Note fall into case 3 to print var and value if exists */ } } /* Case 3 - everything else */ if (!MV_DEFINED(val)) return; MV_FORCE_STR(val); lvzwr_out_targkey(&one); *one.addr = '='; zshow_output(zwr_output, &one); mval_write(zwr_output, val, !htent_added); if (htent_added) { /* output the ";#" tag to indicate this is an alias output */ zwr_output->flush = TRUE; zshow_output(zwr_output, &semi_star); } } else { /* MERGE assignment from local variable */ nsubs = lvzwrite_block->curr_subsc; if (MARG1_IS_GBL(merge_args)) { /* Target is a global var : i.e. MERGE ^gvn1=lcl1. * In this case, mglvnp->gblp[IND1]->gvkey_nsubs would have been initialized in op_merge.c already. * Use that to check if the target node in ^gvn1 exceeds max # of subscripts. */ if (MAX_GVSUBSCRIPTS <= (mglvnp->gblp[IND1]->gvkey_nsubs + nsubs)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_MERGEINCOMPL, 0, ERR_MAXNRSUBSCRIPTS); memcpy(gv_currkey->base, mglvnp->gblp[IND1]->s_gv_currkey->base, mglvnp->gblp[IND1]->s_gv_currkey->end + 1); gv_currkey->end = mglvnp->gblp[IND1]->s_gv_currkey->end; for (n = 0; n < nsubs; n++) { subscp = ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[n].actual; MV_FORCE_STR(subscp); mval2subsc(subscp, gv_currkey, gv_cur_region->std_null_coll); if (!subscp->str.len && (ALWAYS != gv_cur_region->null_subs)) sgnl_gvnulsubsc(); } MV_FORCE_STR(val); gvnh_reg = TREF(gd_targ_gvnh_reg); /* set by op_gvname/op_gvextnam/op_gvnaked done before op_merge */ /* If gvnh_reg corresponds to a spanning global, then determine * gv_cur_region/gv_target/gd_targ_* variables based on updated gv_currkey. */ GV_BIND_SUBSNAME_FROM_GVNH_REG_IF_GVSPAN(gvnh_reg, (TREF(gd_targ_addr)), gv_currkey); /* For spanning globals, "gv_cur_region" points to the target region for ^gvn1 only now. * So do the GVSUBOFLOW check (both for spanning and non-spanning globals) now. */ if (gv_currkey->end >= gv_cur_region->max_key_size) ISSUE_GVSUBOFLOW_ERROR(gv_currkey, KEY_COMPLETE_TRUE); op_gvput(val); } else { /* Target is a local var : pre-process target in case it is a container */ assert(MARG1_IS_LCL(merge_args)); dst_lv = mglvnp->lclp[IND1]; if (!LV_IS_BASE_VAR(dst_lv)) { LV_SBS_DEPTH(dst_lv, FALSE, sbs_depth); if (MAX_LVSUBSCRIPTS < (sbs_depth + nsubs)) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(3) ERR_MERGEINCOMPL, 0, ERR_MAXNRSUBSCRIPTS); } param_list.arg[0] = dst_lv; /* this is already protected from stp_gcol by op_merge so no need to * push this into the stack for stp_gcol protection. */ for (n = 0 ; n < nsubs; n++) { /* Note: no need to do push these mvals on the stack before calling op_putindx * as lvzwrite_block->sub is already protected by stp_gcol_src.h. */ param_list.arg[n+1] = ((zwr_sub_lst *)lvzwrite_block->sub)->subsc_list[n].actual; } param_list.n = n + 1; dst_lv = (lv_val *)callg((callgfnptr)op_putindx, ¶m_list); MV_FORCE_STR(val); assert(!(MV_ALIASCONT & dst_lv->v.mvtype)); /* op_putindx would have already done DECR_AC_REF for us */ dst_lv->v = *val; dst_lv->v.mvtype &= ~MV_ALIASCONT; /* Make sure alias container property does not pass */ } } }
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 */ }