diff --git a/doc/src/sgml/array.sgml b/doc/src/sgml/array.sgml index 4385a09cd9..58878451f0 100644 --- a/doc/src/sgml/array.sgml +++ b/doc/src/sgml/array.sgml @@ -276,6 +276,29 @@ SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill'; for all dimensions, e.g., [1:2][1:1], not [2][1:1]. + + It is possible to omit the lower-bound and/or + upper-bound of a slice specifier; the missing + bound is replaced by the lower or upper limit of the array's subscripts. + For example: + + +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) + + + 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'; + The slice syntaxes with omitted lower-bound and/or + upper-bound 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). diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 29f058ce5c..d4dc2dcd21 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -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, diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index ba04b7227c..4cf14b6f71 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 356fcafeb4..a13d83181b 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 63fae82aba..fe2c643c34 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -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); } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c4bed8a5ef..223ef175de 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -434,7 +434,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type columnDef columnOptions %type def_elem reloption_elem old_aggr_elem operator_def_elem %type 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 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); } diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 4130cbff5e..591a1f3a68 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -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); } diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 1b3fcd629c..8c2c38dbe6 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -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)) diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 67c9b357c8..359fb1462b 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -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), diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 0ab839dc73..280808ae4f 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -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, ']'); } diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 9142e94b07..abd4dd166c 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -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; /* diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 60c1ca2c8d..4dbcc10e33 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -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 diff --git a/src/include/utils/array.h b/src/include/utils/array.h index c25b80d272..716e75637b 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -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); diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out index 73fb5a248b..baccca14af 100644 --- a/src/test/regress/expected/arrays.out +++ b/src/test/regress/expected/arrays.out @@ -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 -- diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql index b1dd651440..a2c3db1127 100644 --- a/src/test/regress/sql/arrays.sql +++ b/src/test/regress/sql/arrays.sql @@ -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 --