From 5ae3cb4534e53eb828ddcffa92c9944007067ae0 Mon Sep 17 00:00:00 2001
From: erthalion <9erthalion6@gmail.com>
Date: Thu, 31 Jan 2019 22:37:19 +0100
Subject: [PATCH v23 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                 | 139 +++++-------------------
 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                   |  19 ++--
 src/include/nodes/subscripting.h                |  42 +++++++
 src/include/parser/parse_node.h                 |   4 +-
 src/include/utils/lsyscache.h                   |   1 +
 23 files changed, 338 insertions(+), 335 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 26610b34b6..901c5a967a 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 86820eecfc..d0055d7698 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 e4a6c20ed0..8567ec5e33 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 237598e110..591649e4a5 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 3a7a858e3a..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
@@ -310,13 +287,8 @@ transformContainerSubscripts(ParseState *pstate,
 	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
@@ -350,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
 			{
@@ -380,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.
 	 */
@@ -445,18 +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 ef2f5b45d8..1e9af41284 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 9dda4820af..dd7acb64c8 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 b4f2d0f35a..2763de4efc 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 f9b1cf2df7..d071f3c017 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -402,14 +402,15 @@ typedef struct SubscriptingRef
 {
 	Expr		xpr;
 	Oid			refcontainertype;	/* type of the container proper */
-	Oid			refelemtype;	/* type of the container elements */
-	int32		reftypmod;		/* typmod of the container (and elements too) */
-	Oid			refcollid;		/* OID of collation, or InvalidOid if none */
-	List	   *refupperindexpr;	/* expressions that evaluate to upper
-									 * container indexes */
-	List	   *reflowerindexpr;	/* expressions that evaluate to lower
-									 * container indexes, or NIL for single
-									 * container element */
+	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
@@ -419,6 +420,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

