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
 --