in src/backend/utils/adt/agtype_ops.c [2000:2206]
static Datum get_agtype_path_all(FunctionCallInfo fcinfo, bool as_text)
{
agtype *agt = AG_GET_ARG_AGTYPE_P(0);
agtype *path = AG_GET_ARG_AGTYPE_P(1);
agtype *res;
int npath;
int i;
bool have_object = false, have_array = false;
agtype_value *agtvp = NULL;
agtype_value tv;
agtype_container *container;
if (AGT_ROOT_IS_SCALAR(path) || AGT_ROOT_IS_OBJECT(path))
{
ereport(ERROR,(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("right operand must be an array")));
}
if (AGT_ROOT_IS_SCALAR(agt))
{
agt = agtype_value_to_agtype(extract_entity_properties(agt, true));
}
npath = AGT_ROOT_COUNT(path);
container = &agt->root;
/* Identify whether we have object, array, or scalar at top-level */
if (AGT_ROOT_IS_OBJECT(agt))
{
have_object = true;
}
else if (AGT_ROOT_IS_ARRAY(agt) && !AGT_ROOT_IS_SCALAR(agt))
{
have_array = true;
}
else
{
Assert(AGT_ROOT_IS_ARRAY(agt) && AGT_ROOT_IS_SCALAR(agt));
/* Extract the scalar value */
if (npath <= 0)
{
agtvp = get_ith_agtype_value_from_container(container, 0);
}
}
/*
* If RHS array is empty, return the entire LHS object/array, based on the
* assumption that we should not do any field or element extractions. In
* case of non-scalar, we can just hand back the agtype without much
* work but for the scalar case, fall through and deal with the value
* below the loop (This inconsistency arises because there's no easy way to
* generate an agtype_value directly for root-level containers)
*/
if (npath <= 0 && agtvp == NULL)
{
if (as_text)
{
PG_RETURN_TEXT_P(cstring_to_text(agtype_to_cstring(NULL, container,
VARSIZE(agt))));
}
else
{
/* not text mode - just hand back the agtype */
AG_RETURN_AGTYPE_P(agt);
}
}
for (i = 0; i < npath; i++)
{
agtype_value *cur_key =
get_ith_agtype_value_from_container(&path->root, i);
if (have_object && cur_key->type == AGTV_STRING)
{
agtvp = find_agtype_value_from_container(container,
AGT_FOBJECT,
cur_key);
}
else if (have_array)
{
long lindex;
uint32 index;
/*
* for array on LHS, there should be an integer or a
* valid integer string on RHS
*/
if (cur_key->type == AGTV_INTEGER)
{
lindex = cur_key->val.int_value;
}
else if (cur_key->type == AGTV_STRING)
{
/*
* extract the integer from the string,
* if character other than a digit is found, return null
*/
char* str = NULL;
lindex = strtol(cur_key->val.string.val, &str, 10);
if (strcmp(str, ""))
{
PG_RETURN_NULL();
}
}
else
{
PG_RETURN_NULL();
}
if (lindex > INT_MAX || lindex < INT_MIN)
{
PG_RETURN_NULL();
}
if (lindex >= 0)
{
index = (uint32) lindex;
}
else
{
/* Handle negative subscript */
uint32 nelements;
/* Container must be an array, but make sure */
if (!AGTYPE_CONTAINER_IS_ARRAY(container))
{
elog(ERROR, "not an agtype array");
}
nelements = AGTYPE_CONTAINER_SIZE(container);
if (-lindex > nelements)
{
PG_RETURN_NULL();
}
else
{
index = nelements + lindex;
}
}
agtvp = get_ith_agtype_value_from_container(container, index);
}
else
{
PG_RETURN_NULL();
}
if (agtvp == NULL)
{
PG_RETURN_NULL();
}
else if (i == npath - 1)
{
break;
}
if (agtvp->type == AGTV_BINARY)
{
agtype_iterator_token r;
agtype_iterator *it =
agtype_iterator_init((agtype_container *)
agtvp->val.binary.data);
r = agtype_iterator_next(&it, &tv, true);
container = (agtype_container *) agtvp->val.binary.data;
have_object = r == WAGT_BEGIN_OBJECT;
have_array = r == WAGT_BEGIN_ARRAY;
}
else
{
have_object = agtvp->type == AGTV_OBJECT;
have_array = agtvp->type == AGTV_ARRAY;
}
}
if (as_text)
{
/* special-case output for string and null values */
if (agtvp->type == AGTV_STRING)
{
PG_RETURN_TEXT_P(cstring_to_text_with_len(agtvp->val.string.val,
agtvp->val.string.len));
}
if (agtvp->type == AGTV_NULL)
{
PG_RETURN_NULL();
}
}
res = agtype_value_to_agtype(agtvp);
if (as_text)
{
PG_RETURN_TEXT_P(cstring_to_text(agtype_to_cstring(NULL,
&res->root,
VARSIZE(res))));
}
else
{
/* not text mode - just hand back the agtype */
AG_RETURN_AGTYPE_P(res);
}
}