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

Commit dab5604

Browse files
jianhe-funCommitfest Bot
authored and
Commitfest Bot
committed
enhance json_array, json_object expression is immutable or not
this will make to_json_is_immutable, to_jsonb_is_immutable recurse to composite data type or array type elements. also add extensive regress tests for it. discussion: https://postgr.es/m/CACJufxFz%3DOsXQdsMJ-cqoqspD9aJrwntsQP-U2A-UaV_M%2B-S9g%40mail.gmail.com
1 parent e050af2 commit dab5604

File tree

7 files changed

+281
-31
lines changed

7 files changed

+281
-31
lines changed

src/backend/optimizer/util/clauses.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -406,10 +406,14 @@ contain_mutable_functions_walker(Node *node, void *context)
406406
foreach(lc, ctor->args)
407407
{
408408
Oid typid = exprType(lfirst(lc));
409+
bool contain_mutable = false;
409410

410-
if (is_jsonb ?
411-
!to_jsonb_is_immutable(typid) :
412-
!to_json_is_immutable(typid))
411+
if (is_jsonb)
412+
to_jsonb_is_immutable(typid, &contain_mutable);
413+
else
414+
to_json_is_immutable(typid, &contain_mutable);
415+
416+
if(contain_mutable)
413417
return true;
414418
}
415419

src/backend/utils/adt/json.c

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414
#include "postgres.h"
1515

16+
#include "access/relation.h"
1617
#include "catalog/pg_proc.h"
1718
#include "catalog/pg_type.h"
1819
#include "common/hashfn.h"
@@ -28,6 +29,7 @@
2829
#include "utils/json.h"
2930
#include "utils/jsonfuncs.h"
3031
#include "utils/lsyscache.h"
32+
#include "utils/rel.h"
3133
#include "utils/typcache.h"
3234

3335

@@ -692,15 +694,56 @@ row_to_json_pretty(PG_FUNCTION_ARGS)
692694
/*
693695
* Is the given type immutable when coming out of a JSON context?
694696
*
695-
* At present, datetimes are all considered mutable, because they
696-
* depend on timezone. XXX we should also drill down into objects
697-
* and arrays, but do not.
697+
* At present, datetimes are all considered mutable, because they depend on
698+
* timezone.
698699
*/
699-
bool
700-
to_json_is_immutable(Oid typoid)
700+
void
701+
to_json_is_immutable(Oid typoid, bool *contain_mutable)
701702
{
703+
char att_typtype = get_typtype(typoid);
702704
JsonTypeCategory tcategory;
703705
Oid outfuncoid;
706+
Oid att_typelem;
707+
708+
/* since this function recurses, it could be driven to stack overflow */
709+
check_stack_depth();
710+
711+
Assert(contain_mutable != NULL);
712+
713+
if (att_typtype == TYPTYPE_DOMAIN)
714+
to_json_is_immutable(getBaseType(typoid), contain_mutable);
715+
else if (att_typtype == TYPTYPE_COMPOSITE)
716+
{
717+
/*
718+
* For a composite type, recurse into its attributes.
719+
*/
720+
Relation relation;
721+
TupleDesc tupdesc;
722+
int i;
723+
724+
relation = relation_open(get_typ_typrelid(typoid), AccessShareLock);
725+
726+
tupdesc = RelationGetDescr(relation);
727+
728+
for (i = 0; i < tupdesc->natts; i++)
729+
{
730+
Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
731+
732+
if (attr->attisdropped)
733+
continue;
734+
to_json_is_immutable(attr->atttypid, contain_mutable);
735+
}
736+
relation_close(relation, AccessShareLock);
737+
}
738+
else if (att_typtype == TYPTYPE_RANGE)
739+
{
740+
to_json_is_immutable(get_range_subtype(typoid), contain_mutable);
741+
}
742+
else if (OidIsValid((att_typelem = get_element_type(typoid))))
743+
{
744+
/* recurse into array element type */
745+
to_json_is_immutable(att_typelem, contain_mutable);
746+
}
704747

705748
json_categorize_type(typoid, false, &tcategory, &outfuncoid);
706749

@@ -710,26 +753,25 @@ to_json_is_immutable(Oid typoid)
710753
case JSONTYPE_JSON:
711754
case JSONTYPE_JSONB:
712755
case JSONTYPE_NULL:
713-
return true;
756+
break;
714757

715758
case JSONTYPE_DATE:
716759
case JSONTYPE_TIMESTAMP:
717760
case JSONTYPE_TIMESTAMPTZ:
718-
return false;
761+
*contain_mutable = true;
762+
break;
719763

720764
case JSONTYPE_ARRAY:
721-
return false; /* TODO recurse into elements */
722-
723765
case JSONTYPE_COMPOSITE:
724-
return false; /* TODO recurse into fields */
766+
break;
725767

726768
case JSONTYPE_NUMERIC:
727769
case JSONTYPE_CAST:
728770
case JSONTYPE_OTHER:
729-
return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
771+
if (func_volatile(outfuncoid) != PROVOLATILE_IMMUTABLE)
772+
*contain_mutable = true;
773+
break;
730774
}
731-
732-
return false; /* not reached */
733775
}
734776

735777
/*

src/backend/utils/adt/jsonb.c

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "postgres.h"
1414

1515
#include "access/htup_details.h"
16+
#include "access/relation.h"
1617
#include "catalog/pg_proc.h"
1718
#include "catalog/pg_type.h"
1819
#include "funcapi.h"
@@ -23,6 +24,7 @@
2324
#include "utils/jsonb.h"
2425
#include "utils/jsonfuncs.h"
2526
#include "utils/lsyscache.h"
27+
#include "utils/rel.h"
2628
#include "utils/typcache.h"
2729

2830
typedef struct JsonbInState
@@ -1041,15 +1043,57 @@ add_jsonb(Datum val, bool is_null, JsonbInState *result,
10411043
/*
10421044
* Is the given type immutable when coming out of a JSONB context?
10431045
*
1044-
* At present, datetimes are all considered mutable, because they
1045-
* depend on timezone. XXX we should also drill down into objects and
1046-
* arrays, but do not.
1046+
* At present, datetimes are all considered mutable, because they depend on
1047+
* timezone.
10471048
*/
1048-
bool
1049-
to_jsonb_is_immutable(Oid typoid)
1049+
void
1050+
to_jsonb_is_immutable(Oid typoid, bool *contain_mutable)
10501051
{
1052+
char att_typtype = get_typtype(typoid);
10511053
JsonTypeCategory tcategory;
10521054
Oid outfuncoid;
1055+
Oid att_typelem;
1056+
1057+
/* since this function recurses, it could be driven to stack overflow */
1058+
check_stack_depth();
1059+
1060+
Assert(contain_mutable != NULL);
1061+
1062+
if (att_typtype == TYPTYPE_DOMAIN)
1063+
to_jsonb_is_immutable(getBaseType(typoid), contain_mutable);
1064+
else if (att_typtype == TYPTYPE_COMPOSITE)
1065+
{
1066+
/*
1067+
* For a composite type, recurse into its attributes.
1068+
*/
1069+
Relation relation;
1070+
TupleDesc tupdesc;
1071+
int i;
1072+
1073+
relation = relation_open(get_typ_typrelid(typoid), AccessShareLock);
1074+
1075+
tupdesc = RelationGetDescr(relation);
1076+
1077+
for (i = 0; i < tupdesc->natts; i++)
1078+
{
1079+
Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
1080+
1081+
if (attr->attisdropped)
1082+
continue;
1083+
1084+
to_jsonb_is_immutable(attr->atttypid, contain_mutable);
1085+
}
1086+
relation_close(relation, AccessShareLock);
1087+
}
1088+
else if (att_typtype == TYPTYPE_RANGE)
1089+
{
1090+
to_jsonb_is_immutable(get_range_subtype(typoid), contain_mutable);
1091+
}
1092+
else if (OidIsValid((att_typelem = get_element_type(typoid))))
1093+
{
1094+
/* recurse into array element type */
1095+
to_jsonb_is_immutable(att_typelem, contain_mutable);
1096+
}
10531097

10541098
json_categorize_type(typoid, true, &tcategory, &outfuncoid);
10551099

@@ -1059,26 +1103,25 @@ to_jsonb_is_immutable(Oid typoid)
10591103
case JSONTYPE_BOOL:
10601104
case JSONTYPE_JSON:
10611105
case JSONTYPE_JSONB:
1062-
return true;
1106+
break;
10631107

10641108
case JSONTYPE_DATE:
10651109
case JSONTYPE_TIMESTAMP:
10661110
case JSONTYPE_TIMESTAMPTZ:
1067-
return false;
1111+
*contain_mutable = true;
1112+
break;
10681113

10691114
case JSONTYPE_ARRAY:
1070-
return false; /* TODO recurse into elements */
1071-
10721115
case JSONTYPE_COMPOSITE:
1073-
return false; /* TODO recurse into fields */
1116+
break;
10741117

10751118
case JSONTYPE_NUMERIC:
10761119
case JSONTYPE_CAST:
10771120
case JSONTYPE_OTHER:
1078-
return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
1121+
if (func_volatile(outfuncoid) != PROVOLATILE_IMMUTABLE)
1122+
*contain_mutable = true;
1123+
break;
10791124
}
1080-
1081-
return false; /* not reached */
10821125
}
10831126

10841127
/*

src/include/utils/json.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ extern void escape_json_with_len(StringInfo buf, const char *str, int len);
2222
extern void escape_json_text(StringInfo buf, const text *txt);
2323
extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid,
2424
const int *tzp);
25-
extern bool to_json_is_immutable(Oid typoid);
25+
extern void to_json_is_immutable(Oid typoid, bool *contain_mutable);
2626
extern Datum json_build_object_worker(int nargs, const Datum *args, const bool *nulls,
2727
const Oid *types, bool absent_on_null,
2828
bool unique_keys);

src/include/utils/jsonb.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ extern Datum jsonb_set_element(Jsonb *jb, Datum *path, int path_len,
430430
JsonbValue *newval);
431431
extern Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
432432
bool *isnull, bool as_text);
433-
extern bool to_jsonb_is_immutable(Oid typoid);
433+
extern void to_jsonb_is_immutable(Oid typoid, bool *contain_mutable);
434434
extern Datum jsonb_build_object_worker(int nargs, const Datum *args, const bool *nulls,
435435
const Oid *types, bool absent_on_null,
436436
bool unique_keys);

src/test/regress/expected/sqljson.out

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,105 @@ CREATE OR REPLACE VIEW public.json_array_subquery_view AS
11091109
FROM ( SELECT foo.i
11101110
FROM ( VALUES (1), (2), (NULL::integer), (4)) foo(i)) q(a)) AS "json_array"
11111111
DROP VIEW json_array_subquery_view;
1112+
create type comp1 as (a int, b date);
1113+
create domain d2 as comp1;
1114+
create domain mydomain as timestamptz;
1115+
create type mydomainrange as range(subtype=mydomain);
1116+
create type comp3 as (a int, b mydomainrange);
1117+
create table t1(a text[], b timestamp, c timestamptz, d date,
1118+
f1 comp1[], f2 timestamp[],
1119+
f3 d2[], f4 mydomainrange[], f5 comp3);
1120+
--JSON_OBJECTAGG, JSON_ARRAYAGG is aggregate function, can not be used in index
1121+
create index xx on t1(JSON_OBJECTAGG(a: b ABSENT ON NULL WITH UNIQUE KEYS RETURNING jsonb));
1122+
ERROR: aggregate functions are not allowed in index expressions
1123+
LINE 1: create index xx on t1(JSON_OBJECTAGG(a: b ABSENT ON NULL WIT...
1124+
^
1125+
create index xx on t1(JSON_OBJECTAGG(a: b ABSENT ON NULL WITH UNIQUE KEYS RETURNING json));
1126+
ERROR: aggregate functions are not allowed in index expressions
1127+
LINE 1: create index xx on t1(JSON_OBJECTAGG(a: b ABSENT ON NULL WIT...
1128+
^
1129+
create index xx on t1(JSON_ARRAYAGG(a RETURNING jsonb));
1130+
ERROR: aggregate functions are not allowed in index expressions
1131+
LINE 1: create index xx on t1(JSON_ARRAYAGG(a RETURNING jsonb));
1132+
^
1133+
create index xx on t1(JSON_ARRAYAGG(a RETURNING json));
1134+
ERROR: aggregate functions are not allowed in index expressions
1135+
LINE 1: create index xx on t1(JSON_ARRAYAGG(a RETURNING json));
1136+
^
1137+
-- jsonb: create expression index via json_array
1138+
create index on t1(json_array(a returning jsonb)); --ok
1139+
create index on t1(json_array(b returning jsonb)); --error
1140+
ERROR: functions in index expression must be marked IMMUTABLE
1141+
create index on t1(json_array(c returning jsonb)); --error
1142+
ERROR: functions in index expression must be marked IMMUTABLE
1143+
create index on t1(json_array(d returning jsonb)); --error
1144+
ERROR: functions in index expression must be marked IMMUTABLE
1145+
create index on t1(json_array(f1 returning jsonb)); --error
1146+
ERROR: functions in index expression must be marked IMMUTABLE
1147+
create index on t1(json_array(f2 returning jsonb)); --error
1148+
ERROR: functions in index expression must be marked IMMUTABLE
1149+
create index on t1(json_array(f3 returning jsonb)); --error
1150+
ERROR: functions in index expression must be marked IMMUTABLE
1151+
create index on t1(json_array(f4 returning jsonb)); --error
1152+
ERROR: functions in index expression must be marked IMMUTABLE
1153+
create index on t1(json_array(f5 returning jsonb)); --error
1154+
ERROR: functions in index expression must be marked IMMUTABLE
1155+
--jsonb: create expression index via json_object
1156+
create index on t1(json_object('hello' value a returning jsonb)); --ok
1157+
create index on t1(json_object('hello' value b returning jsonb)); --error
1158+
ERROR: functions in index expression must be marked IMMUTABLE
1159+
create index on t1(json_object('hello' value c returning jsonb)); --error
1160+
ERROR: functions in index expression must be marked IMMUTABLE
1161+
create index on t1(json_object('hello' value d returning jsonb)); --error
1162+
ERROR: functions in index expression must be marked IMMUTABLE
1163+
create index on t1(json_object('hello' value f1 returning jsonb)); --error
1164+
ERROR: functions in index expression must be marked IMMUTABLE
1165+
create index on t1(json_object('hello' value f2 returning jsonb)); --error
1166+
ERROR: functions in index expression must be marked IMMUTABLE
1167+
create index on t1(json_object('hello' value f3 returning jsonb)); --error
1168+
ERROR: functions in index expression must be marked IMMUTABLE
1169+
create index on t1(json_object('hello' value f4 returning jsonb)); --error
1170+
ERROR: functions in index expression must be marked IMMUTABLE
1171+
create index on t1(json_object('hello' value f5 returning jsonb)); --error
1172+
ERROR: functions in index expression must be marked IMMUTABLE
1173+
-- data type json don't have default operator class for access method "btree" so
1174+
-- we use a generated column to test whether the JSON_ARRAY expression is
1175+
-- immutable
1176+
alter table t1 add column f10 json generated always as (json_array(a returning json)); --ok
1177+
alter table t1 add column f11 json generated always as (json_array(b returning json)); --error
1178+
ERROR: generation expression is not immutable
1179+
alter table t1 add column f11 json generated always as (json_array(c returning json)); --error
1180+
ERROR: generation expression is not immutable
1181+
alter table t1 add column f11 json generated always as (json_array(d returning json)); --error
1182+
ERROR: generation expression is not immutable
1183+
alter table t1 add column f11 json generated always as (json_array(f1 returning json)); --error
1184+
ERROR: generation expression is not immutable
1185+
alter table t1 add column f11 json generated always as (json_array(f2 returning json)); --error
1186+
ERROR: generation expression is not immutable
1187+
alter table t1 add column f11 json generated always as (json_array(f4 returning json)); --error
1188+
ERROR: generation expression is not immutable
1189+
alter table t1 add column f11 json generated always as (json_array(f5 returning json)); --error
1190+
ERROR: generation expression is not immutable
1191+
-- data type json don't have default operator class for access method "btree" so
1192+
-- we use a generated column to test whether the JSON_OBJECT expression is
1193+
-- immutable
1194+
alter table t1 add column f11 json generated always as (json_object('hello' value a returning json)); --ok
1195+
alter table t1 add column f12 json generated always as (json_object('hello' value b returning json)); --error
1196+
ERROR: generation expression is not immutable
1197+
alter table t1 add column f12 json generated always as (json_object('hello' value c returning json)); --error
1198+
ERROR: generation expression is not immutable
1199+
alter table t1 add column f12 json generated always as (json_object('hello' value d returning json)); --error
1200+
ERROR: generation expression is not immutable
1201+
alter table t1 add column f12 json generated always as (json_object('hello' value f1 returning json)); --error
1202+
ERROR: generation expression is not immutable
1203+
alter table t1 add column f12 json generated always as (json_object('hello' value f2 returning json)); --error
1204+
ERROR: generation expression is not immutable
1205+
alter table t1 add column f12 json generated always as (json_object('hello' value f3 returning json)); --error
1206+
ERROR: generation expression is not immutable
1207+
alter table t1 add column f12 json generated always as (json_object('hello' value f4 returning json)); --error
1208+
ERROR: generation expression is not immutable
1209+
alter table t1 add column f12 json generated always as (json_object('hello' value f5 returning json)); --error
1210+
ERROR: generation expression is not immutable
11121211
-- IS JSON predicate
11131212
SELECT NULL IS JSON;
11141213
?column?

0 commit comments

Comments
 (0)