diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 8f2a2315d8..cec21e42c0 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -17689,20 +17689,6 @@ strict $.**.HR - - - number . abs() - number - - - Absolute value of the given number - - - jsonb_path_query('{"z": -0.3}', '$.z.abs()') - 0.3 - - - number . ceiling() @@ -17731,6 +17717,20 @@ strict $.**.HR + + + number . abs() + number + + + Absolute value of the given number + + + jsonb_path_query('{"z": -0.3}', '$.z.abs()') + 0.3 + + + string . datetime() diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c index 8ff9b5646f..c5ba3b7f1d 100644 --- a/src/backend/utils/adt/jsonpath.c +++ b/src/backend/utils/adt/jsonpath.c @@ -439,10 +439,10 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext, break; case jpiType: case jpiSize: - case jpiDouble: case jpiAbs: - case jpiCeiling: case jpiFloor: + case jpiCeiling: + case jpiDouble: case jpiKeyValue: break; default: @@ -610,6 +610,18 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, if (printBracketes) appendStringInfoChar(buf, ')'); break; + case jpiPlus: + case jpiMinus: + if (printBracketes) + appendStringInfoChar(buf, '('); + appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-'); + jspGetArg(v, &elem); + printJsonPathItem(buf, &elem, false, + operationPriority(elem.type) <= + operationPriority(v->type)); + if (printBracketes) + appendStringInfoChar(buf, ')'); + break; case jpiFilter: appendStringInfoString(buf, "?("); jspGetArg(v, &elem); @@ -700,35 +712,23 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, v->content.anybounds.first, v->content.anybounds.last); break; - case jpiPlus: - case jpiMinus: - if (printBracketes) - appendStringInfoChar(buf, '('); - appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-'); - jspGetArg(v, &elem); - printJsonPathItem(buf, &elem, false, - operationPriority(elem.type) <= - operationPriority(v->type)); - if (printBracketes) - appendStringInfoChar(buf, ')'); - break; case jpiType: appendStringInfoString(buf, ".type()"); break; case jpiSize: appendStringInfoString(buf, ".size()"); break; - case jpiDouble: - appendStringInfoString(buf, ".double()"); - break; case jpiAbs: appendStringInfoString(buf, ".abs()"); break; + case jpiFloor: + appendStringInfoString(buf, ".floor()"); + break; case jpiCeiling: appendStringInfoString(buf, ".ceiling()"); break; - case jpiFloor: - appendStringInfoString(buf, ".floor()"); + case jpiDouble: + appendStringInfoString(buf, ".double()"); break; case jpiDatetime: appendStringInfoString(buf, ".datetime("); @@ -771,11 +771,11 @@ jspOperationName(JsonPathItemType type) return "<="; case jpiGreaterOrEqual: return ">="; - case jpiAdd: case jpiPlus: + case jpiAdd: return "+"; - case jpiSub: case jpiMinus: + case jpiSub: return "-"; case jpiMul: return "*"; @@ -783,26 +783,26 @@ jspOperationName(JsonPathItemType type) return "/"; case jpiMod: return "%"; - case jpiType: - return "type"; - case jpiSize: - return "size"; - case jpiDouble: - return "double"; - case jpiAbs: - return "abs"; - case jpiCeiling: - return "ceiling"; - case jpiFloor: - return "floor"; - case jpiDatetime: - return "datetime"; - case jpiKeyValue: - return "keyvalue"; case jpiStartsWith: return "starts with"; case jpiLikeRegex: return "like_regex"; + case jpiType: + return "type"; + case jpiSize: + return "size"; + case jpiKeyValue: + return "keyvalue"; + case jpiDouble: + return "double"; + case jpiAbs: + return "abs"; + case jpiFloor: + return "floor"; + case jpiCeiling: + return "ceiling"; + case jpiDatetime: + return "datetime"; default: elog(ERROR, "unrecognized jsonpath item type: %d", type); return NULL; @@ -893,10 +893,10 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos) case jpiAnyKey: case jpiType: case jpiSize: - case jpiDouble: case jpiAbs: - case jpiCeiling: case jpiFloor: + case jpiCeiling: + case jpiDouble: case jpiKeyValue: case jpiLast: break; @@ -935,9 +935,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos) case jpiNot: case jpiExists: case jpiIsUnknown: - case jpiFilter: case jpiPlus: case jpiMinus: + case jpiFilter: case jpiDatetime: read_int32(v->content.arg, base, pos); break; @@ -989,6 +989,13 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a) v->type == jpiRoot || v->type == jpiVariable || v->type == jpiLast || + v->type == jpiAdd || + v->type == jpiSub || + v->type == jpiMul || + v->type == jpiDiv || + v->type == jpiMod || + v->type == jpiPlus || + v->type == jpiMinus || v->type == jpiEqual || v->type == jpiNotEqual || v->type == jpiGreater || @@ -999,19 +1006,12 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a) v->type == jpiOr || v->type == jpiNot || v->type == jpiIsUnknown || - v->type == jpiAdd || - v->type == jpiPlus || - v->type == jpiSub || - v->type == jpiMinus || - v->type == jpiMul || - v->type == jpiDiv || - v->type == jpiMod || v->type == jpiType || v->type == jpiSize || - v->type == jpiDouble || v->type == jpiAbs || - v->type == jpiCeiling || v->type == jpiFloor || + v->type == jpiCeiling || + v->type == jpiDouble || v->type == jpiDatetime || v->type == jpiKeyValue || v->type == jpiStartsWith || diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c index 86b5b76d4e..9a09604f64 100644 --- a/src/backend/utils/adt/jsonpath_exec.c +++ b/src/backend/utils/adt/jsonpath_exec.c @@ -874,6 +874,33 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp, } break; + case jpiAdd: + return executeBinaryArithmExpr(cxt, jsp, jb, + numeric_add_opt_error, found); + + case jpiSub: + return executeBinaryArithmExpr(cxt, jsp, jb, + numeric_sub_opt_error, found); + + case jpiMul: + return executeBinaryArithmExpr(cxt, jsp, jb, + numeric_mul_opt_error, found); + + case jpiDiv: + return executeBinaryArithmExpr(cxt, jsp, jb, + numeric_div_opt_error, found); + + case jpiMod: + return executeBinaryArithmExpr(cxt, jsp, jb, + numeric_mod_opt_error, found); + + case jpiPlus: + return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found); + + case jpiMinus: + return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus, + found); + case jpiFilter: { JsonPathBool st; @@ -953,33 +980,6 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp, } break; - case jpiAdd: - return executeBinaryArithmExpr(cxt, jsp, jb, - numeric_add_opt_error, found); - - case jpiPlus: - return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found); - - case jpiSub: - return executeBinaryArithmExpr(cxt, jsp, jb, - numeric_sub_opt_error, found); - - case jpiMinus: - return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus, - found); - - case jpiMul: - return executeBinaryArithmExpr(cxt, jsp, jb, - numeric_mul_opt_error, found); - - case jpiDiv: - return executeBinaryArithmExpr(cxt, jsp, jb, - numeric_div_opt_error, found); - - case jpiMod: - return executeBinaryArithmExpr(cxt, jsp, jb, - numeric_mod_opt_error, found); - case jpiType: { JsonbValue *jbv = palloc(sizeof(*jbv)); @@ -1021,6 +1021,18 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp, } break; + case jpiAbs: + return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs, + found); + + case jpiFloor: + return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor, + found); + + case jpiCeiling: + return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil, + found); + case jpiDouble: { JsonbValue jbv; @@ -1086,18 +1098,6 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp, } break; - case jpiAbs: - return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs, - found); - - case jpiCeiling: - return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil, - found); - - case jpiFloor: - return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor, - found); - case jpiDatetime: if (unwrap && JsonbType(jb) == jbvArray) return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false); diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y index 4233eedc1b..adc259d5bf 100644 --- a/src/backend/utils/adt/jsonpath_gram.y +++ b/src/backend/utils/adt/jsonpath_gram.y @@ -80,7 +80,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr, %token OR_P AND_P NOT_P %token LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P %token ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P -%token TYPE_P SIZE_P DOUBLE_P ABS_P CEILING_P FLOOR_P KEYVALUE_P +%token ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P %token DATETIME_P %type result @@ -206,10 +206,10 @@ accessor_expr: expr: accessor_expr { $$ = makeItemList($1); } | '(' expr ')' { $$ = $2; } - | expr '+' expr { $$ = makeItemBinary(jpiAdd, $1, $3); } | '+' expr %prec UMINUS { $$ = makeItemUnary(jpiPlus, $2); } - | expr '-' expr { $$ = makeItemBinary(jpiSub, $1, $3); } | '-' expr %prec UMINUS { $$ = makeItemUnary(jpiMinus, $2); } + | expr '+' expr { $$ = makeItemBinary(jpiAdd, $1, $3); } + | expr '-' expr { $$ = makeItemBinary(jpiSub, $1, $3); } | expr '*' expr { $$ = makeItemBinary(jpiMul, $1, $3); } | expr '/' expr { $$ = makeItemBinary(jpiDiv, $1, $3); } | expr '%' expr { $$ = makeItemBinary(jpiMod, $1, $3); } @@ -278,28 +278,28 @@ key_name: | EXISTS_P | STRICT_P | LAX_P - | LAST_P - | FLAG_P - | TYPE_P - | SIZE_P - | DOUBLE_P | ABS_P - | CEILING_P + | SIZE_P + | TYPE_P | FLOOR_P + | DOUBLE_P + | CEILING_P | DATETIME_P | KEYVALUE_P + | LAST_P | STARTS_P | WITH_P | LIKE_REGEX_P + | FLAG_P ; method: - TYPE_P { $$ = jpiType; } + ABS_P { $$ = jpiAbs; } | SIZE_P { $$ = jpiSize; } - | DOUBLE_P { $$ = jpiDouble; } - | ABS_P { $$ = jpiAbs; } - | CEILING_P { $$ = jpiCeiling; } + | TYPE_P { $$ = jpiType; } | FLOOR_P { $$ = jpiFloor; } + | DOUBLE_P { $$ = jpiDouble; } + | CEILING_P { $$ = jpiCeiling; } | KEYVALUE_P { $$ = jpiKeyValue; } ; %% diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h index 59dc233a08..f0181e045f 100644 --- a/src/include/utils/jsonpath.h +++ b/src/include/utils/jsonpath.h @@ -66,6 +66,13 @@ typedef enum JsonPathItemType jpiGreater, /* expr > expr */ jpiLessOrEqual, /* expr <= expr */ jpiGreaterOrEqual, /* expr >= expr */ + jpiAdd, /* expr + expr */ + jpiSub, /* expr - expr */ + jpiMul, /* expr * expr */ + jpiDiv, /* expr / expr */ + jpiMod, /* expr % expr */ + jpiPlus, /* + expr */ + jpiMinus, /* - expr */ jpiAnyArray, /* [*] */ jpiAnyKey, /* .* */ jpiIndexArray, /* [subscript, ...] */ @@ -76,28 +83,14 @@ typedef enum JsonPathItemType jpiVariable, /* $variable */ jpiFilter, /* ? (predicate) */ jpiExists, /* EXISTS (expr) predicate */ - - /* - * For better maintainability or readability, keep the order of the below - * jsonpath Operators and Methods at the other places, like in the - * documentation, switch() cases, keywords list, etc., too. - */ - jpiAdd, /* expr + expr */ - jpiPlus, /* + expr */ - jpiSub, /* expr - expr */ - jpiMinus, /* - expr */ - jpiMul, /* expr * expr */ - jpiDiv, /* expr / expr */ - jpiMod, /* expr % expr */ jpiType, /* .type() item method */ jpiSize, /* .size() item method */ - jpiDouble, /* .double() item method */ jpiAbs, /* .abs() item method */ - jpiCeiling, /* .ceiling() item method */ jpiFloor, /* .floor() item method */ + jpiCeiling, /* .ceiling() item method */ + jpiDouble, /* .double() item method */ jpiDatetime, /* .datetime() item method */ jpiKeyValue, /* .keyvalue() item method */ - jpiSubscript, /* array subscript: 'expr' or 'expr TO expr' */ jpiLast, /* LAST array subscript */ jpiStartsWith, /* STARTS WITH predicate */