/*
|
* Licensed to the Apache Software Foundation (ASF) under one
|
* or more contributor license agreements. See the NOTICE file
|
* distributed with this work for additional information
|
* regarding copyright ownership. The ASF licenses this file
|
* to you under the Apache License, Version 2.0 (the
|
* "License"); you may not use this file except in compliance
|
* with the License. You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing,
|
* software distributed under the License is distributed on an
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* KIND, either express or implied. See the License for the
|
* specific language governing permissions and limitations
|
* under the License.
|
*/
|
|
/*
|
* Functions for operators in Cypher expressions.
|
*/
|
|
#include "postgres.h"
|
|
#include <math.h>
|
#include <limits.h>
|
|
#include "utils/agtype.h"
|
#include "utils/builtins.h"
|
#include "pg_fix.h"
|
|
static agtype *agtype_concat_impl(agtype *agt1, agtype *agt2);
|
static agtype_value *iterator_concat(agtype_iterator **it1,
|
agtype_iterator **it2,
|
agtype_parse_state **state);
|
static void concat_to_agtype_string(agtype_value *result, char *lhs, int llen,
|
char *rhs, int rlen);
|
static char *get_string_from_agtype_value(agtype_value *agtv, int *length);
|
static Datum get_agtype_path_all(FunctionCallInfo fcinfo, bool as_text);
|
static agtype *delete_from_object(agtype *agt, char *keyptr, int keylen);
|
static agtype *delete_from_array(agtype *agt, agtype* indexes);
|
|
static void concat_to_agtype_string(agtype_value *result, char *lhs, int llen,
|
char *rhs, int rlen)
|
{
|
int length = llen + rlen;
|
char *buffer = result->val.string.val;
|
|
Assert(llen >= 0 && rlen >= 0);
|
check_string_length(length);
|
buffer = palloc(length);
|
|
strncpy(buffer, lhs, llen);
|
strncpy(buffer + llen, rhs, rlen);
|
|
result->type = AGTV_STRING;
|
result->val.string.len = length;
|
result->val.string.val = buffer;
|
}
|
|
static char *get_string_from_agtype_value(agtype_value *agtv, int *length)
|
{
|
Datum number;
|
char *string;
|
|
switch (agtv->type)
|
{
|
case AGTV_INTEGER:
|
number = DirectFunctionCall1(int8out,
|
Int8GetDatum(agtv->val.int_value));
|
string = DatumGetCString(number);
|
*length = strlen(string);
|
return string;
|
case AGTV_FLOAT:
|
number = DirectFunctionCall1(float8out,
|
Float8GetDatum(agtv->val.float_value));
|
string = DatumGetCString(number);
|
*length = strlen(string);
|
|
if (is_decimal_needed(string))
|
{
|
char *str = palloc(*length + 2);
|
strncpy(str, string, *length);
|
strncpy(str + *length, ".0", 2);
|
*length += 2;
|
string = str;
|
}
|
return string;
|
case AGTV_STRING:
|
*length = agtv->val.string.len;
|
return agtv->val.string.val;
|
|
case AGTV_NUMERIC:
|
string = DatumGetCString(DirectFunctionCall1(numeric_out,
|
PointerGetDatum(agtv->val.numeric)));
|
*length = strlen(string);
|
return string;
|
|
case AGTV_NULL:
|
case AGTV_BOOL:
|
case AGTV_ARRAY:
|
case AGTV_OBJECT:
|
case AGTV_BINARY:
|
default:
|
*length = 0;
|
return NULL;
|
}
|
return NULL;
|
}
|
|
Datum get_numeric_datum_from_agtype_value(agtype_value *agtv)
|
{
|
switch (agtv->type)
|
{
|
case AGTV_INTEGER:
|
return DirectFunctionCall1(int8_numeric,
|
Int8GetDatum(agtv->val.int_value));
|
case AGTV_FLOAT:
|
return DirectFunctionCall1(float8_numeric,
|
Float8GetDatum(agtv->val.float_value));
|
case AGTV_NUMERIC:
|
return NumericGetDatum(agtv->val.numeric);
|
|
default:
|
break;
|
}
|
|
return 0;
|
}
|
|
bool is_numeric_result(agtype_value *lhs, agtype_value *rhs)
|
{
|
if (((lhs->type == AGTV_NUMERIC || rhs->type == AGTV_NUMERIC) &&
|
(lhs->type == AGTV_INTEGER || lhs->type == AGTV_FLOAT ||
|
rhs->type == AGTV_INTEGER || rhs->type == AGTV_FLOAT )) ||
|
(lhs->type == AGTV_NUMERIC && rhs->type == AGTV_NUMERIC))
|
{
|
return true;
|
}
|
return false;
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_add);
|
|
/* agtype addition and concat function for + operator */
|
Datum agtype_add(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs = AG_GET_ARG_AGTYPE_P(0);
|
agtype *rhs = AG_GET_ARG_AGTYPE_P(1);
|
agtype_value *agtv_lhs;
|
agtype_value *agtv_rhs;
|
agtype_value agtv_result;
|
|
/* If both are not scalars */
|
if (!(AGT_ROOT_IS_SCALAR(lhs) && AGT_ROOT_IS_SCALAR(rhs)))
|
{
|
Datum agt = AGTYPE_P_GET_DATUM(agtype_concat_impl(lhs, rhs));
|
|
PG_RETURN_DATUM(agt);
|
}
|
|
/* Both are scalar */
|
agtv_lhs = get_ith_agtype_value_from_container(&lhs->root, 0);
|
agtv_rhs = get_ith_agtype_value_from_container(&rhs->root, 0);
|
|
/*
|
* One or both values is a string OR one is a string and the other is
|
* either an integer, float, or numeric. If so, concatenate them.
|
*/
|
if ((agtv_lhs->type == AGTV_STRING || agtv_rhs->type == AGTV_STRING) &&
|
(agtv_lhs->type == AGTV_INTEGER || agtv_lhs->type == AGTV_FLOAT ||
|
agtv_lhs->type == AGTV_NUMERIC || agtv_lhs->type == AGTV_STRING ||
|
agtv_rhs->type == AGTV_INTEGER || agtv_rhs->type == AGTV_FLOAT ||
|
agtv_rhs->type == AGTV_NUMERIC || agtv_rhs->type == AGTV_STRING))
|
{
|
int llen = 0;
|
char *lhs = get_string_from_agtype_value(agtv_lhs, &llen);
|
int rlen = 0;
|
char *rhs = get_string_from_agtype_value(agtv_rhs, &rlen);
|
|
concat_to_agtype_string(&agtv_result, lhs, llen, rhs, rlen);
|
}
|
/* Both are integers - regular addition */
|
else if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_INTEGER)
|
{
|
agtv_result.type = AGTV_INTEGER;
|
agtv_result.val.int_value = agtv_lhs->val.int_value +
|
agtv_rhs->val.int_value;
|
}
|
/* Both are floats - regular addition */
|
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_FLOAT)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = agtv_lhs->val.float_value +
|
agtv_rhs->val.float_value;
|
}
|
/* The left is a float, the right is an integer - regular addition */
|
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_INTEGER)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = agtv_lhs->val.float_value +
|
agtv_rhs->val.int_value;
|
}
|
/* The right is a float, the left is an integer - regular addition */
|
else if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_FLOAT)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = agtv_lhs->val.int_value +
|
agtv_rhs->val.float_value;
|
}
|
/* Is this a numeric result */
|
else if (is_numeric_result(agtv_lhs, agtv_rhs))
|
{
|
Datum numd, lhsd, rhsd;
|
|
lhsd = get_numeric_datum_from_agtype_value(agtv_lhs);
|
rhsd = get_numeric_datum_from_agtype_value(agtv_rhs);
|
numd = DirectFunctionCall2(numeric_add, lhsd, rhsd);
|
|
agtv_result.type = AGTV_NUMERIC;
|
agtv_result.val.numeric = DatumGetNumeric(numd);
|
}
|
/* if both operands are scalar(vertex/edge/path), concat the two */
|
else if (AGT_ROOT_IS_SCALAR(lhs) && AGT_ROOT_IS_SCALAR(rhs))
|
{
|
Datum agt = AGTYPE_P_GET_DATUM(agtype_concat_impl(lhs, rhs));
|
|
PG_RETURN_DATUM(agt);
|
}
|
else
|
{
|
/* Not a covered case, error out */
|
ereport(ERROR,
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("Invalid input parameter types for agtype_add")));
|
}
|
|
AG_RETURN_AGTYPE_P(agtype_value_to_agtype(&agtv_result));
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_any_add);
|
|
/* agtype addition between bigint and agtype */
|
Datum agtype_any_add(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs;
|
agtype *rhs;
|
Datum result;
|
|
lhs = get_one_agtype_from_variadic_args(fcinfo, 0, 2);
|
rhs = get_one_agtype_from_variadic_args(fcinfo, 1, 1);
|
|
if (lhs == NULL || rhs == NULL)
|
{
|
PG_RETURN_NULL();
|
}
|
|
result = DirectFunctionCall2(agtype_add, AGTYPE_P_GET_DATUM(lhs),
|
AGTYPE_P_GET_DATUM(rhs));
|
|
AG_RETURN_AGTYPE_P(DATUM_GET_AGTYPE_P(result));
|
}
|
|
/*
|
* For the given indexes array, delete elements at those indexes
|
* from the passed in agtype array.
|
*/
|
static agtype *delete_from_array(agtype *agt, agtype *indexes)
|
{
|
agtype_parse_state *state = NULL;
|
agtype_iterator *it, *it_indexes = NULL;
|
uint32 i = 0, n;
|
agtype_value v, *res = NULL;
|
agtype_iterator_token r;
|
|
if (!AGT_ROOT_IS_ARRAY(agt) || AGT_ROOT_IS_SCALAR(agt))
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("cannot delete from scalar or object"
|
"using integer index")));
|
}
|
|
// array is empty, pass the original array
|
if (AGT_ROOT_COUNT(agt) == 0)
|
{
|
return agt;
|
}
|
|
// start buidiling the result agtype array
|
it = agtype_iterator_init(&agt->root);
|
|
r = agtype_iterator_next(&it, &v, false);
|
Assert(r == WAGT_BEGIN_ARRAY);
|
|
n = v.val.array.num_elems;
|
|
push_agtype_value(&state, r, NULL);
|
|
while ((r = agtype_iterator_next(&it, &v, true)) != WAGT_DONE)
|
{
|
if (r == WAGT_ELEM)
|
{
|
/*
|
* use logic similar to agtype_contains to check
|
* if the current index (itself or in inverted form)
|
* is contained in the indexes array,
|
* if yes, skip the element at that index in agt array
|
* else add the element in result agtype array
|
*/
|
agtype_value cur_idx, neg_idx;
|
agtype *cur_idx_agt, *neg_idx_agt;
|
agtype_iterator *it_cur_idx, *it_neg_idx;
|
bool contains_idx, contains_neg_idx;
|
|
cur_idx.type = AGTV_INTEGER;
|
cur_idx.val.int_value = i++;
|
cur_idx_agt = agtype_value_to_agtype(&cur_idx);
|
|
neg_idx.type = AGTV_INTEGER;
|
neg_idx.val.int_value = cur_idx.val.int_value - n;
|
neg_idx_agt = agtype_value_to_agtype(&neg_idx);
|
|
it_cur_idx = agtype_iterator_init(&cur_idx_agt->root);
|
it_neg_idx = agtype_iterator_init(&neg_idx_agt->root);
|
|
it_indexes = agtype_iterator_init(&indexes->root);
|
contains_idx = agtype_deep_contains(&it_indexes, &it_cur_idx);
|
|
// re-initialize indexes array iterator
|
it_indexes = agtype_iterator_init(&indexes->root);
|
contains_neg_idx = agtype_deep_contains(&it_indexes, &it_neg_idx);
|
|
if (contains_idx || contains_neg_idx)
|
{
|
continue;
|
}
|
}
|
|
res = push_agtype_value(&state, r, r < WAGT_BEGIN_ARRAY ? &v : NULL);
|
}
|
|
Assert(res != NULL);
|
|
return agtype_value_to_agtype(res);
|
}
|
|
/*
|
* For the given key delete that property from the passed in agtype
|
* object.
|
*/
|
static agtype *delete_from_object(agtype *agt, char *keyptr, int keylen)
|
{
|
agtype_parse_state *state = NULL;
|
agtype_iterator *it;
|
agtype_value v, *res = NULL;
|
bool skipNested = false;
|
agtype_iterator_token r;
|
|
if (!AGT_ROOT_IS_OBJECT(agt))
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("cannot delete from scalar or array"
|
"using string key")));
|
}
|
|
if (AGT_ROOT_COUNT(agt) == 0)
|
{
|
return agt;
|
}
|
|
it = agtype_iterator_init(&agt->root);
|
|
while ((r = agtype_iterator_next(&it, &v, skipNested)) != WAGT_DONE)
|
{
|
skipNested = true;
|
|
/*
|
* Checks the key to compare against the passed in key to be
|
* deleted. do not add the key and value to the new agtype being
|
* constructed.
|
*/
|
if ((r == WAGT_ELEM || r == WAGT_KEY) &&
|
(v.type == AGTV_STRING && keylen == v.val.string.len &&
|
memcmp(keyptr, v.val.string.val, keylen) == 0))
|
{
|
/* skip corresponding value as well */
|
if (r == WAGT_KEY)
|
{
|
(void) agtype_iterator_next(&it, &v, true);
|
}
|
|
continue;
|
}
|
|
res = push_agtype_value(&state, r, r < WAGT_BEGIN_ARRAY ? &v : NULL);
|
}
|
|
Assert(res != NULL);
|
|
return agtype_value_to_agtype(res);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_sub);
|
|
/*
|
* agtype subtraction function for - operator
|
*/
|
Datum agtype_sub(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs = AG_GET_ARG_AGTYPE_P(0);
|
agtype *rhs = AG_GET_ARG_AGTYPE_P(1);
|
agtype_value *agtv_lhs;
|
agtype_value *agtv_rhs;
|
agtype_value agtv_result;
|
|
/*
|
* Logic to handle when the rhs is a non scalar array. In this
|
* case;
|
* 1. if the lhs is an object, the values in the rhs array
|
* are string keys to be removed from the object.
|
* 2. if the lhs is an array, the values in the rhs array
|
* are integer indexes at which values should be removed from array.
|
* otherwise throw an error
|
*/
|
if (AGT_ROOT_IS_ARRAY(rhs) && !AGT_ROOT_IS_SCALAR(rhs))
|
{
|
agtype_iterator *it = NULL;
|
agtype_value elem;
|
|
if (AGT_ROOT_IS_OBJECT(lhs))
|
{
|
/*
|
* if rhs array contains any non-string element, error out
|
* else delete the given keys in the rhs array from lhs object
|
*/
|
while ((it = get_next_list_element(it, &rhs->root, &elem)))
|
{
|
if (elem.type == AGTV_STRING)
|
{
|
lhs = delete_from_object(lhs, elem.val.string.val,
|
elem.val.string.len);
|
}
|
else
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("expected agtype string, not agtype %s",
|
agtype_value_type_to_string(elem.type))));
|
}
|
}
|
}
|
else if (AGT_ROOT_IS_ARRAY(lhs) && !(AGT_ROOT_IS_SCALAR(lhs)))
|
{
|
/*
|
* if rhs array contains any non-integer element, error out
|
* else delete the values at the given indexes in rhs array
|
* from the lhs array
|
*/
|
while ((it = get_next_list_element(it, &rhs->root, &elem)))
|
{
|
if (elem.type != AGTV_INTEGER)
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("expected agtype integer, not agtype %s",
|
agtype_value_type_to_string(elem.type))));
|
}
|
}
|
|
lhs = delete_from_array(lhs, rhs);
|
}
|
else
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("must be object or array, not a scalar value")));
|
}
|
|
AG_RETURN_AGTYPE_P(lhs);
|
}
|
|
/*
|
* When the lhs is an object and rhs is a string, remove the key from
|
* the object.
|
* When the lhs is an array and the rhs is an integer then
|
* remove the value at that index from the array,
|
* otherwise give an error
|
*/
|
if(!AGT_ROOT_IS_SCALAR(lhs))
|
{
|
agtype_value *key;
|
key = get_ith_agtype_value_from_container(&rhs->root, 0);
|
|
if (AGT_ROOT_IS_OBJECT(lhs) && key->type == AGTV_STRING)
|
{
|
AG_RETURN_AGTYPE_P(delete_from_object(lhs, key->val.string.val,
|
key->val.string.len));
|
}
|
else if (AGT_ROOT_IS_ARRAY(lhs) && key->type == AGTV_INTEGER)
|
{
|
AG_RETURN_AGTYPE_P(delete_from_array(lhs, rhs));
|
}
|
else
|
{
|
if (AGT_ROOT_IS_OBJECT(lhs))
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("expected agtype string, not agtype %s",
|
agtype_value_type_to_string(key->type))));
|
}
|
else if (AGT_ROOT_IS_ARRAY(lhs))
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("expected agtype integer, not agtype %s",
|
agtype_value_type_to_string(key->type))));
|
}
|
}
|
}
|
|
agtv_lhs = get_ith_agtype_value_from_container(&lhs->root, 0);
|
agtv_rhs = get_ith_agtype_value_from_container(&rhs->root, 0);
|
|
if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_INTEGER)
|
{
|
agtv_result.type = AGTV_INTEGER;
|
agtv_result.val.int_value = agtv_lhs->val.int_value -
|
agtv_rhs->val.int_value;
|
}
|
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_FLOAT)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = agtv_lhs->val.float_value -
|
agtv_rhs->val.float_value;
|
}
|
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_INTEGER)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = agtv_lhs->val.float_value -
|
agtv_rhs->val.int_value;
|
}
|
else if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_FLOAT)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = agtv_lhs->val.int_value -
|
agtv_rhs->val.float_value;
|
}
|
/* Is this a numeric result */
|
else if (is_numeric_result(agtv_lhs, agtv_rhs))
|
{
|
Datum numd, lhsd, rhsd;
|
|
lhsd = get_numeric_datum_from_agtype_value(agtv_lhs);
|
rhsd = get_numeric_datum_from_agtype_value(agtv_rhs);
|
numd = DirectFunctionCall2(numeric_sub, lhsd, rhsd);
|
|
agtv_result.type = AGTV_NUMERIC;
|
agtv_result.val.numeric = DatumGetNumeric(numd);
|
}
|
else
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("Invalid input parameter types for agtype_sub")));
|
}
|
|
AG_RETURN_AGTYPE_P(agtype_value_to_agtype(&agtv_result));
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_any_sub);
|
|
/* agtype subtraction between bigint and agtype */
|
Datum agtype_any_sub(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs;
|
agtype *rhs;
|
Datum result;
|
|
lhs = get_one_agtype_from_variadic_args(fcinfo, 0, 2);
|
rhs = get_one_agtype_from_variadic_args(fcinfo, 1, 1);
|
|
if (lhs == NULL || rhs == NULL)
|
{
|
PG_RETURN_NULL();
|
}
|
|
result = DirectFunctionCall2(agtype_sub, AGTYPE_P_GET_DATUM(lhs),
|
AGTYPE_P_GET_DATUM(rhs));
|
|
AG_RETURN_AGTYPE_P(DATUM_GET_AGTYPE_P(result));
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_neg);
|
|
/*
|
* agtype negation function for unary - operator
|
*/
|
Datum agtype_neg(PG_FUNCTION_ARGS)
|
{
|
agtype *v = AG_GET_ARG_AGTYPE_P(0);
|
agtype_value *agtv_value;
|
agtype_value agtv_result;
|
|
if (!(AGT_ROOT_IS_SCALAR(v)))
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("must be scalar value, not array or object")));
|
|
PG_RETURN_NULL();
|
}
|
|
agtv_value = get_ith_agtype_value_from_container(&v->root, 0);
|
|
if (agtv_value->type == AGTV_INTEGER)
|
{
|
agtv_result.type = AGTV_INTEGER;
|
agtv_result.val.int_value = -agtv_value->val.int_value;
|
}
|
else if (agtv_value->type == AGTV_FLOAT)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = -agtv_value->val.float_value;
|
}
|
else if (agtv_value->type == AGTV_NUMERIC)
|
{
|
Datum numd, vald;
|
|
vald = NumericGetDatum(agtv_value->val.numeric);
|
numd = DirectFunctionCall1(numeric_uminus, vald);
|
|
agtv_result.type = AGTV_NUMERIC;
|
agtv_result.val.numeric = DatumGetNumeric(numd);
|
}
|
else
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("Invalid input parameter type for agtype_neg")));
|
|
AG_RETURN_AGTYPE_P(agtype_value_to_agtype(&agtv_result));
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_mul);
|
|
/*
|
* agtype multiplication function for * operator
|
*/
|
Datum agtype_mul(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs = AG_GET_ARG_AGTYPE_P(0);
|
agtype *rhs = AG_GET_ARG_AGTYPE_P(1);
|
agtype_value *agtv_lhs;
|
agtype_value *agtv_rhs;
|
agtype_value agtv_result;
|
|
if (!(AGT_ROOT_IS_SCALAR(lhs)) || !(AGT_ROOT_IS_SCALAR(rhs)))
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("must be scalar value, not array or object")));
|
|
PG_RETURN_NULL();
|
}
|
|
agtv_lhs = get_ith_agtype_value_from_container(&lhs->root, 0);
|
agtv_rhs = get_ith_agtype_value_from_container(&rhs->root, 0);
|
|
if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_INTEGER)
|
{
|
agtv_result.type = AGTV_INTEGER;
|
agtv_result.val.int_value = agtv_lhs->val.int_value *
|
agtv_rhs->val.int_value;
|
}
|
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_FLOAT)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = agtv_lhs->val.float_value *
|
agtv_rhs->val.float_value;
|
}
|
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_INTEGER)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = agtv_lhs->val.float_value *
|
agtv_rhs->val.int_value;
|
}
|
else if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_FLOAT)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = agtv_lhs->val.int_value *
|
agtv_rhs->val.float_value;
|
}
|
/* Is this a numeric result */
|
else if (is_numeric_result(agtv_lhs, agtv_rhs))
|
{
|
Datum numd, lhsd, rhsd;
|
|
lhsd = get_numeric_datum_from_agtype_value(agtv_lhs);
|
rhsd = get_numeric_datum_from_agtype_value(agtv_rhs);
|
numd = DirectFunctionCall2(numeric_mul, lhsd, rhsd);
|
|
agtv_result.type = AGTV_NUMERIC;
|
agtv_result.val.numeric = DatumGetNumeric(numd);
|
}
|
else
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("Invalid input parameter types for agtype_mul")));
|
|
AG_RETURN_AGTYPE_P(agtype_value_to_agtype(&agtv_result));
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_any_mul);
|
|
/* agtype multiplication between bigint and agtype */
|
Datum agtype_any_mul(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs;
|
agtype *rhs;
|
Datum result;
|
|
lhs = get_one_agtype_from_variadic_args(fcinfo, 0, 2);
|
rhs = get_one_agtype_from_variadic_args(fcinfo, 1, 1);
|
|
if (lhs == NULL || rhs == NULL)
|
{
|
PG_RETURN_NULL();
|
}
|
|
result = DirectFunctionCall2(agtype_mul, AGTYPE_P_GET_DATUM(lhs),
|
AGTYPE_P_GET_DATUM(rhs));
|
|
AG_RETURN_AGTYPE_P(DATUM_GET_AGTYPE_P(result));
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_div);
|
|
/*
|
* agtype division function for / operator
|
*/
|
Datum agtype_div(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs = AG_GET_ARG_AGTYPE_P(0);
|
agtype *rhs = AG_GET_ARG_AGTYPE_P(1);
|
agtype_value *agtv_lhs;
|
agtype_value *agtv_rhs;
|
agtype_value agtv_result;
|
|
if (!(AGT_ROOT_IS_SCALAR(lhs)) || !(AGT_ROOT_IS_SCALAR(rhs)))
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("must be scalar value, not array or object")));
|
|
PG_RETURN_NULL();
|
}
|
|
agtv_lhs = get_ith_agtype_value_from_container(&lhs->root, 0);
|
agtv_rhs = get_ith_agtype_value_from_container(&rhs->root, 0);
|
|
if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_INTEGER)
|
{
|
if (agtv_rhs->val.int_value == 0)
|
{
|
ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO),
|
errmsg("division by zero")));
|
PG_RETURN_NULL();
|
}
|
|
agtv_result.type = AGTV_INTEGER;
|
agtv_result.val.int_value = agtv_lhs->val.int_value /
|
agtv_rhs->val.int_value;
|
}
|
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_FLOAT)
|
{
|
if (agtv_rhs->val.float_value == 0)
|
{
|
ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO),
|
errmsg("division by zero")));
|
PG_RETURN_NULL();
|
}
|
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = agtv_lhs->val.float_value /
|
agtv_rhs->val.float_value;
|
}
|
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_INTEGER)
|
{
|
if (agtv_rhs->val.int_value == 0)
|
{
|
ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO),
|
errmsg("division by zero")));
|
PG_RETURN_NULL();
|
}
|
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = agtv_lhs->val.float_value /
|
agtv_rhs->val.int_value;
|
}
|
else if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_FLOAT)
|
{
|
if (agtv_rhs->val.float_value == 0)
|
{
|
ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO),
|
errmsg("division by zero")));
|
PG_RETURN_NULL();
|
}
|
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = agtv_lhs->val.int_value /
|
agtv_rhs->val.float_value;
|
}
|
/* Is this a numeric result */
|
else if (is_numeric_result(agtv_lhs, agtv_rhs))
|
{
|
Datum numd, lhsd, rhsd;
|
|
lhsd = get_numeric_datum_from_agtype_value(agtv_lhs);
|
rhsd = get_numeric_datum_from_agtype_value(agtv_rhs);
|
numd = DirectFunctionCall2(numeric_div, lhsd, rhsd);
|
|
agtv_result.type = AGTV_NUMERIC;
|
agtv_result.val.numeric = DatumGetNumeric(numd);
|
}
|
else
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("Invalid input parameter types for agtype_div")));
|
|
AG_RETURN_AGTYPE_P(agtype_value_to_agtype(&agtv_result));
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_any_div);
|
|
/* agtype division between bigint and agtype */
|
Datum agtype_any_div(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs;
|
agtype *rhs;
|
Datum result;
|
|
lhs = get_one_agtype_from_variadic_args(fcinfo, 0, 2);
|
rhs = get_one_agtype_from_variadic_args(fcinfo, 1, 1);
|
|
if (lhs == NULL || rhs == NULL)
|
{
|
PG_RETURN_NULL();
|
}
|
|
result = DirectFunctionCall2(agtype_div, AGTYPE_P_GET_DATUM(lhs),
|
AGTYPE_P_GET_DATUM(rhs));
|
|
AG_RETURN_AGTYPE_P(DATUM_GET_AGTYPE_P(result));
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_mod);
|
|
/*
|
* agtype modulo function for % operator
|
*/
|
Datum agtype_mod(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs = AG_GET_ARG_AGTYPE_P(0);
|
agtype *rhs = AG_GET_ARG_AGTYPE_P(1);
|
agtype_value *agtv_lhs;
|
agtype_value *agtv_rhs;
|
agtype_value agtv_result;
|
|
if (!(AGT_ROOT_IS_SCALAR(lhs)) || !(AGT_ROOT_IS_SCALAR(rhs)))
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("must be scalar value, not array or object")));
|
|
PG_RETURN_NULL();
|
}
|
|
agtv_lhs = get_ith_agtype_value_from_container(&lhs->root, 0);
|
agtv_rhs = get_ith_agtype_value_from_container(&rhs->root, 0);
|
|
if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_INTEGER)
|
{
|
agtv_result.type = AGTV_INTEGER;
|
agtv_result.val.int_value = agtv_lhs->val.int_value %
|
agtv_rhs->val.int_value;
|
}
|
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_FLOAT)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = fmod(agtv_lhs->val.float_value,
|
agtv_rhs->val.float_value);
|
}
|
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_INTEGER)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = fmod(agtv_lhs->val.float_value,
|
agtv_rhs->val.int_value);
|
}
|
else if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_FLOAT)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = fmod(agtv_lhs->val.int_value,
|
agtv_rhs->val.float_value);
|
}
|
/* Is this a numeric result */
|
else if (is_numeric_result(agtv_lhs, agtv_rhs))
|
{
|
Datum numd, lhsd, rhsd;
|
|
lhsd = get_numeric_datum_from_agtype_value(agtv_lhs);
|
rhsd = get_numeric_datum_from_agtype_value(agtv_rhs);
|
numd = DirectFunctionCall2(numeric_mod, lhsd, rhsd);
|
|
agtv_result.type = AGTV_NUMERIC;
|
agtv_result.val.numeric = DatumGetNumeric(numd);
|
}
|
else
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("Invalid input parameter types for agtype_mod")));
|
|
AG_RETURN_AGTYPE_P(agtype_value_to_agtype(&agtv_result));
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_any_mod);
|
|
/* agtype modulo between bigint and agtype */
|
Datum agtype_any_mod(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs;
|
agtype *rhs;
|
Datum result;
|
|
lhs = get_one_agtype_from_variadic_args(fcinfo, 0, 2);
|
rhs = get_one_agtype_from_variadic_args(fcinfo, 1, 1);
|
|
if (lhs == NULL || rhs == NULL)
|
{
|
PG_RETURN_NULL();
|
}
|
|
result = DirectFunctionCall2(agtype_mod, AGTYPE_P_GET_DATUM(lhs),
|
AGTYPE_P_GET_DATUM(rhs));
|
|
AG_RETURN_AGTYPE_P(DATUM_GET_AGTYPE_P(result));
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_pow);
|
|
/*
|
* agtype power function for ^ operator
|
*/
|
Datum agtype_pow(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs = AG_GET_ARG_AGTYPE_P(0);
|
agtype *rhs = AG_GET_ARG_AGTYPE_P(1);
|
agtype_value *agtv_lhs;
|
agtype_value *agtv_rhs;
|
agtype_value agtv_result;
|
|
if (!(AGT_ROOT_IS_SCALAR(lhs)) || !(AGT_ROOT_IS_SCALAR(rhs)))
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("must be scalar value, not array or object")));
|
|
PG_RETURN_NULL();
|
}
|
|
agtv_lhs = get_ith_agtype_value_from_container(&lhs->root, 0);
|
agtv_rhs = get_ith_agtype_value_from_container(&rhs->root, 0);
|
|
if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_INTEGER)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = pow(agtv_lhs->val.int_value,
|
agtv_rhs->val.int_value);
|
}
|
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_FLOAT)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = pow(agtv_lhs->val.float_value,
|
agtv_rhs->val.float_value);
|
}
|
else if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_INTEGER)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = pow(agtv_lhs->val.float_value,
|
agtv_rhs->val.int_value);
|
}
|
else if (agtv_lhs->type == AGTV_INTEGER && agtv_rhs->type == AGTV_FLOAT)
|
{
|
agtv_result.type = AGTV_FLOAT;
|
agtv_result.val.float_value = pow(agtv_lhs->val.int_value,
|
agtv_rhs->val.float_value);
|
}
|
/* Is this a numeric result */
|
else if (is_numeric_result(agtv_lhs, agtv_rhs))
|
{
|
Datum numd, lhsd, rhsd;
|
|
lhsd = get_numeric_datum_from_agtype_value(agtv_lhs);
|
rhsd = get_numeric_datum_from_agtype_value(agtv_rhs);
|
numd = DirectFunctionCall2(numeric_power, lhsd, rhsd);
|
|
agtv_result.type = AGTV_NUMERIC;
|
agtv_result.val.numeric = DatumGetNumeric(numd);
|
}
|
else
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("Invalid input parameter types for agtype_pow")));
|
|
AG_RETURN_AGTYPE_P(agtype_value_to_agtype(&agtv_result));
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_eq);
|
|
Datum agtype_eq(PG_FUNCTION_ARGS)
|
{
|
agtype *agtype_lhs = AG_GET_ARG_AGTYPE_P(0);
|
agtype *agtype_rhs = AG_GET_ARG_AGTYPE_P(1);
|
bool result;
|
|
result = (compare_agtype_containers_orderability(&agtype_lhs->root,
|
&agtype_rhs->root) == 0);
|
|
PG_FREE_IF_COPY(agtype_lhs, 0);
|
PG_FREE_IF_COPY(agtype_rhs, 1);
|
|
PG_RETURN_BOOL(result);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_any_eq);
|
|
Datum agtype_any_eq(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs;
|
agtype *rhs;
|
Datum result;
|
|
lhs = get_one_agtype_from_variadic_args(fcinfo, 0, 2);
|
rhs = get_one_agtype_from_variadic_args(fcinfo, 1, 1);
|
|
if (lhs == NULL || rhs == NULL)
|
{
|
PG_RETURN_NULL();
|
}
|
|
result = DirectFunctionCall2(agtype_eq, AGTYPE_P_GET_DATUM(lhs),
|
AGTYPE_P_GET_DATUM(rhs));
|
|
PG_RETURN_BOOL(result);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_ne);
|
|
Datum agtype_ne(PG_FUNCTION_ARGS)
|
{
|
agtype *agtype_lhs = AG_GET_ARG_AGTYPE_P(0);
|
agtype *agtype_rhs = AG_GET_ARG_AGTYPE_P(1);
|
bool result = true;
|
|
result = (compare_agtype_containers_orderability(&agtype_lhs->root,
|
&agtype_rhs->root) != 0);
|
|
PG_FREE_IF_COPY(agtype_lhs, 0);
|
PG_FREE_IF_COPY(agtype_rhs, 1);
|
|
PG_RETURN_BOOL(result);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_any_ne);
|
|
Datum agtype_any_ne(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs;
|
agtype *rhs;
|
Datum result;
|
|
lhs = get_one_agtype_from_variadic_args(fcinfo, 0, 2);
|
rhs = get_one_agtype_from_variadic_args(fcinfo, 1, 1);
|
|
if (lhs == NULL || rhs == NULL)
|
{
|
PG_RETURN_NULL();
|
}
|
|
result = DirectFunctionCall2(agtype_ne, AGTYPE_P_GET_DATUM(lhs),
|
AGTYPE_P_GET_DATUM(rhs));
|
|
PG_RETURN_BOOL(result);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_lt);
|
|
Datum agtype_lt(PG_FUNCTION_ARGS)
|
{
|
agtype *agtype_lhs = AG_GET_ARG_AGTYPE_P(0);
|
agtype *agtype_rhs = AG_GET_ARG_AGTYPE_P(1);
|
bool result;
|
|
result = (compare_agtype_containers_orderability(&agtype_lhs->root,
|
&agtype_rhs->root) < 0);
|
|
PG_FREE_IF_COPY(agtype_lhs, 0);
|
PG_FREE_IF_COPY(agtype_rhs, 1);
|
|
PG_RETURN_BOOL(result);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_any_lt);
|
|
Datum agtype_any_lt(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs;
|
agtype *rhs;
|
Datum result;
|
|
lhs = get_one_agtype_from_variadic_args(fcinfo, 0, 2);
|
rhs = get_one_agtype_from_variadic_args(fcinfo, 1, 1);
|
|
if (lhs == NULL || rhs == NULL)
|
{
|
PG_RETURN_NULL();
|
}
|
|
result = DirectFunctionCall2(agtype_lt, AGTYPE_P_GET_DATUM(lhs),
|
AGTYPE_P_GET_DATUM(rhs));
|
|
PG_RETURN_BOOL(result);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_gt);
|
|
Datum agtype_gt(PG_FUNCTION_ARGS)
|
{
|
agtype *agtype_lhs = AG_GET_ARG_AGTYPE_P(0);
|
agtype *agtype_rhs = AG_GET_ARG_AGTYPE_P(1);
|
bool result;
|
|
result = (compare_agtype_containers_orderability(&agtype_lhs->root,
|
&agtype_rhs->root) > 0);
|
|
PG_FREE_IF_COPY(agtype_lhs, 0);
|
PG_FREE_IF_COPY(agtype_rhs, 1);
|
|
PG_RETURN_BOOL(result);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_any_gt);
|
|
Datum agtype_any_gt(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs;
|
agtype *rhs;
|
Datum result;
|
|
lhs = get_one_agtype_from_variadic_args(fcinfo, 0, 2);
|
rhs = get_one_agtype_from_variadic_args(fcinfo, 1, 1);
|
|
if (lhs == NULL || rhs == NULL)
|
{
|
PG_RETURN_NULL();
|
}
|
|
result = DirectFunctionCall2(agtype_gt, AGTYPE_P_GET_DATUM(lhs),
|
AGTYPE_P_GET_DATUM(rhs));
|
|
PG_RETURN_BOOL(result);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_le);
|
|
Datum agtype_le(PG_FUNCTION_ARGS)
|
{
|
agtype *agtype_lhs = AG_GET_ARG_AGTYPE_P(0);
|
agtype *agtype_rhs = AG_GET_ARG_AGTYPE_P(1);
|
bool result;
|
|
result = (compare_agtype_containers_orderability(&agtype_lhs->root,
|
&agtype_rhs->root) <= 0);
|
|
PG_FREE_IF_COPY(agtype_lhs, 0);
|
PG_FREE_IF_COPY(agtype_rhs, 1);
|
|
PG_RETURN_BOOL(result);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_any_le);
|
|
Datum agtype_any_le(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs;
|
agtype *rhs;
|
Datum result;
|
|
lhs = get_one_agtype_from_variadic_args(fcinfo, 0, 2);
|
rhs = get_one_agtype_from_variadic_args(fcinfo, 1, 1);
|
|
if (lhs == NULL || rhs == NULL)
|
{
|
PG_RETURN_NULL();
|
}
|
|
result = DirectFunctionCall2(agtype_le, AGTYPE_P_GET_DATUM(lhs),
|
AGTYPE_P_GET_DATUM(rhs));
|
|
PG_RETURN_BOOL(result);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_ge);
|
|
Datum agtype_ge(PG_FUNCTION_ARGS)
|
{
|
agtype *agtype_lhs = AG_GET_ARG_AGTYPE_P(0);
|
agtype *agtype_rhs = AG_GET_ARG_AGTYPE_P(1);
|
bool result;
|
|
result = (compare_agtype_containers_orderability(&agtype_lhs->root,
|
&agtype_rhs->root) >= 0);
|
|
PG_FREE_IF_COPY(agtype_lhs, 0);
|
PG_FREE_IF_COPY(agtype_rhs, 1);
|
|
PG_RETURN_BOOL(result);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_any_ge);
|
|
Datum agtype_any_ge(PG_FUNCTION_ARGS)
|
{
|
agtype *lhs;
|
agtype *rhs;
|
Datum result;
|
|
lhs = get_one_agtype_from_variadic_args(fcinfo, 0, 2);
|
rhs = get_one_agtype_from_variadic_args(fcinfo, 1, 1);
|
|
if (lhs == NULL || rhs == NULL)
|
{
|
PG_RETURN_NULL();
|
}
|
|
result = DirectFunctionCall2(agtype_ge, AGTYPE_P_GET_DATUM(lhs),
|
AGTYPE_P_GET_DATUM(rhs));
|
|
PG_RETURN_BOOL(result);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_exists_agtype);
|
/*
|
* ? operator for agtype. Returns true if the string exists as top-level keys
|
*/
|
Datum agtype_exists_agtype(PG_FUNCTION_ARGS)
|
{
|
agtype *agt = AG_GET_ARG_AGTYPE_P(0);
|
agtype *key = AG_GET_ARG_AGTYPE_P(1);
|
agtype_value *aval;
|
agtype_value *v = NULL;
|
|
if (AGT_ROOT_IS_SCALAR(agt))
|
{
|
agt = agtype_value_to_agtype(extract_entity_properties(agt, false));
|
}
|
|
if (AGT_ROOT_IS_SCALAR(key))
|
{
|
aval = get_ith_agtype_value_from_container(&key->root, 0);
|
}
|
else
|
{
|
PG_RETURN_BOOL(false);
|
}
|
|
if (AGT_ROOT_IS_OBJECT(agt) &&
|
aval->type == AGTV_STRING)
|
{
|
v = find_agtype_value_from_container(&agt->root,
|
AGT_FOBJECT,
|
aval);
|
}
|
else if (AGT_ROOT_IS_ARRAY(agt) &&
|
aval->type != AGTV_NULL)
|
{
|
v = find_agtype_value_from_container(&agt->root,
|
AGT_FARRAY,
|
aval);
|
}
|
|
PG_RETURN_BOOL(v != NULL);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_exists_any_agtype);
|
/*
|
* ?| operator for agtype. Returns true if any of the array strings exist as
|
* top-level keys
|
*/
|
Datum agtype_exists_any_agtype(PG_FUNCTION_ARGS)
|
{
|
agtype *agt = AG_GET_ARG_AGTYPE_P(0);
|
agtype *keys = AG_GET_ARG_AGTYPE_P(1);
|
agtype_value elem;
|
agtype_iterator *it = NULL;
|
|
if (AGT_ROOT_IS_SCALAR(agt))
|
{
|
agt = agtype_value_to_agtype(extract_entity_properties(agt, true));
|
}
|
|
if (!AGT_ROOT_IS_SCALAR(keys) && !AGT_ROOT_IS_OBJECT(keys))
|
{
|
while ((it = get_next_list_element(it, &keys->root, &elem)))
|
{
|
if (IS_A_AGTYPE_SCALAR(&elem))
|
{
|
if (AGT_ROOT_IS_OBJECT(agt) &&
|
(&elem)->type == AGTV_STRING &&
|
find_agtype_value_from_container(&agt->root,
|
AGT_FOBJECT,
|
&elem))
|
{
|
PG_RETURN_BOOL(true);
|
}
|
else if (AGT_ROOT_IS_ARRAY(agt) &&
|
(&elem)->type != AGTV_NULL &&
|
find_agtype_value_from_container(&agt->root,
|
AGT_FARRAY,
|
&elem))
|
{
|
PG_RETURN_BOOL(true);
|
}
|
}
|
else
|
{
|
PG_RETURN_BOOL(false);
|
}
|
}
|
}
|
else
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("invalid agtype value for right operand")));
|
}
|
|
PG_RETURN_BOOL(false);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_exists_all_agtype);
|
/*
|
* ?& operator for agtype. Returns true if all of the array strings exist as
|
* top-level keys
|
*/
|
Datum agtype_exists_all_agtype(PG_FUNCTION_ARGS)
|
{
|
agtype *agt = AG_GET_ARG_AGTYPE_P(0);
|
agtype *keys = AG_GET_ARG_AGTYPE_P(1);
|
agtype_value elem;
|
agtype_iterator *it = NULL;
|
|
if (AGT_ROOT_IS_SCALAR(agt))
|
{
|
agt = agtype_value_to_agtype(extract_entity_properties(agt, true));
|
}
|
|
if (!AGT_ROOT_IS_SCALAR(keys) && !AGT_ROOT_IS_OBJECT(keys))
|
{
|
while ((it = get_next_list_element(it, &keys->root, &elem)))
|
{
|
if (IS_A_AGTYPE_SCALAR(&elem))
|
{
|
if ((&elem)->type == AGTV_NULL)
|
{
|
continue;
|
}
|
else if (AGT_ROOT_IS_OBJECT(agt) &&
|
(&elem)->type == AGTV_STRING &&
|
find_agtype_value_from_container(&agt->root,
|
AGT_FOBJECT,
|
&elem))
|
{
|
continue;
|
}
|
else if (AGT_ROOT_IS_ARRAY(agt) &&
|
find_agtype_value_from_container(&agt->root,
|
AGT_FARRAY,
|
&elem))
|
{
|
continue;
|
}
|
else
|
{
|
PG_RETURN_BOOL(false);
|
}
|
}
|
else
|
{
|
PG_RETURN_BOOL(false);
|
}
|
}
|
}
|
else
|
{
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("invalid agtype value for right operand")));
|
}
|
|
PG_RETURN_BOOL(true);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_contains);
|
/*
|
* @> operator for agtype. Returns true if the right agtype path/value entries
|
* contained at the top level within the left agtype value
|
*/
|
Datum agtype_contains(PG_FUNCTION_ARGS)
|
{
|
agtype_iterator *constraint_it = NULL;
|
agtype_iterator *property_it = NULL;
|
agtype *properties = NULL;
|
agtype *constraints = NULL;
|
|
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
|
{
|
PG_RETURN_BOOL(false);
|
}
|
|
properties = AG_GET_ARG_AGTYPE_P(0);
|
constraints = AG_GET_ARG_AGTYPE_P(1);
|
|
if (AGT_ROOT_IS_SCALAR(properties)
|
&& AGTE_IS_AGTYPE(properties->root.children[0]))
|
{
|
properties =
|
agtype_value_to_agtype(extract_entity_properties(properties,
|
false));
|
}
|
|
if (AGT_ROOT_IS_SCALAR(constraints)
|
&& AGTE_IS_AGTYPE(constraints->root.children[0]))
|
{
|
constraints =
|
agtype_value_to_agtype(extract_entity_properties(constraints,
|
false));
|
}
|
|
if (AGT_ROOT_IS_OBJECT(properties) != AGT_ROOT_IS_OBJECT(constraints))
|
{
|
PG_RETURN_BOOL(false);
|
}
|
|
property_it = agtype_iterator_init(&properties->root);
|
constraint_it = agtype_iterator_init(&constraints->root);
|
|
PG_RETURN_BOOL(agtype_deep_contains(&property_it, &constraint_it));
|
}
|
|
|
PG_FUNCTION_INFO_V1(agtype_contained_by);
|
/*
|
* <@ operator for agtype. Returns true if the left agtype path/value entries
|
* contained at the top level within the right agtype value
|
*/
|
Datum agtype_contained_by(PG_FUNCTION_ARGS)
|
{
|
agtype_iterator *constraint_it, *property_it;
|
agtype *properties, *constraints;
|
|
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
|
{
|
PG_RETURN_BOOL(false);
|
}
|
|
properties = AG_GET_ARG_AGTYPE_P(0);
|
constraints = AG_GET_ARG_AGTYPE_P(1);
|
|
if (AGT_ROOT_IS_SCALAR(properties)
|
&& AGTE_IS_AGTYPE(properties->root.children[0]))
|
{
|
properties =
|
agtype_value_to_agtype(extract_entity_properties(properties,
|
false));
|
}
|
|
if (AGT_ROOT_IS_SCALAR(constraints)
|
&& AGTE_IS_AGTYPE(constraints->root.children[0]))
|
{
|
constraints =
|
agtype_value_to_agtype(extract_entity_properties(constraints,
|
false));
|
}
|
|
constraint_it = agtype_iterator_init(&constraints->root);
|
property_it = agtype_iterator_init(&properties->root);
|
|
PG_RETURN_BOOL(agtype_deep_contains(&constraint_it, &property_it));
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_exists);
|
/*
|
* ? operator for agtype. Returns true if the string exists as top-level keys
|
*/
|
Datum agtype_exists(PG_FUNCTION_ARGS)
|
{
|
agtype *agt = AG_GET_ARG_AGTYPE_P(0);
|
text *key = PG_GETARG_TEXT_PP(1);
|
agtype_value aval;
|
agtype_value *v = NULL;
|
|
/*
|
* We only match Object keys (which are naturally always Strings), or
|
* string elements in arrays. In particular, we do not match non-string
|
* scalar elements. Existence of a key/element is only considered at the
|
* top level. No recursion occurs.
|
*/
|
aval.type = AGTV_STRING;
|
aval.val.string.val = VARDATA_ANY(key);
|
aval.val.string.len = VARSIZE_ANY_EXHDR(key);
|
|
v = find_agtype_value_from_container(&agt->root,
|
AGT_FOBJECT | AGT_FARRAY,
|
&aval);
|
|
PG_RETURN_BOOL(v != NULL);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_exists_any);
|
/*
|
* ?| operator for agtype. Returns true if any of the array strings exist as
|
* top-level keys
|
*/
|
Datum agtype_exists_any(PG_FUNCTION_ARGS)
|
{
|
agtype *agt = AG_GET_ARG_AGTYPE_P(0);
|
ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
|
int i;
|
Datum *key_datums;
|
bool *key_nulls;
|
int elem_count;
|
|
deconstruct_array(keys, TEXTOID, -1, false, 'i', &key_datums, &key_nulls,
|
&elem_count);
|
|
for (i = 0; i < elem_count; i++)
|
{
|
agtype_value strVal;
|
|
if (key_nulls[i])
|
{
|
continue;
|
}
|
|
strVal.type = AGTV_STRING;
|
strVal.val.string.val = VARDATA(key_datums[i]);
|
strVal.val.string.len = VARSIZE(key_datums[i]) - VARHDRSZ;
|
|
if (find_agtype_value_from_container(&agt->root,
|
AGT_FOBJECT | AGT_FARRAY,
|
&strVal) != NULL)
|
{
|
PG_RETURN_BOOL(true);
|
}
|
}
|
|
PG_RETURN_BOOL(false);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_exists_all);
|
/*
|
* ?& operator for agtype. Returns true if all of the array strings exist as
|
* top-level keys
|
*/
|
Datum agtype_exists_all(PG_FUNCTION_ARGS)
|
{
|
agtype *agt = AG_GET_ARG_AGTYPE_P(0);
|
ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
|
int i;
|
Datum *key_datums;
|
bool *key_nulls;
|
int elem_count;
|
|
deconstruct_array(keys, TEXTOID, -1, false, 'i', &key_datums, &key_nulls,
|
&elem_count);
|
|
for (i = 0; i < elem_count; i++)
|
{
|
agtype_value strVal;
|
|
if (key_nulls[i])
|
{
|
continue;
|
}
|
|
strVal.type = AGTV_STRING;
|
strVal.val.string.val = VARDATA(key_datums[i]);
|
strVal.val.string.len = VARSIZE(key_datums[i]) - VARHDRSZ;
|
|
if (find_agtype_value_from_container(&agt->root,
|
AGT_FOBJECT | AGT_FARRAY,
|
&strVal) == NULL)
|
{
|
PG_RETURN_BOOL(false);
|
}
|
}
|
|
PG_RETURN_BOOL(true);
|
}
|
|
PG_FUNCTION_INFO_V1(agtype_concat);
|
|
Datum agtype_concat(PG_FUNCTION_ARGS)
|
{
|
agtype *agt_lhs = AG_GET_ARG_AGTYPE_P(0);
|
agtype *agt_rhs = AG_GET_ARG_AGTYPE_P(1);
|
|
/*
|
* Jsonb returns NULL for PG Null, but not for jsonb's NULL value,
|
* so we do the same.
|
*/
|
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
|
{
|
PG_RETURN_NULL();
|
}
|
|
AG_RETURN_AGTYPE_P(agtype_concat_impl(agt_lhs, agt_rhs));
|
}
|
|
static agtype *agtype_concat_impl(agtype *agt1, agtype *agt2)
|
{
|
agtype_parse_state *state = NULL;
|
agtype_value *res;
|
agtype_iterator *it1;
|
agtype_iterator *it2;
|
|
/*
|
* If one of the agtype is empty, just return the other if it's not scalar
|
* and both are of the same kind. If it's a scalar or they are of
|
* different kinds we need to perform the concatenation even if one is
|
* empty.
|
*/
|
if (AGT_ROOT_IS_OBJECT(agt1) == AGT_ROOT_IS_OBJECT(agt2))
|
{
|
if (AGT_ROOT_COUNT(agt1) == 0 && !AGT_ROOT_IS_SCALAR(agt2))
|
{
|
return agt2;
|
}
|
else if (AGT_ROOT_COUNT(agt2) == 0 && !AGT_ROOT_IS_SCALAR(agt1))
|
{
|
return agt1;
|
}
|
}
|
|
it1 = agtype_iterator_init(&agt1->root);
|
it2 = agtype_iterator_init(&agt2->root);
|
|
res = iterator_concat(&it1, &it2, &state);
|
|
Assert(res != NULL);
|
|
return (agtype_value_to_agtype(res));
|
}
|
|
/*
|
* Iterate over all agtype objects and merge them into one.
|
* The logic of this function copied from the same hstore function,
|
* except the case, when it1 & it2 represents jbvObject.
|
* In that case we just append the content of it2 to it1 without any
|
* verifications.
|
*/
|
static agtype_value *iterator_concat(agtype_iterator **it1,
|
agtype_iterator **it2,
|
agtype_parse_state **state)
|
{
|
agtype_value v1, v2, *res = NULL;
|
agtype_iterator_token r1, r2, rk1, rk2;
|
|
r1 = rk1 = agtype_iterator_next(it1, &v1, false);
|
r2 = rk2 = agtype_iterator_next(it2, &v2, false);
|
|
/*
|
* Both elements are objects.
|
*/
|
if (rk1 == WAGT_BEGIN_OBJECT && rk2 == WAGT_BEGIN_OBJECT)
|
{
|
/*
|
* Append all tokens from v1 to res, except last WAGT_END_OBJECT
|
* (because res will not be finished yet).
|
*/
|
push_agtype_value(state, r1, NULL);
|
|
while ((r1 = agtype_iterator_next(it1, &v1, true)) != WAGT_END_OBJECT)
|
{
|
Assert(r1 == WAGT_KEY || r1 == WAGT_VALUE);
|
push_agtype_value(state, r1, &v1);
|
}
|
|
/*
|
* Append all tokens from v2 to res, except last WAGT_END_OBJECT
|
*/
|
while ((r2 = agtype_iterator_next(it2, &v2, true)) != WAGT_END_OBJECT)
|
{
|
Assert(r2 == WAGT_KEY || r2 == WAGT_VALUE);
|
push_agtype_value(state, r2, &v2);
|
}
|
|
/*
|
* Append the last token WAGT_END_OBJECT to complete res
|
*/
|
res = push_agtype_value(state, WAGT_END_OBJECT, NULL);
|
}
|
/*
|
* Both elements are arrays (either can be scalar).
|
*/
|
else if (rk1 == WAGT_BEGIN_ARRAY && rk2 == WAGT_BEGIN_ARRAY)
|
{
|
push_agtype_value(state, r1, NULL);
|
|
while ((r1 = agtype_iterator_next(it1, &v1, true)) != WAGT_END_ARRAY)
|
{
|
Assert(r1 == WAGT_ELEM);
|
push_agtype_value(state, r1, &v1);
|
}
|
|
while ((r2 = agtype_iterator_next(it2, &v2, true)) != WAGT_END_ARRAY)
|
{
|
Assert(r2 == WAGT_ELEM);
|
push_agtype_value(state, r2, &v2);
|
}
|
|
res = push_agtype_value(state, WAGT_END_ARRAY, NULL);
|
}
|
/* have we got array || object or object || array? */
|
else if (((rk1 == WAGT_BEGIN_ARRAY && !(*it1)->is_scalar) &&
|
rk2 == WAGT_BEGIN_OBJECT) ||
|
(rk1 == WAGT_BEGIN_OBJECT &&
|
(rk2 == WAGT_BEGIN_ARRAY && !(*it2)->is_scalar)))
|
{
|
agtype_iterator **it_array = rk1 == WAGT_BEGIN_ARRAY ? it1 : it2;
|
agtype_iterator **it_object = rk1 == WAGT_BEGIN_OBJECT ? it1 : it2;
|
|
bool prepend = (rk1 == WAGT_BEGIN_OBJECT);
|
|
push_agtype_value(state, WAGT_BEGIN_ARRAY, NULL);
|
|
if (prepend)
|
{
|
push_agtype_value(state, WAGT_BEGIN_OBJECT, NULL);
|
|
while ((r1 = agtype_iterator_next(it_object, &v1, true)) !=
|
WAGT_END_OBJECT)
|
{
|
Assert(r1 == WAGT_KEY || r1 == WAGT_VALUE);
|
push_agtype_value(state, r1, &v1);
|
}
|
|
push_agtype_value(state, WAGT_END_OBJECT, NULL);
|
|
while ((r2 = agtype_iterator_next(it_array, &v2, true)) !=
|
WAGT_END_ARRAY)
|
{
|
Assert(r2 == WAGT_ELEM);
|
push_agtype_value(state, r2, &v2);
|
}
|
|
res = push_agtype_value(state, WAGT_END_ARRAY, NULL);
|
}
|
else
|
{
|
while ((r1 = agtype_iterator_next(it_array, &v1, true)) !=
|
WAGT_END_ARRAY)
|
{
|
Assert(r1 == WAGT_ELEM);
|
push_agtype_value(state, r1, &v1);
|
}
|
|
push_agtype_value(state, WAGT_BEGIN_OBJECT, NULL);
|
|
while ((r2 = agtype_iterator_next(it_object, &v2, true)) !=
|
WAGT_END_OBJECT)
|
{
|
Assert(r2 == WAGT_KEY || r2 == WAGT_VALUE);
|
push_agtype_value(state, r2,&v2);
|
}
|
|
push_agtype_value(state, WAGT_END_OBJECT, NULL);
|
|
res = push_agtype_value(state, WAGT_END_ARRAY, NULL);
|
}
|
}
|
else if (rk1 == WAGT_BEGIN_OBJECT)
|
{
|
/*
|
* We have object || array.
|
*/
|
Assert(rk1 == WAGT_BEGIN_OBJECT);
|
Assert(rk2 == WAGT_BEGIN_ARRAY);
|
|
push_agtype_value(state, WAGT_BEGIN_ARRAY, NULL);
|
push_agtype_value(state, WAGT_BEGIN_OBJECT, NULL);
|
|
while ((r1 = agtype_iterator_next(it1, &v1, true)) != WAGT_END_OBJECT)
|
{
|
Assert(r1 == WAGT_KEY || r1 == WAGT_VALUE);
|
push_agtype_value(state, r1, &v1);
|
}
|
|
push_agtype_value(state, WAGT_END_OBJECT, NULL);
|
|
while ((r2 = agtype_iterator_next(it2, &v2, true)) != WAGT_END_ARRAY)
|
{
|
if (v2.type < AGTV_VERTEX || v2.type > AGTV_PATH)
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("invalid right operand for agtype "
|
"concatenation")));
|
}
|
|
Assert(r2 == WAGT_ELEM);
|
|
push_agtype_value(state, r2, &v2);
|
}
|
|
res = push_agtype_value(state, WAGT_END_ARRAY, NULL);
|
}
|
else
|
{
|
/*
|
* We have array || object.
|
*/
|
Assert(rk1 == WAGT_BEGIN_ARRAY);
|
Assert(rk2 == WAGT_BEGIN_OBJECT);
|
|
push_agtype_value(state, WAGT_BEGIN_ARRAY, NULL);
|
|
while ((r1 = agtype_iterator_next(it1, &v1, true)) != WAGT_END_ARRAY)
|
{
|
if (v1.type < AGTV_VERTEX || v1.type > AGTV_PATH)
|
{
|
ereport(ERROR,
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
errmsg("invalid left operand for agtype "
|
"concatenation")));
|
}
|
|
Assert(r1 == WAGT_ELEM);
|
|
push_agtype_value(state, r1, &v1);
|
}
|
|
push_agtype_value(state, WAGT_BEGIN_OBJECT, NULL);
|
|
while ((r2 = agtype_iterator_next(it2, &v2, true)) != WAGT_END_OBJECT)
|
{
|
Assert(r2 == WAGT_KEY || r2 == WAGT_VALUE);
|
push_agtype_value(state, r2, &v2);
|
}
|
|
push_agtype_value(state, WAGT_END_OBJECT, NULL);
|
|
res = push_agtype_value(state, WAGT_END_ARRAY, NULL);
|
}
|
|
return res;
|
}
|
|
/*
|
* agtype path extraction operator '#>'. The right operand can
|
* either be an array of object keys or array indexes for extracting
|
* agtype sub-object or sub-array from the left operand.
|
*/
|
PG_FUNCTION_INFO_V1(agtype_extract_path);
|
|
Datum agtype_extract_path(PG_FUNCTION_ARGS)
|
{
|
return get_agtype_path_all(fcinfo, false);
|
}
|
|
/*
|
* agtype path extraction operator '#>>' that returns the extracted path
|
* as text.
|
*/
|
PG_FUNCTION_INFO_V1(agtype_extract_path_text);
|
|
Datum agtype_extract_path_text(PG_FUNCTION_ARGS)
|
{
|
return get_agtype_path_all(fcinfo, true);
|
}
|
|
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);
|
}
|
}
|