static Datum get_agtype_path_all()

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);
    }
}