case jpiNull:
appendStringInfoString(buf, "null");
break;
- case jpiKey:
- if (inKey)
- appendStringInfoChar(buf, '.');
- escape_json(buf, jspGetString(v, NULL));
- break;
case jpiString:
escape_json(buf, jspGetString(v, NULL));
break;
- case jpiVariable:
- appendStringInfoChar(buf, '$');
- escape_json(buf, jspGetString(v, NULL));
- break;
case jpiNumeric:
if (jspHasNext(v))
appendStringInfoChar(buf, '(');
if (printBracketes)
appendStringInfoChar(buf, ')');
break;
- case jpiLikeRegex:
- if (printBracketes)
- appendStringInfoChar(buf, '(');
-
- jspInitByBuffer(&elem, v->base, v->content.like_regex.expr);
- printJsonPathItem(buf, &elem, false,
- operationPriority(elem.type) <=
- operationPriority(v->type));
-
- appendStringInfoString(buf, " like_regex ");
-
- escape_json(buf, v->content.like_regex.pattern);
-
- if (v->content.like_regex.flags)
- {
- appendStringInfoString(buf, " flag \"");
-
- if (v->content.like_regex.flags & JSP_REGEX_ICASE)
- appendStringInfoChar(buf, 'i');
- if (v->content.like_regex.flags & JSP_REGEX_DOTALL)
- appendStringInfoChar(buf, 's');
- if (v->content.like_regex.flags & JSP_REGEX_MLINE)
- appendStringInfoChar(buf, 'm');
- if (v->content.like_regex.flags & JSP_REGEX_WSPACE)
- appendStringInfoChar(buf, 'x');
- if (v->content.like_regex.flags & JSP_REGEX_QUOTE)
- appendStringInfoChar(buf, 'q');
-
- appendStringInfoChar(buf, '"');
- }
-
- 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);
- printJsonPathItem(buf, &elem, false, false);
- appendStringInfoChar(buf, ')');
- break;
case jpiNot:
appendStringInfoString(buf, "!(");
jspGetArg(v, &elem);
printJsonPathItem(buf, &elem, false, false);
appendStringInfoString(buf, ") is unknown");
break;
- case jpiExists:
- appendStringInfoString(buf, "exists (");
+ case jpiPlus:
+ case jpiMinus:
+ if (printBracketes)
+ appendStringInfoChar(buf, '(');
+ appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
jspGetArg(v, &elem);
- printJsonPathItem(buf, &elem, false, false);
- appendStringInfoChar(buf, ')');
- break;
- case jpiCurrent:
- Assert(!inKey);
- appendStringInfoChar(buf, '@');
- break;
- case jpiRoot:
- Assert(!inKey);
- appendStringInfoChar(buf, '$');
- break;
- case jpiLast:
- appendStringInfoString(buf, "last");
+ printJsonPathItem(buf, &elem, false,
+ operationPriority(elem.type) <=
+ operationPriority(v->type));
+ if (printBracketes)
+ appendStringInfoChar(buf, ')');
break;
case jpiAnyArray:
appendStringInfoString(buf, "[*]");
v->content.anybounds.first,
v->content.anybounds.last);
break;
+ case jpiKey:
+ if (inKey)
+ appendStringInfoChar(buf, '.');
+ escape_json(buf, jspGetString(v, NULL));
+ break;
+ case jpiCurrent:
+ Assert(!inKey);
+ appendStringInfoChar(buf, '@');
+ break;
+ case jpiRoot:
+ Assert(!inKey);
+ appendStringInfoChar(buf, '$');
+ break;
+ case jpiVariable:
+ appendStringInfoChar(buf, '$');
+ escape_json(buf, jspGetString(v, NULL));
+ break;
+ case jpiFilter:
+ appendStringInfoString(buf, "?(");
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiExists:
+ appendStringInfoString(buf, "exists (");
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ appendStringInfoChar(buf, ')');
+ break;
case jpiType:
appendStringInfoString(buf, ".type()");
break;
case jpiKeyValue:
appendStringInfoString(buf, ".keyvalue()");
break;
+ case jpiLast:
+ appendStringInfoString(buf, "last");
+ break;
+ case jpiLikeRegex:
+ if (printBracketes)
+ appendStringInfoChar(buf, '(');
+
+ jspInitByBuffer(&elem, v->base, v->content.like_regex.expr);
+ printJsonPathItem(buf, &elem, false,
+ operationPriority(elem.type) <=
+ operationPriority(v->type));
+
+ appendStringInfoString(buf, " like_regex ");
+
+ escape_json(buf, v->content.like_regex.pattern);
+
+ if (v->content.like_regex.flags)
+ {
+ appendStringInfoString(buf, " flag \"");
+
+ if (v->content.like_regex.flags & JSP_REGEX_ICASE)
+ appendStringInfoChar(buf, 'i');
+ if (v->content.like_regex.flags & JSP_REGEX_DOTALL)
+ appendStringInfoChar(buf, 's');
+ if (v->content.like_regex.flags & JSP_REGEX_MLINE)
+ appendStringInfoChar(buf, 'm');
+ if (v->content.like_regex.flags & JSP_REGEX_WSPACE)
+ appendStringInfoChar(buf, 'x');
+ if (v->content.like_regex.flags & JSP_REGEX_QUOTE)
+ appendStringInfoChar(buf, 'q');
+
+ appendStringInfoChar(buf, '"');
+ }
+
+ if (printBracketes)
+ appendStringInfoChar(buf, ')');
+ break;
default:
elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
}
return "<=";
case jpiGreaterOrEqual:
return ">=";
- case jpiPlus:
case jpiAdd:
+ case jpiPlus:
return "+";
- case jpiMinus:
case jpiSub:
+ case jpiMinus:
return "-";
case jpiMul:
return "*";
return "/";
case jpiMod:
return "%";
- 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 jpiDouble:
+ return "double";
case jpiDatetime:
return "datetime";
+ case jpiKeyValue:
+ return "keyvalue";
+ case jpiStartsWith:
+ return "starts with";
+ case jpiLikeRegex:
+ return "like_regex";
default:
elog(ERROR, "unrecognized jsonpath item type: %d", type);
return NULL;
case jpiKeyValue:
case jpiLast:
break;
- case jpiKey:
case jpiString:
+ case jpiKey:
case jpiVariable:
read_int32(v->content.value.datalen, base, pos);
/* FALLTHROUGH */
break;
case jpiAnd:
case jpiOr:
- case jpiAdd:
- case jpiSub:
- case jpiMul:
- case jpiDiv:
- case jpiMod:
case jpiEqual:
case jpiNotEqual:
case jpiLess:
case jpiGreater:
case jpiLessOrEqual:
case jpiGreaterOrEqual:
+ case jpiAdd:
+ case jpiSub:
+ case jpiMul:
+ case jpiDiv:
+ case jpiMod:
case jpiStartsWith:
read_int32(v->content.args.left, base, pos);
read_int32(v->content.args.right, base, pos);
break;
- case jpiLikeRegex:
- read_int32(v->content.like_regex.flags, base, pos);
- read_int32(v->content.like_regex.expr, base, pos);
- read_int32(v->content.like_regex.patternlen, base, pos);
- v->content.like_regex.pattern = base + pos;
- break;
case jpiNot:
- case jpiExists:
case jpiIsUnknown:
+ case jpiExists:
case jpiPlus:
case jpiMinus:
case jpiFilter:
read_int32(v->content.anybounds.first, base, pos);
read_int32(v->content.anybounds.last, base, pos);
break;
+ case jpiLikeRegex:
+ read_int32(v->content.like_regex.flags, base, pos);
+ read_int32(v->content.like_regex.expr, base, pos);
+ read_int32(v->content.like_regex.patternlen, base, pos);
+ v->content.like_regex.pattern = base + pos;
+ break;
default:
elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
}
void
jspGetArg(JsonPathItem *v, JsonPathItem *a)
{
- Assert(v->type == jpiFilter ||
- v->type == jpiNot ||
+ Assert(v->type == jpiNot ||
v->type == jpiIsUnknown ||
- v->type == jpiExists ||
v->type == jpiPlus ||
v->type == jpiMinus ||
+ v->type == jpiFilter ||
+ v->type == jpiExists ||
v->type == jpiDatetime);
jspInitByBuffer(a, v->base, v->content.arg);
{
if (jspHasNext(v))
{
- Assert(v->type == jpiString ||
+ Assert(v->type == jpiNull ||
+ v->type == jpiString ||
v->type == jpiNumeric ||
v->type == jpiBool ||
- v->type == jpiNull ||
- v->type == jpiKey ||
- v->type == jpiAny ||
- v->type == jpiAnyArray ||
- v->type == jpiAnyKey ||
- v->type == jpiIndexArray ||
- v->type == jpiFilter ||
- v->type == jpiCurrent ||
- v->type == jpiExists ||
- v->type == jpiRoot ||
- v->type == jpiVariable ||
- v->type == jpiLast ||
+ v->type == jpiAnd ||
+ v->type == jpiOr ||
+ v->type == jpiNot ||
+ v->type == jpiIsUnknown ||
+ v->type == jpiEqual ||
+ v->type == jpiNotEqual ||
+ v->type == jpiLess ||
+ v->type == jpiGreater ||
+ v->type == jpiLessOrEqual ||
+ v->type == jpiGreaterOrEqual ||
v->type == jpiAdd ||
v->type == jpiSub ||
v->type == jpiMul ||
v->type == jpiMod ||
v->type == jpiPlus ||
v->type == jpiMinus ||
- v->type == jpiEqual ||
- v->type == jpiNotEqual ||
- v->type == jpiGreater ||
- v->type == jpiGreaterOrEqual ||
- v->type == jpiLess ||
- v->type == jpiLessOrEqual ||
- v->type == jpiAnd ||
- v->type == jpiOr ||
- v->type == jpiNot ||
- v->type == jpiIsUnknown ||
+ v->type == jpiAnyArray ||
+ v->type == jpiAnyKey ||
+ v->type == jpiIndexArray ||
+ v->type == jpiAny ||
+ v->type == jpiKey ||
+ v->type == jpiCurrent ||
+ v->type == jpiRoot ||
+ v->type == jpiVariable ||
+ v->type == jpiFilter ||
+ v->type == jpiExists ||
v->type == jpiType ||
v->type == jpiSize ||
v->type == jpiAbs ||
v->type == jpiDouble ||
v->type == jpiDatetime ||
v->type == jpiKeyValue ||
+ v->type == jpiLast ||
v->type == jpiStartsWith ||
v->type == jpiLikeRegex);
switch (jsp->type)
{
+ case jpiNull:
+ case jpiBool:
+ case jpiNumeric:
+ case jpiString:
+ case jpiVariable:
+ {
+ JsonbValue vbuf;
+ JsonbValue *v;
+ bool hasNext = jspGetNext(jsp, &elem);
+
+ if (!hasNext && !found && jsp->type != jpiVariable)
+ {
+ /*
+ * Skip evaluation, but not for variables. We must
+ * trigger an error for the missing variable.
+ */
+ res = jperOk;
+ break;
+ }
+
+ v = hasNext ? &vbuf : palloc(sizeof(*v));
+
+ baseObject = cxt->baseObject;
+ getJsonPathItem(cxt, jsp, v);
+
+ res = executeNextItem(cxt, jsp, &elem,
+ v, found, hasNext);
+ cxt->baseObject = baseObject;
+ }
+ break;
+
/* all boolean item types: */
case jpiAnd:
case jpiOr:
break;
}
- case jpiKey:
- if (JsonbType(jb) == jbvObject)
- {
- JsonbValue *v;
- JsonbValue key;
-
- key.type = jbvString;
- key.val.string.val = jspGetString(jsp, &key.val.string.len);
-
- v = findJsonbValueFromContainer(jb->val.binary.data,
- JB_FOBJECT, &key);
+ case jpiAdd:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_add_opt_error, found);
- if (v != NULL)
- {
- res = executeNextItem(cxt, jsp, NULL,
- v, found, false);
+ case jpiSub:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_sub_opt_error, found);
- /* free value if it was not added to found list */
- if (jspHasNext(jsp) || !found)
- pfree(v);
- }
- else if (!jspIgnoreStructuralErrors(cxt))
- {
- Assert(found);
+ case jpiMul:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_mul_opt_error, found);
- if (!jspThrowErrors(cxt))
- return jperError;
+ case jpiDiv:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_div_opt_error, found);
- ereport(ERROR,
- (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
- errmsg("JSON object does not contain key \"%s\"",
- pnstrdup(key.val.string.val,
- key.val.string.len))));
- }
- }
- else if (unwrap && JsonbType(jb) == jbvArray)
- return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
- else if (!jspIgnoreStructuralErrors(cxt))
- {
- Assert(found);
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
- errmsg("jsonpath member accessor can only be applied to an object"))));
- }
- break;
+ case jpiMod:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_mod_opt_error, found);
- case jpiRoot:
- jb = cxt->root;
- baseObject = setBaseObject(cxt, jb, 0);
- res = executeNextItem(cxt, jsp, NULL, jb, found, true);
- cxt->baseObject = baseObject;
- break;
+ case jpiPlus:
+ return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
- case jpiCurrent:
- res = executeNextItem(cxt, jsp, NULL, cxt->current,
- found, true);
- break;
+ case jpiMinus:
+ return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
+ found);
case jpiAnyArray:
if (JsonbType(jb) == jbvArray)
errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
break;
+ case jpiAnyKey:
+ if (JsonbType(jb) == jbvObject)
+ {
+ bool hasNext = jspGetNext(jsp, &elem);
+
+ if (jb->type != jbvBinary)
+ elog(ERROR, "invalid jsonb object type: %d", jb->type);
+
+ return executeAnyItem
+ (cxt, hasNext ? &elem : NULL,
+ jb->val.binary.data, found, 1, 1, 1,
+ false, jspAutoUnwrap(cxt));
+ }
+ else if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
+ else if (!jspIgnoreStructuralErrors(cxt))
+ {
+ Assert(found);
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
+ errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
+ }
+ break;
+
case jpiIndexArray:
if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
{
}
break;
- case jpiLast:
+ case jpiAny:
{
- JsonbValue tmpjbv;
- JsonbValue *lastjbv;
- int last;
bool hasNext = jspGetNext(jsp, &elem);
- if (cxt->innermostArraySize < 0)
- elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
-
- if (!hasNext && !found)
+ /* first try without any intermediate steps */
+ if (jsp->content.anybounds.first == 0)
{
- res = jperOk;
- break;
- }
-
- last = cxt->innermostArraySize - 1;
+ bool savedIgnoreStructuralErrors;
- lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
+ savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
+ cxt->ignoreStructuralErrors = true;
+ res = executeNextItem(cxt, jsp, &elem,
+ jb, found, true);
+ cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
- lastjbv->type = jbvNumeric;
- lastjbv->val.numeric = int64_to_numeric(last);
+ if (res == jperOk && !found)
+ break;
+ }
- res = executeNextItem(cxt, jsp, &elem,
- lastjbv, found, hasNext);
+ if (jb->type == jbvBinary)
+ res = executeAnyItem
+ (cxt, hasNext ? &elem : NULL,
+ jb->val.binary.data, found,
+ 1,
+ jsp->content.anybounds.first,
+ jsp->content.anybounds.last,
+ true, jspAutoUnwrap(cxt));
+ break;
}
- break;
- case jpiAnyKey:
+ case jpiKey:
if (JsonbType(jb) == jbvObject)
{
- bool hasNext = jspGetNext(jsp, &elem);
+ JsonbValue *v;
+ JsonbValue key;
- if (jb->type != jbvBinary)
- elog(ERROR, "invalid jsonb object type: %d", jb->type);
+ key.type = jbvString;
+ key.val.string.val = jspGetString(jsp, &key.val.string.len);
- return executeAnyItem
- (cxt, hasNext ? &elem : NULL,
- jb->val.binary.data, found, 1, 1, 1,
- false, jspAutoUnwrap(cxt));
+ v = findJsonbValueFromContainer(jb->val.binary.data,
+ JB_FOBJECT, &key);
+
+ if (v != NULL)
+ {
+ res = executeNextItem(cxt, jsp, NULL,
+ v, found, false);
+
+ /* free value if it was not added to found list */
+ if (jspHasNext(jsp) || !found)
+ pfree(v);
+ }
+ else if (!jspIgnoreStructuralErrors(cxt))
+ {
+ Assert(found);
+
+ if (!jspThrowErrors(cxt))
+ return jperError;
+
+ ereport(ERROR,
+ (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
+ errmsg("JSON object does not contain key \"%s\"",
+ pnstrdup(key.val.string.val,
+ key.val.string.len))));
+ }
}
else if (unwrap && JsonbType(jb) == jbvArray)
return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
{
Assert(found);
RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
- errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
+ (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
+ errmsg("jsonpath member accessor can only be applied to an object"))));
}
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 jpiCurrent:
+ res = executeNextItem(cxt, jsp, NULL, cxt->current,
+ found, true);
+ break;
- case jpiMinus:
- return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
- found);
+ case jpiRoot:
+ jb = cxt->root;
+ baseObject = setBaseObject(cxt, jb, 0);
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ cxt->baseObject = baseObject;
+ break;
case jpiFilter:
{
break;
}
- case jpiAny:
- {
- bool hasNext = jspGetNext(jsp, &elem);
-
- /* first try without any intermediate steps */
- if (jsp->content.anybounds.first == 0)
- {
- bool savedIgnoreStructuralErrors;
-
- savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
- cxt->ignoreStructuralErrors = true;
- res = executeNextItem(cxt, jsp, &elem,
- jb, found, true);
- cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
-
- if (res == jperOk && !found)
- break;
- }
-
- if (jb->type == jbvBinary)
- res = executeAnyItem
- (cxt, hasNext ? &elem : NULL,
- jb->val.binary.data, found,
- 1,
- jsp->content.anybounds.first,
- jsp->content.anybounds.last,
- true, jspAutoUnwrap(cxt));
- break;
- }
-
- case jpiNull:
- case jpiBool:
- case jpiNumeric:
- case jpiString:
- case jpiVariable:
- {
- JsonbValue vbuf;
- JsonbValue *v;
- bool hasNext = jspGetNext(jsp, &elem);
-
- if (!hasNext && !found && jsp->type != jpiVariable)
- {
- /*
- * Skip evaluation, but not for variables. We must
- * trigger an error for the missing variable.
- */
- res = jperOk;
- break;
- }
-
- v = hasNext ? &vbuf : palloc(sizeof(*v));
-
- baseObject = cxt->baseObject;
- getJsonPathItem(cxt, jsp, v);
-
- res = executeNextItem(cxt, jsp, &elem,
- v, found, hasNext);
- cxt->baseObject = baseObject;
- }
- break;
-
case jpiType:
{
JsonbValue *jbv = palloc(sizeof(*jbv));
return executeKeyValueMethod(cxt, jsp, jb, found);
+ case jpiLast:
+ {
+ JsonbValue tmpjbv;
+ JsonbValue *lastjbv;
+ int last;
+ bool hasNext = jspGetNext(jsp, &elem);
+
+ if (cxt->innermostArraySize < 0)
+ elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
+
+ if (!hasNext && !found)
+ {
+ res = jperOk;
+ break;
+ }
+
+ last = cxt->innermostArraySize - 1;
+
+ lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
+
+ lastjbv->type = jbvNumeric;
+ lastjbv->val.numeric = int64_to_numeric(last);
+
+ res = executeNextItem(cxt, jsp, &elem,
+ lastjbv, found, hasNext);
+ }
+ break;
+
default:
elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
}