fis-gtm/sr_port/op_zprevious.c

217 lines
8.4 KiB
C

/****************************************************************
* *
* Copyright 2001, 2012 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include "mdef.h"
#include "gtm_string.h"
#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "stringpool.h"
#include "op.h"
#include "gvcst_protos.h" /* for gvcst_data,gvcst_zprevious prototype */
#include "change_reg.h"
#include "gvsub2str.h"
#include "gvcmx.h"
#include "gvusr.h"
#include "filestruct.h"
#include "gdscc.h"
#include "jnl.h"
GBLREF gv_namehead *gv_target;
GBLREF gv_key *gv_currkey;
GBLREF gv_key *gv_altkey;
GBLREF gd_region *gv_cur_region;
GBLREF gd_addr *gd_header;
GBLREF gd_binding *gd_map;
GBLREF gd_binding *gd_map_top;
GBLREF sgmnt_addrs *cs_addrs;
GBLREF spdesc stringpool;
/* op_gvorder should generally be maintained in parallel */
void op_zprevious(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;
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 = gv_cur_region->dyn.addr->acc_meth;
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))
{
if (!gv_target->root) /* global does not exist */
found = FALSE;
else
found = gvcst_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;
stringpool.free = gvsub2str(&gv_altkey->base[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 (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 + 3)); /* until names are not in midents */
for (map = gd_map_top - 1; (map > (gd_map + 1)) &&
(0 >= memcmp(gv_currkey->base, map->name,
((MAX_MIDENT_LEN + 2) == gv_currkey->end) ? MAX_MIDENT_LEN : gv_currkey->end - 1)); map--)
;
for (map++; map > gd_map; 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 ((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 */
if (memcmp(gv_altkey->base, (map - 1)->name, gv_altkey->end) < 0)
{
found = FALSE;
break;
}
name.addr = (char *)gv_altkey->base;
name.len = gv_altkey->end - 1;
if (dba_cm == acc_meth)
break;
GV_BIND_NAME_AND_ROOT_SEARCH(gd_header, &name);
if (gv_cur_region != map->reg.addr)
{
found = FALSE;
break;
}
if ((gv_target->root) && gvcst_data())
break;
}
if (found)
break;
else if ((map - 1) > gd_map)
{
assert(SIZEOF((map - 1)->name) == SIZEOF(mident_fixed));
assert(0 == (map - 1)->name[SIZEOF((map - 1)->name) - 1]);
gv_currkey->end = mid_len((mident_fixed *)((map - 1)->name));
assert(gv_currkey->end <= MAX_MIDENT_LEN);
memcpy(gv_currkey->base, (map - 1)->name, gv_currkey->end);
gv_currkey->base[gv_currkey->end++] = KEY_DELIMITER;
gv_currkey->base[gv_currkey->end] = KEY_DELIMITER;
assert(gv_currkey->top > gv_currkey->end); /* ensure we are within allocated bounds */
}
}
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)
{
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(name.len + 1);
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 */
/* No need to restore gv_currkey (to what it was at function entry) as it is already set to NULL */
}
return;
}