diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c index 928552d551..7b60b36189 100644 --- a/src/backend/utils/adt/jsonb.c +++ b/src/backend/utils/adt/jsonb.c @@ -18,7 +18,9 @@ #include "funcapi.h" #include "libpq/pqformat.h" #include "miscadmin.h" +#include "nodes/supportnodes.h" #include "utils/builtins.h" +#include "utils/fmgroids.h" #include "utils/json.h" #include "utils/jsonb.h" #include "utils/jsonfuncs.h" @@ -2069,6 +2071,64 @@ jsonb_numeric(PG_FUNCTION_ARGS) PG_RETURN_NUMERIC(retValue); } +Datum +jsonb_object_field_numeric(PG_FUNCTION_ARGS) +{ + Jsonb *jb = PG_GETARG_JSONB_P(0); + text *key = PG_GETARG_TEXT_PP(1); + JsonbValue *v; + JsonbValue vbuf; + + if (!JB_ROOT_IS_OBJECT(jb)) + PG_RETURN_NULL(); + + v = getKeyJsonValueFromContainer(&jb->root, + VARDATA_ANY(key), + VARSIZE_ANY_EXHDR(key), + &vbuf); + + if (v != NULL) + { + if (v->type == jbvNumeric) + PG_RETURN_NUMERIC(v->val.numeric); + + cannotCastJsonbValue(v->type, "numeric"); + } + + PG_RETURN_NULL(); +} + +Datum +jsonb_numeric_support(PG_FUNCTION_ARGS) +{ + Node *rawreq = (Node *) PG_GETARG_POINTER(0); + Node *ret = NULL; + + if (IsA(rawreq, SupportRequestSimplify)) + { + SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq; + FuncExpr *func = req->fcall; + OpExpr *opexpr = list_nth(func->args, 0); + + /* + * Transform jsonb_object_field calls that directly cast to numeric + * into a direct call to jsonb_object_field_numeric. This allows us + * to directly access the numeric field and return it directly thus + * saving casting from jsonb to numeric. + */ + if (IsA(opexpr, OpExpr) && opexpr->opfuncid == F_JSONB_OBJECT_FIELD) + { + opexpr->opfuncid = F_JSONB_OBJECT_FIELD_NUMERIC; + PG_RETURN_POINTER(opexpr); + } + + PG_RETURN_POINTER(ret); + } + + PG_RETURN_POINTER(ret); +} + + Datum jsonb_int2(PG_FUNCTION_ARGS) { diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index ff5436acac..10aed0b0b1 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -4642,8 +4642,12 @@ proname => 'bool', prorettype => 'bool', proargtypes => 'jsonb', prosrc => 'jsonb_bool' }, { oid => '3449', descr => 'convert jsonb to numeric', - proname => 'numeric', prorettype => 'numeric', proargtypes => 'jsonb', + proname => 'numeric', prosupport => 'jsonb_numeric_support', + prorettype => 'numeric', proargtypes => 'jsonb', prosrc => 'jsonb_numeric' }, +{ oid => '8394', descr => 'planner support for jsonb casting support', + proname => 'jsonb_numeric_support', prorettype => 'internal', + proargtypes => 'internal', prosrc => 'jsonb_numeric_support' }, { oid => '3450', descr => 'convert jsonb to int2', proname => 'int2', prorettype => 'int2', proargtypes => 'jsonb', prosrc => 'jsonb_int2' }, @@ -10083,6 +10087,10 @@ proargtypes => 'jsonb _text', proallargtypes => '{jsonb,_text}', proargmodes => '{i,v}', proargnames => '{from_json,path_elems}', prosrc => 'jsonb_extract_path' }, +{ oid => '8395', + proname => 'jsonb_object_field_numeric', prorettype => 'numeric', + proargtypes => 'jsonb text', proargnames => '{from_json, field_name}', + prosrc => 'jsonb_object_field_numeric' }, { oid => '3940', descr => 'get value from jsonb as text with path elements', proname => 'jsonb_extract_path_text', provariadic => 'text', prorettype => 'text', proargtypes => 'jsonb _text',