void op_fnzprevious(lv_val *src, mval *key, mval *dst) { int cur_subscr, length; mval tmp_sbs; lvTreeNode *node; lvTree *lvt; boolean_t is_canonical, get_last; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; if (src && (lvt = LV_GET_CHILD(src))) /* caution: assignment */ { MV_FORCE_DEFINED(key); /* If last subscript is null, $zprev returns the last subscript in that level. */ get_last = FALSE; if (MV_IS_STRING(key) && (0 == key->str.len)) get_last = TRUE; if (get_last) node = lvAvlTreeLast(lvt); else { is_canonical = MV_IS_CANONICAL(key); if (!is_canonical) { assert(!TREE_KEY_SUBSCR_IS_CANONICAL(key->mvtype)); if (TREF(local_collseq)) { ALLOC_XFORM_BUFF(key->str.len); tmp_sbs.mvtype = MV_STR; tmp_sbs.str.len = TREF(max_lcl_coll_xform_bufsiz); assert(NULL != TREF(lcl_coll_xform_buff)); tmp_sbs.str.addr = TREF(lcl_coll_xform_buff); do_xform(TREF(local_collseq), XFORM, &key->str, &tmp_sbs.str, &length); tmp_sbs.str.len = length; s2pool(&(tmp_sbs.str)); key = &tmp_sbs; } } else { /* Need to set canonical bit before calling tree search functions. * But input mval could be read-only so cannot modify that even if temporarily. * So take a copy of the mval and modify that instead. */ tmp_sbs = *key; key = &tmp_sbs; MV_FORCE_NUM(key); TREE_KEY_SUBSCR_SET_MV_CANONICAL_BIT(key); /* used by the lvAvlTreeKeyPrev function */ } node = lvAvlTreeKeyPrev(lvt, key); } /* If STDNULLCOLL, skip to the previous subscript should the current subscript be "" */ if (TREF(local_collseq_stdnull) && (NULL != node) && LV_NODE_KEY_IS_NULL_SUBS(node)) { assert(LVNULLSUBS_OK == TREF(lv_null_subs)); node = lvAvlTreePrev(node); } } else node = NULL; if (NULL == node) { dst->mvtype = MV_STR; dst->str.len = 0; } else { LV_NODE_GET_KEY(node, dst); /* Get node key into "dst" depending on the structure type of "node" */ /* Code outside lv_tree.c does not currently know to make use of MV_CANONICAL bit so reset it * until the entire codebase gets fixed to maintain MV_CANONICAL bit accurately at which point, * this RESET can be removed */ TREE_KEY_SUBSCR_RESET_MV_CANONICAL_BIT(dst); if (TREF(local_collseq) && MV_IS_STRING(dst)) { ALLOC_XFORM_BUFF(dst->str.len); assert(NULL != TREF(lcl_coll_xform_buff)); tmp_sbs.str.addr = TREF(lcl_coll_xform_buff); tmp_sbs.str.len = TREF(max_lcl_coll_xform_bufsiz); do_xform(TREF(local_collseq), XBACK, &dst->str, &tmp_sbs.str, &length); tmp_sbs.str.len = length; s2pool(&(tmp_sbs.str)); dst->str = tmp_sbs.str; } } return; }
void op_fnorder(lv_val *src, mval *key, mval *dst) { mval tmp_sbs; int length; boolean_t is_canonical, is_fnnext, get_first; lvTree *lvt; lvTreeNode *node; uint4 mvt; /* Local copy of mvtype, bit ands use a int4, so do conversion once */ mstr *str; int4 intval; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; is_fnnext = TREF(in_op_fnnext); TREF(in_op_fnnext) = FALSE; if (src && (lvt = LV_GET_CHILD(src))) /* caution: assignment */ { MV_FORCE_DEFINED(key); /* If last subscript is null, $ORDER returns the first subscript in that level. * With the obsoleted $NEXT function, a subscript of -1 also triggers the same behavior. */ get_first = FALSE; if (MV_IS_STRING(key) && (0 == key->str.len)) get_first = TRUE; else if (is_fnnext) { mvt = key->mvtype; if (!(mvt & (MV_NM | MV_NUM_APPROX))) { /* Not currently in numeric form. Is it cannonical? */ if (val_iscan(key)) { /* Yes, convert it to numeric */ (void)s2n(key); mvt = key->mvtype; if (!(mvt & MV_NM)) rts_error(VARLSTCNT(1) ERR_NUMOFLOW); } else /* No, not numeric. Note the fact for future reference */ mvt = key->mvtype |= MV_NUM_APPROX; } if (MV_IS_TRUEINT(key, &intval) && (MINUS_ONE == key->m[1])) get_first = TRUE; } if (get_first) node = lvAvlTreeFirst(lvt); else { is_canonical = MV_IS_CANONICAL(key); if (!is_canonical) { assert(!TREE_KEY_SUBSCR_IS_CANONICAL(key->mvtype)); if (TREF(local_collseq)) { ALLOC_XFORM_BUFF(key->str.len); tmp_sbs.mvtype = MV_STR; tmp_sbs.str.len = TREF(max_lcl_coll_xform_bufsiz); assert(NULL != TREF(lcl_coll_xform_buff)); tmp_sbs.str.addr = TREF(lcl_coll_xform_buff); do_xform(TREF(local_collseq), XFORM, &key->str, &tmp_sbs.str, &length); tmp_sbs.str.len = length; s2pool(&(tmp_sbs.str)); key = &tmp_sbs; } } else { /* Need to set canonical bit before calling tree search functions. * But input mval could be read-only so cannot modify that even if temporarily. * So take a copy of the mval and modify that instead. */ tmp_sbs = *key; key = &tmp_sbs; MV_FORCE_NUM(key); TREE_KEY_SUBSCR_SET_MV_CANONICAL_BIT(key); /* used by the lvTreeKeyNext function */ } node = lvAvlTreeKeyNext(lvt, key); } /* If STDNULLCOLL, skip to the next subscript should the current subscript be "" */ if (TREF(local_collseq_stdnull) && (NULL != node) && LV_NODE_KEY_IS_NULL_SUBS(node)) { assert(LVNULLSUBS_OK == TREF(lv_null_subs)); node = lvAvlTreeNext(node); } } else node = NULL; if (NULL == node) { if (!is_fnnext) { dst->mvtype = MV_STR; dst->str.len = 0; } else MV_FORCE_MVAL(dst, -1); } else { LV_NODE_GET_KEY(node, dst); /* Get node key into "dst" depending on the structure type of "node" */ /* Code outside lv_tree.c does not currently know to make use of MV_CANONICAL bit so reset it * until the entire codebase gets fixed to maintain MV_CANONICAL bit accurately at which point, * this RESET can be removed */ TREE_KEY_SUBSCR_RESET_MV_CANONICAL_BIT(dst); if (TREF(local_collseq) && MV_IS_STRING(dst)) { ALLOC_XFORM_BUFF(dst->str.len); assert(NULL != TREF(lcl_coll_xform_buff)); tmp_sbs.str.addr = TREF(lcl_coll_xform_buff); tmp_sbs.str.len = TREF(max_lcl_coll_xform_bufsiz); do_xform(TREF(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_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; } }