agtype_iterator_token agtype_iterator_next()

in src/backend/utils/adt/agtype_util.c [1022:1167]


agtype_iterator_token agtype_iterator_next(agtype_iterator **it,
                                           agtype_value *val, bool skip_nested)
{
    if (*it == NULL)
        return WAGT_DONE;

    /*
     * When stepping into a nested container, we jump back here to start
     * processing the child. We will not recurse further in one call, because
     * processing the child will always begin in AGTI_ARRAY_START or
     * AGTI_OBJECT_START state.
     */
recurse:
    switch ((*it)->state)
    {
    case AGTI_ARRAY_START:
        /* Set v to array on first array call */
        val->type = AGTV_ARRAY;
        val->val.array.num_elems = (*it)->num_elems;

        /*
         * v->val.array.elems is not actually set, because we aren't doing
         * a full conversion
         */
        val->val.array.raw_scalar = (*it)->is_scalar;
        (*it)->curr_index = 0;
        (*it)->curr_data_offset = 0;
        (*it)->curr_value_offset = 0; /* not actually used */
        /* Set state for next call */
        (*it)->state = AGTI_ARRAY_ELEM;
        return WAGT_BEGIN_ARRAY;

    case AGTI_ARRAY_ELEM:
        if ((*it)->curr_index >= (*it)->num_elems)
        {
            /*
             * All elements within array already processed.  Report this
             * to caller, and give it back original parent iterator (which
             * independently tracks iteration progress at its level of
             * nesting).
             */
            *it = free_and_get_parent(*it);
            return WAGT_END_ARRAY;
        }

        fill_agtype_value((*it)->container, (*it)->curr_index,
                          (*it)->data_proper, (*it)->curr_data_offset, val);

        AGTE_ADVANCE_OFFSET((*it)->curr_data_offset,
                            (*it)->children[(*it)->curr_index]);
        (*it)->curr_index++;

        if (!IS_A_AGTYPE_SCALAR(val) && !skip_nested)
        {
            /* Recurse into container. */
            *it = iterator_from_container(val->val.binary.data, *it);
            goto recurse;
        }
        else
        {
            /*
             * Scalar item in array, or a container and caller didn't want
             * us to recurse into it.
             */
            return WAGT_ELEM;
        }

    case AGTI_OBJECT_START:
        /* Set v to object on first object call */
        val->type = AGTV_OBJECT;
        val->val.object.num_pairs = (*it)->num_elems;

        /*
         * v->val.object.pairs is not actually set, because we aren't
         * doing a full conversion
         */
        (*it)->curr_index = 0;
        (*it)->curr_data_offset = 0;
        (*it)->curr_value_offset = get_agtype_offset((*it)->container,
                                                     (*it)->num_elems);
        /* Set state for next call */
        (*it)->state = AGTI_OBJECT_KEY;
        return WAGT_BEGIN_OBJECT;

    case AGTI_OBJECT_KEY:
        if ((*it)->curr_index >= (*it)->num_elems)
        {
            /*
             * All pairs within object already processed.  Report this to
             * caller, and give it back original containing iterator
             * (which independently tracks iteration progress at its level
             * of nesting).
             */
            *it = free_and_get_parent(*it);
            return WAGT_END_OBJECT;
        }
        else
        {
            /* Return key of a key/value pair.  */
            fill_agtype_value((*it)->container, (*it)->curr_index,
                              (*it)->data_proper, (*it)->curr_data_offset,
                              val);
            if (val->type != AGTV_STRING)
                ereport(ERROR,
                        (errmsg("unexpected agtype type as object key %d",
                                val->type)));

            /* Set state for next call */
            (*it)->state = AGTI_OBJECT_VALUE;
            return WAGT_KEY;
        }

    case AGTI_OBJECT_VALUE:
        /* Set state for next call */
        (*it)->state = AGTI_OBJECT_KEY;

        fill_agtype_value((*it)->container,
                          (*it)->curr_index + (*it)->num_elems,
                          (*it)->data_proper, (*it)->curr_value_offset, val);

        AGTE_ADVANCE_OFFSET((*it)->curr_data_offset,
                            (*it)->children[(*it)->curr_index]);
        AGTE_ADVANCE_OFFSET(
            (*it)->curr_value_offset,
            (*it)->children[(*it)->curr_index + (*it)->num_elems]);
        (*it)->curr_index++;

        /*
         * Value may be a container, in which case we recurse with new,
         * child iterator (unless the caller asked not to, by passing
         * skip_nested).
         */
        if (!IS_A_AGTYPE_SCALAR(val) && !skip_nested)
        {
            *it = iterator_from_container(val->val.binary.data, *it);
            goto recurse;
        }
        else
        {
            return WAGT_VALUE;
        }
    }

    ereport(ERROR, (errmsg("invalid iterator state %d", (*it)->state)));
    return -1;
}