Allow omitting one or both boundaries in an array slice specifier.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 23 Dec 2015 02:05:16 +0000 (21:05 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 23 Dec 2015 02:05:29 +0000 (21:05 -0500)
Omitted boundaries represent the upper or lower limit of the corresponding
array subscript.  This allows simpler specification of many common
use-cases.

(Revised version of commit 9246af6799819847faa33baf441251003acbb8fe)

YUriy Zhuravlev

15 files changed:
doc/src/sgml/array.sgml
src/backend/executor/execQual.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/parser/gram.y
src/backend/parser/parse_node.c
src/backend/parser/parse_target.c
src/backend/utils/adt/arrayfuncs.c
src/backend/utils/adt/ruleutils.c
src/include/nodes/parsenodes.h
src/include/nodes/primnodes.h
src/include/utils/array.h
src/test/regress/expected/arrays.out
src/test/regress/sql/arrays.sql

index 4385a09cd9798326afa5da38ca94460bbe1d8178..58878451f007c85aae2a05877d0087ec713edaaf 100644 (file)
@@ -276,6 +276,29 @@ SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
   for all dimensions, e.g., <literal>[1:2][1:1]</>, not <literal>[2][1:1]</>.
  </para>
 
+ <para>
+  It is possible to omit the <replaceable>lower-bound</replaceable> and/or
+  <replaceable>upper-bound</replaceable> of a slice specifier; the missing
+  bound is replaced by the lower or upper limit of the array's subscripts.
+  For example:
+
+<programlisting>
+SELECT schedule[:2][2:] FROM sal_emp WHERE name = 'Bill';
+
+        schedule
+------------------------
+ {{lunch},{presentation}}
+(1 row)
+
+SELECT schedule[:][1:1] FROM sal_emp WHERE name = 'Bill';
+
+        schedule
+------------------------
+ {{meeting},{training}}
+(1 row)
+</programlisting>
+ </para>
+
  <para>
   An array subscript expression will return null if either the array itself or
   any of the subscript expressions are null.  Also, null is returned if a
@@ -391,6 +414,10 @@ UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
     WHERE name = 'Carol';
 </programlisting>
 
+  The slice syntaxes with omitted <replaceable>lower-bound</replaceable> and/or
+  <replaceable>upper-bound</replaceable> can be used too, but only when
+  updating an array value that is not NULL or zero-dimensional (otherwise,
+  there is no existing subscript limit to substitute).
  </para>
 
  <para>
index 29f058ce5cbb1e7f7a8e10f712f84288a8be9ccb..d4dc2dcd21a313c73b2dab08abb1ce4a467edf2e 100644 (file)
@@ -271,6 +271,8 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                j = 0;
    IntArray    upper,
                lower;
+   bool        upperProvided[MAXDIM],
+               lowerProvided[MAXDIM];
    int        *lIndex;
 
    array_source = ExecEvalExpr(astate->refexpr,
@@ -300,6 +302,15 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                     errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
                            i + 1, MAXDIM)));
 
+       if (eltstate == NULL)
+       {
+           /* Slice bound is omitted, so use array's upper bound */
+           Assert(astate->reflowerindexpr != NIL);
+           upperProvided[i++] = false;
+           continue;
+       }
+       upperProvided[i] = true;
+
        upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
                                                     econtext,
                                                     &eisnull,
@@ -328,6 +339,14 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                         errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
                                j + 1, MAXDIM)));
 
+           if (eltstate == NULL)
+           {
+               /* Slice bound is omitted, so use array's lower bound */
+               lowerProvided[j++] = false;
+               continue;
+           }
+           lowerProvided[j] = true;
+
            lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
                                                         econtext,
                                                         &eisnull,
@@ -398,6 +417,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
                econtext->caseValue_datum =
                    array_get_slice(array_source, i,
                                    upper.indx, lower.indx,
+                                   upperProvided, lowerProvided,
                                    astate->refattrlength,
                                    astate->refelemlength,
                                    astate->refelembyval,
@@ -456,6 +476,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
        else
            return array_set_slice(array_source, i,
                                   upper.indx, lower.indx,
+                                  upperProvided, lowerProvided,
                                   sourceData,
                                   eisnull,
                                   astate->refattrlength,
@@ -475,6 +496,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
    else
        return array_get_slice(array_source, i,
                               upper.indx, lower.indx,
+                              upperProvided, lowerProvided,
                               astate->refattrlength,
                               astate->refelemlength,
                               astate->refelembyval,
index ba04b7227ca86b6a2a692a346ba841cc03e2513a..4cf14b6f71fe85774e91cbd5a2749b171f85c09d 100644 (file)
@@ -2401,6 +2401,7 @@ _copyAIndices(const A_Indices *from)
 {
    A_Indices  *newnode = makeNode(A_Indices);
 
+   COPY_SCALAR_FIELD(is_slice);
    COPY_NODE_FIELD(lidx);
    COPY_NODE_FIELD(uidx);
 
index 356fcafeb49a9773fa25e524a176aa6f73308961..a13d83181b94c78fd86376fe5a878905038ccc16 100644 (file)
@@ -2151,6 +2151,7 @@ _equalAStar(const A_Star *a, const A_Star *b)
 static bool
 _equalAIndices(const A_Indices *a, const A_Indices *b)
 {
+   COMPARE_SCALAR_FIELD(is_slice);
    COMPARE_NODE_FIELD(lidx);
    COMPARE_NODE_FIELD(uidx);
 
index 63fae82aba00807b625241a2fef577e9d2af5a95..fe2c643c34832248576a6aab6defed69a8491624 100644 (file)
@@ -2763,6 +2763,7 @@ _outA_Indices(StringInfo str, const A_Indices *node)
 {
    WRITE_NODE_TYPE("A_INDICES");
 
+   WRITE_BOOL_FIELD(is_slice);
    WRITE_NODE_FIELD(lidx);
    WRITE_NODE_FIELD(uidx);
 }
index c4bed8a5ef7a9c84db656fe80adb9d94af01e33a..223ef175dee3ebfc9886d167960d5e3a44527eed 100644 (file)
@@ -434,7 +434,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <node>   columnDef columnOptions
 %type <defelt> def_elem reloption_elem old_aggr_elem operator_def_elem
 %type <node>   def_arg columnElem where_clause where_or_current_clause
-               a_expr b_expr c_expr AexprConst indirection_el
+               a_expr b_expr c_expr AexprConst indirection_el opt_slice_bound
                columnref in_expr having_clause func_table array_expr
                ExclusionWhereClause
 %type <list>   rowsfrom_item rowsfrom_list opt_col_def_list
@@ -13191,19 +13191,26 @@ indirection_el:
            | '[' a_expr ']'
                {
                    A_Indices *ai = makeNode(A_Indices);
+                   ai->is_slice = false;
                    ai->lidx = NULL;
                    ai->uidx = $2;
                    $$ = (Node *) ai;
                }
-           | '[' a_expr ':' a_expr ']'
+           | '[' opt_slice_bound ':' opt_slice_bound ']'
                {
                    A_Indices *ai = makeNode(A_Indices);
+                   ai->is_slice = true;
                    ai->lidx = $2;
                    ai->uidx = $4;
                    $$ = (Node *) ai;
                }
        ;
 
+opt_slice_bound:
+           a_expr                                  { $$ = $1; }
+           | /*EMPTY*/                             { $$ = NULL; }
+       ;
+
 indirection:
            indirection_el                          { $$ = list_make1($1); }
            | indirection indirection_el            { $$ = lappend($1, $2); }
index 4130cbff5edf69a0361c02d11dede4c2972cc904..591a1f3a681ac24a8d847af0fc1c673cd3d32b41 100644 (file)
@@ -311,18 +311,18 @@ transformArraySubscripts(ParseState *pstate,
        elementType = transformArrayType(&arrayType, &arrayTypMod);
 
    /*
-    * A list containing only single subscripts refers to a single array
-    * element.  If any of the items are double subscripts (lower:upper), then
-    * the subscript expression means an array slice operation. In this case,
-    * we supply a default lower bound of 1 for any items that contain only a
-    * single subscript.  We have to prescan the indirection list to see if
-    * there are any double subscripts.
+    * A list containing only simple subscripts refers to a single array
+    * element.  If any of the items are slice specifiers (lower:upper), then
+    * the subscript expression means an array slice operation.  In this case,
+    * we convert any non-slice items to slices by treating the single
+    * subscript as the upper bound and supplying an assumed lower bound of 1.
+    * We have to prescan the list to see if there are any slice items.
     */
    foreach(idx, indirection)
    {
        A_Indices  *ai = (A_Indices *) lfirst(idx);
 
-       if (ai->lidx != NULL)
+       if (ai->is_slice)
        {
            isSlice = true;
            break;
@@ -356,7 +356,7 @@ transformArraySubscripts(ParseState *pstate,
                             errmsg("array subscript must have type integer"),
                        parser_errposition(pstate, exprLocation(ai->lidx))));
            }
-           else
+           else if (!ai->is_slice)
            {
                /* Make a constant 1 */
                subexpr = (Node *) makeConst(INT4OID,
@@ -367,21 +367,38 @@ transformArraySubscripts(ParseState *pstate,
                                             false,
                                             true);     /* pass by value */
            }
+           else
+           {
+               /* Slice with omitted lower bound, put NULL into the list */
+               subexpr = NULL;
+           }
            lowerIndexpr = lappend(lowerIndexpr, subexpr);
        }
-       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
+           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;
+       }
        upperIndexpr = lappend(upperIndexpr, subexpr);
    }
 
index 1b3fcd629c109194bb8eb6831407192a7a7914de..8c2c38dbe67db27c50bb4cc7035f1164527c6790 100644 (file)
@@ -650,7 +650,7 @@ transformAssignmentIndirection(ParseState *pstate,
        if (IsA(n, A_Indices))
        {
            subscripts = lappend(subscripts, n);
-           if (((A_Indices *) n)->lidx != NULL)
+           if (((A_Indices *) n)->is_slice)
                isSlice = true;
        }
        else if (IsA(n, A_Star))
index 67c9b357c852d5bb7b008589b2c8d62614e1d012..359fb1462bc6ddb692051f1f691bc2a0b5723a32 100644 (file)
@@ -1995,6 +1995,8 @@ array_get_element_expanded(Datum arraydatum,
  * nSubscripts: number of subscripts supplied (must be same for upper/lower)
  * upperIndx[]: the upper subscript values
  * lowerIndx[]: the lower subscript values
+ * upperProvided[]: true for provided upper subscript values
+ * lowerProvided[]: true for provided lower subscript values
  * arraytyplen: pg_type.typlen for the array type
  * elmlen: pg_type.typlen for the array's element type
  * elmbyval: pg_type.typbyval for the array's element type
@@ -2003,6 +2005,9 @@ array_get_element_expanded(Datum arraydatum,
  * Outputs:
  * The return value is the new array Datum (it's never NULL)
  *
+ * Omitted upper and lower subscript values are replaced by the corresponding
+ * array bound.
+ *
  * NOTE: we assume it is OK to scribble on the provided subscript arrays
  * lowerIndx[] and upperIndx[].  These are generally just temporaries.
  */
@@ -2011,6 +2016,8 @@ array_get_slice(Datum arraydatum,
                int nSubscripts,
                int *upperIndx,
                int *lowerIndx,
+               bool *upperProvided,
+               bool *lowerProvided,
                int arraytyplen,
                int elmlen,
                bool elmbyval,
@@ -2081,9 +2088,9 @@ array_get_slice(Datum arraydatum,
 
    for (i = 0; i < nSubscripts; i++)
    {
-       if (lowerIndx[i] < lb[i])
+       if (!lowerProvided[i] || lowerIndx[i] < lb[i])
            lowerIndx[i] = lb[i];
-       if (upperIndx[i] >= (dim[i] + lb[i]))
+       if (!upperProvided[i] || upperIndx[i] >= (dim[i] + lb[i]))
            upperIndx[i] = dim[i] + lb[i] - 1;
        if (lowerIndx[i] > upperIndx[i])
            return PointerGetDatum(construct_empty_array(elemtype));
@@ -2708,6 +2715,8 @@ array_set_element_expanded(Datum arraydatum,
  * nSubscripts: number of subscripts supplied (must be same for upper/lower)
  * upperIndx[]: the upper subscript values
  * lowerIndx[]: the lower subscript values
+ * upperProvided[]: true for provided upper subscript values
+ * lowerProvided[]: true for provided lower subscript values
  * srcArrayDatum: the source for the inserted values
  * isNull: indicates whether srcArrayDatum is NULL
  * arraytyplen: pg_type.typlen for the array type
@@ -2719,6 +2728,9 @@ array_set_element_expanded(Datum arraydatum,
  *       A new array is returned, just like the old except for the
  *       modified range.  The original array object is not changed.
  *
+ * Omitted upper and lower subscript values are replaced by the corresponding
+ * array bound.
+ *
  * For one-dimensional arrays only, we allow the array to be extended
  * by assigning to positions outside the existing subscript range; any
  * positions between the existing elements and the new ones are set to NULLs.
@@ -2735,6 +2747,8 @@ array_set_slice(Datum arraydatum,
                int nSubscripts,
                int *upperIndx,
                int *lowerIndx,
+               bool *upperProvided,
+               bool *lowerProvided,
                Datum srcArrayDatum,
                bool isNull,
                int arraytyplen,
@@ -2806,6 +2820,13 @@ array_set_slice(Datum arraydatum,
 
        for (i = 0; i < nSubscripts; i++)
        {
+           if (!upperProvided[i] || !lowerProvided[i])
+               ereport(ERROR,
+                       (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+               errmsg("array slice subscript must provide both boundaries"),
+               errdetail("When assigning to a slice of an empty array value,"
+                         " slice boundaries must be fully specified.")));
+
            dim[i] = 1 + upperIndx[i] - lowerIndx[i];
            lb[i] = lowerIndx[i];
        }
@@ -2839,6 +2860,10 @@ array_set_slice(Datum arraydatum,
    if (ndim == 1)
    {
        Assert(nSubscripts == 1);
+       if (!lowerProvided[0])
+           lowerIndx[0] = lb[0];
+       if (!upperProvided[0])
+           upperIndx[0] = dim[0] + lb[0] - 1;
        if (lowerIndx[0] > upperIndx[0])
            ereport(ERROR,
                    (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
@@ -2867,6 +2892,10 @@ array_set_slice(Datum arraydatum,
         */
        for (i = 0; i < nSubscripts; i++)
        {
+           if (!lowerProvided[i])
+               lowerIndx[i] = lb[i];
+           if (!upperProvided[i])
+               upperIndx[i] = dim[i] + lb[i] - 1;
            if (lowerIndx[i] > upperIndx[i])
                ereport(ERROR,
                        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
index 0ab839dc736f0ba5124e1ae9d0a355d00b1a5e9e..280808ae4f9f7f0c041ca1634beb557ca447b4f9 100644 (file)
@@ -9372,10 +9372,12 @@ printSubscripts(ArrayRef *aref, deparse_context *context)
        appendStringInfoChar(buf, '[');
        if (lowlist_item)
        {
+           /* If subexpression is NULL, get_rule_expr prints nothing */
            get_rule_expr((Node *) lfirst(lowlist_item), context, false);
            appendStringInfoChar(buf, ':');
            lowlist_item = lnext(lowlist_item);
        }
+       /* If subexpression is NULL, get_rule_expr prints nothing */
        get_rule_expr((Node *) lfirst(uplist_item), context, false);
        appendStringInfoChar(buf, ']');
    }
index 9142e94b070d9a16c0eacfe9b4b9231f5c4914c2..abd4dd166ccda1d0c95d28ad2687c4acdf789c17 100644 (file)
@@ -157,9 +157,10 @@ typedef struct Query
    List       *constraintDeps; /* a list of pg_constraint OIDs that the query
                                 * depends on to be semantically valid */
 
-   List       *withCheckOptions;   /* a list of WithCheckOption's, which are
-                                    * only added during rewrite and therefore
-                                    * are not written out as part of Query. */
+   List       *withCheckOptions;       /* a list of WithCheckOption's, which
+                                        * are only added during rewrite and
+                                        * therefore are not written out as
+                                        * part of Query. */
 } Query;
 
 
@@ -351,13 +352,17 @@ typedef struct A_Star
 } A_Star;
 
 /*
- * A_Indices - array subscript or slice bounds ([lidx:uidx] or [uidx])
+ * A_Indices - array subscript or slice bounds ([idx] or [lidx:uidx])
+ *
+ * In slice case, either or both of lidx and uidx can be NULL (omitted).
+ * In non-slice case, uidx holds the single subscript and lidx is always NULL.
  */
 typedef struct A_Indices
 {
    NodeTag     type;
-   Node       *lidx;           /* NULL if it's a single subscript */
-   Node       *uidx;
+   bool        is_slice;       /* true if slice (i.e., colon present) */
+   Node       *lidx;           /* slice lower bound, if any */
+   Node       *uidx;           /* subscript, or slice upper bound if any */
 } A_Indices;
 
 /*
index 60c1ca2c8dc1c404ba348aefd341ca01eb225ed2..4dbcc10e33705353a3548610909167f6bb72d3f8 100644 (file)
@@ -341,6 +341,9 @@ typedef struct WindowFunc
  * reflowerindexpr must be the same length as refupperindexpr when it
  * is not NIL.
  *
+ * In the slice case, individual expressions in the subscript lists can be
+ * NULL, meaning "substitute the array's current lower or upper bound".
+ *
  * Note: the result datatype is the element type when fetching a single
  * element; but it is the array type when doing subarray fetch or either
  * type of store.
@@ -360,7 +363,7 @@ typedef struct ArrayRef
    List       *refupperindexpr;/* expressions that evaluate to upper array
                                 * indexes */
    List       *reflowerindexpr;/* expressions that evaluate to lower array
-                                * indexes */
+                                * indexes, or NIL for single array element */
    Expr       *refexpr;        /* the expression that evaluates to an array
                                 * value */
    Expr       *refassgnexpr;   /* expression for the source value, or NULL if
index c25b80d272aeb2c1b824f033b72fd8ef3afe28c1..716e75637b05c918f7847a8f2d84993042e3d4ad 100644 (file)
@@ -377,9 +377,11 @@ extern Datum array_set_element(Datum arraydatum, int nSubscripts, int *indx,
                  int arraytyplen, int elmlen, bool elmbyval, char elmalign);
 extern Datum array_get_slice(Datum arraydatum, int nSubscripts,
                int *upperIndx, int *lowerIndx,
+               bool *upperProvided, bool *lowerProvided,
                int arraytyplen, int elmlen, bool elmbyval, char elmalign);
 extern Datum array_set_slice(Datum arraydatum, int nSubscripts,
                int *upperIndx, int *lowerIndx,
+               bool *upperProvided, bool *lowerProvided,
                Datum srcArrayDatum, bool isNull,
                int arraytyplen, int elmlen, bool elmbyval, char elmalign);
 
index 73fb5a248b47cf181422158c8c6a3bca351c72ca..baccca14afdf13cacef79b25998c90f8b3475b42 100644 (file)
@@ -125,6 +125,16 @@ SELECT a[1:3],
  {16,25,23} | {}                    | {foobar,new_word} | {{elt2}}
 (3 rows)
 
+SELECT b[1:1][2][2],
+       d[1:1][2]
+   FROM arrtest;
+           b           |       d       
+-----------------------+---------------
+ {{{113,142},{1,147}}} | {}
+ {}                    | {}
+ {}                    | {{elt1,elt2}}
+(3 rows)
+
 INSERT INTO arrtest(a) VALUES('{1,null,3}');
 SELECT a FROM arrtest;
        a       
@@ -152,6 +162,107 @@ SELECT a,b,c FROM arrtest;
  [4:4]={NULL}  | {3,4}                 | {foo,new_word}
 (3 rows)
 
+-- test mixed slice/scalar subscripting
+select '{{1,2,3},{4,5,6},{7,8,9}}'::int[];
+           int4            
+---------------------------
+ {{1,2,3},{4,5,6},{7,8,9}}
+(1 row)
+
+select ('{{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2];
+     int4      
+---------------
+ {{1,2},{4,5}}
+(1 row)
+
+select '[0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}'::int[];
+                 int4                 
+--------------------------------------
+ [0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}
+(1 row)
+
+select ('[0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2];
+     int4      
+---------------
+ {{5,6},{8,9}}
+(1 row)
+
+-- test slices with empty lower and/or upper index
+CREATE TEMP TABLE arrtest_s (
+  a       int2[],
+  b       int2[][]
+);
+INSERT INTO arrtest_s VALUES ('{1,2,3,4,5}', '{{1,2,3}, {4,5,6}, {7,8,9}}');
+INSERT INTO arrtest_s VALUES ('[0:4]={1,2,3,4,5}', '[0:2][0:2]={{1,2,3}, {4,5,6}, {7,8,9}}');
+SELECT * FROM arrtest_s;
+         a         |                  b                   
+-------------------+--------------------------------------
+ {1,2,3,4,5}       | {{1,2,3},{4,5,6},{7,8,9}}
+ [0:4]={1,2,3,4,5} | [0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}
+(2 rows)
+
+SELECT a[:3], b[:2][:2] FROM arrtest_s;
+     a     |             b             
+-----------+---------------------------
+ {1,2,3}   | {{1,2},{4,5}}
+ {1,2,3,4} | {{1,2,3},{4,5,6},{7,8,9}}
+(2 rows)
+
+SELECT a[2:], b[2:][2:] FROM arrtest_s;
+     a     |       b       
+-----------+---------------
+ {2,3,4,5} | {{5,6},{8,9}}
+ {3,4,5}   | {{9}}
+(2 rows)
+
+SELECT a[:], b[:] FROM arrtest_s;
+      a      |             b             
+-------------+---------------------------
+ {1,2,3,4,5} | {{1,2,3},{4,5,6},{7,8,9}}
+ {1,2,3,4,5} | {{1,2,3},{4,5,6},{7,8,9}}
+(2 rows)
+
+-- updates
+UPDATE arrtest_s SET a[:3] = '{11, 12, 13}', b[:2][:2] = '{{11,12}, {14,15}}'
+  WHERE array_lower(a,1) = 1;
+SELECT * FROM arrtest_s;
+         a         |                  b                   
+-------------------+--------------------------------------
+ [0:4]={1,2,3,4,5} | [0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}
+ {11,12,13,4,5}    | {{11,12,3},{14,15,6},{7,8,9}}
+(2 rows)
+
+UPDATE arrtest_s SET a[3:] = '{23, 24, 25}', b[2:][2:] = '{{25,26}, {28,29}}';
+SELECT * FROM arrtest_s;
+          a          |                   b                   
+---------------------+---------------------------------------
+ [0:4]={1,2,3,23,24} | [0:2][0:2]={{1,2,3},{4,5,6},{7,8,25}}
+ {11,12,23,24,25}    | {{11,12,3},{14,25,26},{7,28,29}}
+(2 rows)
+
+UPDATE arrtest_s SET a[:] = '{11, 12, 13, 14, 15}';
+SELECT * FROM arrtest_s;
+           a            |                   b                   
+------------------------+---------------------------------------
+ [0:4]={11,12,13,14,15} | [0:2][0:2]={{1,2,3},{4,5,6},{7,8,25}}
+ {11,12,13,14,15}       | {{11,12,3},{14,25,26},{7,28,29}}
+(2 rows)
+
+UPDATE arrtest_s SET a[:] = '{23, 24, 25}';  -- fail, too small
+ERROR:  source array too small
+INSERT INTO arrtest_s VALUES(NULL, NULL);
+UPDATE arrtest_s SET a[:] = '{11, 12, 13, 14, 15}';  -- fail, no good with null
+ERROR:  array slice subscript must provide both boundaries
+DETAIL:  When assigning to a slice of an empty array value, slice boundaries must be fully specified.
+-- check with fixed-length-array type, such as point
+SELECT f1[0:1] FROM POINT_TBL;
+ERROR:  slices of fixed-length arrays not implemented
+SELECT f1[0:] FROM POINT_TBL;
+ERROR:  slices of fixed-length arrays not implemented
+SELECT f1[:1] FROM POINT_TBL;
+ERROR:  slices of fixed-length arrays not implemented
+SELECT f1[:] FROM POINT_TBL;
+ERROR:  slices of fixed-length arrays not implemented
 --
 -- test array extension
 --
index b1dd65144050b97a1a2d81b667a38ca112d5c192..a2c3db112742f093e37989961f9e40d6829707fe 100644 (file)
@@ -86,6 +86,10 @@ SELECT a[1:3],
           d[1:1][2:2]
    FROM arrtest;
 
+SELECT b[1:1][2][2],
+       d[1:1][2]
+   FROM arrtest;
+
 INSERT INTO arrtest(a) VALUES('{1,null,3}');
 SELECT a FROM arrtest;
 UPDATE arrtest SET a[4] = NULL WHERE a[2] IS NULL;
@@ -93,6 +97,43 @@ SELECT a FROM arrtest WHERE a[2] IS NULL;
 DELETE FROM arrtest WHERE a[2] IS NULL AND b IS NULL;
 SELECT a,b,c FROM arrtest;
 
+-- test mixed slice/scalar subscripting
+select '{{1,2,3},{4,5,6},{7,8,9}}'::int[];
+select ('{{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2];
+select '[0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}'::int[];
+select ('[0:2][0:2]={{1,2,3},{4,5,6},{7,8,9}}'::int[])[1:2][2];
+
+-- test slices with empty lower and/or upper index
+CREATE TEMP TABLE arrtest_s (
+  a       int2[],
+  b       int2[][]
+);
+INSERT INTO arrtest_s VALUES ('{1,2,3,4,5}', '{{1,2,3}, {4,5,6}, {7,8,9}}');
+INSERT INTO arrtest_s VALUES ('[0:4]={1,2,3,4,5}', '[0:2][0:2]={{1,2,3}, {4,5,6}, {7,8,9}}');
+
+SELECT * FROM arrtest_s;
+SELECT a[:3], b[:2][:2] FROM arrtest_s;
+SELECT a[2:], b[2:][2:] FROM arrtest_s;
+SELECT a[:], b[:] FROM arrtest_s;
+
+-- updates
+UPDATE arrtest_s SET a[:3] = '{11, 12, 13}', b[:2][:2] = '{{11,12}, {14,15}}'
+  WHERE array_lower(a,1) = 1;
+SELECT * FROM arrtest_s;
+UPDATE arrtest_s SET a[3:] = '{23, 24, 25}', b[2:][2:] = '{{25,26}, {28,29}}';
+SELECT * FROM arrtest_s;
+UPDATE arrtest_s SET a[:] = '{11, 12, 13, 14, 15}';
+SELECT * FROM arrtest_s;
+UPDATE arrtest_s SET a[:] = '{23, 24, 25}';  -- fail, too small
+INSERT INTO arrtest_s VALUES(NULL, NULL);
+UPDATE arrtest_s SET a[:] = '{11, 12, 13, 14, 15}';  -- fail, no good with null
+
+-- check with fixed-length-array type, such as point
+SELECT f1[0:1] FROM POINT_TBL;
+SELECT f1[0:] FROM POINT_TBL;
+SELECT f1[:1] FROM POINT_TBL;
+SELECT f1[:] FROM POINT_TBL;
+
 --
 -- test array extension
 --