if (PG_NARGS() == 4)
{
vars = PG_GETARG_JSONB_P(2);
silent = PG_GETARG_BOOL(3);
}
res = executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
countVariablesFromJsonb,
jb, !silent, NULL, tz);
PG_FREE_IF_COPY(jb, 0);
PG_FREE_IF_COPY(jp, 1);
if (jperIsError(res))
PG_RETURN_NULL();
PG_RETURN_BOOL(res == jperOk);
}
Datum
jsonb_path_exists(PG_FUNCTION_ARGS)
{ return jsonb_path_exists_internal(fcinfo, false);
}
Datum
jsonb_path_exists_tz(PG_FUNCTION_ARGS)
{ return jsonb_path_exists_internal(fcinfo, true);
}
/* *jsonb_path_exists_opr *Implementationofoperator"jsonb@?jsonpath"(2-argumentversionof *jsonb_path_exists()).
*/
Datum
jsonb_path_exists_opr(PG_FUNCTION_ARGS)
{ /* just call the other one -- it can handle both cases */ return jsonb_path_exists_internal(fcinfo, false);
}
if (JsonValueListLength(&found) == 1)
{
JsonbValue *jbv = JsonValueListHead(&found);
if (jbv->type == jbvBool)
PG_RETURN_BOOL(jbv->val.boolean);
if (jbv->type == jbvNull)
PG_RETURN_NULL();
}
if (!silent)
ereport(ERROR,
(errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
errmsg("single boolean result is expected")));
PG_RETURN_NULL();
}
Datum
jsonb_path_match(PG_FUNCTION_ARGS)
{ return jsonb_path_match_internal(fcinfo, false);
}
Datum
jsonb_path_match_tz(PG_FUNCTION_ARGS)
{ return jsonb_path_match_internal(fcinfo, true);
}
/* *jsonb_path_match_opr *Implementationofoperator"jsonb@@jsonpath"(2-argumentversionof *jsonb_path_match()).
*/
Datum
jsonb_path_match_opr(PG_FUNCTION_ARGS)
{ /* just call the other one -- it can handle both cases */ return jsonb_path_match_internal(fcinfo, false);
}
/* *jsonb_path_query *Executesjsonpathforgivenjsonbdocumentandreturnsresultas *rowset.
*/ static Datum
jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
{
FuncCallContext *funcctx;
List *found;
JsonbValue *v;
ListCell *c;
if (SRF_IS_FIRSTCALL())
{
JsonPath *jp;
Jsonb *jb;
MemoryContext oldcontext;
Jsonb *vars; bool silent;
JsonValueList found = {0};
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)
{ /* *Skipevaluation,butnotforvariables.Wemust *triggeranerrorforthemissingvariable.
*/
res = jperOk; break;
}
/* all boolean item types: */ case jpiAnd: case jpiOr: case jpiNot: case jpiIsUnknown: case jpiEqual: case jpiNotEqual: case jpiLess: case jpiGreater: case jpiLessOrEqual: case jpiGreaterOrEqual: case jpiExists: case jpiStartsWith: case jpiLikeRegex:
{
JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
res = appendBoolResult(cxt, jsp, found, st); 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 jpiAnyArray: if (JsonbType(jb) == jbvArray)
{ bool hasNext = jspGetNext(jsp, &elem);
res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
jb, found, jspAutoUnwrap(cxt));
} elseif (jspAutoWrap(cxt))
res = executeNextItem(cxt, jsp, NULL, jb, found, true); elseif (!jspIgnoreStructuralErrors(cxt))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
errmsg("jsonpath wildcard array accessor can only be applied to an array")))); break;
case jpiAnyKey: if (JsonbType(jb) == jbvObject)
{ bool hasNext = jspGetNext(jsp, &elem);
return executeAnyItem
(cxt, hasNext ? &elem : NULL,
jb->val.binary.data, found, 1, 1, 1, false, jspAutoUnwrap(cxt));
} elseif (unwrap && JsonbType(jb) == jbvArray) return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false); elseif (!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))
{ int innermostArraySize = cxt->innermostArraySize; int i; int size = JsonbArraySize(jb); bool singleton = size < 0; bool hasNext = jspGetNext(jsp, &elem);
if (singleton)
size = 1;
cxt->innermostArraySize = size; /* for LAST evaluation */
for (i = 0; i < jsp->content.array.nelems; i++)
{
JsonPathItem from;
JsonPathItem to;
int32 index;
int32 index_from;
int32 index_to; bool range = jspGetArraySubscript(jsp, &from,
&to, i);
res = getArrayIndex(cxt, &from, jb, &index_from);
if (jperIsError(res)) break;
if (range)
{
res = getArrayIndex(cxt, &to, jb, &index_to);
if (jperIsError(res)) break;
} else
index_to = index_from;
if (!jspIgnoreStructuralErrors(cxt) &&
(index_from < 0 ||
index_from > index_to ||
index_to >= size))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
errmsg("jsonpath array subscript is out of bounds"))));
if (index_from < 0)
index_from = 0;
if (index_to >= size)
index_to = size - 1;
res = jperNotFound;
for (index = index_from; index <= index_to; index++)
{
JsonbValue *v; bool copy;
if (singleton)
{
v = jb;
copy = true;
} else
{
v = getIthJsonbValueFromContainer(jb->val.binary.data,
(uint32) index);
if (v == NULL) continue;
copy = false;
}
if (!hasNext && !found) return jperOk;
res = executeNextItem(cxt, jsp, &elem, v, found,
copy);
if (jperIsError(res)) break;
if (res == jperOk && !found) break;
}
if (jperIsError(res)) break;
if (res == jperOk && !found) break;
}
cxt->innermostArraySize = innermostArraySize;
} elseif (!jspIgnoreStructuralErrors(cxt))
{
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
errmsg("jsonpath array accessor can only be applied to an array"))));
} break;
case jpiAny:
{ bool hasNext = jspGetNext(jsp, &elem);
/* first try without any intermediate steps */ if (jsp->content.anybounds.first == 0)
{ bool savedIgnoreStructuralErrors;
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);
} elseif (!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))));
}
} elseif (unwrap && JsonbType(jb) == jbvArray) return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false); elseif (!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 jpiCurrent:
res = executeNextItem(cxt, jsp, NULL, cxt->current,
found, true); break;
res = executeNextItem(cxt, jsp, NULL, jbv,
found, false);
} break;
case jpiSize:
{ int size = JsonbArraySize(jb);
if (size < 0)
{ if (!jspAutoWrap(cxt))
{ if (!jspIgnoreStructuralErrors(cxt))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
errmsg("jsonpath item method .%s() can only be applied to an array",
jspOperationName(jsp->type))))); break;
}
val = float8in_internal(tmp,
NULL, "double precision",
tmp,
(Node *) &escontext);
if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
tmp, jspOperationName(jsp->type), "double precision")))); if (isinf(val) || isnan(val))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
jspOperationName(jsp->type)))));
res = jperOk;
} elseif (jb->type == jbvString)
{ /* cast string as double */ double val; char *tmp = pnstrdup(jb->val.string.val,
jb->val.string.len);
ErrorSaveContext escontext = {T_ErrorSaveContext};
val = float8in_internal(tmp,
NULL, "double precision",
tmp,
(Node *) &escontext);
if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
tmp, jspOperationName(jsp->type), "double precision")))); if (isinf(val) || isnan(val))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
jspOperationName(jsp->type)))));
if (res == jperNotFound)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
jspOperationName(jsp->type)))));
res = executeNextItem(cxt, jsp, NULL, jb, found, true);
} break;
case jpiDatetime: case jpiDate: case jpiTime: case jpiTimeTz: case jpiTimestamp: case jpiTimestampTz: if (unwrap && JsonbType(jb) == jbvArray) return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
if (jb->type == jbvNumeric)
{ bool have_error;
int64 val;
val = numeric_int8_opt_error(jb->val.numeric, &have_error); if (have_error)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
DatumGetCString(DirectFunctionCall1(numeric_out,
NumericGetDatum(jb->val.numeric))),
jspOperationName(jsp->type), "bigint"))));
datum = Int64GetDatum(val);
res = jperOk;
} elseif (jb->type == jbvString)
{ /* cast string as bigint */ char *tmp = pnstrdup(jb->val.string.val,
jb->val.string.len);
ErrorSaveContext escontext = {T_ErrorSaveContext}; bool noerr;
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
tmp, jspOperationName(jsp->type), "bigint"))));
res = jperOk;
}
if (res == jperNotFound)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
jspOperationName(jsp->type)))));
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
tmp, jspOperationName(jsp->type), "boolean"))));
res = jperOk;
} elseif (jb->type == jbvString)
{ /* cast string as boolean */ char *tmp = pnstrdup(jb->val.string.val,
jb->val.string.len);
if (!parse_bool(tmp, &bval))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
tmp, jspOperationName(jsp->type), "boolean"))));
res = jperOk;
}
if (res == jperNotFound)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("jsonpath item method .%s() can only be applied to a boolean, string, or numeric value",
jspOperationName(jsp->type)))));
if (jb->type == jbvNumeric)
{
num = jb->val.numeric; if (numeric_is_nan(num) || numeric_is_inf(num))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
jspOperationName(jsp->type)))));
if (jsp->type == jpiDecimal)
numstr = DatumGetCString(DirectFunctionCall1(numeric_out,
NumericGetDatum(num)));
res = jperOk;
} elseif (jb->type == jbvString)
{ /* cast string as number */
Datum datum; bool noerr;
ErrorSaveContext escontext = {T_ErrorSaveContext};
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
numstr, jspOperationName(jsp->type), "numeric"))));
num = DatumGetNumeric(datum); if (numeric_is_nan(num) || numeric_is_inf(num))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
jspOperationName(jsp->type)))));
res = jperOk;
}
if (res == jperNotFound)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
jspOperationName(jsp->type)))));
/* *Ifwehavearguments,thentheymustbetheprecisionand *optionalscaleusedin.decimal().Convertthemtothe *typmodequivalentandthentruncatethenumericvalueper *thistypmoddetails.
*/ if (jsp->type == jpiDecimal && jsp->content.args.left)
{
Datum numdatum;
Datum 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); if (elem.type != jpiNumeric)
elog(ERROR, "invalid jsonpath item type for .decimal() precision");
precision = numeric_int4_opt_error(jspGetNumeric(&elem),
&have_error); if (have_error)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("precision of jsonpath item method .%s() is out of range for type integer",
jspOperationName(jsp->type)))));
if (jsp->content.args.right)
{
jspGetRightArg(jsp, &elem); if (elem.type != jpiNumeric)
elog(ERROR, "invalid jsonpath item type for .decimal() scale");
scale = numeric_int4_opt_error(jspGetNumeric(&elem),
&have_error); if (have_error)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("scale of jsonpath item method .%s() is out of range for type integer",
jspOperationName(jsp->type)))));
}
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
numstr, jspOperationName(jsp->type), "numeric"))));
num = DatumGetNumeric(numdatum);
pfree(arrtypmod);
}
if (jb->type == jbvNumeric)
{ bool have_error;
int32 val;
val = numeric_int4_opt_error(jb->val.numeric, &have_error); if (have_error)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
DatumGetCString(DirectFunctionCall1(numeric_out,
NumericGetDatum(jb->val.numeric))),
jspOperationName(jsp->type), "integer"))));
datum = Int32GetDatum(val);
res = jperOk;
} elseif (jb->type == jbvString)
{ /* cast string as integer */ char *tmp = pnstrdup(jb->val.string.val,
jb->val.string.len);
ErrorSaveContext escontext = {T_ErrorSaveContext}; bool noerr;
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
tmp, jspOperationName(jsp->type), "integer"))));
res = jperOk;
}
if (res == jperNotFound)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
jspOperationName(jsp->type)))));
JsonEncodeDateTime(buf,
jb->val.datetime.value,
jb->val.datetime.typid,
&jb->val.datetime.tz);
tmp = pstrdup(buf);
} break; case jbvNull: case jbvArray: case jbvObject: case jbvBinary:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("jsonpath item method .%s() can only be applied to a boolean, string, numeric, or datetime value",
jspOperationName(jsp->type))))); break;
}
jb = &jbv;
Assert(tmp != NULL); /* We must have set tmp above */
jb->val.string.val = tmp;
jb->val.string.len = strlen(jb->val.string.val);
jb->type = jbvString;
res = executeNextItem(cxt, jsp, NULL, jb, found, true);
} break;
case jpiIsUnknown:
jspGetArg(jsp, &larg);
res = executeBoolItem(cxt, &larg, jb, false); return res == jpbUnknown ? jpbTrue : jpbFalse;
case jpiEqual: case jpiNotEqual: case jpiLess: case jpiGreater: case jpiLessOrEqual: case jpiGreaterOrEqual:
jspGetLeftArg(jsp, &larg);
jspGetRightArg(jsp, &rarg); return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
executeComparison, cxt);
/* Left argument is always auto-unwrapped. */
res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq); if (jperIsError(res)) return jpbUnknown;
if (rarg)
{ /* Right argument is conditionally auto-unwrapped. */
res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
unwrapRightArg, &rseq); if (jperIsError(res)) return jpbUnknown;
}
JsonValueListInitIterator(&lseq, &lseqit); while ((lval = JsonValueListNext(&lseq, &lseqit)))
{
JsonValueListIterator rseqit;
JsonbValue *rval; bool first = true;
/* Loop over right arg sequence or do single pass otherwise */ while (rarg ? (rval != NULL) : first)
{
JsonPathBool res = exec(pred, lval, rval, param);
if (res == jpbUnknown)
{ if (jspStrictAbsenceOfErrors(cxt)) return jpbUnknown;
if (JsonValueListLength(&lseq) != 1 ||
!(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
errmsg("left operand of jsonpath operator %s is not a single numeric value",
jspOperationName(jsp->type)))));
if (JsonValueListLength(&rseq) != 1 ||
!(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
errmsg("right operand of jsonpath operator %s is not a single numeric value",
jspOperationName(jsp->type)))));
if (jspThrowErrors(cxt))
{
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) return jperOk;
JsonValueListInitIterator(&seq, &it); while ((val = JsonValueListNext(&seq, &it)))
{ if ((val = getScalar(val, jbvNumeric)))
{ if (!found && !hasNext) return jperOk;
} else
{ if (!found && !hasNext) continue; /* skip non-numerics processing */
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
errmsg("operand of unary jsonpath operator %s is not a numeric value",
jspOperationName(jsp->type)))));
}
if (func)
val->val.numeric =
DatumGetNumeric(DirectFunctionCall1(func,
NumericGetDatum(val->val.numeric)));
if (!(jb = getScalar(jb, jbvNumeric)))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("jsonpath item method .%s() can only be applied to a numeric value",
jspOperationName(jsp->type)))));
datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
if (!jspGetNext(jsp, &next) && !found) return jperOk;
/* *Implementationofthe.datetime()andrelatedmethods. * *Convertsastringintoadate/timevalue.Theactualtypeisdeterminedat *runtime. *Ifanargumentisprovided,thisargumentisusedasatemplatestring. *Otherwise,thefirstfittingISOformatisselected. * *.date(),.time(),.time_tz(),.timestamp(),.timestamp_tz()methodsdon't *haveaformat,soISOformatisused.However,exceptfor.date(),theyall *takeanoptionaltimeprecision.
*/ static JsonPathExecResult
executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
JsonbValue *jb, JsonValueList *found)
{
JsonbValue jbvbuf;
Datum value;
text *datetime;
Oid collid;
Oid typid;
int32 typmod = -1; int tz = 0; bool hasNext;
JsonPathExecResult res = jperNotFound;
JsonPathItem elem;
int32 time_precision = -1;
if (!(jb = getScalar(jb, jbvString)))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
errmsg("jsonpath item method .%s() can only be applied to a string",
jspOperationName(jsp->type)))));
if (elem.type != jpiNumeric)
elog(ERROR, "invalid jsonpath item type for %s argument",
jspOperationName(jsp->type));
time_precision = numeric_int4_opt_error(jspGetNumeric(&elem),
&have_error); if (have_error)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
errmsg("time precision of jsonpath item method .%s() is out of range for type integer",
jspOperationName(jsp->type)))));
}
/* loop until datetime format fits */ for (i = 0; i < lengthof(fmt_str); i++)
{
ErrorSaveContext escontext = {T_ErrorSaveContext};
if (!fmt_txt[i])
{
MemoryContext oldcxt =
MemoryContextSwitchTo(TopMemoryContext);
if (!escontext.error_occurred)
{
res = jperOk; break;
}
}
if (res == jperNotFound)
{ if (jsp->type == jpiDatetime)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
errmsg("%s format is not recognized: \"%s\"", "datetime", text_to_cstring(datetime)),
errhint("Use a datetime template argument to specify the input data format.")))); else
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
errmsg("%s format is not recognized: \"%s\"",
jspOperationName(jsp->type), text_to_cstring(datetime)))));
}
}
/* *parse_datetime()processestheentireinputstringperthetemplateor *ISOformatandreturnstheDatuminbestfitteddatetimetype.So,if *thiscallisforaspecificdatatype,thenwedotheconversionhere. *Throwanerrorforincompatibletypes.
*/ switch (jsp->type)
{ case jpiDatetime: /* Nothing to do for DATETIME */ break; case jpiDate:
{ /* Convert result type to date */ switch (typid)
{ case DATEOID: /* Nothing to do for DATE */ break; case TIMEOID: case TIMETZOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
errmsg("%s format is not recognized: \"%s\"", "date", text_to_cstring(datetime))))); break; case TIMESTAMPOID:
value = DirectFunctionCall1(timestamp_date,
value); break; case TIMESTAMPTZOID:
checkTimezoneIsUsedForCast(cxt->useTz, "timestamptz", "date");
value = DirectFunctionCall1(timestamptz_date,
value); break; default:
elog(ERROR, "type with oid %u not supported", typid);
}
typid = DATEOID;
} break; case jpiTime:
{ /* Convert result type to time without time zone */ switch (typid)
{ case DATEOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
errmsg("%s format is not recognized: \"%s\"", "time", text_to_cstring(datetime))))); break; case TIMEOID: /* Nothing to do for TIME */ break; case TIMETZOID:
checkTimezoneIsUsedForCast(cxt->useTz, "timetz", "time");
value = DirectFunctionCall1(timetz_time,
value); break; case TIMESTAMPOID:
value = DirectFunctionCall1(timestamp_time,
value); break; case TIMESTAMPTZOID:
checkTimezoneIsUsedForCast(cxt->useTz, "timestamptz", "time");
value = DirectFunctionCall1(timestamptz_time,
value); break; default:
elog(ERROR, "type with oid %u not supported", typid);
}
/* Force the user-given time precision, if any */ if (time_precision != -1)
{
TimeADT result;
/* Get a warning when precision is reduced */
time_precision = anytime_typmod_check(false,
time_precision);
result = DatumGetTimeADT(value);
AdjustTimeForTypmod(&result, time_precision);
value = TimeADTGetDatum(result);
/* Update the typmod value with the user-given precision */
typmod = time_precision;
}
typid = TIMEOID;
} break; case jpiTimeTz:
{ /* Convert result type to time with time zone */ switch (typid)
{ case DATEOID: case TIMESTAMPOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
errmsg("%s format is not recognized: \"%s\"", "time_tz", text_to_cstring(datetime))))); break; case TIMEOID:
checkTimezoneIsUsedForCast(cxt->useTz, "time", "timetz");
value = DirectFunctionCall1(time_timetz,
value); break; case TIMETZOID: /* Nothing to do for TIMETZ */ break; case TIMESTAMPTZOID:
value = DirectFunctionCall1(timestamptz_timetz,
value); break; default:
elog(ERROR, "type with oid %u not supported", typid);
}
/* Force the user-given time precision, if any */ if (time_precision != -1)
{
TimeTzADT *result;
/* Get a warning when precision is reduced */
time_precision = anytime_typmod_check(true,
time_precision);
result = DatumGetTimeTzADTP(value);
AdjustTimeForTypmod(&result->time, time_precision);
value = TimeTzADTPGetDatum(result);
/* Update the typmod value with the user-given precision */
typmod = time_precision;
}
typid = TIMETZOID;
} break; case jpiTimestamp:
{ /* Convert result type to timestamp without time zone */ switch (typid)
{ case DATEOID:
value = DirectFunctionCall1(date_timestamp,
value); break; case TIMEOID: case TIMETZOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
errmsg("%s format is not recognized: \"%s\"", "timestamp", text_to_cstring(datetime))))); break; case TIMESTAMPOID: /* Nothing to do for TIMESTAMP */ break; case TIMESTAMPTZOID:
checkTimezoneIsUsedForCast(cxt->useTz, "timestamptz", "timestamp");
value = DirectFunctionCall1(timestamptz_timestamp,
value); break; default:
elog(ERROR, "type with oid %u not supported", typid);
}
/* Force the user-given time precision, if any */ if (time_precision != -1)
{
Timestamp result;
ErrorSaveContext escontext = {T_ErrorSaveContext};
/* Get a warning when precision is reduced */
time_precision = anytimestamp_typmod_check(false,
time_precision);
result = DatumGetTimestamp(value);
AdjustTimestampForTypmod(&result, time_precision,
(Node *) &escontext); if (escontext.error_occurred) /* should not happen */
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
errmsg("time precision of jsonpath item method .%s() is invalid",
jspOperationName(jsp->type)))));
value = TimestampGetDatum(result);
/* Update the typmod value with the user-given precision */
typmod = time_precision;
}
value = DirectFunctionCall1(date_timestamptz,
value); break; case TIMEOID: case TIMETZOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
errmsg("%s format is not recognized: \"%s\"", "timestamp_tz", text_to_cstring(datetime))))); break; case TIMESTAMPOID:
checkTimezoneIsUsedForCast(cxt->useTz, "timestamp", "timestamptz");
value = DirectFunctionCall1(timestamp_timestamptz,
value); break; case TIMESTAMPTZOID: /* Nothing to do for TIMESTAMPTZ */ break; default:
elog(ERROR, "type with oid %u not supported", typid);
}
/* Force the user-given time precision, if any */ if (time_precision != -1)
{
Timestamp result;
ErrorSaveContext escontext = {T_ErrorSaveContext};
/* Get a warning when precision is reduced */
time_precision = anytimestamp_typmod_check(true,
time_precision);
result = DatumGetTimestampTz(value);
AdjustTimestampForTypmod(&result, time_precision,
(Node *) &escontext); if (escontext.error_occurred) /* should not happen */
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
errmsg("time precision of jsonpath item method .%s() is invalid",
jspOperationName(jsp->type)))));
value = TimestampTzGetDatum(result);
/* Update the typmod value with the user-given precision */
typmod = time_precision;
}
if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
errmsg("jsonpath item method .%s() can only be applied to an object",
jspOperationName(jsp->type)))));
jbc = jb->val.binary.data;
if (!JsonContainerSize(jbc)) return jperNotFound; /* no key-value pairs */
/* construct object id from its base object and offset inside that */
id = jb->type != jbvBinary ? 0 :
(int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
JsonItemFromDatum(JsonbPGetDatum(jb), JSONBOID, -1, res); break;
} default:
ereport(ERROR,
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not convert value of type %s to jsonpath",
format_type_be(typid)));
}
}
/* Initialize numeric value from the given datum */ staticvoid
JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num)
{
jbv->type = jbvNumeric;
jbv->val.numeric = DatumGetNumeric(num);
}
if (vars && !JsonContainerIsObject(&vars->root))
{
ereport(ERROR,
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("\"vars\" argument is not an object"),
errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object."));
}
/* count of base objects */ return vars != NULL ? 1 : 0;
}
/**************** Support functions for JsonPath execution *****************/
if (JsonValueListLength(&found) != 1 ||
!(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
errmsg("jsonpath array subscript is not a single numeric value"))));
if (have_error)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
errmsg("jsonpath array subscript is out of integer range"))));
return jperOk;
}
/* Save base object and its id needed for the execution of .keyvalue(). */ static JsonBaseObjectInfo
setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
{
JsonBaseObjectInfo baseObject = cxt->baseObject;
/* *Returnsjbv*typeofJsonbValue.Note,itneverreturnsjbvBinaryasis.
*/ staticint
JsonbType(JsonbValue *jb)
{ int type = jb->type;
if (jb->type == jbvBinary)
{
JsonbContainer *jbc = jb->val.binary.data;
/* Scalars should be always extracted during jsonpath execution. */
Assert(!JsonContainerIsScalar(jbc));
if (JsonContainerIsObject(jbc))
type = jbvObject; elseif (JsonContainerIsArray(jbc))
type = jbvArray; else
elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
}
return type;
}
/* Get scalar of given type or NULL on type mismatch */ static JsonbValue *
getScalar(JsonbValue *scalar, enum jbvType type)
{ /* Scalars should be always extracted during jsonpath execution. */
Assert(scalar->type != jbvBinary ||
!JsonContainerIsScalar(scalar->val.binary.data));
return scalar->type == type ? scalar : NULL;
}
/* Construct a JSON array from the item list */ static JsonbValue *
wrapItemsInArray(const JsonValueList *items)
{
JsonbParseState *ps = NULL;
JsonValueListIterator it;
JsonbValue *jbv;
pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
JsonValueListInitIterator(items, &it); while ((jbv = JsonValueListNext(items, &it)))
pushJsonbValue(&ps, WJB_ELEM, jbv);
/* Check if the timezone required for casting from type1 to type2 is used */ staticvoid
checkTimezoneIsUsedForCast(bool useTz, constchar *type1, constchar *type2)
{ if (!useTz)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot convert value from %s to %s without time zone usage",
type1, type2),
errhint("Use *_tz() function for time zone support.")));
}
/* Convert time datum to timetz datum */ static Datum
castTimeToTimeTz(Datum time, bool useTz)
{
checkTimezoneIsUsedForCast(useTz, "time", "timetz");
if (wrap) return JsonbPGetDatum(JsonbValueToJsonb(wrapItemsInArray(&found)));
/* No wrapping means only one item is expected. */ if (count > 1)
{ if (error)
{
*error = true; return (Datum) 0;
}
if (column_name)
ereport(ERROR,
(errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
errmsg("JSON path expression for column \"%s\" must return single item when no wrapper is requested",
column_name),
errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array."))); else
ereport(ERROR,
(errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
errmsg("JSON path expression in JSON_QUERY must return single item when no wrapper is requested"),
errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
}
if (singleton) return JsonbPGetDatum(JsonbValueToJsonb(singleton));
/* JSON_VALUE expects to get only singletons. */ if (count > 1)
{ if (error)
{
*error = true; return NULL;
}
if (column_name)
ereport(ERROR,
(errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
errmsg("JSON path expression for column \"%s\" must return single scalar item",
column_name))); else
ereport(ERROR,
(errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
errmsg("JSON path expression in JSON_VALUE must return single scalar item")));
}
res = JsonValueListHead(&found); if (res->type == jbvBinary && JsonContainerIsScalar(res->val.binary.data))
JsonbExtractScalar(res->val.binary.data, res);
/* JSON_VALUE expects to get only scalars. */ if (!IsAJsonbScalar(res))
{ if (error)
{
*error = true; return NULL;
}
if (column_name)
ereport(ERROR,
(errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
errmsg("JSON path expression for column \"%s\" must return single scalar item",
column_name))); else
ereport(ERROR,
(errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
errmsg("JSON path expression in JSON_VALUE must return single scalar item")));
}
if (!IsA(state, TableFuncScanState))
elog(ERROR, "%s called with invalid TableFuncScanState", fname);
result = (JsonTableExecContext *) state->opaque; if (result->magic != JSON_TABLE_EXEC_CONTEXT_MAGIC)
elog(ERROR, "%s called with invalid TableFuncScanState", fname);
res = executeJsonPath(planstate->path, planstate->args,
GetJsonPathVar, CountJsonPathVars,
js, scan->errorOnError,
&planstate->found, true);
MemoryContextSwitchTo(oldcxt);
if (jperIsError(res))
{
Assert(!scan->errorOnError);
JsonValueListClear(&planstate->found);
}
/* Reset plan iterator to the beginning of the item list */
JsonValueListInitIterator(&planstate->found, &planstate->iter);
planstate->current.value = PointerGetDatum(NULL);
planstate->current.isnull = true;
planstate->ordinal = 0;
}
/* Process nested plan(s), if any. */ if (planstate->nested)
{ /* Re-evaluate the nested path using the above parent row. */
JsonTableResetNestedPlan(planstate->nested);
/* Fetch row from left sibling. */ if (!JsonTablePlanNextRow(planstate->left))
{ /* *Leftsiblingranoutofrows,sostartfetchingfromtheright *sibling.
*/ if (!JsonTablePlanNextRow(planstate->right))
{ /* Right sibling ran out of rows too, so there are no more rows. */ returnfalse;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.