Re: [HACKERS] [PATCH] Generic type subscripting

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: [HACKERS] [PATCH] Generic type subscripting
Дата
Msg-id 2155075.1607096548@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: [HACKERS] [PATCH] Generic type subscripting  (Pavel Stehule <pavel.stehule@gmail.com>)
Ответы Re: [HACKERS] [PATCH] Generic type subscripting  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-hackers
Pavel Stehule <pavel.stehule@gmail.com> writes:
> I tested the last patch on my FC33 Lenovo T520 (I7) and I don't see 15%
> slowdown too .. On my comp there is a slowdown of about 1.5-3%. I used your
> function arraytest.

After repeating the experiment a few times, I think I was misled
by ASLR variance (ie, hot code falling within or across cache
lines depending on where the backend executable gets loaded).
I'd tried a couple of postmaster restarts, but seemingly not
enough to expose the full variance in runtime that's due to that.
I do still see a 2% or so penalty when comparing best-case runtimes,
which is consistent with other people's reports.

However, 2% is still more than I want to pay for this feature,
and after studying the patchset awhile I don't think it's tried
hard at all on execution efficiency.  We should eliminate the
ExecEvalSubscriptingRef* interface layer altogether, and have
ExecInterpExpr dispatch directly to container-type-specific code,
so that we end up with approximately the same call depth as before.
With the patches shown below, we are (as best I can tell) right
about on par with the existing code's runtime.  This patch also gets
rid of a few more undesirable assumptions in the core executor ---
for instance, AFAICS there is no need for *any* hard-wired limit
on the number of subscripts within the core executor.  (What a
particular container type chooses to support is its business,
of course.)  We also need not back off on the specificity of error
messages, since the complaints that were in ExecEvalSubscriptingRef*
are now in container-type-specific code.

There were other things not to like about the way v35 chose to
refactor the executor support.  In particular, I don't think it
understood the point of having the EEOP_SBSREF_SUBSCRIPT steps,
which is to only transform the subscript Datums to internal form
once, even when we have to use them twice in OLD and ASSIGN steps.
Admittedly DatumGetInt32 is pretty cheap, but this cannot be said
of reading text datums as the 0003 patch wishes to do.  (BTW, 0003 is
seriously buggy in that regard, as it's failing to cope with toasted
or even short-header inputs.  We really don't want to detoast twice,
so that has to be dealt with in the SUBSCRIPT step.)  I also felt that
processing the subscripts one-at-a-time wasn't necessarily a great
solution, as one can imagine container semantics where they need to be
handled more holistically.  So I replaced EEOP_SBSREF_SUBSCRIPT with
EEOP_SBSREF_SUBSCRIPTS, which is executed just once after all the
subscript Datums have been collected.  (This does mean that we lose
the optimization of short-circuiting as soon as we've found a NULL
subscript, but I'm not troubled by that.  I note in particular that
the core code shouldn't be forcing a particular view of what to do
with null subscripts onto all container types.)

The two patches attached cover the same territory as v35's 0001 and
0002, but I split it up differently because I didn't see much point
in a division that has a nonfunctional code state in the middle.
0001 below is just concerned with revising things enough so that the
core executor doesn't have any assumption about a maximum number of
subscripts.  Then 0002 incorporates what was in v35 0001+0002, revised
with what seems to me a better set of execution APIs.

There are a bunch of loose ends yet, the first three introduced
by me and the rest being pre-existing problems:

* I don't have a lot of confidence in the LLVM changes --- they seem
to work, but I don't really understand that code, and in particular
I don't understand the difference between TypeParamBool and
TypeStorageBool.  So there might be something subtly wrong with the
code generation for EEOP_SBSREF_SUBSCRIPTS.

* As things stand here, there's no difference among the expression
step types EEOP_SBSREF_OLD, EEOP_SBSREF_ASSIGN, and EEOP_SBSREF_FETCH;
they dispatch to different support routines but the core executor's
behavior is identical.  So we could fold them all into one step type,
and lose nothing except perhaps debugging visibility.  Should we do
that, or keep them separate?

* I've not rebased v35-0003 and later onto this design, and don't
intend to do so myself.

* The patchset adds a CREATE TYPE option, but fails to provide any
pg_dump support for that option.  (There's no test coverage either.
Maybe further on, we should extend hstore or another contrib type
to have subscripting support, if only to have testing of that?)

* CREATE TYPE fails to create a dependency from a type to its
subscripting function.  (Related to which, the changes to the
GenerateTypeDependencies call in TypeShellMake are surely wrong.)

* findTypeSubscriptingFunction contains dead code (not to mention sadly
incorrect comments).

* What is refnestedfunc?  That sure seems to be dead code.

* I'm not on board with including refindexprslice in the transformed
expression, either.  AFAICS that is the untransformed subscript list,
which has *no* business being included in the finished parsetree.
Probably that needs to be passed to the type-specific
transform/validate code separately.

* I've not really reviewed the parse analysis changes, but what is
the motivation for separating the prepare and validate callbacks?
It looks like those could be merged.

* exprType (and exprTypeMod, perhaps) seem to be assuming more than
they should about subscripting semantics.  I think it should be
possible for the type-specific code to define what the result type
of a subscripting transformation is, without hard-wired rules like
these.

* The new code added to arrayfuncs.c seems like it doesn't really
belong there (the fact that it forces adding a ton of new #include's
is a good sign that it doesn't fit with the existing code there).
I'm inclined to propose that we should break that out into a new .c
file, maybe "arraysubs.c".

* The proposed documentation in 0004 is pretty poor.  You might
as well drop all of xsubscripting.sgml and just say "look at
the existing code for examples".  (Splitting the array interface
code out into a new file would help with that, too, as there'd be
a well-defined set of code to point to.)

            regards, tom lane

diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 79b325c7cf..c8382e9381 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -2523,11 +2523,19 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
                         ExprState *state, Datum *resv, bool *resnull)
 {
     bool        isAssignment = (sbsref->refassgnexpr != NULL);
-    SubscriptingRefState *sbsrefstate = palloc0(sizeof(SubscriptingRefState));
+    int            nupper = list_length(sbsref->refupperindexpr);
+    int            nlower = list_length(sbsref->reflowerindexpr);
+    SubscriptingRefState *sbsrefstate;
+    char       *ptr;
     List       *adjust_jumps = NIL;
     ListCell   *lc;
     int            i;

+    /* Allocate enough space for per-subscript arrays too */
+    sbsrefstate = palloc0(MAXALIGN(sizeof(SubscriptingRefState)) +
+                          (nupper + nlower) * (sizeof(Datum) +
+                                               2 * sizeof(bool)));
+
     /* Fill constant fields of SubscriptingRefState */
     sbsrefstate->isassignment = isAssignment;
     sbsrefstate->refelemtype = sbsref->refelemtype;
@@ -2536,6 +2544,22 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
                          &sbsrefstate->refelemlength,
                          &sbsrefstate->refelembyval,
                          &sbsrefstate->refelemalign);
+    sbsrefstate->numupper = nupper;
+    sbsrefstate->numlower = nlower;
+    /* Set up per-subscript arrays */
+    ptr = ((char *) sbsrefstate) + MAXALIGN(sizeof(SubscriptingRefState));
+    sbsrefstate->upperindex = (Datum *) ptr;
+    ptr += nupper * sizeof(Datum);
+    sbsrefstate->lowerindex = (Datum *) ptr;
+    ptr += nlower * sizeof(Datum);
+    sbsrefstate->upperprovided = (bool *) ptr;
+    ptr += nupper * sizeof(bool);
+    sbsrefstate->lowerprovided = (bool *) ptr;
+    ptr += nlower * sizeof(bool);
+    sbsrefstate->upperindexnull = (bool *) ptr;
+    ptr += nupper * sizeof(bool);
+    sbsrefstate->lowerindexnull = (bool *) ptr;
+    /* ptr += nlower * sizeof(bool); */

     /*
      * Evaluate array input.  It's safe to do so into resv/resnull, because we
@@ -2548,7 +2572,8 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
     /*
      * If refexpr yields NULL, and it's a fetch, then result is NULL.  We can
      * implement this with just JUMP_IF_NULL, since we evaluated the array
-     * into the desired target location.
+     * into the desired target location.  (XXX is it okay to impose these
+     * semantics on all forms of subscripting?)
      */
     if (!isAssignment)
     {
@@ -2559,19 +2584,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)
@@ -2582,28 +2594,18 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
         if (!e)
         {
             sbsrefstate->upperprovided[i] = false;
-            i++;
-            continue;
+            sbsrefstate->upperindexnull[i] = true;
+        }
+        else
+        {
+            sbsrefstate->upperprovided[i] = true;
+            /* Each subscript is evaluated into appropriate array entry */
+            ExecInitExprRec(e, state,
+                            &sbsrefstate->upperindex[i],
+                            &sbsrefstate->upperindexnull[i]);
         }
-
-        sbsrefstate->upperprovided[i] = true;
-
-        /* Each subscript is evaluated into subscriptvalue/subscriptnull */
-        ExecInitExprRec(e, state,
-                        &sbsrefstate->subscriptvalue, &sbsrefstate->subscriptnull);
-
-        /* ... and then SBSREF_SUBSCRIPT saves it into step's workspace */
-        scratch->opcode = EEOP_SBSREF_SUBSCRIPT;
-        scratch->d.sbsref_subscript.state = sbsrefstate;
-        scratch->d.sbsref_subscript.off = i;
-        scratch->d.sbsref_subscript.isupper = true;
-        scratch->d.sbsref_subscript.jumpdone = -1;    /* adjust later */
-        ExprEvalPushStep(state, scratch);
-        adjust_jumps = lappend_int(adjust_jumps,
-                                   state->steps_len - 1);
         i++;
     }
-    sbsrefstate->numupper = i;

     /* Evaluate lower subscripts similarly */
     i = 0;
@@ -2615,33 +2617,26 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
         if (!e)
         {
             sbsrefstate->lowerprovided[i] = false;
-            i++;
-            continue;
+            sbsrefstate->lowerindexnull[i] = true;
+        }
+        else
+        {
+            sbsrefstate->lowerprovided[i] = true;
+            /* Each subscript is evaluated into appropriate array entry */
+            ExecInitExprRec(e, state,
+                            &sbsrefstate->lowerindex[i],
+                            &sbsrefstate->lowerindexnull[i]);
         }
-
-        sbsrefstate->lowerprovided[i] = true;
-
-        /* Each subscript is evaluated into subscriptvalue/subscriptnull */
-        ExecInitExprRec(e, state,
-                        &sbsrefstate->subscriptvalue, &sbsrefstate->subscriptnull);
-
-        /* ... and then SBSREF_SUBSCRIPT saves it into step's workspace */
-        scratch->opcode = EEOP_SBSREF_SUBSCRIPT;
-        scratch->d.sbsref_subscript.state = sbsrefstate;
-        scratch->d.sbsref_subscript.off = i;
-        scratch->d.sbsref_subscript.isupper = false;
-        scratch->d.sbsref_subscript.jumpdone = -1;    /* adjust later */
-        ExprEvalPushStep(state, scratch);
-        adjust_jumps = lappend_int(adjust_jumps,
-                                   state->steps_len - 1);
         i++;
     }
-    sbsrefstate->numlower = i;

-    /* Should be impossible if parser is sane, but check anyway: */
-    if (sbsrefstate->numlower != 0 &&
-        sbsrefstate->numupper != sbsrefstate->numlower)
-        elog(ERROR, "upper and lower index lists are not same length");
+    /* SBSREF_SUBSCRIPTS checks and converts all the subscripts at once */
+    scratch->opcode = EEOP_SBSREF_SUBSCRIPTS;
+    scratch->d.sbsref_subscript.state = sbsrefstate;
+    scratch->d.sbsref_subscript.jumpdone = -1;    /* adjust later */
+    ExprEvalPushStep(state, scratch);
+    adjust_jumps = lappend_int(adjust_jumps,
+                               state->steps_len - 1);

     if (isAssignment)
     {
@@ -2686,7 +2681,6 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
         scratch->opcode = EEOP_SBSREF_ASSIGN;
         scratch->d.sbsref.state = sbsrefstate;
         ExprEvalPushStep(state, scratch);
-
     }
     else
     {
@@ -2694,7 +2688,6 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
         scratch->opcode = EEOP_SBSREF_FETCH;
         scratch->d.sbsref.state = sbsrefstate;
         ExprEvalPushStep(state, scratch);
-
     }

     /* adjust jump targets */
@@ -2702,7 +2695,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
     {
         ExprEvalStep *as = &state->steps[lfirst_int(lc)];

-        if (as->opcode == EEOP_SBSREF_SUBSCRIPT)
+        if (as->opcode == EEOP_SBSREF_SUBSCRIPTS)
         {
             Assert(as->d.sbsref_subscript.jumpdone == -1);
             as->d.sbsref_subscript.jumpdone = state->steps_len;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index c09371ad58..1853405026 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -417,7 +417,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
         &&CASE_EEOP_FIELDSELECT,
         &&CASE_EEOP_FIELDSTORE_DEFORM,
         &&CASE_EEOP_FIELDSTORE_FORM,
-        &&CASE_EEOP_SBSREF_SUBSCRIPT,
+        &&CASE_EEOP_SBSREF_SUBSCRIPTS,
         &&CASE_EEOP_SBSREF_OLD,
         &&CASE_EEOP_SBSREF_ASSIGN,
         &&CASE_EEOP_SBSREF_FETCH,
@@ -1396,9 +1396,9 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
             EEO_NEXT();
         }

-        EEO_CASE(EEOP_SBSREF_SUBSCRIPT)
+        EEO_CASE(EEOP_SBSREF_SUBSCRIPTS)
         {
-            /* Process an array subscript */
+            /* Process array subscript(s) */

             /* too complex for an inline implementation */
             if (ExecEvalSubscriptingRef(state, op))
@@ -3123,42 +3123,87 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
 }

 /*
- * Process a subscript in a SubscriptingRef expression.
+ * Process the subscripts in a SubscriptingRef expression.
  *
- * If subscript is NULL, throw error in assignment case, or in fetch case
+ * If any subscript is NULL, throw error in assignment case, or in fetch case
  * set result to NULL and return false (instructing caller to skip the rest
  * of the SubscriptingRef sequence).
  *
- * Subscript expression result is in subscriptvalue/subscriptnull.
- * On success, integer subscript value has been saved in upperindex[] or
- * lowerindex[] for use later.
+ * We convert all the subscripts to plain integers and save them in the
+ * sbsrefstate->workspace array.
  */
 bool
 ExecEvalSubscriptingRef(ExprState *state, ExprEvalStep *op)
 {
-    SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state;
+    SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
     int           *indexes;
-    int            off;

-    /* If any index expr yields NULL, result is NULL or error */
-    if (sbsrefstate->subscriptnull)
+    /*
+     * Allocate workspace if first time through.  This is also a good place to
+     * enforce the implementation limit on number of array subscripts.
+     */
+    if (sbsrefstate->workspace == NULL)
     {
-        if (sbsrefstate->isassignment)
+        if (sbsrefstate->numupper > MAXDIM)
             ereport(ERROR,
-                    (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-                     errmsg("array subscript in assignment must not be null")));
-        *op->resnull = true;
-        return false;
+                    (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                     errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+                            sbsrefstate->numupper, MAXDIM)));
+
+        /* Should be impossible if parser is sane, but check anyway: */
+        if (sbsrefstate->numlower != 0 &&
+            sbsrefstate->numupper != sbsrefstate->numlower)
+            elog(ERROR, "upper and lower index lists are not same length");
+
+        /*
+         * Workspace always has room for MAXDIM subscripts even if we don't
+         * have that many.  This is necessary because array_get/set_slice may
+         * scribble on the extra entries.
+         */
+        sbsrefstate->workspace =
+            MemoryContextAlloc(GetMemoryChunkContext(sbsrefstate),
+                               2 * MAXDIM * sizeof(int));
     }

-    /* Convert datum to int, save in appropriate place */
-    if (op->d.sbsref_subscript.isupper)
-        indexes = sbsrefstate->upperindex;
-    else
-        indexes = sbsrefstate->lowerindex;
-    off = op->d.sbsref_subscript.off;
+    /* Process upper subscripts */
+    indexes = (int *) sbsrefstate->workspace;
+    for (int i = 0; i < sbsrefstate->numupper; i++)
+    {
+        if (sbsrefstate->upperprovided[i])
+        {
+            /* If any index expr yields NULL, result is NULL or error */
+            if (sbsrefstate->upperindexnull[i])
+            {
+                if (sbsrefstate->isassignment)
+                    ereport(ERROR,
+                            (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                             errmsg("array subscript in assignment must not be null")));
+                *op->resnull = true;
+                return false;
+            }
+            indexes[i] = DatumGetInt32(sbsrefstate->upperindex[i]);
+        }
+    }

-    indexes[off] = DatumGetInt32(sbsrefstate->subscriptvalue);
+    /* Likewise for lower subscripts */
+    indexes += MAXDIM;
+    for (int i = 0; i < sbsrefstate->numlower; i++)
+    {
+        if (sbsrefstate->lowerprovided[i])
+        {
+            /* If any index expr yields NULL, result is NULL or error */
+            if (sbsrefstate->lowerindexnull[i])
+            {
+                if (sbsrefstate->isassignment)
+                    ereport(ERROR,
+                            (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                             errmsg("array subscript in assignment must not be null")));
+                *op->resnull = true;
+                return false;
+            }
+            indexes[i] = DatumGetInt32(sbsrefstate->lowerindex[i]);
+        }
+    }

     return true;
 }
@@ -3166,12 +3211,15 @@ ExecEvalSubscriptingRef(ExprState *state, ExprEvalStep *op)
 /*
  * Evaluate SubscriptingRef fetch.
  *
- * Source container is in step's result variable.
+ * Source container is in step's result variable,
+ * and indexes have already been evaluated into workspace array.
  */
 void
 ExecEvalSubscriptingRefFetch(ExprState *state, ExprEvalStep *op)
 {
     SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
+    int           *upperindex = (int *) sbsrefstate->workspace;
+    int           *lowerindex = upperindex + MAXDIM;

     /* Should not get here if source container (or any subscript) is null */
     Assert(!(*op->resnull));
@@ -3181,7 +3229,7 @@ ExecEvalSubscriptingRefFetch(ExprState *state, ExprEvalStep *op)
         /* Scalar case */
         *op->resvalue = array_get_element(*op->resvalue,
                                           sbsrefstate->numupper,
-                                          sbsrefstate->upperindex,
+                                          upperindex,
                                           sbsrefstate->refattrlength,
                                           sbsrefstate->refelemlength,
                                           sbsrefstate->refelembyval,
@@ -3193,8 +3241,8 @@ ExecEvalSubscriptingRefFetch(ExprState *state, ExprEvalStep *op)
         /* Slice case */
         *op->resvalue = array_get_slice(*op->resvalue,
                                         sbsrefstate->numupper,
-                                        sbsrefstate->upperindex,
-                                        sbsrefstate->lowerindex,
+                                        upperindex,
+                                        lowerindex,
                                         sbsrefstate->upperprovided,
                                         sbsrefstate->lowerprovided,
                                         sbsrefstate->refattrlength,
@@ -3214,6 +3262,8 @@ void
 ExecEvalSubscriptingRefOld(ExprState *state, ExprEvalStep *op)
 {
     SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
+    int           *upperindex = (int *) sbsrefstate->workspace;
+    int           *lowerindex = upperindex + MAXDIM;

     if (*op->resnull)
     {
@@ -3226,7 +3276,7 @@ ExecEvalSubscriptingRefOld(ExprState *state, ExprEvalStep *op)
         /* Scalar case */
         sbsrefstate->prevvalue = array_get_element(*op->resvalue,
                                                    sbsrefstate->numupper,
-                                                   sbsrefstate->upperindex,
+                                                   upperindex,
                                                    sbsrefstate->refattrlength,
                                                    sbsrefstate->refelemlength,
                                                    sbsrefstate->refelembyval,
@@ -3239,8 +3289,8 @@ ExecEvalSubscriptingRefOld(ExprState *state, ExprEvalStep *op)
         /* this is currently unreachable */
         sbsrefstate->prevvalue = array_get_slice(*op->resvalue,
                                                  sbsrefstate->numupper,
-                                                 sbsrefstate->upperindex,
-                                                 sbsrefstate->lowerindex,
+                                                 upperindex,
+                                                 lowerindex,
                                                  sbsrefstate->upperprovided,
                                                  sbsrefstate->lowerprovided,
                                                  sbsrefstate->refattrlength,
@@ -3260,7 +3310,9 @@ ExecEvalSubscriptingRefOld(ExprState *state, ExprEvalStep *op)
 void
 ExecEvalSubscriptingRefAssign(ExprState *state, ExprEvalStep *op)
 {
-    SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state;
+    SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
+    int           *upperindex = (int *) sbsrefstate->workspace;
+    int           *lowerindex = upperindex + MAXDIM;

     /*
      * For an assignment to a fixed-length container type, both the original
@@ -3290,7 +3342,7 @@ ExecEvalSubscriptingRefAssign(ExprState *state, ExprEvalStep *op)
         /* Scalar case */
         *op->resvalue = array_set_element(*op->resvalue,
                                           sbsrefstate->numupper,
-                                          sbsrefstate->upperindex,
+                                          upperindex,
                                           sbsrefstate->replacevalue,
                                           sbsrefstate->replacenull,
                                           sbsrefstate->refattrlength,
@@ -3303,8 +3355,8 @@ ExecEvalSubscriptingRefAssign(ExprState *state, ExprEvalStep *op)
         /* Slice case */
         *op->resvalue = array_set_slice(*op->resvalue,
                                         sbsrefstate->numupper,
-                                        sbsrefstate->upperindex,
-                                        sbsrefstate->lowerindex,
+                                        upperindex,
+                                        lowerindex,
                                         sbsrefstate->upperprovided,
                                         sbsrefstate->lowerprovided,
                                         sbsrefstate->replacevalue,
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index f232397cab..bc9b6771e3 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -1746,7 +1746,7 @@ llvm_compile_expr(ExprState *state)
                 LLVMBuildBr(b, opblocks[opno + 1]);
                 break;

-            case EEOP_SBSREF_SUBSCRIPT:
+            case EEOP_SBSREF_SUBSCRIPTS:
                 {
                     int            jumpdone = op->d.sbsref_subscript.jumpdone;
                     LLVMValueRef v_ret;
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index a7ea7656c7..4c8a739bc4 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -2044,7 +2044,8 @@ array_get_element_expanded(Datum arraydatum,
  * array bound.
  *
  * NOTE: we assume it is OK to scribble on the provided subscript arrays
- * lowerIndx[] and upperIndx[].  These are generally just temporaries.
+ * lowerIndx[] and upperIndx[]; also, these arrays must be of size MAXDIM
+ * even when nSubscripts is less.  These are generally just temporaries.
  */
 Datum
 array_get_slice(Datum arraydatum,
@@ -2772,7 +2773,8 @@ array_set_element_expanded(Datum arraydatum,
  * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
  *
  * NOTE: we assume it is OK to scribble on the provided index arrays
- * lowerIndx[] and upperIndx[].  These are generally just temporaries.
+ * lowerIndx[] and upperIndx[]; also, these arrays must be of size MAXDIM
+ * even when nSubscripts is less.  These are generally just temporaries.
  *
  * NOTE: For assignments, we throw an error for silly subscripts etc,
  * rather than returning a NULL or empty array as the fetch operations do.
diff --git a/src/include/c.h b/src/include/c.h
index b21e4074dd..12ea056a35 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -592,13 +592,9 @@ typedef uint32 CommandId;
 #define InvalidCommandId    (~(CommandId)0)

 /*
- * Array indexing support
+ * Maximum number of array subscripts, for regular varlena arrays
  */
 #define MAXDIM 6
-typedef struct
-{
-    int            indx[MAXDIM];
-}            IntArray;

 /* ----------------
  *        Variable-length datatypes all share the 'struct varlena' header.
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index abb489e206..b768c30b74 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -185,8 +185,8 @@ typedef enum ExprEvalOp
      */
     EEOP_FIELDSTORE_FORM,

-    /* Process a container subscript; short-circuit expression to NULL if NULL */
-    EEOP_SBSREF_SUBSCRIPT,
+    /* Process container subscripts; possibly short-circuit result to NULL */
+    EEOP_SBSREF_SUBSCRIPTS,

     /*
      * Compute old container element/slice when a SubscriptingRef assignment
@@ -494,13 +494,11 @@ typedef struct ExprEvalStep
             int            ncolumns;
         }            fieldstore;

-        /* for EEOP_SBSREF_SUBSCRIPT */
+        /* for EEOP_SBSREF_SUBSCRIPTS */
         struct
         {
             /* too big to have inline */
             struct SubscriptingRefState *state;
-            int            off;    /* 0-based index of this subscript */
-            bool        isupper;    /* is it upper or lower subscript? */
             int            jumpdone;    /* jump here on null */
         }            sbsref_subscript;

@@ -646,20 +644,21 @@ typedef struct SubscriptingRefState
     bool        refelembyval;    /* is the element type pass-by-value? */
     char        refelemalign;    /* typalign of the element type */

-    /* numupper and upperprovided[] are filled at compile time */
-    /* at runtime, extracted subscript datums get stored in upperindex[] */
+    /* workspace for type-specific subscripting code */
+    void       *workspace;
+
+    /* numupper and upperprovided[] are filled at expression compile time */
+    /* at runtime, subscripts are computed in upperindex[]/upperindexnull[] */
     int            numupper;
-    bool        upperprovided[MAXDIM];
-    int            upperindex[MAXDIM];
+    bool       *upperprovided;    /* indicates if this position is supplied */
+    Datum       *upperindex;
+    bool       *upperindexnull;

     /* similarly for lower indexes, if any */
     int            numlower;
-    bool        lowerprovided[MAXDIM];
-    int            lowerindex[MAXDIM];
-
-    /* subscript expressions get evaluated into here */
-    Datum        subscriptvalue;
-    bool        subscriptnull;
+    bool       *lowerprovided;
+    Datum       *lowerindex;
+    bool       *lowerindexnull;

     /* for assignment, new value to assign is evaluated into here */
     Datum        replacevalue;
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 70cfdb2c9d..f5835e89dd 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2858,6 +2858,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/Catalog.pm b/src/backend/catalog/Catalog.pm
index dd39a086ce..b4dfa26518 100644
--- a/src/backend/catalog/Catalog.pm
+++ b/src/backend/catalog/Catalog.pm
@@ -384,6 +384,7 @@ sub GenerateArrayTypes
         # Arrays require INT alignment, unless the element type requires
         # DOUBLE alignment.
         $array_type{typalign} = $elem_type->{typalign} eq 'd' ? 'd' : 'i';
+        $array_type{typsubshandler} = 'array_subscript_handler';

         # Fill in the rest of the array entry's fields.
         foreach my $column (@$pgtype_schema)
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 4cd7d76938..d157f6f5ac 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -1091,7 +1091,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 */
 }

 /* --------------------------------
@@ -1370,7 +1371,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 */
+                   F_ARRAY_SUBSCRIPT_HANDLER);    /* array implementation */

         pfree(relarrayname);
     }
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index aeb4a54f63..bd67512e26 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -119,6 +119,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;
@@ -159,10 +160,10 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
         GenerateTypeDependencies(tup,
                                  pg_type_desc,
                                  NULL,
-                                 NULL,
                                  0,
                                  false,
                                  false,
+                                 InvalidOid,
                                  false);

     /* Post creation hook for new shell type */
@@ -220,7 +221,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;
@@ -373,6 +375,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
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 483bb65ddc..596c6cf3ca 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -115,6 +115,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);
@@ -149,6 +150,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;
@@ -167,6 +169,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;
@@ -188,6 +191,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
     Oid            typoid;
     ListCell   *pl;
     ObjectAddress address;
+    Oid            subscriptingParseOid = InvalidOid;

     /*
      * As of Postgres 8.4, we require superuser privilege to create a base
@@ -288,6 +292,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)
@@ -358,6 +364,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);
@@ -482,6 +490,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
@@ -563,7 +575,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);

     /*
@@ -604,7 +617,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 */
+               F_ARRAY_SUBSCRIPT_HANDLER);

     pfree(array_type);

@@ -667,6 +681,7 @@ DefineDomain(CreateDomainStmt *stmt)
     Oid            receiveProcedure;
     Oid            sendProcedure;
     Oid            analyzeProcedure;
+    Oid            subscriptingHandlerProcedure;
     bool        byValue;
     char        category;
     char        delimiter;
@@ -800,6 +815,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);
@@ -1005,7 +1023,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.
@@ -1045,7 +1064,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 */
+               F_ARRAY_SUBSCRIPT_HANDLER); /* array subscripting implementation */

     pfree(domainArrayName);

@@ -1160,7 +1180,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);
@@ -1200,7 +1221,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 */
+               F_ARRAY_SUBSCRIPT_HANDLER);    /* array subscripting implementation */

     pfree(enumArrayName);

@@ -1488,7 +1510,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 == InvalidOid || typoid == address.objectId);
     typoid = address.objectId;

@@ -1531,7 +1554,8 @@ DefineRange(CreateRangeStmt *stmt)
                -1,                /* typMod (Domains only) */
                0,                /* Array dimensions of typbasetype */
                false,            /* Type NOT NULL */
-               InvalidOid);        /* typcollation */
+               InvalidOid,        /* typcollation */
+               F_ARRAY_SUBSCRIPT_HANDLER);    /* array subscripting implementation */

     pfree(rangeArrayName);

@@ -1904,6 +1928,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 parse functions always take two INTERNAL arguments and
+         * return INTERNAL.
+         */
+        argList[0] = INTERNALOID;
+        nargs = 1;
+    }
+    else
+    {
+        /*
+         * Subscripting fetch/assign functions 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 c8382e9381..473e595662 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -40,6 +40,7 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/subscripting.h"
 #include "optimizer/optimizer.h"
 #include "pgstat.h"
 #include "utils/acl.h"
@@ -2522,6 +2523,7 @@ static void
 ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
                         ExprState *state, Datum *resv, bool *resnull)
 {
+    SubscriptRoutines *sbsroutines = getSubscriptingRoutines(sbsref->refcontainertype);
     bool        isAssignment = (sbsref->refassgnexpr != NULL);
     int            nupper = list_length(sbsref->refupperindexpr);
     int            nlower = list_length(sbsref->reflowerindexpr);
@@ -2538,12 +2540,6 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,

     /* 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->numupper = nupper;
     sbsrefstate->numlower = nlower;
     /* Set up per-subscript arrays */
@@ -2561,6 +2557,14 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
     sbsrefstate->lowerindexnull = (bool *) ptr;
     /* ptr += nlower * sizeof(bool); */

+    /*
+     * Let the container-type-specific code have a chance.  It must fill in
+     * the sbs_subscripts, sbs_fetch, sbs_assign, and sbs_fetch_old function
+     * pointers for us to possibly use in execution steps below; and it can
+     * optionally set up some data pointed to by the workspace field.
+     */
+    sbsroutines->exec_setup(sbsref, sbsrefstate);
+
     /*
      * Evaluate array input.  It's safe to do so into resv/resnull, because we
      * won't use that as target for any of the other subexpressions, and it'll
@@ -2632,6 +2636,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,

     /* SBSREF_SUBSCRIPTS checks and converts all the subscripts at once */
     scratch->opcode = EEOP_SBSREF_SUBSCRIPTS;
+    scratch->d.sbsref_subscript.subscriptfunc = sbsrefstate->sbs_subscripts;
     scratch->d.sbsref_subscript.state = sbsrefstate;
     scratch->d.sbsref_subscript.jumpdone = -1;    /* adjust later */
     ExprEvalPushStep(state, scratch);
@@ -2660,6 +2665,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
         if (isAssignmentIndirectionExpr(sbsref->refassgnexpr))
         {
             scratch->opcode = EEOP_SBSREF_OLD;
+            scratch->d.sbsref.subscriptfunc = sbsrefstate->sbs_fetch_old;
             scratch->d.sbsref.state = sbsrefstate;
             ExprEvalPushStep(state, scratch);
         }
@@ -2679,6 +2685,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,

         /* and perform the assignment */
         scratch->opcode = EEOP_SBSREF_ASSIGN;
+        scratch->d.sbsref.subscriptfunc = sbsrefstate->sbs_assign;
         scratch->d.sbsref.state = sbsrefstate;
         ExprEvalPushStep(state, scratch);
     }
@@ -2686,6 +2693,7 @@ ExecInitSubscriptingRef(ExprEvalStep *scratch, SubscriptingRef *sbsref,
     {
         /* array fetch is much simpler */
         scratch->opcode = EEOP_SBSREF_FETCH;
+        scratch->d.sbsref.subscriptfunc = sbsrefstate->sbs_fetch;
         scratch->d.sbsref.state = sbsrefstate;
         ExprEvalPushStep(state, scratch);
     }
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 1853405026..a4b71fb554 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -1398,10 +1398,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)

         EEO_CASE(EEOP_SBSREF_SUBSCRIPTS)
         {
-            /* Process array subscript(s) */
-
-            /* too complex for an inline implementation */
-            if (ExecEvalSubscriptingRef(state, op))
+            /* Process container subscript(s) */
+            if (op->d.sbsref_subscript.subscriptfunc(state, op, econtext))
             {
                 EEO_NEXT();
             }
@@ -1419,9 +1417,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
              * referenced (via a CaseTestExpr) inside the assignment
              * expression.
              */
-
-            /* too complex for an inline implementation */
-            ExecEvalSubscriptingRefOld(state, op);
+            op->d.sbsref.subscriptfunc(state, op, econtext);

             EEO_NEXT();
         }
@@ -1431,19 +1427,17 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
          */
         EEO_CASE(EEOP_SBSREF_ASSIGN)
         {
-            /* too complex for an inline implementation */
-            ExecEvalSubscriptingRefAssign(state, op);
+            op->d.sbsref.subscriptfunc(state, op, econtext);

             EEO_NEXT();
         }

         /*
-         * Fetch subset of an array.
+         * Perform SubscriptingRef fetch
          */
         EEO_CASE(EEOP_SBSREF_FETCH)
         {
-            /* too complex for an inline implementation */
-            ExecEvalSubscriptingRefFetch(state, op);
+            op->d.sbsref.subscriptfunc(state, op, econtext);

             EEO_NEXT();
         }
@@ -3122,252 +3116,6 @@ ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
     *op->resnull = false;
 }

-/*
- * Process the subscripts in a SubscriptingRef expression.
- *
- * If any subscript is NULL, throw error in assignment case, or in fetch case
- * set result to NULL and return false (instructing caller to skip the rest
- * of the SubscriptingRef sequence).
- *
- * We convert all the subscripts to plain integers and save them in the
- * sbsrefstate->workspace array.
- */
-bool
-ExecEvalSubscriptingRef(ExprState *state, ExprEvalStep *op)
-{
-    SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
-    int           *indexes;
-
-    /*
-     * Allocate workspace if first time through.  This is also a good place to
-     * enforce the implementation limit on number of array subscripts.
-     */
-    if (sbsrefstate->workspace == NULL)
-    {
-        if (sbsrefstate->numupper > MAXDIM)
-            ereport(ERROR,
-                    (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-                     errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
-                            sbsrefstate->numupper, MAXDIM)));
-
-        /* Should be impossible if parser is sane, but check anyway: */
-        if (sbsrefstate->numlower != 0 &&
-            sbsrefstate->numupper != sbsrefstate->numlower)
-            elog(ERROR, "upper and lower index lists are not same length");
-
-        /*
-         * Workspace always has room for MAXDIM subscripts even if we don't
-         * have that many.  This is necessary because array_get/set_slice may
-         * scribble on the extra entries.
-         */
-        sbsrefstate->workspace =
-            MemoryContextAlloc(GetMemoryChunkContext(sbsrefstate),
-                               2 * MAXDIM * sizeof(int));
-    }
-
-    /* Process upper subscripts */
-    indexes = (int *) sbsrefstate->workspace;
-    for (int i = 0; i < sbsrefstate->numupper; i++)
-    {
-        if (sbsrefstate->upperprovided[i])
-        {
-            /* If any index expr yields NULL, result is NULL or error */
-            if (sbsrefstate->upperindexnull[i])
-            {
-                if (sbsrefstate->isassignment)
-                    ereport(ERROR,
-                            (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-                             errmsg("array subscript in assignment must not be null")));
-                *op->resnull = true;
-                return false;
-            }
-            indexes[i] = DatumGetInt32(sbsrefstate->upperindex[i]);
-        }
-    }
-
-    /* Likewise for lower subscripts */
-    indexes += MAXDIM;
-    for (int i = 0; i < sbsrefstate->numlower; i++)
-    {
-        if (sbsrefstate->lowerprovided[i])
-        {
-            /* If any index expr yields NULL, result is NULL or error */
-            if (sbsrefstate->lowerindexnull[i])
-            {
-                if (sbsrefstate->isassignment)
-                    ereport(ERROR,
-                            (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-                             errmsg("array subscript in assignment must not be null")));
-                *op->resnull = true;
-                return false;
-            }
-            indexes[i] = DatumGetInt32(sbsrefstate->lowerindex[i]);
-        }
-    }
-
-    return true;
-}
-
-/*
- * Evaluate SubscriptingRef fetch.
- *
- * Source container is in step's result variable,
- * and indexes have already been evaluated into workspace array.
- */
-void
-ExecEvalSubscriptingRefFetch(ExprState *state, ExprEvalStep *op)
-{
-    SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
-    int           *upperindex = (int *) sbsrefstate->workspace;
-    int           *lowerindex = upperindex + MAXDIM;
-
-    /* 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,
-                                          upperindex,
-                                          sbsrefstate->refattrlength,
-                                          sbsrefstate->refelemlength,
-                                          sbsrefstate->refelembyval,
-                                          sbsrefstate->refelemalign,
-                                          op->resnull);
-    }
-    else
-    {
-        /* Slice case */
-        *op->resvalue = array_get_slice(*op->resvalue,
-                                        sbsrefstate->numupper,
-                                        upperindex,
-                                        lowerindex,
-                                        sbsrefstate->upperprovided,
-                                        sbsrefstate->lowerprovided,
-                                        sbsrefstate->refattrlength,
-                                        sbsrefstate->refelemlength,
-                                        sbsrefstate->refelembyval,
-                                        sbsrefstate->refelemalign);
-    }
-}
-
-/*
- * Compute old container element/slice value for a SubscriptingRef assignment
- * expression. Will only be generated if the new-value subexpression
- * contains SubscriptingRef or FieldStore. The value is stored into the
- * SubscriptingRefState's prevvalue/prevnull fields.
- */
-void
-ExecEvalSubscriptingRefOld(ExprState *state, ExprEvalStep *op)
-{
-    SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
-    int           *upperindex = (int *) sbsrefstate->workspace;
-    int           *lowerindex = upperindex + MAXDIM;
-
-    if (*op->resnull)
-    {
-        /* whole array 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,
-                                                   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,
-                                                 upperindex,
-                                                 lowerindex,
-                                                 sbsrefstate->upperprovided,
-                                                 sbsrefstate->lowerprovided,
-                                                 sbsrefstate->refattrlength,
-                                                 sbsrefstate->refelemlength,
-                                                 sbsrefstate->refelembyval,
-                                                 sbsrefstate->refelemalign);
-        sbsrefstate->prevnull = false;
-    }
-}
-
-/*
- * Evaluate SubscriptingRef assignment.
- *
- * Input container (possibly null) is in result area, replacement value is in
- * SubscriptingRefState's replacevalue/replacenull.
- */
-void
-ExecEvalSubscriptingRefAssign(ExprState *state, ExprEvalStep *op)
-{
-    SubscriptingRefState *sbsrefstate = op->d.sbsref.state;
-    int           *upperindex = (int *) sbsrefstate->workspace;
-    int           *lowerindex = upperindex + MAXDIM;
-
-    /*
-     * 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,
-                                          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,
-                                        upperindex,
-                                        lowerindex,
-                                        sbsrefstate->upperprovided,
-                                        sbsrefstate->lowerprovided,
-                                        sbsrefstate->replacevalue,
-                                        sbsrefstate->replacenull,
-                                        sbsrefstate->refattrlength,
-                                        sbsrefstate->refelemlength,
-                                        sbsrefstate->refelembyval,
-                                        sbsrefstate->refelemalign);
-    }
-}
-
 /*
  * Evaluate a rowtype coercion operation.
  * This may require rearranging field positions.
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index bc9b6771e3..e7f0d84521 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -1116,22 +1116,35 @@ llvm_compile_expr(ExprState *state)
                 }

             case EEOP_SBSREF_OLD:
-                build_EvalXFunc(b, mod, "ExecEvalSubscriptingRefOld",
-                                v_state, op);
-                LLVMBuildBr(b, opblocks[opno + 1]);
-                break;
-
             case EEOP_SBSREF_ASSIGN:
-                build_EvalXFunc(b, mod, "ExecEvalSubscriptingRefAssign",
-                                v_state, op);
-                LLVMBuildBr(b, opblocks[opno + 1]);
-                break;
-
             case EEOP_SBSREF_FETCH:
-                build_EvalXFunc(b, mod, "ExecEvalSubscriptingRefFetch",
-                                v_state, op);
-                LLVMBuildBr(b, opblocks[opno + 1]);
-                break;
+                {
+                    LLVMTypeRef param_types[3];
+                    LLVMValueRef v_params[3];
+                    LLVMTypeRef v_functype;
+                    LLVMValueRef v_func;
+
+                    param_types[0] = l_ptr(StructExprState);
+                    param_types[1] = l_ptr(TypeSizeT);
+                    param_types[2] = l_ptr(StructExprContext);
+
+                    v_functype = LLVMFunctionType(LLVMVoidType(),
+                                                  param_types,
+                                                  lengthof(param_types),
+                                                  false);
+                    v_func = l_ptr_const(op->d.sbsref.subscriptfunc,
+                                         l_ptr(v_functype));
+
+                    v_params[0] = v_state;
+                    v_params[1] = l_ptr_const(op, l_ptr(TypeSizeT));
+                    v_params[2] = v_econtext;
+                    LLVMBuildCall(b,
+                                  v_func,
+                                  v_params, lengthof(v_params), "");
+
+                    LLVMBuildBr(b, opblocks[opno + 1]);
+                    break;
+                }

             case EEOP_CASE_TESTVAL:
                 {
@@ -1749,10 +1762,29 @@ llvm_compile_expr(ExprState *state)
             case EEOP_SBSREF_SUBSCRIPTS:
                 {
                     int            jumpdone = op->d.sbsref_subscript.jumpdone;
+                    LLVMTypeRef param_types[3];
+                    LLVMValueRef v_params[3];
+                    LLVMTypeRef v_functype;
+                    LLVMValueRef v_func;
                     LLVMValueRef v_ret;

-                    v_ret = build_EvalXFunc(b, mod, "ExecEvalSubscriptingRef",
-                                            v_state, op);
+                    param_types[0] = l_ptr(StructExprState);
+                    param_types[1] = l_ptr(TypeSizeT);
+                    param_types[2] = l_ptr(StructExprContext);
+
+                    v_functype = LLVMFunctionType(TypeParamBool,
+                                                  param_types,
+                                                  lengthof(param_types),
+                                                  false);
+                    v_func = l_ptr_const(op->d.sbsref_subscript.subscriptfunc,
+                                         l_ptr(v_functype));
+
+                    v_params[0] = v_state;
+                    v_params[1] = l_ptr_const(op, l_ptr(TypeSizeT));
+                    v_params[2] = v_econtext;
+                    v_ret = LLVMBuildCall(b,
+                                          v_func,
+                                          v_params, lengthof(v_params), "");
                     v_ret = LLVMBuildZExt(b, v_ret, TypeStorageBool, "");

                     LLVMBuildCondBr(b,
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
index 1ed3cafa2f..ae3c88aad9 100644
--- a/src/backend/jit/llvm/llvmjit_types.c
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -124,10 +124,6 @@ void       *referenced_functions[] =
     ExecEvalSQLValueFunction,
     ExecEvalScalarArrayOp,
     ExecEvalSubPlan,
-    ExecEvalSubscriptingRef,
-    ExecEvalSubscriptingRefAssign,
-    ExecEvalSubscriptingRefFetch,
-    ExecEvalSubscriptingRefOld,
     ExecEvalSysVar,
     ExecEvalWholeRowVar,
     ExecEvalXmlExpr,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 910906f639..90aebb270b 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1548,8 +1548,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 687609f59e..9d54830c3d 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -276,8 +276,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/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 1dc873ed25..a78d3b634f 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -70,7 +70,7 @@ exprType(const Node *expr)
                 const SubscriptingRef *sbsref = (const SubscriptingRef *) expr;

                 /* slice and/or store operations yield the container type */
-                if (sbsref->reflowerindexpr || sbsref->refassgnexpr)
+                if (IsAssignment(sbsref) || sbsref->reflowerindexpr)
                     type = sbsref->refcontainertype;
                 else
                     type = sbsref->refelemtype;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 9c73c605a4..7edaa4c3d2 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1194,8 +1194,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 169d5581b9..7ece697c75 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -671,8 +671,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 36002f059d..12e11744ec 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -20,6 +20,7 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/subscripting.h"
 #include "optimizer/optimizer.h"
 #include "parser/analyze.h"
 #include "parser/parse_agg.h"
@@ -431,6 +432,8 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
 {
     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;
@@ -461,13 +464,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,
@@ -484,13 +494,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 6e98fe55fc..31e40acc64 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -184,21 +184,12 @@ pcb_error_callback(void *arg)
  * 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
@@ -219,25 +210,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;
 }

 /*
@@ -254,10 +226,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 coercion) is placed in
+ * separate procedures 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 the case the current container type
+ * doesn't have a subscripting procedure.
  *
  * pstate            Parse state
  * containerBase    Already-transformed expression for the container as a whole
@@ -284,16 +261,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
@@ -327,29 +300,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
             {
@@ -357,63 +307,12 @@ 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.
      */
@@ -422,13 +321,12 @@ 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;
 }
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 9de0cff833..3230661eac 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -20,6 +20,7 @@
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/subscripting.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_func.h"
@@ -848,27 +849,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 its custom code 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"
@@ -879,6 +874,8 @@ transformAssignmentIndirection(ParseState *pstate,
                      errhint("You will need to rewrite or cast the expression."),
                      parser_errposition(pstate, location)));
     }
+    else
+        result = rhs;

     return result;
 }
@@ -903,26 +900,39 @@ transformAssignmentSubscripts(ParseState *pstate,
     Node       *result;
     Oid            containerType;
     int32        containerTypMod;
-    Oid            elementTypeId;
-    Oid            typeNeeded;
     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);
@@ -932,25 +942,22 @@ transformAssignmentSubscripts(ParseState *pstate,
                                          NULL,
                                          targetName,
                                          true,
-                                         typeNeeded,
-                                         containerTypMod,
+                                         sbsref->refassgntype,
+                                         sbsref->reftypmod,
                                          collationNeeded,
                                          indirection,
                                          next_indirection,
                                          rhs,
                                          location);

-    /* process subscripts */
-    result = (Node *) transformContainerSubscripts(pstate,
-                                                   basenode,
-                                                   containerType,
-                                                   elementTypeId,
-                                                   containerTypMod,
-                                                   subscripts,
-                                                   rhs);
+    /* Provide fully prepared subscripting 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/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 4c8a739bc4..b7415e8790 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -19,19 +19,25 @@

 #include "access/htup_details.h"
 #include "catalog/pg_type.h"
+#include "executor/execExpr.h"
 #include "funcapi.h"
 #include "libpq/pqformat.h"
+#include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
+#include "nodes/subscripting.h"
 #include "nodes/supportnodes.h"
 #include "optimizer/optimizer.h"
+#include "parser/parse_coerce.h"
 #include "port/pg_bitutils.h"
 #include "utils/array.h"
 #include "utils/arrayaccess.h"
 #include "utils/builtins.h"
 #include "utils/datum.h"
+#include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 #include "utils/selfuncs.h"
+#include "utils/syscache.h"
 #include "utils/typcache.h"


@@ -88,6 +94,25 @@ typedef struct ArrayIteratorData
     int            current_item;    /* the item # we're at in the array */
 }            ArrayIteratorData;

+/* SubscriptingRefState.workspace for array subscripting operations */
+typedef struct ArraySubWorkspace
+{
+    /* Values determined during expression compilation */
+    Oid            refelemtype;    /* OID of the array element type */
+    int16        refattrlength;    /* typlen of array type */
+    int16        refelemlength;    /* typlen of the array element type */
+    bool        refelembyval;    /* is the element type pass-by-value? */
+    char        refelemalign;    /* typalign of the element type */
+
+    /*
+     * Subscript values converted to integers.  Note that these arrays must be
+     * of length MAXDIM even when dealing with fewer subscripts, because
+     * array_get/set_slice may scribble on the extra entries.
+     */
+    int            upperindex[MAXDIM];
+    int            lowerindex[MAXDIM];
+} ArraySubWorkspace;
+
 static bool array_isspace(char ch);
 static int    ArrayCount(const char *str, int *dim, char typdelim);
 static void ReadArrayStr(char *arrayStr, const char *origStr,
@@ -6628,3 +6653,518 @@ width_bucket_array_variable(Datum operand,

     return left;
 }
+
+
+/*
+ * XXX undocumented is unacceptable
+ */
+static SubscriptingRef *
+array_subscript_prepare(bool isAssignment, SubscriptingRef *sbsref)
+{
+    Oid            array_type = sbsref->refcontainertype;
+    HeapTuple    type_tuple_container;
+    Form_pg_type type_struct_container;
+    bool        is_slice = sbsref->reflowerindexpr != NIL;
+
+    /* Get the type tuple for the container */
+    type_tuple_container = SearchSysCache1(TYPEOID, ObjectIdGetDatum(array_type));
+    if (!HeapTupleIsValid(type_tuple_container))
+        elog(ERROR, "cache lookup failed for type %u", array_type);
+    type_struct_container = (Form_pg_type) GETSTRUCT(type_tuple_container);
+
+    /* needn't check typisdefined since this will fail anyway */
+    sbsref->refelemtype = type_struct_container->typelem;
+
+    /* Identify type that RHS must provide */
+    if (isAssignment)
+        sbsref->refassgntype = is_slice ? sbsref->refcontainertype : sbsref->refelemtype;
+
+    ReleaseSysCache(type_tuple_container);
+
+    return sbsref;
+}
+
+/*
+ * XXX undocumented is unacceptable
+ */
+static SubscriptingRef *
+array_subscript_validate(bool isAssignment, SubscriptingRef *sbsref,
+                         ParseState *pstate)
+{
+    bool        is_slice = sbsref->reflowerindexpr != NIL;
+    Oid            typeneeded = InvalidOid,
+                typesource = InvalidOid;
+    Node       *new_from;
+    Node       *subexpr;
+    List       *upperIndexpr = NIL;
+    List       *lowerIndexpr = NIL;
+    ListCell   *u,
+               *l,
+               *s;
+
+    foreach(u, sbsref->refupperindexpr)
+    {
+        subexpr = (Node *) lfirst(u);
+
+        if (subexpr == NULL)
+        {
+            upperIndexpr = lappend(upperIndexpr, subexpr);
+            continue;
+        }
+
+        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(subexpr))));
+
+        upperIndexpr = lappend(upperIndexpr, subexpr);
+    }
+
+    sbsref->refupperindexpr = upperIndexpr;
+
+    forboth(l, sbsref->reflowerindexpr, s, sbsref->refindexprslice)
+    {
+        A_Indices  *ai = (A_Indices *) lfirst(s);
+
+        subexpr = (Node *) lfirst(l);
+
+        if (subexpr == NULL && !ai->is_slice)
+        {
+            /* Make a constant 1 */
+            subexpr = (Node *) makeConst(INT4OID,
+                                         -1,
+                                         InvalidOid,
+                                         sizeof(int32),
+                                         Int32GetDatum(1),
+                                         false,
+                                         true); /* pass by value */
+        }
+
+        if (subexpr == NULL)
+        {
+            lowerIndexpr = lappend(lowerIndexpr, subexpr);
+            continue;
+        }
+
+        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(subexpr))));
+
+        lowerIndexpr = lappend(lowerIndexpr, subexpr);
+    }
+
+    sbsref->reflowerindexpr = lowerIndexpr;
+
+    if (isAssignment)
+    {
+        SubscriptingRef *assignRef = (SubscriptingRef *) sbsref;
+        Node       *assignExpr = (Node *) assignRef->refassgnexpr;
+
+        typesource = exprType(assignExpr);
+        typeneeded = is_slice ? sbsref->refcontainertype : sbsref->refelemtype;
+        new_from = coerce_to_target_type(pstate,
+                                         assignExpr, typesource,
+                                         typeneeded, sbsref->reftypmod,
+                                         COERCION_ASSIGNMENT,
+                                         COERCE_IMPLICIT_CAST,
+                                         -1);
+        if (new_from == NULL)
+            ereport(ERROR,
+                    (errcode(ERRCODE_DATATYPE_MISMATCH),
+                     errmsg("array assignment requires type %s"
+                            " but expression is of type %s",
+                            format_type_be(sbsref->refelemtype),
+                            format_type_be(typesource)),
+                     errhint("You will need to rewrite or cast the expression."),
+                     parser_errposition(pstate, exprLocation(assignExpr))));
+        assignRef->refassgnexpr = (Expr *) new_from;
+    }
+
+    sbsref->refnestedfunc = F_ARRAY_SUBSCRIPT_HANDLER;
+
+    /* 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)));
+
+    return sbsref;
+}
+
+/*
+ * Process the subscripts in a SubscriptingRef expression.
+ *
+ * If any subscript is NULL, throw error in assignment case, or in fetch case
+ * set result to NULL and return false (instructing caller to skip the rest
+ * of the SubscriptingRef sequence).
+ *
+ * We convert all the subscripts to plain integers and save them in the
+ * sbsrefstate->workspace arrays.
+ */
+static bool
+array_subscript_subscripts(ExprState *state,
+                           ExprEvalStep *op,
+                           ExprContext *econtext)
+{
+    SubscriptingRefState *sbsrefstate = op->d.sbsref_subscript.state;
+    ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbsrefstate->workspace;
+
+    /* Process upper subscripts */
+    for (int i = 0; i < sbsrefstate->numupper; i++)
+    {
+        if (sbsrefstate->upperprovided[i])
+        {
+            /* If any index expr yields NULL, result is NULL or error */
+            if (sbsrefstate->upperindexnull[i])
+            {
+                if (sbsrefstate->isassignment)
+                    ereport(ERROR,
+                            (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                             errmsg("array subscript in assignment must not be null")));
+                *op->resnull = true;
+                return false;
+            }
+            workspace->upperindex[i] = DatumGetInt32(sbsrefstate->upperindex[i]);
+        }
+    }
+
+    /* Likewise for lower subscripts */
+    for (int i = 0; i < sbsrefstate->numlower; i++)
+    {
+        if (sbsrefstate->lowerprovided[i])
+        {
+            /* If any index expr yields NULL, result is NULL or error */
+            if (sbsrefstate->lowerindexnull[i])
+            {
+                if (sbsrefstate->isassignment)
+                    ereport(ERROR,
+                            (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                             errmsg("array subscript in assignment must not be null")));
+                *op->resnull = true;
+                return false;
+            }
+            workspace->lowerindex[i] = DatumGetInt32(sbsrefstate->lowerindex[i]);
+        }
+    }
+
+    return true;
+}
+
+/*
+ * Evaluate SubscriptingRef fetch for an array element.
+ *
+ * Source container is in step's result variable,
+ * and indexes have already been evaluated into workspace array.
+ */
+static void
+array_subscript_fetch(ExprState *state,
+                      ExprEvalStep *op,
+                      ExprContext *econtext)
+{
+    SubscriptingRefState *sbstate = op->d.sbsref.state;
+    ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbstate->workspace;
+
+    /* Should not get here if source array (or any subscript) is null */
+    Assert(!(*op->resnull));
+
+    *op->resvalue = array_get_element(*op->resvalue,
+                                      sbstate->numupper,
+                                      workspace->upperindex,
+                                      workspace->refattrlength,
+                                      workspace->refelemlength,
+                                      workspace->refelembyval,
+                                      workspace->refelemalign,
+                                      op->resnull);
+}
+
+/*
+ * Evaluate SubscriptingRef fetch for an array slice.
+ *
+ * Source container is in step's result variable,
+ * and indexes have already been evaluated into workspace array.
+ */
+static void
+array_subscript_fetch_slice(ExprState *state,
+                            ExprEvalStep *op,
+                            ExprContext *econtext)
+{
+    SubscriptingRefState *sbstate = op->d.sbsref.state;
+    ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbstate->workspace;
+
+    /* Should not get here if source array (or any subscript) is null */
+    Assert(!(*op->resnull));
+
+    *op->resvalue = array_get_slice(*op->resvalue,
+                                    sbstate->numupper,
+                                    workspace->upperindex,
+                                    workspace->lowerindex,
+                                    sbstate->upperprovided,
+                                    sbstate->lowerprovided,
+                                    workspace->refattrlength,
+                                    workspace->refelemlength,
+                                    workspace->refelembyval,
+                                    workspace->refelemalign);
+}
+
+/*
+ * Evaluate SubscriptingRef assignment for an array element assignment.
+ *
+ * Input container (possibly null) is in result area, replacement value is in
+ * SubscriptingRefState's replacevalue/replacenull.
+ */
+static void
+array_subscript_assign(ExprState *state,
+                       ExprEvalStep *op,
+                       ExprContext *econtext)
+{
+    SubscriptingRefState *sbstate = op->d.sbsref.state;
+    ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbstate->workspace;
+    Datum        arraySource = *op->resvalue;
+
+    /*
+     * For an assignment to a fixed-length array type, both the original array
+     * and the value to be assigned into it must be non-NULL, else we punt and
+     * return the original array.
+     */
+    if (workspace->refattrlength > 0)
+    {
+        if (*op->resnull || sbstate->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)
+    {
+        arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype));
+        *op->resnull = false;
+    }
+
+    *op->resvalue = array_set_element(arraySource,
+                                      sbstate->numupper,
+                                      workspace->upperindex,
+                                      sbstate->replacevalue,
+                                      sbstate->replacenull,
+                                      workspace->refattrlength,
+                                      workspace->refelemlength,
+                                      workspace->refelembyval,
+                                      workspace->refelemalign);
+}
+
+/*
+ * Evaluate SubscriptingRef assignment for an array slice assignment.
+ *
+ * Input container (possibly null) is in result area, replacement value is in
+ * SubscriptingRefState's replacevalue/replacenull.
+ */
+static void
+array_subscript_assign_slice(ExprState *state,
+                             ExprEvalStep *op,
+                             ExprContext *econtext)
+{
+    SubscriptingRefState *sbstate = op->d.sbsref.state;
+    ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbstate->workspace;
+    Datum        arraySource = *op->resvalue;
+
+    /*
+     * For an assignment to a fixed-length array type, both the original array
+     * and the value to be assigned into it must be non-NULL, else we punt and
+     * return the original array.
+     */
+    if (workspace->refattrlength > 0)
+    {
+        if (*op->resnull || sbstate->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)
+    {
+        arraySource = PointerGetDatum(construct_empty_array(workspace->refelemtype));
+        *op->resnull = false;
+    }
+
+    *op->resvalue = array_set_slice(arraySource,
+                                    sbstate->numupper,
+                                    workspace->upperindex,
+                                    workspace->lowerindex,
+                                    sbstate->upperprovided,
+                                    sbstate->lowerprovided,
+                                    sbstate->replacevalue,
+                                    sbstate->replacenull,
+                                    workspace->refattrlength,
+                                    workspace->refelemlength,
+                                    workspace->refelembyval,
+                                    workspace->refelemalign);
+}
+
+/*
+ * Compute old array element value for a SubscriptingRef assignment
+ * expression.  Will only be called if the new-value subexpression
+ * contains SubscriptingRef or FieldStore.  The value is stored into the
+ * SubscriptingRefState's prevvalue/prevnull fields.
+ */
+static void
+array_subscript_fetch_old(ExprState *state,
+                          ExprEvalStep *op,
+                          ExprContext *econtext)
+{
+    SubscriptingRefState *sbstate = op->d.sbsref.state;
+    ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbstate->workspace;
+
+    if (*op->resnull)
+    {
+        /* whole array is null, so any element is too */
+        sbstate->prevvalue = (Datum) 0;
+        sbstate->prevnull = true;
+    }
+    else
+        sbstate->prevvalue = array_get_element(*op->resvalue,
+                                               sbstate->numupper,
+                                               workspace->upperindex,
+                                               workspace->refattrlength,
+                                               workspace->refelemlength,
+                                               workspace->refelembyval,
+                                               workspace->refelemalign,
+                                               &sbstate->prevnull);
+}
+
+/*
+ * Compute old array slice value for a SubscriptingRef assignment
+ * expression.  Will only be called if the new-value subexpression
+ * contains SubscriptingRef or FieldStore.  The value is stored into the
+ * SubscriptingRefState's prevvalue/prevnull fields.
+ */
+static void
+array_subscript_fetch_old_slice(ExprState *state,
+                                ExprEvalStep *op,
+                                ExprContext *econtext)
+{
+    SubscriptingRefState *sbstate = op->d.sbsref.state;
+    ArraySubWorkspace *workspace = (ArraySubWorkspace *) sbstate->workspace;
+
+    if (*op->resnull)
+    {
+        /* whole array is null, so any slice is too */
+        sbstate->prevvalue = (Datum) 0;
+        sbstate->prevnull = true;
+    }
+    else
+    {
+        sbstate->prevvalue = array_get_slice(*op->resvalue,
+                                             sbstate->numupper,
+                                             workspace->upperindex,
+                                             workspace->lowerindex,
+                                             sbstate->upperprovided,
+                                             sbstate->lowerprovided,
+                                             workspace->refattrlength,
+                                             workspace->refelemlength,
+                                             workspace->refelembyval,
+                                             workspace->refelemalign);
+        /* slices of non-null arrays are never null */
+        sbstate->prevnull = false;
+    }
+}
+
+/*
+ * Set up execution state for an array subscript operation.
+ */
+static void
+array_exec_setup(SubscriptingRef *sbsref,
+                 SubscriptingRefState *sbsrefstate)
+{
+    bool        is_slice = (sbsrefstate->numlower != 0);
+    ArraySubWorkspace *workspace;
+
+    /*
+     * Allocate type-specific workspace.  This is also a good place to enforce
+     * the implementation limit on number of array subscripts.
+     */
+    if (sbsrefstate->numupper > MAXDIM)
+        ereport(ERROR,
+                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+                        sbsrefstate->numupper, MAXDIM)));
+
+    /* Should be impossible if parser is sane, but check anyway: */
+    if (sbsrefstate->numlower != 0 &&
+        sbsrefstate->numupper != sbsrefstate->numlower)
+        elog(ERROR, "upper and lower index lists are not same length");
+
+    workspace = (ArraySubWorkspace *)
+        MemoryContextAlloc(GetMemoryChunkContext(sbsrefstate),
+                           sizeof(ArraySubWorkspace));
+    sbsrefstate->workspace = workspace;
+
+    /*
+     * Collect datatype details we'll need at execution.
+     */
+    workspace->refelemtype = sbsref->refelemtype;
+    workspace->refattrlength = get_typlen(sbsref->refcontainertype);
+    get_typlenbyvalalign(sbsref->refelemtype,
+                         &workspace->refelemlength,
+                         &workspace->refelembyval,
+                         &workspace->refelemalign);
+
+    /* Pass back pointers to step execution functions */
+    sbsrefstate->sbs_subscripts = array_subscript_subscripts;
+    if (is_slice)
+    {
+        sbsrefstate->sbs_fetch = array_subscript_fetch_slice;
+        sbsrefstate->sbs_assign = array_subscript_assign_slice;
+        sbsrefstate->sbs_fetch_old = array_subscript_fetch_old_slice;
+    }
+    else
+    {
+        sbsrefstate->sbs_fetch = array_subscript_fetch;
+        sbsrefstate->sbs_assign = array_subscript_assign;
+        sbsrefstate->sbs_fetch_old = array_subscript_fetch_old;
+    }
+}
+
+/*
+ * Handle array-type subscripting logic.
+ */
+Datum
+array_subscript_handler(PG_FUNCTION_ARGS)
+{
+    SubscriptRoutines *sbsroutines = (SubscriptRoutines *)
+    palloc(sizeof(SubscriptRoutines));
+
+    sbsroutines->prepare = array_subscript_prepare;
+    sbsroutines->validate = array_subscript_validate;
+    sbsroutines->exec_setup = array_exec_setup;
+
+    PG_RETURN_POINTER(sbsroutines);
+}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c2c6df2a4f..f516a5011e 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -7999,7 +7999,7 @@ get_rule_expr(Node *node, deparse_context *context,
                  * EXPLAIN tries to print the targetlist of a plan resulting
                  * from such a statement.
                  */
-                if (sbsref->refassgnexpr)
+                if (IsAssignment(sbsref))
                 {
                     Node       *refassgnexpr;

diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index ae23299162..1297695692 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2966,6 +2966,51 @@ type_is_collatable(Oid typid)
 }


+/*
+ * get_typsubshandler
+ *
+ *        Given the type OID, return the type's subscripting procedure's OID,
+ *        if it has one.
+ */
+RegProcedure
+get_typsubshandler(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;
+}
+
+/*
+ * getSubscriptingRoutines
+ *
+ *        Given the type OID, fetch the type's subscripting functions.
+ *        Fail if type is not subscriptable.
+ */
+struct SubscriptRoutines *
+getSubscriptingRoutines(Oid typid)
+{
+    RegProcedure typsubshandler = get_typsubshandler(typid);
+
+    if (!OidIsValid(typsubshandler))
+        ereport(ERROR,
+                (errcode(ERRCODE_DATATYPE_MISMATCH),
+                 errmsg("cannot subscript type %s because it does not support subscripting",
+                        format_type_be(typid))));
+
+    return (struct SubscriptRoutines *)
+        DatumGetPointer(OidFunctionCall0(typsubshandler));
+}
+
+
 /*                ---------- STATISTICS CACHE ----------                     */

 /*
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index fc2202b843..9fa9990efe 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10936,6 +10936,13 @@
   proargnames =>
'{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,float8_pass_by_value,data_page_checksum_version}',
   prosrc => 'pg_control_init' },

+{ oid => '6099',
+  descr => 'Array subscripting logic',
+  proname => 'array_subscript_handler',
+  prorettype => 'internal',
+  proargtypes => 'internal',
+  prosrc => 'array_subscript_handler' },
+
 # collation management functions
 { oid => '3445', descr => 'import collations from operating system',
   proname => 'pg_import_system_collations', procost => '100',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 21a467a7a7..9f29461e39 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -50,7 +50,8 @@
   typname => 'name', typlen => 'NAMEDATALEN', typbyval => 'f',
   typcategory => 'S', typelem => 'char', typinput => 'namein',
   typoutput => 'nameout', typreceive => 'namerecv', typsend => 'namesend',
-  typalign => 'c', typcollation => 'C' },
+  typalign => 'c', typcollation => 'C',
+  typsubshandler => 'array_subscript_handler' },
 { oid => '20', array_type_oid => '1016',
   descr => '~18 digit integer, 8-byte storage',
   typname => 'int8', typlen => '8', typbyval => 'FLOAT8PASSBYVAL',
@@ -66,7 +67,7 @@
   typname => 'int2vector', typlen => '-1', typbyval => 'f', typcategory => 'A',
   typelem => 'int2', typinput => 'int2vectorin', typoutput => 'int2vectorout',
   typreceive => 'int2vectorrecv', typsend => 'int2vectorsend',
-  typalign => 'i' },
+  typalign => 'i', typsubshandler => 'array_subscript_handler' },
 { oid => '23', array_type_oid => '1007',
   descr => '-2 billion to 2 billion integer, 4-byte storage',
   typname => 'int4', typlen => '4', typbyval => 't', typcategory => 'N',
@@ -105,7 +106,8 @@
   descr => 'array of oids, used in system tables',
   typname => 'oidvector', typlen => '-1', typbyval => 'f', typcategory => 'A',
   typelem => 'oid', typinput => 'oidvectorin', typoutput => 'oidvectorout',
-  typreceive => 'oidvectorrecv', typsend => 'oidvectorsend', typalign => 'i' },
+  typreceive => 'oidvectorrecv', typsend => 'oidvectorsend', typalign => 'i',
+  typsubshandler => 'array_subscript_handler' },

 # hand-built rowtype entries for bootstrapped catalogs
 # NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations
@@ -179,32 +181,37 @@
   descr => 'geometric point \'(x, y)\'',
   typname => 'point', typlen => '16', typbyval => 'f', typcategory => 'G',
   typelem => 'float8', typinput => 'point_in', typoutput => 'point_out',
-  typreceive => 'point_recv', typsend => 'point_send', typalign => 'd' },
+  typreceive => 'point_recv', typsend => 'point_send', typalign => 'd',
+  typsubshandler => 'array_subscript_handler' },
 { oid => '601', array_type_oid => '1018',
   descr => 'geometric line segment \'(pt1,pt2)\'',
   typname => 'lseg', typlen => '32', typbyval => 'f', typcategory => 'G',
   typelem => 'point', typinput => 'lseg_in', typoutput => 'lseg_out',
-  typreceive => 'lseg_recv', typsend => 'lseg_send', typalign => 'd' },
+  typreceive => 'lseg_recv', typsend => 'lseg_send', typalign => 'd',
+  typsubshandler => 'array_subscript_handler' },
 { oid => '602', array_type_oid => '1019',
   descr => 'geometric path \'(pt1,...)\'',
   typname => 'path', typlen => '-1', typbyval => 'f', typcategory => 'G',
   typinput => 'path_in', typoutput => 'path_out', typreceive => 'path_recv',
-  typsend => 'path_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'path_send', typalign => 'd', typstorage => 'x',
+  typsubshandler => 'array_subscript_handler' },
 { oid => '603', array_type_oid => '1020',
   descr => 'geometric box \'(lower left,upper right)\'',
   typname => 'box', typlen => '32', typbyval => 'f', typcategory => 'G',
   typdelim => ';', typelem => 'point', typinput => 'box_in',
   typoutput => 'box_out', typreceive => 'box_recv', typsend => 'box_send',
-  typalign => 'd' },
+  typalign => 'd', typsubshandler => 'array_subscript_handler' },
 { oid => '604', array_type_oid => '1027',
   descr => 'geometric polygon \'(pt1,...)\'',
   typname => 'polygon', typlen => '-1', typbyval => 'f', typcategory => 'G',
   typinput => 'poly_in', typoutput => 'poly_out', typreceive => 'poly_recv',
-  typsend => 'poly_send', typalign => 'd', typstorage => 'x' },
+  typsend => 'poly_send', typalign => 'd', typstorage => 'x',
+  typsubshandler => 'array_subscript_handler' },
 { oid => '628', array_type_oid => '629', descr => 'geometric line',
   typname => 'line', typlen => '24', typbyval => 'f', typcategory => 'G',
   typelem => 'float8', typinput => 'line_in', typoutput => 'line_out',
-  typreceive => 'line_recv', typsend => 'line_send', typalign => 'd' },
+  typreceive => 'line_recv', typsend => 'line_send', typalign => 'd',
+  typsubshandler => 'array_subscript_handler' },

 # OIDS 700 - 799

@@ -263,7 +270,7 @@
 { oid => '1033', array_type_oid => '1034', descr => 'access control list',
   typname => 'aclitem', typlen => '12', typbyval => 'f', typcategory => 'U',
   typinput => 'aclitemin', typoutput => 'aclitemout', typreceive => '-',
-  typsend => '-', typalign => 'i' },
+  typsend => '-', typalign => 'i', typsubshandler => 'array_subscript_handler' },
 { oid => '1042', array_type_oid => '1014',
   descr => 'char(length), blank-padded string, fixed storage length',
   typname => 'bpchar', typlen => '-1', typbyval => 'f', typcategory => 'S',
@@ -302,7 +309,8 @@
   typcategory => 'D', typispreferred => 't', typinput => 'timestamptz_in',
   typoutput => 'timestamptz_out', typreceive => 'timestamptz_recv',
   typsend => 'timestamptz_send', typmodin => 'timestamptztypmodin',
-  typmodout => 'timestamptztypmodout', typalign => 'd' },
+  typmodout => 'timestamptztypmodout', typalign => 'd',
+  typsubshandler => 'array_subscript_handler' },
 { oid => '1186', array_type_oid => '1187',
   descr => '@ <number> <units>, time interval',
   typname => 'interval', typlen => '16', typbyval => 'f', typcategory => 'T',
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 6099e5f57c..3e10179e1c 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -221,6 +221,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 NULL, 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 */

     /*
@@ -363,7 +369,8 @@ extern ObjectAddress TypeCreate(Oid newTypeOid,
                                 int32 typeMod,
                                 int32 typNDims,
                                 bool typeNotNull,
-                                Oid typeCollation);
+                                Oid typeCollation,
+                                Oid subscriptingParseProcedure);

 extern void GenerateTypeDependencies(HeapTuple typeTuple,
                                      Relation typeCatalog,
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index b768c30b74..baa2363bed 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -32,6 +32,11 @@ typedef void (*ExecEvalSubroutine) (ExprState *state,
                                     struct ExprEvalStep *op,
                                     ExprContext *econtext);

+/* API for out-of-line evaluation subroutines returning bool */
+typedef bool (*ExecEvalBoolSubroutine) (ExprState *state,
+                                        struct ExprEvalStep *op,
+                                        ExprContext *econtext);
+
 /*
  * Discriminator for ExprEvalSteps.
  *
@@ -497,6 +502,7 @@ typedef struct ExprEvalStep
         /* for EEOP_SBSREF_SUBSCRIPTS */
         struct
         {
+            ExecEvalBoolSubroutine subscriptfunc;    /* evaluation subroutine */
             /* too big to have inline */
             struct SubscriptingRefState *state;
             int            jumpdone;    /* jump here on null */
@@ -505,6 +511,7 @@ typedef struct ExprEvalStep
         /* for EEOP_SBSREF_OLD / ASSIGN / FETCH */
         struct
         {
+            ExecEvalSubroutine subscriptfunc;    /* evaluation subroutine */
             /* too big to have inline */
             struct SubscriptingRefState *state;
         }            sbsref;
@@ -638,12 +645,6 @@ typedef struct SubscriptingRefState
 {
     bool        isassignment;    /* is it assignment, or just fetch? */

-    Oid            refelemtype;    /* OID of the container element type */
-    int16        refattrlength;    /* typlen of container type */
-    int16        refelemlength;    /* typlen of the container element type */
-    bool        refelembyval;    /* is the element type pass-by-value? */
-    char        refelemalign;    /* typalign of the element type */
-
     /* workspace for type-specific subscripting code */
     void       *workspace;

@@ -667,6 +668,17 @@ typedef struct SubscriptingRefState
     /* if we have a nested assignment, SBSREF_OLD puts old value here */
     Datum        prevvalue;
     bool        prevnull;
+
+    /*
+     * Step execution function pointers returned by exec_setup method.  These
+     * are not needed at runtime, only during expression compilation; but it's
+     * not worth complicating exec_setup's API by making an additional struct
+     * to hold them.
+     */
+    ExecEvalBoolSubroutine sbs_subscripts;    /* process subscripts */
+    ExecEvalSubroutine sbs_fetch;    /* function to fetch an element */
+    ExecEvalSubroutine sbs_assign;    /* function to assign an element */
+    ExecEvalSubroutine sbs_fetch_old;    /* fetch old value for assignment */
 } SubscriptingRefState;


@@ -711,10 +723,6 @@ extern void ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op,
                                      ExprContext *econtext);
 extern void ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op,
                                    ExprContext *econtext);
-extern bool ExecEvalSubscriptingRef(ExprState *state, ExprEvalStep *op);
-extern void ExecEvalSubscriptingRefFetch(ExprState *state, ExprEvalStep *op);
-extern void ExecEvalSubscriptingRefOld(ExprState *state, ExprEvalStep *op);
-extern void ExecEvalSubscriptingRefAssign(ExprState *state, ExprEvalStep *op);
 extern void ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op,
                                    ExprContext *econtext);
 extern void ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op);
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index cdbe781c73..36ceac084e 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -425,13 +425,18 @@ 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 */

@@ -439,6 +444,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..1aba2f80bc
--- /dev/null
+++ b/src/include/nodes/subscripting.h
@@ -0,0 +1,38 @@
+/*-------------------------------------------------------------------------
+ *
+ * subscripting.h
+ *        API for generic type subscripting
+ *
+ * Portions Copyright (c) 1996-2020, 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 void (*SubscriptingExecSetup) (SubscriptingRef *sbsref,
+                                       struct SubscriptingRefState *sbsrefstate);
+
+typedef struct SubscriptRoutines
+{
+    SubscriptingPrepare prepare;
+    SubscriptingValidate validate;
+    SubscriptingExecSetup exec_setup;
+} SubscriptRoutines;
+
+#endif                            /* SUBSCRIPTING_H */
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index d25819aa28..fcc6c426e7 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -313,7 +313,7 @@ extern void setup_parser_errposition_callback(ParseCallbackState *pcbstate,
                                               ParseState *pstate, int location);
 extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate);

-extern Oid    transformContainerType(Oid *containerType, int32 *containerTypmod);
+extern void transformContainerType(Oid *containerType, int32 *containerTypmod);

 extern SubscriptingRef *transformContainerSubscripts(ParseState *pstate,
                                                      Node *containerBase,
@@ -322,6 +322,7 @@ extern SubscriptingRef *transformContainerSubscripts(ParseState *pstate,
                                                      int32 containerTypMod,
                                                      List *indirection,
                                                      Node *assignFrom);
+
 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 fecfe1f4f6..38cd2940df 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -17,6 +17,9 @@
 #include "access/htup.h"
 #include "nodes/pg_list.h"

+/* avoid including subscripting.h here */
+struct SubscriptRoutines;
+
 /* Result list element for get_op_btree_interpretation */
 typedef struct OpBtreeInterpretation
 {
@@ -172,6 +175,8 @@ extern void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena);
 extern Oid    get_typmodin(Oid typid);
 extern Oid    get_typcollation(Oid typid);
 extern bool type_is_collatable(Oid typid);
+extern RegProcedure get_typsubshandler(Oid typid);
+extern struct SubscriptRoutines *getSubscriptingRoutines(Oid typid);
 extern Oid    getBaseType(Oid typid);
 extern Oid    getBaseTypeAndTypmod(Oid typid, int32 *typmod);
 extern int32 get_typavgwidth(Oid typid, int32 typmod);

В списке pgsql-hackers по дате отправления:

Предыдущее
От: David Rowley
Дата:
Сообщение: Re: Hybrid Hash/Nested Loop joins and caching results from subplans
Следующее
От: Laurenz Albe
Дата:
Сообщение: Re: Add session statistics to pg_stat_database