示例#1
0
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;
}
示例#2
0
void op_gvorder (mval *v)
{
	int4			n;
	gd_binding		*map;
	mstr			name;
	enum db_acc_method	acc_meth;
	boolean_t		found, ok_to_change_currkey;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	acc_meth = gv_cur_region->dyn.addr->acc_meth;
	/* Modify gv_currkey such that a gvcst_search of the resulting gv_currkey will find the next available subscript.
	 * But in case of dba_usr (the custom implementation of $ORDER which is overloaded for DDP 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.
	 */
	ok_to_change_currkey = (dba_usr != acc_meth);
	if (ok_to_change_currkey)
	{	/* Modify gv_currkey to reflect the next possible key value in collating order */
		if (!TREF(gv_last_subsc_null) || gv_cur_region->std_null_coll)
		{
			*(&gv_currkey->base[0] + gv_currkey->end - 1) = 1;
			*(&gv_currkey->base[0] + gv_currkey->end + 1) = 0;
			gv_currkey->end += 1;
		} else
		{
			assert(STR_SUB_PREFIX == gv_currkey->base[gv_currkey->prev]);
			assert(KEY_DELIMITER == gv_currkey->base[gv_currkey->end]);
			assert(KEY_DELIMITER == gv_currkey->base[gv_currkey->end - 1]);
			assert(2 == (gv_currkey->end - gv_currkey->prev));
			*(&gv_currkey->base[0] + gv_currkey->prev) = 01;
		}
	}
	if (gv_currkey->prev)
	{
		if (acc_meth == dba_bg || acc_meth == dba_mm)
		{
			if (gv_target->root == 0)	/* global does not exist */
				found = FALSE;
			else
				found = gvcst_order();
		} else if (acc_meth == dba_cm)
			found = gvcmx_order();
		else
		 	found = gvusr_order();
		v->mvtype = 0; /* so stp_gcol (if invoked below) can free up space currently occupied by
				* this to-be-overwritten mval */
		if (found)
		{
			gv_altkey->prev = gv_currkey->prev;

			if (!(IS_STP_SPACE_AVAILABLE(MAX_KEY_SZ)))
 			{
				if (*(&gv_altkey->base[0] + gv_altkey->prev) != 0xFF)
		 			n = MAX_FORM_NUM_SUBLEN;
				else
				{
					n = gv_altkey->end - gv_altkey->prev;
					assert (n > 0);
				}
				ENSURE_STP_FREE_SPACE(n);
			}
	 		v->str.addr = (char *)stringpool.free;
	 		stringpool.free = gvsub2str (&gv_altkey->base[0] + gv_altkey->prev, stringpool.free, 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 (ok_to_change_currkey)
		{	/* Restore gv_currkey to what it was at function entry time */
			if (!TREF(gv_last_subsc_null) || gv_cur_region->std_null_coll)
			{
				assert(1 == gv_currkey->base[gv_currkey->end - 2]);
				assert(KEY_DELIMITER == gv_currkey->base[gv_currkey->end-1]);
				assert(KEY_DELIMITER == gv_currkey->base[gv_currkey->end]);
				gv_currkey->base[gv_currkey->end - 2] = KEY_DELIMITER;
				gv_currkey->end--;
			} else
			{
				assert(01 == gv_currkey->base[gv_currkey->prev]);
				gv_currkey->base[gv_currkey->prev] = STR_SUB_PREFIX;
			}
		}
	} else	/* the following section is for $O(^gname) */
	{
		assert (2 < gv_currkey->end);
		assert (gv_currkey->end < (MAX_MIDENT_LEN + 3));	/* until names are not in midents */
		map = gd_map + 1;
		while (map < gd_map_top &&
			(memcmp(gv_currkey->base, map->name,
				gv_currkey->end == (MAX_MIDENT_LEN + 2) ? MAX_MIDENT_LEN : gv_currkey->end - 1) >= 0))
		{
			map++;
		}

		for (; map < gd_map_top; ++map)
		{
			gv_cur_region = map->reg.addr;
			if (!gv_cur_region->open)
				gv_init_reg(gv_cur_region);
			change_reg();
			acc_meth = gv_cur_region->dyn.addr->acc_meth;

			for (; ;)		/* search region, entries in directory tree could be empty */
			{
				if (acc_meth == dba_bg || acc_meth == dba_mm)
				{
					gv_target = cs_addrs->dir_tree;
					found = gvcst_order ();
				} else if (acc_meth == dba_cm)
					found = gvcmx_order ();
				else
				 	found = gvusr_order();
				if (!found)
					break;
				assert (1 < gv_altkey->end);
				assert (gv_altkey->end < (MAX_MIDENT_LEN + 2));	/* until names are not in midents */
				if (memcmp(gv_altkey->base, map->name, gv_altkey->end - 1) > 0)
				{
					found = FALSE;
					break;
				}
				name.addr = (char *)&gv_altkey->base[0];
				name.len = gv_altkey->end - 1;
				if (acc_meth == dba_cm)
					break;
				GV_BIND_NAME_AND_ROOT_SEARCH(gd_header, &name);
				if (gv_cur_region != map->reg.addr)
				{
					found = FALSE;
					break;
				}
				if ((gv_target->root != 0) && (gvcst_data() != 0))
					break;
				*(&gv_currkey->base[0] + gv_currkey->end - 1) = 1;
				*(&gv_currkey->base[0] + gv_currkey->end + 1) = 0;
				gv_currkey->end += 1;
			}
			if (found)
				break;
			else
			{
				assert(SIZEOF(map->name) == SIZEOF(mident_fixed));
				gv_currkey->end = mid_len((mident_fixed *)map->name);
				memcpy(&gv_currkey->base[0], map->name, gv_currkey->end);
				gv_currkey->base[ gv_currkey->end - 1 ] -= 1;
				gv_currkey->base[ gv_currkey->end ] = 0xFF;	/* back off 1 spot from map */
				gv_currkey->base[ gv_currkey->end + 1] = 0;
				gv_currkey->base[ gv_currkey->end + 2] = 0;
				gv_currkey->end += 2;
			}
		}
		/* 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] = 0;
		v->mvtype = 0; /* so stp_gcol (if invoked below) can free up space currently occupied by
				* this to-be-overwritten mval */
		if (found)
		{
			if (!IS_STP_SPACE_AVAILABLE(name.len + 1))
			{
				v->str.len = 0;	/* so stp_gcol ignores otherwise incompletely setup mval */
				INVOKE_STP_GCOL(name.len + 1);
			}
#ifdef mips
			/* the following line works around a tandem compiler bug. */
			v->str.addr = (char *)0;
#endif
			v->str.addr = (char *)stringpool.free;
			*stringpool.free++ = '^';
			memcpy (stringpool.free, name.addr, name.len);
			stringpool.free += name.len;
			v->str.len = 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 */
	}
	return;
}