Numeric error suppression in jsonpath
Add support of numeric error suppression to jsonpath as it's required by standard. This commit doesn't use PG_TRY()/PG_CATCH() in order to implement that. Instead, it provides internal versions of numeric functions used, which support error suppression. Discussion: https://postgr.es/m/fcc6fc6a-b497-f39a-923d-aa34d0c588e8%402ndQuadrant.com Author: Alexander Korotkov, Nikita Glukhov Reviewed-by: Tomas Vondra
This commit is contained in:
parent
72b6460336
commit
16d489b0fe
|
@ -12209,7 +12209,7 @@ table2-mapping
|
||||||
<para>
|
<para>
|
||||||
The <literal>@?</literal> and <literal>@@</literal> operators suppress
|
The <literal>@?</literal> and <literal>@@</literal> operators suppress
|
||||||
errors including: lacking object field or array element, unexpected JSON
|
errors including: lacking object field or array element, unexpected JSON
|
||||||
item type.
|
item type and numeric errors.
|
||||||
This behavior might be helpful while searching over JSON document
|
This behavior might be helpful while searching over JSON document
|
||||||
collections of varying structure.
|
collections of varying structure.
|
||||||
</para>
|
</para>
|
||||||
|
|
|
@ -336,8 +336,19 @@ float8in(PG_FUNCTION_ARGS)
|
||||||
PG_RETURN_FLOAT8(float8in_internal(num, NULL, "double precision", num));
|
PG_RETURN_FLOAT8(float8in_internal(num, NULL, "double precision", num));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Convenience macro: set *have_error flag (if provided) or throw error */
|
||||||
|
#define RETURN_ERROR(throw_error) \
|
||||||
|
do { \
|
||||||
|
if (have_error) { \
|
||||||
|
*have_error = true; \
|
||||||
|
return 0.0; \
|
||||||
|
} else { \
|
||||||
|
throw_error; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* float8in_internal - guts of float8in()
|
* float8in_internal_opt_error - guts of float8in()
|
||||||
*
|
*
|
||||||
* This is exposed for use by functions that want a reasonably
|
* This is exposed for use by functions that want a reasonably
|
||||||
* platform-independent way of inputting doubles. The behavior is
|
* platform-independent way of inputting doubles. The behavior is
|
||||||
|
@ -353,10 +364,14 @@ float8in(PG_FUNCTION_ARGS)
|
||||||
*
|
*
|
||||||
* "num" could validly be declared "const char *", but that results in an
|
* "num" could validly be declared "const char *", but that results in an
|
||||||
* unreasonable amount of extra casting both here and in callers, so we don't.
|
* unreasonable amount of extra casting both here and in callers, so we don't.
|
||||||
|
*
|
||||||
|
* When "*have_error" flag is provided, it's set instead of throwing an
|
||||||
|
* error. This is helpful when caller need to handle errors by itself.
|
||||||
*/
|
*/
|
||||||
double
|
double
|
||||||
float8in_internal(char *num, char **endptr_p,
|
float8in_internal_opt_error(char *num, char **endptr_p,
|
||||||
const char *type_name, const char *orig_string)
|
const char *type_name, const char *orig_string,
|
||||||
|
bool *have_error)
|
||||||
{
|
{
|
||||||
double val;
|
double val;
|
||||||
char *endptr;
|
char *endptr;
|
||||||
|
@ -370,10 +385,10 @@ float8in_internal(char *num, char **endptr_p,
|
||||||
* strtod() on different platforms.
|
* strtod() on different platforms.
|
||||||
*/
|
*/
|
||||||
if (*num == '\0')
|
if (*num == '\0')
|
||||||
ereport(ERROR,
|
RETURN_ERROR(ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||||
errmsg("invalid input syntax for type %s: \"%s\"",
|
errmsg("invalid input syntax for type %s: \"%s\"",
|
||||||
type_name, orig_string)));
|
type_name, orig_string))));
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
val = strtod(num, &endptr);
|
val = strtod(num, &endptr);
|
||||||
|
@ -446,17 +461,19 @@ float8in_internal(char *num, char **endptr_p,
|
||||||
char *errnumber = pstrdup(num);
|
char *errnumber = pstrdup(num);
|
||||||
|
|
||||||
errnumber[endptr - num] = '\0';
|
errnumber[endptr - num] = '\0';
|
||||||
ereport(ERROR,
|
RETURN_ERROR(ereport(ERROR,
|
||||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||||
errmsg("\"%s\" is out of range for type double precision",
|
errmsg("\"%s\" is out of range for "
|
||||||
errnumber)));
|
"type double precision",
|
||||||
|
errnumber))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
RETURN_ERROR(ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||||
errmsg("invalid input syntax for type %s: \"%s\"",
|
errmsg("invalid input syntax for type "
|
||||||
type_name, orig_string)));
|
"%s: \"%s\"",
|
||||||
|
type_name, orig_string))));
|
||||||
}
|
}
|
||||||
#ifdef HAVE_BUGGY_SOLARIS_STRTOD
|
#ifdef HAVE_BUGGY_SOLARIS_STRTOD
|
||||||
else
|
else
|
||||||
|
@ -479,14 +496,27 @@ float8in_internal(char *num, char **endptr_p,
|
||||||
if (endptr_p)
|
if (endptr_p)
|
||||||
*endptr_p = endptr;
|
*endptr_p = endptr;
|
||||||
else if (*endptr != '\0')
|
else if (*endptr != '\0')
|
||||||
ereport(ERROR,
|
RETURN_ERROR(ereport(ERROR,
|
||||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
||||||
errmsg("invalid input syntax for type %s: \"%s\"",
|
errmsg("invalid input syntax for type "
|
||||||
type_name, orig_string)));
|
"%s: \"%s\"",
|
||||||
|
type_name, orig_string))));
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interfact to float8in_internal_opt_error() without "have_error" argument.
|
||||||
|
*/
|
||||||
|
double
|
||||||
|
float8in_internal(char *num, char **endptr_p,
|
||||||
|
const char *type_name, const char *orig_string)
|
||||||
|
{
|
||||||
|
return float8in_internal_opt_error(num, endptr_p, type_name,
|
||||||
|
orig_string, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* float8out - converts float8 number to a string
|
* float8out - converts float8 number to a string
|
||||||
* using a standard output format
|
* using a standard output format
|
||||||
|
|
|
@ -179,6 +179,7 @@ typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
|
||||||
JsonbValue *larg,
|
JsonbValue *larg,
|
||||||
JsonbValue *rarg,
|
JsonbValue *rarg,
|
||||||
void *param);
|
void *param);
|
||||||
|
typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
|
||||||
|
|
||||||
static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
|
static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
|
||||||
Jsonb *json, bool throwErrors, JsonValueList *result);
|
Jsonb *json, bool throwErrors, JsonValueList *result);
|
||||||
|
@ -212,8 +213,8 @@ static JsonPathBool executePredicate(JsonPathExecContext *cxt,
|
||||||
JsonbValue *jb, bool unwrapRightArg,
|
JsonbValue *jb, bool unwrapRightArg,
|
||||||
JsonPathPredicateCallback exec, void *param);
|
JsonPathPredicateCallback exec, void *param);
|
||||||
static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
|
static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
|
||||||
JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
|
JsonPathItem *jsp, JsonbValue *jb,
|
||||||
JsonValueList *found);
|
BinaryArithmFunc func, JsonValueList *found);
|
||||||
static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
|
static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
|
||||||
JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
|
JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
|
||||||
JsonValueList *found);
|
JsonValueList *found);
|
||||||
|
@ -830,23 +831,23 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
||||||
|
|
||||||
case jpiAdd:
|
case jpiAdd:
|
||||||
return executeBinaryArithmExpr(cxt, jsp, jb,
|
return executeBinaryArithmExpr(cxt, jsp, jb,
|
||||||
numeric_add, found);
|
numeric_add_opt_error, found);
|
||||||
|
|
||||||
case jpiSub:
|
case jpiSub:
|
||||||
return executeBinaryArithmExpr(cxt, jsp, jb,
|
return executeBinaryArithmExpr(cxt, jsp, jb,
|
||||||
numeric_sub, found);
|
numeric_sub_opt_error, found);
|
||||||
|
|
||||||
case jpiMul:
|
case jpiMul:
|
||||||
return executeBinaryArithmExpr(cxt, jsp, jb,
|
return executeBinaryArithmExpr(cxt, jsp, jb,
|
||||||
numeric_mul, found);
|
numeric_mul_opt_error, found);
|
||||||
|
|
||||||
case jpiDiv:
|
case jpiDiv:
|
||||||
return executeBinaryArithmExpr(cxt, jsp, jb,
|
return executeBinaryArithmExpr(cxt, jsp, jb,
|
||||||
numeric_div, found);
|
numeric_div_opt_error, found);
|
||||||
|
|
||||||
case jpiMod:
|
case jpiMod:
|
||||||
return executeBinaryArithmExpr(cxt, jsp, jb,
|
return executeBinaryArithmExpr(cxt, jsp, jb,
|
||||||
numeric_mod, found);
|
numeric_mod_opt_error, found);
|
||||||
|
|
||||||
case jpiPlus:
|
case jpiPlus:
|
||||||
return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
|
return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
|
||||||
|
@ -999,12 +1000,22 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
||||||
{
|
{
|
||||||
char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
|
char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
|
||||||
NumericGetDatum(jb->val.numeric)));
|
NumericGetDatum(jb->val.numeric)));
|
||||||
|
bool have_error = false;
|
||||||
|
|
||||||
(void) float8in_internal(tmp,
|
(void) float8in_internal_opt_error(tmp,
|
||||||
NULL,
|
NULL,
|
||||||
"double precision",
|
"double precision",
|
||||||
tmp);
|
tmp,
|
||||||
|
&have_error);
|
||||||
|
|
||||||
|
if (have_error)
|
||||||
|
RETURN_ERROR(ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
|
||||||
|
errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
|
||||||
|
errdetail("jsonpath item method .%s() "
|
||||||
|
"can only be applied to "
|
||||||
|
"a numeric value",
|
||||||
|
jspOperationName(jsp->type)))));
|
||||||
res = jperOk;
|
res = jperOk;
|
||||||
}
|
}
|
||||||
else if (jb->type == jbvString)
|
else if (jb->type == jbvString)
|
||||||
|
@ -1013,13 +1024,15 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
||||||
double val;
|
double val;
|
||||||
char *tmp = pnstrdup(jb->val.string.val,
|
char *tmp = pnstrdup(jb->val.string.val,
|
||||||
jb->val.string.len);
|
jb->val.string.len);
|
||||||
|
bool have_error = false;
|
||||||
|
|
||||||
val = float8in_internal(tmp,
|
val = float8in_internal_opt_error(tmp,
|
||||||
NULL,
|
NULL,
|
||||||
"double precision",
|
"double precision",
|
||||||
tmp);
|
tmp,
|
||||||
|
&have_error);
|
||||||
|
|
||||||
if (isinf(val))
|
if (have_error || isinf(val))
|
||||||
RETURN_ERROR(ereport(ERROR,
|
RETURN_ERROR(ereport(ERROR,
|
||||||
(errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
|
(errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
|
||||||
errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
|
errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
|
||||||
|
@ -1497,7 +1510,7 @@ executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
|
||||||
*/
|
*/
|
||||||
static JsonPathExecResult
|
static JsonPathExecResult
|
||||||
executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
||||||
JsonbValue *jb, PGFunction func,
|
JsonbValue *jb, BinaryArithmFunc func,
|
||||||
JsonValueList *found)
|
JsonValueList *found)
|
||||||
{
|
{
|
||||||
JsonPathExecResult jper;
|
JsonPathExecResult jper;
|
||||||
|
@ -1506,7 +1519,7 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
||||||
JsonValueList rseq = {0};
|
JsonValueList rseq = {0};
|
||||||
JsonbValue *lval;
|
JsonbValue *lval;
|
||||||
JsonbValue *rval;
|
JsonbValue *rval;
|
||||||
Datum res;
|
Numeric res;
|
||||||
|
|
||||||
jspGetLeftArg(jsp, &elem);
|
jspGetLeftArg(jsp, &elem);
|
||||||
|
|
||||||
|
@ -1542,16 +1555,26 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
||||||
"is not a singleton numeric value",
|
"is not a singleton numeric value",
|
||||||
jspOperationName(jsp->type)))));
|
jspOperationName(jsp->type)))));
|
||||||
|
|
||||||
res = DirectFunctionCall2(func,
|
if (jspThrowErrors(cxt))
|
||||||
NumericGetDatum(lval->val.numeric),
|
{
|
||||||
NumericGetDatum(rval->val.numeric));
|
res = func(lval->val.numeric, rval->val.numeric, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
res = func(lval->val.numeric, rval->val.numeric, &error);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return jperError;
|
||||||
|
}
|
||||||
|
|
||||||
if (!jspGetNext(jsp, &elem) && !found)
|
if (!jspGetNext(jsp, &elem) && !found)
|
||||||
return jperOk;
|
return jperOk;
|
||||||
|
|
||||||
lval = palloc(sizeof(*lval));
|
lval = palloc(sizeof(*lval));
|
||||||
lval->type = jbvNumeric;
|
lval->type = jbvNumeric;
|
||||||
lval->val.numeric = DatumGetNumeric(res);
|
lval->val.numeric = res;
|
||||||
|
|
||||||
return executeNextItem(cxt, jsp, &elem, lval, found, false);
|
return executeNextItem(cxt, jsp, &elem, lval, found, false);
|
||||||
}
|
}
|
||||||
|
@ -2108,6 +2131,7 @@ getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
|
||||||
JsonValueList found = {0};
|
JsonValueList found = {0};
|
||||||
JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
|
JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
|
||||||
Datum numeric_index;
|
Datum numeric_index;
|
||||||
|
bool have_error = false;
|
||||||
|
|
||||||
if (jperIsError(res))
|
if (jperIsError(res))
|
||||||
return res;
|
return res;
|
||||||
|
@ -2124,7 +2148,15 @@ getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
|
||||||
NumericGetDatum(jbv->val.numeric),
|
NumericGetDatum(jbv->val.numeric),
|
||||||
Int32GetDatum(0));
|
Int32GetDatum(0));
|
||||||
|
|
||||||
*index = DatumGetInt32(DirectFunctionCall1(numeric_int4, numeric_index));
|
*index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
|
||||||
|
&have_error);
|
||||||
|
|
||||||
|
if (have_error)
|
||||||
|
RETURN_ERROR(ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
|
||||||
|
errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
|
||||||
|
errdetail("jsonpath array subscript is "
|
||||||
|
"out of integer range"))));
|
||||||
|
|
||||||
return jperOk;
|
return jperOk;
|
||||||
}
|
}
|
||||||
|
|
|
@ -475,10 +475,11 @@ static char *get_str_from_var(const NumericVar *var);
|
||||||
static char *get_str_from_var_sci(const NumericVar *var, int rscale);
|
static char *get_str_from_var_sci(const NumericVar *var, int rscale);
|
||||||
|
|
||||||
static Numeric make_result(const NumericVar *var);
|
static Numeric make_result(const NumericVar *var);
|
||||||
|
static Numeric make_result_opt_error(const NumericVar *var, bool *error);
|
||||||
|
|
||||||
static void apply_typmod(NumericVar *var, int32 typmod);
|
static void apply_typmod(NumericVar *var, int32 typmod);
|
||||||
|
|
||||||
static int32 numericvar_to_int32(const NumericVar *var);
|
static bool numericvar_to_int32(const NumericVar *var, int32 *result);
|
||||||
static bool numericvar_to_int64(const NumericVar *var, int64 *result);
|
static bool numericvar_to_int64(const NumericVar *var, int64 *result);
|
||||||
static void int64_to_numericvar(int64 val, NumericVar *var);
|
static void int64_to_numericvar(int64 val, NumericVar *var);
|
||||||
#ifdef HAVE_INT128
|
#ifdef HAVE_INT128
|
||||||
|
@ -1558,7 +1559,10 @@ width_bucket_numeric(PG_FUNCTION_ARGS)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if result exceeds the range of a legal int4, we ereport here */
|
/* if result exceeds the range of a legal int4, we ereport here */
|
||||||
result = numericvar_to_int32(&result_var);
|
if (!numericvar_to_int32(&result_var, &result))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("integer out of range")));
|
||||||
|
|
||||||
free_var(&count_var);
|
free_var(&count_var);
|
||||||
free_var(&result_var);
|
free_var(&result_var);
|
||||||
|
@ -2406,6 +2410,23 @@ numeric_add(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Numeric num1 = PG_GETARG_NUMERIC(0);
|
Numeric num1 = PG_GETARG_NUMERIC(0);
|
||||||
Numeric num2 = PG_GETARG_NUMERIC(1);
|
Numeric num2 = PG_GETARG_NUMERIC(1);
|
||||||
|
Numeric res;
|
||||||
|
|
||||||
|
res = numeric_add_opt_error(num1, num2, NULL);
|
||||||
|
|
||||||
|
PG_RETURN_NUMERIC(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* numeric_add_opt_error() -
|
||||||
|
*
|
||||||
|
* Internal version of numeric_add(). If "*have_error" flag is provided,
|
||||||
|
* on error it's set to true, NULL returned. This is helpful when caller
|
||||||
|
* need to handle errors by itself.
|
||||||
|
*/
|
||||||
|
Numeric
|
||||||
|
numeric_add_opt_error(Numeric num1, Numeric num2, bool *have_error)
|
||||||
|
{
|
||||||
NumericVar arg1;
|
NumericVar arg1;
|
||||||
NumericVar arg2;
|
NumericVar arg2;
|
||||||
NumericVar result;
|
NumericVar result;
|
||||||
|
@ -2415,7 +2436,7 @@ numeric_add(PG_FUNCTION_ARGS)
|
||||||
* Handle NaN
|
* Handle NaN
|
||||||
*/
|
*/
|
||||||
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
||||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
return make_result(&const_nan);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unpack the values, let add_var() compute the result and return it.
|
* Unpack the values, let add_var() compute the result and return it.
|
||||||
|
@ -2426,11 +2447,11 @@ numeric_add(PG_FUNCTION_ARGS)
|
||||||
init_var(&result);
|
init_var(&result);
|
||||||
add_var(&arg1, &arg2, &result);
|
add_var(&arg1, &arg2, &result);
|
||||||
|
|
||||||
res = make_result(&result);
|
res = make_result_opt_error(&result, have_error);
|
||||||
|
|
||||||
free_var(&result);
|
free_var(&result);
|
||||||
|
|
||||||
PG_RETURN_NUMERIC(res);
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2444,6 +2465,24 @@ numeric_sub(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Numeric num1 = PG_GETARG_NUMERIC(0);
|
Numeric num1 = PG_GETARG_NUMERIC(0);
|
||||||
Numeric num2 = PG_GETARG_NUMERIC(1);
|
Numeric num2 = PG_GETARG_NUMERIC(1);
|
||||||
|
Numeric res;
|
||||||
|
|
||||||
|
res = numeric_sub_opt_error(num1, num2, NULL);
|
||||||
|
|
||||||
|
PG_RETURN_NUMERIC(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* numeric_sub_opt_error() -
|
||||||
|
*
|
||||||
|
* Internal version of numeric_sub(). If "*have_error" flag is provided,
|
||||||
|
* on error it's set to true, NULL returned. This is helpful when caller
|
||||||
|
* need to handle errors by itself.
|
||||||
|
*/
|
||||||
|
Numeric
|
||||||
|
numeric_sub_opt_error(Numeric num1, Numeric num2, bool *have_error)
|
||||||
|
{
|
||||||
NumericVar arg1;
|
NumericVar arg1;
|
||||||
NumericVar arg2;
|
NumericVar arg2;
|
||||||
NumericVar result;
|
NumericVar result;
|
||||||
|
@ -2453,7 +2492,7 @@ numeric_sub(PG_FUNCTION_ARGS)
|
||||||
* Handle NaN
|
* Handle NaN
|
||||||
*/
|
*/
|
||||||
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
||||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
return make_result(&const_nan);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unpack the values, let sub_var() compute the result and return it.
|
* Unpack the values, let sub_var() compute the result and return it.
|
||||||
|
@ -2464,11 +2503,11 @@ numeric_sub(PG_FUNCTION_ARGS)
|
||||||
init_var(&result);
|
init_var(&result);
|
||||||
sub_var(&arg1, &arg2, &result);
|
sub_var(&arg1, &arg2, &result);
|
||||||
|
|
||||||
res = make_result(&result);
|
res = make_result_opt_error(&result, have_error);
|
||||||
|
|
||||||
free_var(&result);
|
free_var(&result);
|
||||||
|
|
||||||
PG_RETURN_NUMERIC(res);
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2482,6 +2521,24 @@ numeric_mul(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Numeric num1 = PG_GETARG_NUMERIC(0);
|
Numeric num1 = PG_GETARG_NUMERIC(0);
|
||||||
Numeric num2 = PG_GETARG_NUMERIC(1);
|
Numeric num2 = PG_GETARG_NUMERIC(1);
|
||||||
|
Numeric res;
|
||||||
|
|
||||||
|
res = numeric_mul_opt_error(num1, num2, NULL);
|
||||||
|
|
||||||
|
PG_RETURN_NUMERIC(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* numeric_mul_opt_error() -
|
||||||
|
*
|
||||||
|
* Internal version of numeric_mul(). If "*have_error" flag is provided,
|
||||||
|
* on error it's set to true, NULL returned. This is helpful when caller
|
||||||
|
* need to handle errors by itself.
|
||||||
|
*/
|
||||||
|
Numeric
|
||||||
|
numeric_mul_opt_error(Numeric num1, Numeric num2, bool *have_error)
|
||||||
|
{
|
||||||
NumericVar arg1;
|
NumericVar arg1;
|
||||||
NumericVar arg2;
|
NumericVar arg2;
|
||||||
NumericVar result;
|
NumericVar result;
|
||||||
|
@ -2491,7 +2548,7 @@ numeric_mul(PG_FUNCTION_ARGS)
|
||||||
* Handle NaN
|
* Handle NaN
|
||||||
*/
|
*/
|
||||||
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
||||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
return make_result(&const_nan);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unpack the values, let mul_var() compute the result and return it.
|
* Unpack the values, let mul_var() compute the result and return it.
|
||||||
|
@ -2506,11 +2563,11 @@ numeric_mul(PG_FUNCTION_ARGS)
|
||||||
init_var(&result);
|
init_var(&result);
|
||||||
mul_var(&arg1, &arg2, &result, arg1.dscale + arg2.dscale);
|
mul_var(&arg1, &arg2, &result, arg1.dscale + arg2.dscale);
|
||||||
|
|
||||||
res = make_result(&result);
|
res = make_result_opt_error(&result, have_error);
|
||||||
|
|
||||||
free_var(&result);
|
free_var(&result);
|
||||||
|
|
||||||
PG_RETURN_NUMERIC(res);
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2524,6 +2581,24 @@ numeric_div(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
Numeric num1 = PG_GETARG_NUMERIC(0);
|
Numeric num1 = PG_GETARG_NUMERIC(0);
|
||||||
Numeric num2 = PG_GETARG_NUMERIC(1);
|
Numeric num2 = PG_GETARG_NUMERIC(1);
|
||||||
|
Numeric res;
|
||||||
|
|
||||||
|
res = numeric_div_opt_error(num1, num2, NULL);
|
||||||
|
|
||||||
|
PG_RETURN_NUMERIC(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* numeric_div_opt_error() -
|
||||||
|
*
|
||||||
|
* Internal version of numeric_div(). If "*have_error" flag is provided,
|
||||||
|
* on error it's set to true, NULL returned. This is helpful when caller
|
||||||
|
* need to handle errors by itself.
|
||||||
|
*/
|
||||||
|
Numeric
|
||||||
|
numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error)
|
||||||
|
{
|
||||||
NumericVar arg1;
|
NumericVar arg1;
|
||||||
NumericVar arg2;
|
NumericVar arg2;
|
||||||
NumericVar result;
|
NumericVar result;
|
||||||
|
@ -2534,7 +2609,7 @@ numeric_div(PG_FUNCTION_ARGS)
|
||||||
* Handle NaN
|
* Handle NaN
|
||||||
*/
|
*/
|
||||||
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
||||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
return make_result(&const_nan);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unpack the arguments
|
* Unpack the arguments
|
||||||
|
@ -2549,16 +2624,25 @@ numeric_div(PG_FUNCTION_ARGS)
|
||||||
*/
|
*/
|
||||||
rscale = select_div_scale(&arg1, &arg2);
|
rscale = select_div_scale(&arg1, &arg2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If "have_error" is provided, check for division by zero here
|
||||||
|
*/
|
||||||
|
if (have_error && (arg2.ndigits == 0 || arg2.digits[0] == 0))
|
||||||
|
{
|
||||||
|
*have_error = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Do the divide and return the result
|
* Do the divide and return the result
|
||||||
*/
|
*/
|
||||||
div_var(&arg1, &arg2, &result, rscale, true);
|
div_var(&arg1, &arg2, &result, rscale, true);
|
||||||
|
|
||||||
res = make_result(&result);
|
res = make_result_opt_error(&result, have_error);
|
||||||
|
|
||||||
free_var(&result);
|
free_var(&result);
|
||||||
|
|
||||||
PG_RETURN_NUMERIC(res);
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2615,25 +2699,52 @@ numeric_mod(PG_FUNCTION_ARGS)
|
||||||
Numeric num1 = PG_GETARG_NUMERIC(0);
|
Numeric num1 = PG_GETARG_NUMERIC(0);
|
||||||
Numeric num2 = PG_GETARG_NUMERIC(1);
|
Numeric num2 = PG_GETARG_NUMERIC(1);
|
||||||
Numeric res;
|
Numeric res;
|
||||||
|
|
||||||
|
res = numeric_mod_opt_error(num1, num2, NULL);
|
||||||
|
|
||||||
|
PG_RETURN_NUMERIC(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* numeric_mod_opt_error() -
|
||||||
|
*
|
||||||
|
* Internal version of numeric_mod(). If "*have_error" flag is provided,
|
||||||
|
* on error it's set to true, NULL returned. This is helpful when caller
|
||||||
|
* need to handle errors by itself.
|
||||||
|
*/
|
||||||
|
Numeric
|
||||||
|
numeric_mod_opt_error(Numeric num1, Numeric num2, bool *have_error)
|
||||||
|
{
|
||||||
|
Numeric res;
|
||||||
NumericVar arg1;
|
NumericVar arg1;
|
||||||
NumericVar arg2;
|
NumericVar arg2;
|
||||||
NumericVar result;
|
NumericVar result;
|
||||||
|
|
||||||
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2))
|
||||||
PG_RETURN_NUMERIC(make_result(&const_nan));
|
return make_result(&const_nan);
|
||||||
|
|
||||||
init_var_from_num(num1, &arg1);
|
init_var_from_num(num1, &arg1);
|
||||||
init_var_from_num(num2, &arg2);
|
init_var_from_num(num2, &arg2);
|
||||||
|
|
||||||
init_var(&result);
|
init_var(&result);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If "have_error" is provided, check for division by zero here
|
||||||
|
*/
|
||||||
|
if (have_error && (arg2.ndigits == 0 || arg2.digits[0] == 0))
|
||||||
|
{
|
||||||
|
*have_error = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
mod_var(&arg1, &arg2, &result);
|
mod_var(&arg1, &arg2, &result);
|
||||||
|
|
||||||
res = make_result(&result);
|
res = make_result_opt_error(&result, NULL);
|
||||||
|
|
||||||
free_var(&result);
|
free_var(&result);
|
||||||
|
|
||||||
PG_RETURN_NUMERIC(res);
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3090,52 +3201,75 @@ int4_numeric(PG_FUNCTION_ARGS)
|
||||||
PG_RETURN_NUMERIC(res);
|
PG_RETURN_NUMERIC(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32
|
||||||
Datum
|
numeric_int4_opt_error(Numeric num, bool *have_error)
|
||||||
numeric_int4(PG_FUNCTION_ARGS)
|
|
||||||
{
|
{
|
||||||
Numeric num = PG_GETARG_NUMERIC(0);
|
|
||||||
NumericVar x;
|
NumericVar x;
|
||||||
int32 result;
|
int32 result;
|
||||||
|
|
||||||
/* XXX would it be better to return NULL? */
|
/* XXX would it be better to return NULL? */
|
||||||
if (NUMERIC_IS_NAN(num))
|
if (NUMERIC_IS_NAN(num))
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
if (have_error)
|
||||||
errmsg("cannot convert NaN to integer")));
|
{
|
||||||
|
*have_error = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot convert NaN to integer")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Convert to variable format, then convert to int4 */
|
/* Convert to variable format, then convert to int4 */
|
||||||
init_var_from_num(num, &x);
|
init_var_from_num(num, &x);
|
||||||
result = numericvar_to_int32(&x);
|
|
||||||
PG_RETURN_INT32(result);
|
if (!numericvar_to_int32(&x, &result))
|
||||||
|
{
|
||||||
|
if (have_error)
|
||||||
|
{
|
||||||
|
*have_error = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("integer out of range")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
numeric_int4(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
Numeric num = PG_GETARG_NUMERIC(0);
|
||||||
|
|
||||||
|
PG_RETURN_INT32(numeric_int4_opt_error(num, NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given a NumericVar, convert it to an int32. If the NumericVar
|
* Given a NumericVar, convert it to an int32. If the NumericVar
|
||||||
* exceeds the range of an int32, raise the appropriate error via
|
* exceeds the range of an int32, false is returned, otherwise true is returned.
|
||||||
* ereport(). The input NumericVar is *not* free'd.
|
* The input NumericVar is *not* free'd.
|
||||||
*/
|
*/
|
||||||
static int32
|
static bool
|
||||||
numericvar_to_int32(const NumericVar *var)
|
numericvar_to_int32(const NumericVar *var, int32 *result)
|
||||||
{
|
{
|
||||||
int32 result;
|
|
||||||
int64 val;
|
int64 val;
|
||||||
|
|
||||||
if (!numericvar_to_int64(var, &val))
|
if (!numericvar_to_int64(var, &val))
|
||||||
ereport(ERROR,
|
return false;
|
||||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("integer out of range")));
|
|
||||||
|
|
||||||
/* Down-convert to int4 */
|
/* Down-convert to int4 */
|
||||||
result = (int32) val;
|
*result = (int32) val;
|
||||||
|
|
||||||
/* Test for overflow by reverse-conversion. */
|
/* Test for overflow by reverse-conversion. */
|
||||||
if ((int64) result != val)
|
return ((int64) *result == val);
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("integer out of range")));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Datum
|
Datum
|
||||||
|
@ -6098,13 +6232,15 @@ get_str_from_var_sci(const NumericVar *var, int rscale)
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* make_result() -
|
* make_result_opt_error() -
|
||||||
*
|
*
|
||||||
* Create the packed db numeric format in palloc()'d memory from
|
* Create the packed db numeric format in palloc()'d memory from
|
||||||
* a variable.
|
* a variable. If "*have_error" flag is provided, on error it's set to
|
||||||
|
* true, NULL returned. This is helpful when caller need to handle errors
|
||||||
|
* by itself.
|
||||||
*/
|
*/
|
||||||
static Numeric
|
static Numeric
|
||||||
make_result(const NumericVar *var)
|
make_result_opt_error(const NumericVar *var, bool *have_error)
|
||||||
{
|
{
|
||||||
Numeric result;
|
Numeric result;
|
||||||
NumericDigit *digits = var->digits;
|
NumericDigit *digits = var->digits;
|
||||||
|
@ -6175,15 +6311,37 @@ make_result(const NumericVar *var)
|
||||||
/* Check for overflow of int16 fields */
|
/* Check for overflow of int16 fields */
|
||||||
if (NUMERIC_WEIGHT(result) != weight ||
|
if (NUMERIC_WEIGHT(result) != weight ||
|
||||||
NUMERIC_DSCALE(result) != var->dscale)
|
NUMERIC_DSCALE(result) != var->dscale)
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
if (have_error)
|
||||||
errmsg("value overflows numeric format")));
|
{
|
||||||
|
*have_error = true;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("value overflows numeric format")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dump_numeric("make_result()", result);
|
dump_numeric("make_result()", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* make_result() -
|
||||||
|
*
|
||||||
|
* An interface to make_result_opt_error() without "have_error" argument.
|
||||||
|
*/
|
||||||
|
static Numeric
|
||||||
|
make_result(const NumericVar *var)
|
||||||
|
{
|
||||||
|
return make_result_opt_error(var, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* apply_typmod() -
|
* apply_typmod() -
|
||||||
*
|
*
|
||||||
|
|
|
@ -40,6 +40,9 @@ extern PGDLLIMPORT int extra_float_digits;
|
||||||
extern int is_infinite(float8 val);
|
extern int is_infinite(float8 val);
|
||||||
extern float8 float8in_internal(char *num, char **endptr_p,
|
extern float8 float8in_internal(char *num, char **endptr_p,
|
||||||
const char *type_name, const char *orig_string);
|
const char *type_name, const char *orig_string);
|
||||||
|
extern float8 float8in_internal_opt_error(char *num, char **endptr_p,
|
||||||
|
const char *type_name, const char *orig_string,
|
||||||
|
bool *have_error);
|
||||||
extern char *float8out_internal(float8 num);
|
extern char *float8out_internal(float8 num);
|
||||||
extern int float4_cmp_internal(float4 a, float4 b);
|
extern int float4_cmp_internal(float4 a, float4 b);
|
||||||
extern int float8_cmp_internal(float8 a, float8 b);
|
extern int float8_cmp_internal(float8 a, float8 b);
|
||||||
|
|
|
@ -61,4 +61,16 @@ int32 numeric_maximum_size(int32 typmod);
|
||||||
extern char *numeric_out_sci(Numeric num, int scale);
|
extern char *numeric_out_sci(Numeric num, int scale);
|
||||||
extern char *numeric_normalize(Numeric num);
|
extern char *numeric_normalize(Numeric num);
|
||||||
|
|
||||||
|
extern Numeric numeric_add_opt_error(Numeric num1, Numeric num2,
|
||||||
|
bool *have_error);
|
||||||
|
extern Numeric numeric_sub_opt_error(Numeric num1, Numeric num2,
|
||||||
|
bool *have_error);
|
||||||
|
extern Numeric numeric_mul_opt_error(Numeric num1, Numeric num2,
|
||||||
|
bool *have_error);
|
||||||
|
extern Numeric numeric_div_opt_error(Numeric num1, Numeric num2,
|
||||||
|
bool *have_error);
|
||||||
|
extern Numeric numeric_mod_opt_error(Numeric num1, Numeric num2,
|
||||||
|
bool *have_error);
|
||||||
|
extern int32 numeric_int4_opt_error(Numeric num, bool *error);
|
||||||
|
|
||||||
#endif /* _PG_NUMERIC_H_ */
|
#endif /* _PG_NUMERIC_H_ */
|
||||||
|
|
|
@ -127,13 +127,23 @@ select jsonb_path_query('[1]', 'strict $[1]', silent => true);
|
||||||
(0 rows)
|
(0 rows)
|
||||||
|
|
||||||
select jsonb '[1]' @? 'lax $[10000000000000000]';
|
select jsonb '[1]' @? 'lax $[10000000000000000]';
|
||||||
ERROR: integer out of range
|
?column?
|
||||||
|
----------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
select jsonb '[1]' @? 'strict $[10000000000000000]';
|
select jsonb '[1]' @? 'strict $[10000000000000000]';
|
||||||
ERROR: integer out of range
|
?column?
|
||||||
|
----------
|
||||||
|
|
||||||
|
(1 row)
|
||||||
|
|
||||||
select jsonb_path_query('[1]', 'lax $[10000000000000000]');
|
select jsonb_path_query('[1]', 'lax $[10000000000000000]');
|
||||||
ERROR: integer out of range
|
ERROR: invalid SQL/JSON subscript
|
||||||
|
DETAIL: jsonpath array subscript is out of integer range
|
||||||
select jsonb_path_query('[1]', 'strict $[10000000000000000]');
|
select jsonb_path_query('[1]', 'strict $[10000000000000000]');
|
||||||
ERROR: integer out of range
|
ERROR: invalid SQL/JSON subscript
|
||||||
|
DETAIL: jsonpath array subscript is out of integer range
|
||||||
select jsonb '[1]' @? '$[0]';
|
select jsonb '[1]' @? '$[0]';
|
||||||
?column?
|
?column?
|
||||||
----------
|
----------
|
||||||
|
@ -1037,9 +1047,19 @@ select jsonb '1' @? '$ ? ($ > 0)';
|
||||||
|
|
||||||
-- arithmetic errors
|
-- arithmetic errors
|
||||||
select jsonb_path_query('[1,2,0,3]', '$[*] ? (2 / @ > 0)');
|
select jsonb_path_query('[1,2,0,3]', '$[*] ? (2 / @ > 0)');
|
||||||
ERROR: division by zero
|
jsonb_path_query
|
||||||
|
------------------
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
select jsonb_path_query('[1,2,0,3]', '$[*] ? ((2 / @ > 0) is unknown)');
|
select jsonb_path_query('[1,2,0,3]', '$[*] ? ((2 / @ > 0) is unknown)');
|
||||||
ERROR: division by zero
|
jsonb_path_query
|
||||||
|
------------------
|
||||||
|
0
|
||||||
|
(1 row)
|
||||||
|
|
||||||
select jsonb_path_query('0', '1 / $');
|
select jsonb_path_query('0', '1 / $');
|
||||||
ERROR: division by zero
|
ERROR: division by zero
|
||||||
select jsonb_path_query('0', '1 / $ + 2');
|
select jsonb_path_query('0', '1 / $ + 2');
|
||||||
|
@ -1502,7 +1522,8 @@ select jsonb_path_query('"1.23"', '$.double()');
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
select jsonb_path_query('"1.23aaa"', '$.double()');
|
select jsonb_path_query('"1.23aaa"', '$.double()');
|
||||||
ERROR: invalid input syntax for type double precision: "1.23aaa"
|
ERROR: non-numeric SQL/JSON item
|
||||||
|
DETAIL: jsonpath item method .double() can only be applied to a numeric value
|
||||||
select jsonb_path_query('"nan"', '$.double()');
|
select jsonb_path_query('"nan"', '$.double()');
|
||||||
jsonb_path_query
|
jsonb_path_query
|
||||||
------------------
|
------------------
|
||||||
|
|
Loading…
Reference in New Issue