Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3be04dc

Browse files
akorotkovNikita Glukhov
authored and
Nikita Glukhov
committed
Improve mutability check for SQL/JSON query functions
1 parent 2c5b48a commit 3be04dc

File tree

8 files changed

+378
-103
lines changed

8 files changed

+378
-103
lines changed

src/backend/optimizer/util/clauses.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include "utils/fmgroids.h"
5252
#include "utils/json.h"
5353
#include "utils/jsonb.h"
54+
#include "utils/jsonpath.h"
5455
#include "utils/lsyscache.h"
5556
#include "utils/memutils.h"
5657
#include "utils/syscache.h"
@@ -689,6 +690,27 @@ contain_mutable_functions_walker(Node *node, void *context)
689690
/* Check all subnodes */
690691
}
691692

693+
if (IsA(node, JsonExpr))
694+
{
695+
JsonExpr *jexpr = castNode(JsonExpr, node);
696+
697+
if (IsA(jexpr->path_spec, Const))
698+
{
699+
Const *c = castNode(Const, jexpr->path_spec);
700+
701+
Assert(c->consttype == JSONPATHOID);
702+
if (c->constisnull)
703+
return false;
704+
705+
return jspIsMutable(DatumGetJsonPathP(c->constvalue),
706+
jexpr->passing_names, jexpr->passing_values);
707+
}
708+
else
709+
{
710+
return true;
711+
}
712+
}
713+
692714
if (IsA(node, SQLValueFunction))
693715
{
694716
/* all variants of SQLValueFunction are stable */

src/backend/utils/adt/formatting.c

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,11 +1018,6 @@ typedef struct NUMProc
10181018
*L_currency_symbol;
10191019
} NUMProc;
10201020

1021-
/* Return flags for DCH_from_char() */
1022-
#define DCH_DATED 0x01
1023-
#define DCH_TIMED 0x02
1024-
#define DCH_ZONED 0x04
1025-
10261021
/* ----------
10271022
* Functions
10281023
* ----------
@@ -6624,3 +6619,43 @@ float8_to_char(PG_FUNCTION_ARGS)
66246619
NUM_TOCHAR_finish;
66256620
PG_RETURN_TEXT_P(result);
66266621
}
6622+
6623+
int
6624+
datetime_format_flags(const char *fmt_str, bool *have_error)
6625+
{
6626+
bool incache;
6627+
int fmt_len = strlen(fmt_str);
6628+
int result;
6629+
FormatNode *format;
6630+
6631+
if (fmt_len > DCH_CACHE_SIZE)
6632+
{
6633+
/*
6634+
* Allocate new memory if format picture is bigger than static cache
6635+
* and do not use cache (call parser always)
6636+
*/
6637+
incache = false;
6638+
6639+
format = (FormatNode *) palloc((fmt_len + 1) * sizeof(FormatNode));
6640+
6641+
parse_format(format, fmt_str, DCH_keywords,
6642+
DCH_suff, DCH_index, DCH_FLAG, NULL);
6643+
}
6644+
else
6645+
{
6646+
/*
6647+
* Use cache buffers
6648+
*/
6649+
DCHCacheEntry *ent = DCH_cache_fetch(fmt_str, false);
6650+
6651+
incache = true;
6652+
format = ent->format;
6653+
}
6654+
6655+
result = DCH_datetime_type(format, have_error);
6656+
6657+
if (!incache)
6658+
pfree(format);
6659+
6660+
return result;
6661+
}

src/backend/utils/adt/jsonpath.c

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@
6767
#include "lib/stringinfo.h"
6868
#include "libpq/pqformat.h"
6969
#include "miscadmin.h"
70+
#include "nodes/nodeFuncs.h"
7071
#include "utils/builtins.h"
72+
#include "utils/formatting.h"
7173
#include "utils/json.h"
7274
#include "utils/jsonpath.h"
7375

@@ -1073,3 +1075,260 @@ jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to,
10731075

10741076
return true;
10751077
}
1078+
1079+
/* SQL/JSON datatype status: */
1080+
typedef enum JsonPathDatatypeStatus
1081+
{
1082+
jpdsNonDateTime, /* null, bool, numeric, string, array, object */
1083+
jpdsDateTime, /* unknown datetime type */
1084+
jpdsDateTimeZoned, /* timetz, timestamptz */
1085+
jpdsDateTimeNonZoned /* time, timestamp, date */
1086+
} JsonPathDatatypeStatus;
1087+
1088+
/* Context for jspIsMutableWalker() */
1089+
typedef struct JsonPathMutableContext
1090+
{
1091+
List *varnames; /* list of variable names */
1092+
List *varexprs; /* list of variable expressions */
1093+
JsonPathDatatypeStatus current; /* status of @ item */
1094+
bool lax; /* jsonpath is lax or strict */
1095+
bool mutable; /* resulting mutability status */
1096+
} JsonPathMutableContext;
1097+
1098+
/*
1099+
* Recursive walker for jspIsMutable()
1100+
*/
1101+
static JsonPathDatatypeStatus
1102+
jspIsMutableWalker(JsonPathItem *jpi, JsonPathMutableContext *cxt)
1103+
{
1104+
JsonPathItem next;
1105+
JsonPathDatatypeStatus status = jpdsNonDateTime;
1106+
1107+
while (!cxt->mutable)
1108+
{
1109+
JsonPathItem arg;
1110+
JsonPathDatatypeStatus leftStatus;
1111+
JsonPathDatatypeStatus rightStatus;
1112+
1113+
switch (jpi->type)
1114+
{
1115+
case jpiRoot:
1116+
Assert(status == jpdsNonDateTime);
1117+
break;
1118+
1119+
case jpiCurrent:
1120+
Assert(status == jpdsNonDateTime);
1121+
status = cxt->current;
1122+
break;
1123+
1124+
case jpiFilter:
1125+
{
1126+
JsonPathDatatypeStatus prevStatus = cxt->current;
1127+
1128+
cxt->current = status;
1129+
jspGetArg(jpi, &arg);
1130+
jspIsMutableWalker(&arg, cxt);
1131+
1132+
cxt->current = prevStatus;
1133+
break;
1134+
}
1135+
1136+
case jpiVariable:
1137+
{
1138+
int32 len;
1139+
const char *name = jspGetString(jpi, &len);
1140+
ListCell *lc1;
1141+
ListCell *lc2;
1142+
1143+
Assert(status == jpdsNonDateTime);
1144+
1145+
forboth(lc1, cxt->varnames, lc2, cxt->varexprs)
1146+
{
1147+
Value *varname = lfirst(lc1);
1148+
Node *varexpr = lfirst(lc2);
1149+
1150+
Assert(IsA(varname, String));
1151+
1152+
if (strncmp(varname->val.str, name, len))
1153+
continue;
1154+
1155+
switch (exprType(varexpr))
1156+
{
1157+
case DATEOID:
1158+
case TIMEOID:
1159+
case TIMESTAMPOID:
1160+
status = jpdsDateTimeNonZoned;
1161+
break;
1162+
1163+
case TIMETZOID:
1164+
case TIMESTAMPTZOID:
1165+
status = jpdsDateTimeZoned;
1166+
break;
1167+
1168+
default:
1169+
status = jpdsNonDateTime;
1170+
break;
1171+
}
1172+
1173+
break;
1174+
}
1175+
break;
1176+
}
1177+
1178+
case jpiEqual:
1179+
case jpiNotEqual:
1180+
case jpiLess:
1181+
case jpiGreater:
1182+
case jpiLessOrEqual:
1183+
case jpiGreaterOrEqual:
1184+
Assert(status == jpdsNonDateTime);
1185+
jspGetLeftArg(jpi, &arg);
1186+
leftStatus = jspIsMutableWalker(&arg, cxt);
1187+
1188+
jspGetRightArg(jpi, &arg);
1189+
rightStatus = jspIsMutableWalker(&arg, cxt);
1190+
1191+
/*
1192+
* Comparison of datetime type with different timezone status
1193+
* is mutable.
1194+
*/
1195+
if (leftStatus != jpdsNonDateTime &&
1196+
rightStatus != jpdsNonDateTime &&
1197+
(leftStatus == jpdsDateTime ||
1198+
rightStatus == jpdsDateTime ||
1199+
leftStatus != rightStatus))
1200+
cxt->mutable = true;
1201+
break;
1202+
1203+
case jpiNot:
1204+
case jpiIsUnknown:
1205+
case jpiExists:
1206+
case jpiPlus:
1207+
case jpiMinus:
1208+
Assert(status == jpdsNonDateTime);
1209+
jspGetArg(jpi, &arg);
1210+
jspIsMutableWalker(&arg, cxt);
1211+
break;
1212+
1213+
case jpiAnd:
1214+
case jpiOr:
1215+
case jpiAdd:
1216+
case jpiSub:
1217+
case jpiMul:
1218+
case jpiDiv:
1219+
case jpiMod:
1220+
case jpiStartsWith:
1221+
Assert(status == jpdsNonDateTime);
1222+
jspGetLeftArg(jpi, &arg);
1223+
jspIsMutableWalker(&arg, cxt);
1224+
jspGetRightArg(jpi, &arg);
1225+
jspIsMutableWalker(&arg, cxt);
1226+
break;
1227+
1228+
case jpiIndexArray:
1229+
for (int i = 0; i < jpi->content.array.nelems; i++)
1230+
{
1231+
JsonPathItem from;
1232+
JsonPathItem to;
1233+
1234+
if (jspGetArraySubscript(jpi, &from, &to, i))
1235+
jspIsMutableWalker(&to, cxt);
1236+
1237+
jspIsMutableWalker(&from, cxt);
1238+
}
1239+
/* FALLTHROUGH */
1240+
1241+
case jpiAnyArray:
1242+
if (!cxt->lax)
1243+
status = jpdsNonDateTime;
1244+
break;
1245+
1246+
case jpiAny:
1247+
if (jpi->content.anybounds.first > 0)
1248+
status = jpdsNonDateTime;
1249+
break;
1250+
1251+
case jpiDatetime:
1252+
if (jpi->content.arg)
1253+
{
1254+
char *template;
1255+
int flags;
1256+
1257+
jspGetArg(jpi, &arg);
1258+
if (arg.type != jpiString)
1259+
{
1260+
status = jpdsNonDateTime;
1261+
break; /* there will be runtime error */
1262+
}
1263+
1264+
template = jspGetString(&arg, NULL);
1265+
flags = datetime_format_flags(template, NULL);
1266+
if (flags & DCH_ZONED)
1267+
status = jpdsDateTimeZoned;
1268+
else
1269+
status = jpdsDateTimeNonZoned;
1270+
}
1271+
else
1272+
{
1273+
status = jpdsDateTime;
1274+
}
1275+
break;
1276+
1277+
case jpiLikeRegex:
1278+
Assert(status == jpdsNonDateTime);
1279+
jspInitByBuffer(&arg, jpi->base, jpi->content.like_regex.expr);
1280+
jspIsMutableWalker(&arg, cxt);
1281+
break;
1282+
1283+
/* literals */
1284+
case jpiNull:
1285+
case jpiString:
1286+
case jpiNumeric:
1287+
case jpiBool:
1288+
/* accessors */
1289+
case jpiKey:
1290+
case jpiAnyKey:
1291+
/* special items */
1292+
case jpiSubscript:
1293+
case jpiLast:
1294+
/* item methods */
1295+
case jpiType:
1296+
case jpiSize:
1297+
case jpiAbs:
1298+
case jpiFloor:
1299+
case jpiCeiling:
1300+
case jpiDouble:
1301+
case jpiKeyValue:
1302+
status = jpdsNonDateTime;
1303+
break;
1304+
}
1305+
1306+
if (!jspGetNext(jpi, &next))
1307+
break;
1308+
1309+
jpi = &next;
1310+
}
1311+
1312+
return status;
1313+
}
1314+
1315+
/*
1316+
* Check whether jsonpath expression is immutable or not.
1317+
*/
1318+
bool
1319+
jspIsMutable(JsonPath *path, List *varnames, List *varexprs)
1320+
{
1321+
JsonPathMutableContext cxt;
1322+
JsonPathItem jpi;
1323+
1324+
cxt.varnames = varnames;
1325+
cxt.varexprs = varexprs;
1326+
cxt.current = jpdsNonDateTime;
1327+
cxt.lax = (path->header & JSONPATH_LAX) != 0;
1328+
cxt.mutable = false;
1329+
1330+
jspInit(&jpi, path);
1331+
jspIsMutableWalker(&jpi, &cxt);
1332+
1333+
return cxt.mutable;
1334+
}

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2890,7 +2890,7 @@ JsonPathExists(Datum jb, JsonPath *jp, List *vars, bool *error)
28902890
{
28912891
JsonPathExecResult res = executeJsonPath(jp, vars, EvalJsonPathVar,
28922892
DatumGetJsonbP(jb), !error, NULL,
2893-
false /* XXX */);
2893+
true);
28942894

28952895
Assert(error || !jperIsError(res));
28962896

@@ -2911,7 +2911,7 @@ JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
29112911
int count;
29122912

29132913
res = executeJsonPath(jp, vars, EvalJsonPathVar, DatumGetJsonbP(jb), !error,
2914-
&found, false /* XXX */);
2914+
&found, true);
29152915

29162916
Assert(error || !jperIsError(res));
29172917

@@ -2978,7 +2978,7 @@ JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars)
29782978
int count;
29792979

29802980
jper = executeJsonPath(jp, vars, EvalJsonPathVar, DatumGetJsonbP(jb), !error,
2981-
&found, false /* XXX */);
2981+
&found, true);
29822982

29832983
Assert(error || !jperIsError(jper));
29842984

0 commit comments

Comments
 (0)