v24-0001-Base-implementation-of-subscripting-mechanism.patch
application/octet-stream
Filename: v24-0001-Base-implementation-of-subscripting-mechanism.patch
Type: application/octet-stream
Part: 2
Patch
Format: format-patch
Series: patch v24-0001
Subject: Base implementation of subscripting mechanism
| File | + | − |
|---|---|---|
| contrib/pg_stat_statements/pg_stat_statements.c | 1 | 0 |
| src/backend/catalog/heap.c | 4 | 2 |
| src/backend/catalog/pg_type.c | 13 | 2 |
| src/backend/commands/typecmds.c | 69 | 8 |
| src/backend/executor/execExpr.c | 5 | 20 |
| src/backend/executor/execExprInterp.c | 17 | 107 |
| src/backend/nodes/copyfuncs.c | 2 | 0 |
| src/backend/nodes/equalfuncs.c | 2 | 0 |
| src/backend/nodes/outfuncs.c | 2 | 0 |
| src/backend/nodes/readfuncs.c | 2 | 0 |
| src/backend/parser/parse_expr.c | 35 | 19 |
| src/backend/parser/parse_node.c | 32 | 109 |
| src/backend/parser/parse_target.c | 46 | 42 |
| src/backend/utils/adt/ruleutils.c | 11 | 10 |
| src/backend/utils/cache/lsyscache.c | 23 | 0 |
| src/include/catalog/pg_class.dat | 1 | 1 |
| src/include/catalog/pg_type.h | 8 | 1 |
| src/include/c.h | 2 | 0 |
| src/include/executor/execExpr.h | 8 | 5 |
| src/include/nodes/primnodes.h | 6 | 0 |
| src/include/nodes/subscripting.h | 42 | 0 |
| src/include/parser/parse_node.h | 3 | 1 |
| src/include/utils/lsyscache.h | 1 | 0 |
From f8481bd9d83b607e3b5a3a8ddbd1385f63c56bd6 Mon Sep 17 00:00:00 2001
From: erthalion <9erthalion6@gmail.com>
Date: Thu, 31 Jan 2019 22:37:19 +0100
Subject: [PATCH v24 1/4] Base implementation of subscripting mechanism
Introduce all the required machinery for generalizing subscripting
operation for a different data types:
* subscripting handler procedure, to set up a relation between data type
and corresponding subscripting logic.
* subscripting routines, that help generalize a subscripting logic,
since it involves few stages, namely preparation (e.g. to figure out
required types), validation (to check the input and return meaningful
error message), fetch (executed when we extract a value using
subscripting), assign (executed when we update a data type with a new
value using subscripting). Without this it would be neccessary to
introduce more new fields to pg_type, which would be too invasive.
All ArrayRef related logic was removed and landed as a separate
subscripting implementation in the following patch from this series. The
rest of the code was rearranged, to e.g. store a type of assigned value
for an assign operation.
Reviewed-by: Tom Lane, Arthur Zakirov
---
contrib/pg_stat_statements/pg_stat_statements.c | 1 +
src/backend/catalog/heap.c | 6 +-
src/backend/catalog/pg_type.c | 15 ++-
src/backend/commands/typecmds.c | 77 +++++++++++--
src/backend/executor/execExpr.c | 25 +----
src/backend/executor/execExprInterp.c | 124 +++------------------
src/backend/nodes/copyfuncs.c | 2 +
src/backend/nodes/equalfuncs.c | 2 +
src/backend/nodes/outfuncs.c | 2 +
src/backend/nodes/readfuncs.c | 2 +
src/backend/parser/parse_expr.c | 54 +++++----
src/backend/parser/parse_node.c | 141 ++++++------------------
src/backend/parser/parse_target.c | 88 ++++++++-------
src/backend/utils/adt/ruleutils.c | 21 ++--
src/backend/utils/cache/lsyscache.c | 23 ++++
src/include/c.h | 2 +
src/include/catalog/pg_class.dat | 2 +-
src/include/catalog/pg_type.h | 9 +-
src/include/executor/execExpr.h | 13 ++-
src/include/nodes/primnodes.h | 6 +
src/include/nodes/subscripting.h | 42 +++++++
src/include/parser/parse_node.h | 4 +-
src/include/utils/lsyscache.h | 1 +
23 files changed, 335 insertions(+), 327 deletions(-)
create mode 100644 src/include/nodes/subscripting.h
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 00cae04eea..5d2142d5bc 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2591,6 +2591,7 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
JumbleExpr(jstate, (Node *) sbsref->refexpr);
JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
+ APP_JUMB(sbsref->refnestedfunc);
}
break;
case T_FuncExpr:
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 3b8c8b193a..fa56e6f9c7 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1027,7 +1027,8 @@ AddNewRelationType(const char *typeName,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ InvalidOid); /* typsubshandler - none */
}
/* --------------------------------
@@ -1308,7 +1309,8 @@ heap_create_with_catalog(const char *relname,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ 0); /* array implementation */
pfree(relarrayname);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 2a51501d8d..54f7ba52a8 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -118,6 +118,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(0);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid);
+ values[Anum_pg_type_typsubshandler - 1] = ObjectIdGetDatum(InvalidOid);
nulls[Anum_pg_type_typdefaultbin - 1] = true;
nulls[Anum_pg_type_typdefault - 1] = true;
nulls[Anum_pg_type_typacl - 1] = true;
@@ -158,10 +159,10 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
GenerateTypeDependencies(typoid,
(Form_pg_type) GETSTRUCT(tup),
NULL,
- NULL,
0,
false,
false,
+ InvalidOid,
false);
/* Post creation hook for new shell type */
@@ -219,7 +220,8 @@ TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
bool typeNotNull,
- Oid typeCollation)
+ Oid typeCollation,
+ Oid subscriptingHandlerProcedure)
{
Relation pg_type_desc;
Oid typeObjectId;
@@ -371,6 +373,7 @@ TypeCreate(Oid newTypeOid,
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation);
+ values[Anum_pg_type_typsubshandler - 1] = ObjectIdGetDatum(subscriptingHandlerProcedure);
/*
* initialize the default binary value for this type. Check for nulls of
@@ -694,6 +697,14 @@ GenerateTypeDependencies(Oid typeObjectId,
/* Normal dependency on the default expression. */
if (defaultExpr)
recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
+
+ if (OidIsValid(typeForm->typsubshandler))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = typeForm->typsubshandler;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index e9c8873ade..3c466b9983 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -95,6 +95,7 @@ static Oid findTypeSendFunction(List *procname, Oid typeOid);
static Oid findTypeTypmodinFunction(List *procname);
static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
+static Oid findTypeSubscriptingFunction(List *procname, Oid typeOid, bool parseFunc);
static Oid findRangeSubOpclass(List *opcname, Oid subtype);
static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype);
@@ -126,6 +127,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
List *typmodinName = NIL;
List *typmodoutName = NIL;
List *analyzeName = NIL;
+ List *subscriptingParseName = NIL;
char category = TYPCATEGORY_USER;
bool preferred = false;
char delimiter = DEFAULT_TYPDELIM;
@@ -144,6 +146,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
DefElem *typmodinNameEl = NULL;
DefElem *typmodoutNameEl = NULL;
DefElem *analyzeNameEl = NULL;
+ DefElem *subscriptingParseNameEl = NULL;
DefElem *categoryEl = NULL;
DefElem *preferredEl = NULL;
DefElem *delimiterEl = NULL;
@@ -166,6 +169,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
Oid resulttype;
ListCell *pl;
ObjectAddress address;
+ Oid subscriptingParseOid = InvalidOid;
/*
* As of Postgres 8.4, we require superuser privilege to create a base
@@ -265,6 +269,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
else if (strcmp(defel->defname, "analyze") == 0 ||
strcmp(defel->defname, "analyse") == 0)
defelp = &analyzeNameEl;
+ else if (strcmp(defel->defname, "subscripting_handler") == 0)
+ defelp = &subscriptingParseNameEl;
else if (strcmp(defel->defname, "category") == 0)
defelp = &categoryEl;
else if (strcmp(defel->defname, "preferred") == 0)
@@ -335,6 +341,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
typmodoutName = defGetQualifiedName(typmodoutNameEl);
if (analyzeNameEl)
analyzeName = defGetQualifiedName(analyzeNameEl);
+ if (subscriptingParseNameEl)
+ subscriptingParseName = defGetQualifiedName(subscriptingParseNameEl);
if (categoryEl)
{
char *p = defGetString(categoryEl);
@@ -516,6 +524,10 @@ DefineType(ParseState *pstate, List *names, List *parameters)
if (analyzeName)
analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
+ if (subscriptingParseName)
+ subscriptingParseOid = findTypeSubscriptingFunction(subscriptingParseName,
+ typoid, true);
+
/*
* Check permissions on functions. We choose to require the creator/owner
* of a type to also own the underlying functions. Since creating a type
@@ -635,7 +647,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ subscriptingParseOid); /* subscripting procedure */
Assert(typoid == address.objectId);
/*
@@ -676,7 +689,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ 0);
pfree(array_type);
@@ -739,6 +753,7 @@ DefineDomain(CreateDomainStmt *stmt)
Oid receiveProcedure;
Oid sendProcedure;
Oid analyzeProcedure;
+ Oid subscriptingHandlerProcedure;
bool byValue;
char category;
char delimiter;
@@ -866,6 +881,9 @@ DefineDomain(CreateDomainStmt *stmt)
/* Analysis function */
analyzeProcedure = baseType->typanalyze;
+ /* Subscripting functions */
+ subscriptingHandlerProcedure = baseType->typsubshandler;
+
/* Inherited default value */
datum = SysCacheGetAttr(TYPEOID, typeTup,
Anum_pg_type_typdefault, &isnull);
@@ -1071,7 +1089,8 @@ DefineDomain(CreateDomainStmt *stmt)
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
typNotNull, /* Type NOT NULL */
- domaincoll); /* type's collation */
+ domaincoll, /* type's collation */
+ subscriptingHandlerProcedure); /* subscripting procedure */
/*
* Create the array type that goes with it.
@@ -1111,7 +1130,8 @@ DefineDomain(CreateDomainStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- domaincoll); /* type's collation */
+ domaincoll, /* type's collation */
+ 0); /* array subscripting implementation */
pfree(domainArrayName);
@@ -1226,7 +1246,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ InvalidOid); /* typsubshandler - none */
/* Enter the enum's values into pg_enum */
EnumValuesCreate(enumTypeAddr.objectId, stmt->vals);
@@ -1266,7 +1287,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ 0); /* array subscripting implementation */
pfree(enumArrayName);
@@ -1554,7 +1576,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation (ranges never have one) */
+ InvalidOid, /* type's collation (ranges never have one) */
+ InvalidOid); /* typsubshandler - none */
Assert(typoid == address.objectId);
/* Create the entry in pg_range */
@@ -1596,7 +1619,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid, /* typcollation */
+ 0); /* array subscripting implementation */
pfree(rangeArrayName);
@@ -1940,6 +1964,43 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
return procOid;
}
+static Oid
+findTypeSubscriptingFunction(List *procname, Oid typeOid, bool parseFunc)
+{
+ Oid argList[2];
+ Oid procOid;
+ int nargs;
+
+ if (parseFunc)
+ {
+ /*
+ * Subscripting function parse always take two INTERNAL argument and
+ * return INTERNAL.
+ */
+ argList[0] = INTERNALOID;
+ nargs = 1;
+ }
+ else
+ {
+ /*
+ * Subscripting functions fetch/assign always take one typeOid
+ * argument, one INTERNAL argument and return typeOid.
+ */
+ argList[0] = typeOid;
+ argList[1] = INTERNALOID;
+ nargs = 2;
+ }
+
+ procOid = LookupFuncName(procname, nargs, argList, true);
+ if (!OidIsValid(procOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(procname, nargs, NIL, argList))));
+
+ return procOid;
+}
+
/*
* Find suitable support functions and opclasses for a range type.
*/
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e4e05753ee..6a3a49ac4c 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -2528,18 +2528,16 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
{
bool isAssignment = (sbsref->refassgnexpr != NULL);
SubscriptingRefState *sbsrefstate = palloc0(sizeof(SubscriptingRefState));
- List *adjust_jumps = NIL;
- ListCell *lc;
- int i;
+ List *adjust_jumps = NIL;
+ ListCell *lc;
+ int i;
+ RegProcedure typsubshandler = get_typsubsprocs(sbsref->refcontainertype);
/* Fill constant fields of SubscriptingRefState */
sbsrefstate->isassignment = isAssignment;
sbsrefstate->refelemtype = sbsref->refelemtype;
sbsrefstate->refattrlength = get_typlen(sbsref->refcontainertype);
- get_typlenbyvalalign(sbsref->refelemtype,
- &sbsrefstate->refelemlength,
- &sbsrefstate->refelembyval,
- &sbsrefstate->refelemalign);
+ sbsrefstate->sbsroutines = (SubscriptRoutines *) OidFunctionCall0(typsubshandler);
/*
* Evaluate array input. It's safe to do so into resv/resnull, because we
@@ -2563,19 +2561,6 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
state->steps_len - 1);
}
- /* Verify subscript list lengths are within limit */
- if (list_length(sbsref->refupperindexpr) > MAXDIM)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
- list_length(sbsref->refupperindexpr), MAXDIM)));
-
- if (list_length(sbsref->reflowerindexpr) > MAXDIM)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
- list_length(sbsref->reflowerindexpr), MAXDIM)));
-
/* Evaluate upper subscripts */
i = 0;
foreach(lc, sbsref->refupperindexpr)
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 66a67c72b2..9384d8014e 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -3058,8 +3058,8 @@ bool
ExecEvalSubscriptingRef(ExprState *state, ExprEvalStep *op)
{
SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state;
- int *indexes;
- int off;
+ Datum *indexes;
+ int off;
/* If any index expr yields NULL, result is NULL or error */
if (sbsrefstate->subscriptnull)
@@ -3079,7 +3079,7 @@ ExecEvalSubscriptingRef(ExprState *state, ExprEvalStep *op)
indexes = sbsrefstate->lowerindex;
off = op->d.sbsref_subscript.off;
- indexes[off] = DatumGetInt32(sbsrefstate->subscriptvalue);
+ indexes[off] = sbsrefstate->subscriptvalue;
return true;
}
@@ -3093,36 +3093,14 @@ void
ExecEvalSubscriptingRefFetch(ExprState *state, ExprEvalStep *op)
{
SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
+ SubscriptRoutines *sbsroutines = sbsrefstate->sbsroutines;
/* Should not get here if source container (or any subscript) is null */
Assert(!(*op->resnull));
- if (sbsrefstate->numlower == 0)
- {
- /* Scalar case */
- *op->resvalue = array_get_element(*op->resvalue,
- sbsrefstate->numupper,
- sbsrefstate->upperindex,
- sbsrefstate->refattrlength,
- sbsrefstate->refelemlength,
- sbsrefstate->refelembyval,
- sbsrefstate->refelemalign,
- op->resnull);
- }
- else
- {
- /* Slice case */
- *op->resvalue = array_get_slice(*op->resvalue,
- sbsrefstate->numupper,
- sbsrefstate->upperindex,
- sbsrefstate->lowerindex,
- sbsrefstate->upperprovided,
- sbsrefstate->lowerprovided,
- sbsrefstate->refattrlength,
- sbsrefstate->refelemlength,
- sbsrefstate->refelembyval,
- sbsrefstate->refelemalign);
- }
+
+ *op->resvalue = sbsroutines->fetch(*op->resvalue, sbsrefstate);
+ *op->resnull = sbsrefstate->resnull;
}
/*
@@ -3135,40 +3113,20 @@ void
ExecEvalSubscriptingRefOld(ExprState *state, ExprEvalStep *op)
{
SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
+ SubscriptRoutines *sbsroutines = sbsrefstate->sbsroutines;
if (*op->resnull)
{
- /* whole array is null, so any element or slice is too */
+ /* whole container is null, so any element or slice is too */
sbsrefstate->prevvalue = (Datum) 0;
sbsrefstate->prevnull = true;
}
- else if (sbsrefstate->numlower == 0)
- {
- /* Scalar case */
- sbsrefstate->prevvalue = array_get_element(*op->resvalue,
- sbsrefstate->numupper,
- sbsrefstate->upperindex,
- sbsrefstate->refattrlength,
- sbsrefstate->refelemlength,
- sbsrefstate->refelembyval,
- sbsrefstate->refelemalign,
- &sbsrefstate->prevnull);
- }
else
{
- /* Slice case */
- /* this is currently unreachable */
- sbsrefstate->prevvalue = array_get_slice(*op->resvalue,
- sbsrefstate->numupper,
- sbsrefstate->upperindex,
- sbsrefstate->lowerindex,
- sbsrefstate->upperprovided,
- sbsrefstate->lowerprovided,
- sbsrefstate->refattrlength,
- sbsrefstate->refelemlength,
- sbsrefstate->refelembyval,
- sbsrefstate->refelemalign);
- sbsrefstate->prevnull = false;
+ sbsrefstate->prevvalue = sbsroutines->fetch(*op->resvalue, sbsrefstate);
+
+ if (sbsrefstate->numlower != 0)
+ sbsrefstate->prevnull = false;
}
}
@@ -3182,59 +3140,11 @@ void
ExecEvalSubscriptingRefAssign(ExprState *state, ExprEvalStep *op)
{
SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state;
+ SubscriptRoutines *sbsroutines = sbsrefstate->sbsroutines;
- /*
- * For an assignment to a fixed-length container type, both the original
- * container and the value to be assigned into it must be non-NULL, else
- * we punt and return the original container.
- */
- if (sbsrefstate->refattrlength > 0)
- {
- if (*op->resnull || sbsrefstate->replacenull)
- return;
- }
-
- /*
- * For assignment to varlena arrays, we handle a NULL original array by
- * substituting an empty (zero-dimensional) array; insertion of the new
- * element will result in a singleton array value. It does not matter
- * whether the new element is NULL.
- */
- if (*op->resnull)
- {
- *op->resvalue = PointerGetDatum(construct_empty_array(sbsrefstate->refelemtype));
- *op->resnull = false;
- }
-
- if (sbsrefstate->numlower == 0)
- {
- /* Scalar case */
- *op->resvalue = array_set_element(*op->resvalue,
- sbsrefstate->numupper,
- sbsrefstate->upperindex,
- sbsrefstate->replacevalue,
- sbsrefstate->replacenull,
- sbsrefstate->refattrlength,
- sbsrefstate->refelemlength,
- sbsrefstate->refelembyval,
- sbsrefstate->refelemalign);
- }
- else
- {
- /* Slice case */
- *op->resvalue = array_set_slice(*op->resvalue,
- sbsrefstate->numupper,
- sbsrefstate->upperindex,
- sbsrefstate->lowerindex,
- sbsrefstate->upperprovided,
- sbsrefstate->lowerprovided,
- sbsrefstate->replacevalue,
- sbsrefstate->replacenull,
- sbsrefstate->refattrlength,
- sbsrefstate->refelemlength,
- sbsrefstate->refelembyval,
- sbsrefstate->refelemalign);
- }
+ sbsrefstate->resnull = *op->resnull;
+ *op->resvalue = sbsroutines->assign(*op->resvalue, sbsrefstate);
+ *op->resnull = sbsrefstate->resnull;
}
/*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 78deade89b..a53f4b718a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1501,8 +1501,10 @@ _copySubscriptingRef(const SubscriptingRef *from)
COPY_SCALAR_FIELD(refcontainertype);
COPY_SCALAR_FIELD(refelemtype);
+ COPY_SCALAR_FIELD(refassgntype);
COPY_SCALAR_FIELD(reftypmod);
COPY_SCALAR_FIELD(refcollid);
+ COPY_SCALAR_FIELD(refnestedfunc);
COPY_NODE_FIELD(refupperindexpr);
COPY_NODE_FIELD(reflowerindexpr);
COPY_NODE_FIELD(refexpr);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 4f2ebe5118..5317f968cd 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -270,8 +270,10 @@ _equalSubscriptingRef(const SubscriptingRef *a, const SubscriptingRef *b)
{
COMPARE_SCALAR_FIELD(refcontainertype);
COMPARE_SCALAR_FIELD(refelemtype);
+ COMPARE_SCALAR_FIELD(refassgntype);
COMPARE_SCALAR_FIELD(reftypmod);
COMPARE_SCALAR_FIELD(refcollid);
+ COMPARE_SCALAR_FIELD(refnestedfunc);
COMPARE_NODE_FIELD(refupperindexpr);
COMPARE_NODE_FIELD(reflowerindexpr);
COMPARE_NODE_FIELD(refexpr);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 8400dd319e..6cb22d24a7 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1165,8 +1165,10 @@ _outSubscriptingRef(StringInfo str, const SubscriptingRef *node)
WRITE_OID_FIELD(refcontainertype);
WRITE_OID_FIELD(refelemtype);
+ WRITE_OID_FIELD(refassgntype);
WRITE_INT_FIELD(reftypmod);
WRITE_OID_FIELD(refcollid);
+ WRITE_OID_FIELD(refnestedfunc);
WRITE_NODE_FIELD(refupperindexpr);
WRITE_NODE_FIELD(reflowerindexpr);
WRITE_NODE_FIELD(refexpr);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 6c2626ee62..a5aa9dee63 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -668,8 +668,10 @@ _readSubscriptingRef(void)
READ_OID_FIELD(refcontainertype);
READ_OID_FIELD(refelemtype);
+ READ_OID_FIELD(refassgntype);
READ_INT_FIELD(reftypmod);
READ_OID_FIELD(refcollid);
+ READ_OID_FIELD(refnestedfunc);
READ_NODE_FIELD(refupperindexpr);
READ_NODE_FIELD(reflowerindexpr);
READ_NODE_FIELD(refexpr);
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 97f535a2f0..898e89e642 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -433,11 +433,13 @@ unknown_attribute(ParseState *pstate, Node *relref, const char *attname,
static Node *
transformIndirection(ParseState *pstate, A_Indirection *ind)
{
- Node *last_srf = pstate->p_last_srf;
- Node *result = transformExprRecurse(pstate, ind->arg);
- List *subscripts = NIL;
- int location = exprLocation(result);
- ListCell *i;
+ Node *last_srf = pstate->p_last_srf;
+ Node *result = transformExprRecurse(pstate, ind->arg);
+ SubscriptRoutines *sbsroutines;
+ SubscriptingRef *sbsref;
+ List *subscripts = NIL;
+ int location = exprLocation(result);
+ ListCell *i;
/*
* We have to split any field-selection operations apart from
@@ -465,13 +467,20 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
/* process subscripts before this field selection */
if (subscripts)
- result = (Node *) transformContainerSubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ {
+ sbsref = transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
+
+ sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype);
+ sbsref = sbsroutines->prepare(false, sbsref);
+ sbsroutines->validate(false, sbsref, pstate);
+ result = (Node *) sbsref;
+ }
subscripts = NIL;
newresult = ParseFuncOrColumn(pstate,
@@ -488,13 +497,20 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
}
/* process trailing subscripts, if any */
if (subscripts)
- result = (Node *) transformContainerSubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ {
+ sbsref = transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
+
+ sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype);
+ sbsref = sbsroutines->prepare(false, sbsref);
+ sbsroutines->validate(false, sbsref, pstate);
+ result = (Node *) sbsref;
+ }
return result;
}
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 1baf7ef31f..ba2b93440b 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -206,21 +206,12 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
* transformContainerType()
* Identify the types involved in a subscripting operation for container
*
- *
- * On entry, containerType/containerTypmod identify the type of the input value
- * to be subscripted (which could be a domain type). These are modified if
- * necessary to identify the actual container type and typmod, and the
- * container's element type is returned. An error is thrown if the input isn't
- * an array type.
+ * On entry, containerType/containerTypmod are modified if necessary to
+ * identify the actual container type and typmod.
*/
-Oid
+void
transformContainerType(Oid *containerType, int32 *containerTypmod)
{
- Oid origContainerType = *containerType;
- Oid elementType;
- HeapTuple type_tuple_container;
- Form_pg_type type_struct_container;
-
/*
* If the input is a domain, smash to base type, and extract the actual
* typmod to be applied to the base type. Subscripting a domain is an
@@ -241,25 +232,6 @@ transformContainerType(Oid *containerType, int32 *containerTypmod)
*containerType = INT2ARRAYOID;
else if (*containerType == OIDVECTOROID)
*containerType = OIDARRAYOID;
-
- /* Get the type tuple for the container */
- type_tuple_container = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*containerType));
- if (!HeapTupleIsValid(type_tuple_container))
- elog(ERROR, "cache lookup failed for type %u", *containerType);
- type_struct_container = (Form_pg_type) GETSTRUCT(type_tuple_container);
-
- /* needn't check typisdefined since this will fail anyway */
-
- elementType = type_struct_container->typelem;
- if (elementType == InvalidOid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("cannot subscript type %s because it is not an array",
- format_type_be(origContainerType))));
-
- ReleaseSysCache(type_tuple_container);
-
- return elementType;
}
/*
@@ -276,10 +248,15 @@ transformContainerType(Oid *containerType, int32 *containerTypmod)
* container. We produce an expression that represents the new container value
* with the source data inserted into the right part of the container.
*
- * For both cases, if the source container is of a domain-over-array type,
- * the result is of the base array type or its element type; essentially,
- * we must fold a domain to its base type before applying subscripting.
- * (Note that int2vector and oidvector are treated as domains here.)
+ * For both cases, this function contains only general subscripting logic while
+ * type-specific logic (e.g. type verifications and coersion) is placen in
+ * separate procedure indicated by typsubshandler. There is only one exception
+ * for now about domain-over-container, if the source container is of a
+ * domain-over-container type, the result is of the base container type or its
+ * element type; essentially, we must fold a domain to its base type before
+ * applying subscripting. (Note that int2vector and oidvector are treated as
+ * domains here.) An error will appear in case if current container type
+ * doesn't have a subscripting procedure.
*
* pstate Parse state
* containerBase Already-transformed expression for the container as a whole
@@ -306,16 +283,12 @@ transformContainerSubscripts(ParseState *pstate,
bool isSlice = false;
List *upperIndexpr = NIL;
List *lowerIndexpr = NIL;
+ List *indexprSlice = NIL;
ListCell *idx;
SubscriptingRef *sbsref;
- /*
- * Caller may or may not have bothered to determine elementType. Note
- * that if the caller did do so, containerType/containerTypMod must be as
- * modified by transformContainerType, ie, smash domain to base type.
- */
- if (!OidIsValid(elementType))
- elementType = transformContainerType(&containerType, &containerTypMod);
+ /* Identify the actual container type and element type involved */
+ transformContainerType(&containerType, &containerTypMod);
/*
* A list containing only simple subscripts refers to a single container
@@ -349,29 +322,6 @@ transformContainerSubscripts(ParseState *pstate,
if (ai->lidx)
{
subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->lidx))));
- }
- else if (!ai->is_slice)
- {
- /* Make a constant 1 */
- subexpr = (Node *) makeConst(INT4OID,
- -1,
- InvalidOid,
- sizeof(int32),
- Int32GetDatum(1),
- false,
- true); /* pass by value */
}
else
{
@@ -379,63 +329,23 @@ transformContainerSubscripts(ParseState *pstate,
subexpr = NULL;
}
lowerIndexpr = lappend(lowerIndexpr, subexpr);
+ indexprSlice = lappend(indexprSlice, ai);
}
else
Assert(ai->lidx == NULL && !ai->is_slice);
if (ai->uidx)
- {
subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->uidx))));
- }
else
{
/* Slice with omitted upper bound, put NULL into the list */
Assert(isSlice && ai->is_slice);
subexpr = NULL;
}
+ subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
upperIndexpr = lappend(upperIndexpr, subexpr);
}
- /*
- * If doing an array store, coerce the source value to the right type.
- * (This should agree with the coercion done by transformAssignedExpr.)
- */
- if (assignFrom != NULL)
- {
- Oid typesource = exprType(assignFrom);
- Oid typeneeded = isSlice ? containerType : elementType;
- Node *newFrom;
-
- newFrom = coerce_to_target_type(pstate,
- assignFrom, typesource,
- typeneeded, containerTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (newFrom == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment requires type %s"
- " but expression is of type %s",
- format_type_be(typeneeded),
- format_type_be(typesource)),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, exprLocation(assignFrom))));
- assignFrom = newFrom;
- }
-
/*
* Ready to build the SubscriptingRef node.
*/
@@ -444,17 +354,30 @@ transformContainerSubscripts(ParseState *pstate,
sbsref->refassgnexpr = (Expr *) assignFrom;
sbsref->refcontainertype = containerType;
- sbsref->refelemtype = elementType;
sbsref->reftypmod = containerTypMod;
/* refcollid will be set by parse_collate.c */
sbsref->refupperindexpr = upperIndexpr;
sbsref->reflowerindexpr = lowerIndexpr;
+ sbsref->refindexprslice = indexprSlice;
sbsref->refexpr = (Expr *) containerBase;
- sbsref->refassgnexpr = (Expr *) assignFrom;
return sbsref;
}
+SubscriptRoutines*
+getSubscriptingRoutines(Oid containerType)
+{
+ RegProcedure typsubshandler = get_typsubsprocs(containerType);
+
+ if (!OidIsValid(typsubshandler))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot subscript type %s because it does not support subscripting",
+ format_type_be(containerType))));
+
+ return (SubscriptRoutines *) OidFunctionCall0(typsubshandler);
+}
+
/*
* make_const
*
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index b70d92b955..2ab53cfa45 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -685,7 +685,7 @@ transformAssignmentIndirection(ParseState *pstate,
Node *rhs,
int location)
{
- Node *result;
+ Node *result = NULL;
List *subscripts = NIL;
bool isSlice = false;
ListCell *i;
@@ -845,27 +845,21 @@ transformAssignmentIndirection(ParseState *pstate,
location);
}
- /* base case: just coerce RHS to match target type ID */
-
- result = coerce_to_target_type(pstate,
- rhs, exprType(rhs),
- targetTypeId, targetTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (result == NULL)
+ /*
+ * Base case: just coerce RHS to match target type ID.
+ * It's necessary only for field selection, since for
+ * subscripting it's custom code who should define types.
+ */
+ if (!targetIsSubscripting)
{
- if (targetIsSubscripting)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment to \"%s\" requires type %s"
- " but expression is of type %s",
- targetName,
- format_type_be(targetTypeId),
- format_type_be(exprType(rhs))),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, location)));
- else
+ result = coerce_to_target_type(pstate,
+ rhs, exprType(rhs),
+ targetTypeId, targetTypMod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+
+ if (result == NULL)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("subfield \"%s\" is of type %s"
@@ -896,29 +890,42 @@ transformAssignmentSubscripts(ParseState *pstate,
Node *rhs,
int location)
{
- Node *result;
- Oid containerType;
- int32 containerTypMod;
- Oid elementTypeId;
- Oid typeNeeded;
- Oid collationNeeded;
+ Node *result;
+ Oid containerType;
+ int32 containerTypMod;
+ Oid collationNeeded;
+ SubscriptingRef *sbsref;
+ SubscriptRoutines *sbsroutines;
Assert(subscripts != NIL);
/* Identify the actual array type and element type involved */
containerType = targetTypeId;
containerTypMod = targetTypMod;
- elementTypeId = transformContainerType(&containerType, &containerTypMod);
- /* Identify type that RHS must provide */
- typeNeeded = isSlice ? containerType : elementTypeId;
+ /* process subscripts */
+ sbsref = transformContainerSubscripts(pstate,
+ basenode,
+ containerType,
+ exprType(rhs),
+ containerTypMod,
+ subscripts,
+ rhs);
+
+ sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype);
+
+ /*
+ * Let custom code provide necessary information about required types:
+ * refelemtype and refassgntype
+ */
+ sbsref = sbsroutines->prepare(rhs != NULL, sbsref);
/*
* container normally has same collation as elements, but there's an
* exception: we might be subscripting a domain over a container type. In
* that case use collation of the base type.
*/
- if (containerType == targetTypeId)
+ if (sbsref->refcontainertype == containerType)
collationNeeded = targetCollation;
else
collationNeeded = get_typcollation(containerType);
@@ -928,24 +935,21 @@ transformAssignmentSubscripts(ParseState *pstate,
NULL,
targetName,
true,
- typeNeeded,
- containerTypMod,
+ sbsref->refassgntype,
+ sbsref->reftypmod,
collationNeeded,
next_indirection,
rhs,
location);
- /* process subscripts */
- result = (Node *) transformContainerSubscripts(pstate,
- basenode,
- containerType,
- elementTypeId,
- containerTypMod,
- subscripts,
- rhs);
+ /* Provide fully prepared subscriptinng information for custom validation */
+ sbsref->refassgnexpr = (Expr *) rhs;
+ sbsroutines->validate(rhs != NULL, sbsref, pstate);
+
+ result = (Node *) sbsref;
/* If target was a domain over container, need to coerce up to the domain */
- if (containerType != targetTypeId)
+ if (sbsref->refcontainertype != targetTypeId)
{
Oid resulttype = exprType(result);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 71adf700fc..66ad044315 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7915,17 +7915,18 @@ get_rule_expr(Node *node, deparse_context *context,
if (need_parens)
appendStringInfoChar(buf, ')');
- /*
- * If there's a refassgnexpr, we want to print the node in the
- * format "container[subscripts] := refassgnexpr". This is
- * not legal SQL, so decompilation of INSERT or UPDATE
- * statements should always use processIndirection as part of
- * the statement-level syntax. We should only see this when
- * EXPLAIN tries to print the targetlist of a plan resulting
- * from such a statement.
- */
- if (sbsref->refassgnexpr)
+ if (IsAssignment(sbsref))
{
+ /*
+ * If there's a refassgnexpr, we want to print the node in the
+ * format "container[subscripts] := refassgnexpr". This is not
+ * legal SQL, so decompilation of INSERT or UPDATE statements
+ * should always use processIndirection as part of the
+ * statement-level syntax. We should only see this when
+ * EXPLAIN tries to print the targetlist of a plan resulting
+ * from such a statement.
+ */
+
Node *refassgnexpr;
/*
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index c13c08a97b..6fbc31aa49 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3150,6 +3150,29 @@ get_range_subtype(Oid rangeOid)
return InvalidOid;
}
+/*
+ * get_typsubshandler
+ *
+ * Given the type OID, return the type's subscripting procedures, if any,
+ * through pointers in arguments.
+ */
+RegProcedure
+get_typsubsprocs(Oid typid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ RegProcedure handler = ((Form_pg_type) GETSTRUCT(tp))->typsubshandler;
+ ReleaseSysCache(tp);
+
+ return handler;
+ }
+ else
+ return InvalidOid;
+}
+
/* ---------- PG_INDEX CACHE ---------- */
/*
diff --git a/src/include/c.h b/src/include/c.h
index 2a082afab1..7b301af80d 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -532,6 +532,8 @@ typedef struct
int indx[MAXDIM];
} IntArray;
+#define MAX_SUBSCRIPT_DEPTH 12
+
/* ----------------
* Variable-length datatypes all share the 'struct varlena' header.
*
diff --git a/src/include/catalog/pg_class.dat b/src/include/catalog/pg_class.dat
index 9bcf28676d..fe0aa5799a 100644
--- a/src/include/catalog/pg_class.dat
+++ b/src/include/catalog/pg_class.dat
@@ -24,7 +24,7 @@
relname => 'pg_type', reltype => 'pg_type', relam => 'heap',
relfilenode => '0', relpages => '0', reltuples => '0', relallvisible => '0',
reltoastrelid => '0', relhasindex => 'f', relisshared => 'f',
- relpersistence => 'p', relkind => 'r', relnatts => '31', relchecks => '0',
+ relpersistence => 'p', relkind => 'r', relnatts => '32', relchecks => '0',
relhasrules => 'f', relhastriggers => 'f', relhassubclass => 'f',
relrowsecurity => 'f', relforcerowsecurity => 'f', relispopulated => 't',
relreplident => 'n', relispartition => 'f', relfrozenxid => '3',
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 2a584b4b13..d98bd0507f 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -217,6 +217,12 @@ CATALOG(pg_type,1247,TypeRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71,TypeRelati
*/
Oid typcollation BKI_DEFAULT(0) BKI_LOOKUP(pg_collation);
+ /*
+ * Type specific subscripting logic. If typsubshandler is none, it means
+ * that this type doesn't support subscripting.
+ */
+ regproc typsubshandler BKI_DEFAULT(-) BKI_LOOKUP(pg_proc);
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/*
@@ -324,7 +330,8 @@ extern ObjectAddress TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims,
bool typeNotNull,
- Oid typeCollation);
+ Oid typeCollation,
+ Oid subscriptingParseProcedure);
extern void GenerateTypeDependencies(Oid typeObjectId,
Form_pg_type typeForm,
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 8e7f7c3d13..7d31fb0ca4 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -19,7 +19,7 @@
/* forward references to avoid circularity */
struct ExprEvalStep;
-struct SubscriptingRefState;
+struct SubscriptRoutines;
/* Bits in ExprState->flags (see also execnodes.h for public flag bits): */
/* expression's interpreter has been initialized */
@@ -672,13 +672,13 @@ typedef struct SubscriptingRefState
/* numupper and upperprovided[] are filled at compile time */
/* at runtime, extracted subscript datums get stored in upperindex[] */
int numupper;
- bool upperprovided[MAXDIM];
- int upperindex[MAXDIM];
+ bool upperprovided[MAX_SUBSCRIPT_DEPTH];
+ Datum upperindex[MAX_SUBSCRIPT_DEPTH];
/* similarly for lower indexes, if any */
int numlower;
- bool lowerprovided[MAXDIM];
- int lowerindex[MAXDIM];
+ bool lowerprovided[MAX_SUBSCRIPT_DEPTH];
+ Datum lowerindex[MAX_SUBSCRIPT_DEPTH];
/* subscript expressions get evaluated into here */
Datum subscriptvalue;
@@ -691,6 +691,9 @@ typedef struct SubscriptingRefState
/* if we have a nested assignment, SBSREF_OLD puts old value here */
Datum prevvalue;
bool prevnull;
+
+ bool resnull;
+ struct SubscriptRoutines *sbsroutines;
} SubscriptingRefState;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 860a84de7c..e7db3b2705 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -403,13 +403,17 @@ typedef struct SubscriptingRef
Expr xpr;
Oid refcontainertype; /* type of the container proper */
Oid refelemtype; /* type of the container elements */
+ Oid refassgntype; /* type of assignment expr that is required */
int32 reftypmod; /* typmod of the container (and elements too) */
Oid refcollid; /* OID of collation, or InvalidOid if none */
+ Oid refnestedfunc; /* OID of type-specific function to handle nested assignment */
List *refupperindexpr; /* expressions that evaluate to upper
* container indexes */
List *reflowerindexpr; /* expressions that evaluate to lower
* container indexes, or NIL for single
* container element */
+ List *refindexprslice; /* whether or not related indexpr from
+ * reflowerindexpr is a slice */
Expr *refexpr; /* the expression that evaluates to a
* container value */
@@ -417,6 +421,8 @@ typedef struct SubscriptingRef
* fetch */
} SubscriptingRef;
+#define IsAssignment(expr) ( ((SubscriptingRef*) expr)->refassgnexpr != NULL )
+
/*
* CoercionContext - distinguishes the allowed set of type casts
*
diff --git a/src/include/nodes/subscripting.h b/src/include/nodes/subscripting.h
new file mode 100644
index 0000000000..1800d5ecf5
--- /dev/null
+++ b/src/include/nodes/subscripting.h
@@ -0,0 +1,42 @@
+/*-------------------------------------------------------------------------
+ *
+ * subscripting.h
+ * API for generic type subscripting
+ *
+ * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/nodes/subscripting.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SUBSCRIPTING_H
+#define SUBSCRIPTING_H
+
+#include "parser/parse_node.h"
+#include "nodes/primnodes.h"
+
+struct ParseState;
+struct SubscriptingRefState;
+
+/* Callback function signatures --- see xsubscripting.sgml for more info. */
+typedef SubscriptingRef * (*SubscriptingPrepare) (bool isAssignment, SubscriptingRef *sbsef);
+
+typedef SubscriptingRef * (*SubscriptingValidate) (bool isAssignment, SubscriptingRef *sbsef,
+ struct ParseState *pstate);
+
+typedef Datum (*SubscriptingFetch) (Datum source, struct SubscriptingRefState *sbsrefstate);
+
+typedef Datum (*SubscriptingAssign) (Datum source, struct SubscriptingRefState *sbrsefstate);
+
+typedef struct SubscriptRoutines
+{
+ SubscriptingPrepare prepare;
+ SubscriptingValidate validate;
+ SubscriptingFetch fetch;
+ SubscriptingAssign assign;
+
+} SubscriptRoutines;
+
+
+#endif /* SUBSCRIPTING_H */
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 7c099e7084..28f7d27c82 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -15,6 +15,7 @@
#define PARSE_NODE_H
#include "nodes/parsenodes.h"
+#include "nodes/subscripting.h"
#include "utils/queryenvironment.h"
#include "utils/relcache.h"
@@ -274,7 +275,7 @@ extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate);
extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno,
int location);
-extern Oid transformContainerType(Oid *containerType, int32 *containerTypmod);
+extern void transformContainerType(Oid *containerType, int32 *containerTypmod);
extern SubscriptingRef *transformContainerSubscripts(ParseState *pstate,
Node *containerBase,
@@ -283,6 +284,7 @@ extern SubscriptingRef *transformContainerSubscripts(ParseState *pstate,
int32 containerTypMod,
List *indirection,
Node *assignFrom);
+extern SubscriptRoutines* getSubscriptingRoutines(Oid containerType);
extern Const *make_const(ParseState *pstate, Value *value, int location);
#endif /* PARSE_NODE_H */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index c8df5bff9f..d7f250a154 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -179,6 +179,7 @@ extern void free_attstatsslot(AttStatsSlot *sslot);
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern RegProcedure get_typsubsprocs(Oid typid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
--
2.16.4