mirror of
https://github.com/postgres/postgres.git
synced 2026-07-09 17:51:00 -04:00
Fix jsonpath .decimal() to honor silent mode
The jsonpath .decimal(precision[, scale]) method built its numeric
typmod by calling numerictypmodin() through DirectFunctionCall1(), which
can throw a hard error for an incorrect set of precision and/or scale
vaulues. This breaks the silent mode supported by this function, that
should not fail.
Most of the jsonpath code uses the soft error reporting to bypass
errors, which is what this fix does by avoiding a direct use of
numerictypmodin(). Its code is refactored to use a new routine called
make_numeric_typmod_safe(), able to take an error context in input.
numerictypmodin() sets no context, mapping to its previous behavior.
The jsonpath code sets or not a context depending on the use of the
silent mode. This result leads to some nice simplifications:
numerictypmodin() feeds on an array, we can now pass directly values for
the scale and precision.
Oversight in 66ea94e8e6.
Author: Ewan Young <kdbase.hack@gmail.com>
Discussion: https://postgr.es/m/CAON2xHMaigKABiyPBBq3Sjd3gp7uWMJXnnMHt=s85V1ij3KP1w@mail.gmail.com
Backpatch-through: 17
This commit is contained in:
parent
1e1d07792e
commit
84001a04d5
5 changed files with 75 additions and 37 deletions
|
|
@ -1463,15 +1463,11 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
|||
if (jsp->type == jpiDecimal && jsp->content.args.left)
|
||||
{
|
||||
Datum numdatum;
|
||||
Datum dtypmod;
|
||||
int32 dtypmod;
|
||||
int32 precision;
|
||||
int32 scale = 0;
|
||||
bool have_error;
|
||||
bool noerr;
|
||||
ArrayType *arrtypmod;
|
||||
Datum datums[2];
|
||||
char pstr[12]; /* sign, 10 digits and '\0' */
|
||||
char sstr[12]; /* sign, 10 digits and '\0' */
|
||||
ErrorSaveContext escontext = {T_ErrorSaveContext};
|
||||
|
||||
jspGetLeftArg(jsp, &elem);
|
||||
|
|
@ -1501,18 +1497,11 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
|||
jspOperationName(jsp->type)))));
|
||||
}
|
||||
|
||||
/*
|
||||
* numerictypmodin() takes the precision and scale in the
|
||||
* form of CString arrays.
|
||||
*/
|
||||
pg_ltoa(precision, pstr);
|
||||
datums[0] = CStringGetDatum(pstr);
|
||||
pg_ltoa(scale, sstr);
|
||||
datums[1] = CStringGetDatum(sstr);
|
||||
arrtypmod = construct_array_builtin(datums, 2, CSTRINGOID);
|
||||
|
||||
dtypmod = DirectFunctionCall1(numerictypmodin,
|
||||
PointerGetDatum(arrtypmod));
|
||||
/* Pack the precision and scale into a numeric typmod */
|
||||
dtypmod = make_numeric_typmod_safe(precision, scale,
|
||||
jspThrowErrors(cxt) ? NULL : (Node *) &escontext);
|
||||
if (escontext.error_occurred)
|
||||
return jperError;
|
||||
|
||||
/* Convert numstr to Numeric with typmod */
|
||||
Assert(numstr != NULL);
|
||||
|
|
@ -1528,7 +1517,6 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
|
|||
numstr, jspOperationName(jsp->type), "numeric"))));
|
||||
|
||||
num = DatumGetNumeric(numdatum);
|
||||
pfree(arrtypmod);
|
||||
}
|
||||
|
||||
jb = &jbv;
|
||||
|
|
|
|||
|
|
@ -1320,6 +1320,29 @@ numeric (PG_FUNCTION_ARGS)
|
|||
PG_RETURN_NUMERIC(new);
|
||||
}
|
||||
|
||||
/*
|
||||
* make_numeric_typmod_safe() -
|
||||
*
|
||||
* Validate a numeric precision/scale and pack them into a typmod value,
|
||||
* with soft error handling.
|
||||
*/
|
||||
int32
|
||||
make_numeric_typmod_safe(int32 precision, int32 scale, Node *escontext)
|
||||
{
|
||||
if (precision < 1 || precision > NUMERIC_MAX_PRECISION)
|
||||
ereturn(escontext, -1,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("NUMERIC precision %d must be between 1 and %d",
|
||||
precision, NUMERIC_MAX_PRECISION)));
|
||||
if (scale < NUMERIC_MIN_SCALE || scale > NUMERIC_MAX_SCALE)
|
||||
ereturn(escontext, -1,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("NUMERIC scale %d must be between %d and %d",
|
||||
scale, NUMERIC_MIN_SCALE, NUMERIC_MAX_SCALE)));
|
||||
|
||||
return make_numeric_typmod(precision, scale);
|
||||
}
|
||||
|
||||
Datum
|
||||
numerictypmodin(PG_FUNCTION_ARGS)
|
||||
{
|
||||
|
|
@ -1331,28 +1354,11 @@ numerictypmodin(PG_FUNCTION_ARGS)
|
|||
tl = ArrayGetIntegerTypmods(ta, &n);
|
||||
|
||||
if (n == 2)
|
||||
{
|
||||
if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("NUMERIC precision %d must be between 1 and %d",
|
||||
tl[0], NUMERIC_MAX_PRECISION)));
|
||||
if (tl[1] < NUMERIC_MIN_SCALE || tl[1] > NUMERIC_MAX_SCALE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("NUMERIC scale %d must be between %d and %d",
|
||||
tl[1], NUMERIC_MIN_SCALE, NUMERIC_MAX_SCALE)));
|
||||
typmod = make_numeric_typmod(tl[0], tl[1]);
|
||||
}
|
||||
typmod = make_numeric_typmod_safe(tl[0], tl[1], NULL);
|
||||
else if (n == 1)
|
||||
{
|
||||
if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("NUMERIC precision %d must be between 1 and %d",
|
||||
tl[0], NUMERIC_MAX_PRECISION)));
|
||||
/* scale defaults to zero */
|
||||
typmod = make_numeric_typmod(tl[0], 0);
|
||||
typmod = make_numeric_typmod_safe(tl[0], 0, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@
|
|||
#include "common/pg_prng.h"
|
||||
#include "fmgr.h"
|
||||
|
||||
/* forward declaration to avoid node.h include */
|
||||
typedef struct Node Node;
|
||||
|
||||
/*
|
||||
* Limits on the precision and scale specifiable in a NUMERIC typmod. The
|
||||
* precision is strictly positive, but the scale may be positive or negative.
|
||||
|
|
@ -103,6 +106,8 @@ extern Numeric numeric_mod_opt_error(Numeric num1, Numeric num2,
|
|||
bool *have_error);
|
||||
extern int32 numeric_int4_opt_error(Numeric num, bool *have_error);
|
||||
extern int64 numeric_int8_opt_error(Numeric num, bool *have_error);
|
||||
extern int32 make_numeric_typmod_safe(int32 precision, int32 scale,
|
||||
Node *escontext);
|
||||
|
||||
extern Numeric random_numeric(pg_prng_state *state,
|
||||
Numeric rmin, Numeric rmax);
|
||||
|
|
|
|||
|
|
@ -2336,6 +2336,38 @@ select jsonb_path_query('12.3', '$.decimal(12345678901,1)');
|
|||
ERROR: precision of jsonpath item method .decimal() is out of range for type integer
|
||||
select jsonb_path_query('12.3', '$.decimal(1,12345678901)');
|
||||
ERROR: scale of jsonpath item method .decimal() is out of range for type integer
|
||||
-- An out-of-range precision or scale does not fail in silent mode.
|
||||
select jsonb_path_query('12345.678', '$.decimal(0, 6)', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('12345.678', '$.decimal(1001, 6)', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('1234.5678', '$.decimal(-6, +2)', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('1234.5678', '$.decimal(6, -1001)', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select jsonb_path_query('1234.5678', '$.decimal(6, 1001)', silent => true);
|
||||
jsonb_path_query
|
||||
------------------
|
||||
(0 rows)
|
||||
|
||||
select '1234.5678'::jsonb @? '$.decimal(0)';
|
||||
?column?
|
||||
----------
|
||||
|
||||
(1 row)
|
||||
|
||||
-- Test .integer()
|
||||
select jsonb_path_query('null', '$.integer()');
|
||||
ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
|
||||
|
|
|
|||
|
|
@ -523,6 +523,13 @@ select jsonb_path_query('0.0012345', '$.decimal(2,4)');
|
|||
select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
|
||||
select jsonb_path_query('12.3', '$.decimal(12345678901,1)');
|
||||
select jsonb_path_query('12.3', '$.decimal(1,12345678901)');
|
||||
-- An out-of-range precision or scale does not fail in silent mode.
|
||||
select jsonb_path_query('12345.678', '$.decimal(0, 6)', silent => true);
|
||||
select jsonb_path_query('12345.678', '$.decimal(1001, 6)', silent => true);
|
||||
select jsonb_path_query('1234.5678', '$.decimal(-6, +2)', silent => true);
|
||||
select jsonb_path_query('1234.5678', '$.decimal(6, -1001)', silent => true);
|
||||
select jsonb_path_query('1234.5678', '$.decimal(6, 1001)', silent => true);
|
||||
select '1234.5678'::jsonb @? '$.decimal(0)';
|
||||
|
||||
-- Test .integer()
|
||||
select jsonb_path_query('null', '$.integer()');
|
||||
|
|
|
|||
Loading…
Reference in a new issue