From 9246af6799819847faa33baf441251003acbb8fe Mon Sep 17 00:00:00 2001 From: Teodor Sigaev Date: Fri, 18 Dec 2015 15:18:58 +0300 Subject: [PATCH] Allow to omit boundaries in array subscript Allow to omiy lower or upper or both boundaries in array subscript for selecting slice of array. Author: YUriy Zhuravlev --- doc/src/sgml/array.sgml | 20 ++++++++++++ src/backend/executor/execQual.c | 44 ++++++++++++++++++++----- src/backend/nodes/copyfuncs.c | 2 ++ src/backend/nodes/equalfuncs.c | 2 ++ src/backend/nodes/outfuncs.c | 2 ++ src/backend/parser/gram.y | 31 ++++++++++++++++++ src/backend/parser/parse_node.c | 49 ++++++++++++++++++---------- src/backend/parser/parse_target.c | 2 +- src/include/nodes/parsenodes.h | 2 ++ src/test/regress/expected/arrays.out | 40 +++++++++++++++++++++++ src/test/regress/output/misc.source | 3 +- src/test/regress/sql/arrays.sql | 15 +++++++++ 12 files changed, 184 insertions(+), 28 deletions(-) diff --git a/doc/src/sgml/array.sgml b/doc/src/sgml/array.sgml index 4385a09cd9..6ee71a5757 100644 --- a/doc/src/sgml/array.sgml +++ b/doc/src/sgml/array.sgml @@ -255,6 +255,26 @@ SELECT schedule[1:2][1:1] FROM sal_emp WHERE name = 'Bill'; ------------------------ {{meeting},{training}} (1 row) + + + Possible to skip the lower-bound or + upper-bound + for get first or last element in slice. + + +SELECT schedule[:][:] FROM sal_emp WHERE name = 'Bill'; + + schedule +------------------------ + {{meeting,lunch},{training,presentation}} +(1 row) + +SELECT schedule[:2][2:] FROM sal_emp WHERE name = 'Bill'; + + schedule +------------------------ + {{lunch},{presentation}} +(1 row) If any dimension is written as a slice, i.e., contains a colon, then all diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 29f058ce5c..d9bf9773fe 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -268,10 +268,12 @@ ExecEvalArrayRef(ArrayRefExprState *astate, bool eisnull; ListCell *l; int i = 0, - j = 0; + j = 0, + indexexpr; IntArray upper, lower; int *lIndex; + AnyArrayType *arrays; array_source = ExecEvalExpr(astate->refexpr, econtext, @@ -293,6 +295,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate, foreach(l, astate->refupperindexpr) { ExprState *eltstate = (ExprState *) lfirst(l); + eisnull = false; if (i >= MAXDIM) ereport(ERROR, @@ -300,10 +303,23 @@ ExecEvalArrayRef(ArrayRefExprState *astate, errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", i + 1, MAXDIM))); - upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, - econtext, - &eisnull, - NULL)); + if (eltstate == NULL && astate->refattrlength <= 0) + { + if (isAssignment) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("cannot determine upper index for empty array"))); + arrays = (AnyArrayType *)DatumGetArrayTypeP(array_source); + indexexpr = AARR_LBOUND(arrays)[i] + AARR_DIMS(arrays)[i] - 1; + } + else + indexexpr = DatumGetInt32(ExecEvalExpr(eltstate, + econtext, + &eisnull, + NULL)); + + upper.indx[i++] = indexexpr; + /* If any index expr yields NULL, result is NULL or error */ if (eisnull) { @@ -321,6 +337,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate, foreach(l, astate->reflowerindexpr) { ExprState *eltstate = (ExprState *) lfirst(l); + eisnull = false; if (j >= MAXDIM) ereport(ERROR, @@ -328,10 +345,19 @@ ExecEvalArrayRef(ArrayRefExprState *astate, errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", j + 1, MAXDIM))); - lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate, - econtext, - &eisnull, - NULL)); + if (eltstate == NULL) + { + arrays = (AnyArrayType *)DatumGetArrayTypeP(array_source); + indexexpr = AARR_LBOUND(arrays)[j]; + } + else + indexexpr = DatumGetInt32(ExecEvalExpr(eltstate, + econtext, + &eisnull, + NULL)); + + lower.indx[j++] = indexexpr; + /* If any index expr yields NULL, result is NULL or error */ if (eisnull) { diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index ba04b7227c..6fc9886209 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2403,6 +2403,8 @@ _copyAIndices(const A_Indices *from) COPY_NODE_FIELD(lidx); COPY_NODE_FIELD(uidx); + COPY_SCALAR_FIELD(lidx_default); + COPY_SCALAR_FIELD(uidx_default); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 356fcafeb4..deca3b787d 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -2153,6 +2153,8 @@ _equalAIndices(const A_Indices *a, const A_Indices *b) { COMPARE_NODE_FIELD(lidx); COMPARE_NODE_FIELD(uidx); + COMPARE_SCALAR_FIELD(lidx_default); + COMPARE_SCALAR_FIELD(uidx_default); return true; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 63fae82aba..1a28dfd2b9 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2765,6 +2765,8 @@ _outA_Indices(StringInfo str, const A_Indices *node) WRITE_NODE_FIELD(lidx); WRITE_NODE_FIELD(uidx); + WRITE_BOOL_FIELD(lidx_default); + WRITE_BOOL_FIELD(uidx_default); } static void diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index c4bed8a5ef..ce95f0f2a7 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -13193,6 +13193,35 @@ indirection_el: A_Indices *ai = makeNode(A_Indices); ai->lidx = NULL; ai->uidx = $2; + ai->lidx_default = false; + ai->uidx_default = false; + $$ = (Node *) ai; + } + | '[' ':' ']' + { + A_Indices *ai = makeNode(A_Indices); + ai->lidx = NULL; + ai->uidx = NULL; + ai->lidx_default = true; + ai->uidx_default = true; + $$ = (Node *) ai; + } + | '[' ':' a_expr ']' + { + A_Indices *ai = makeNode(A_Indices); + ai->lidx = NULL; + ai->uidx = $3; + ai->lidx_default = true; + ai->uidx_default = false; + $$ = (Node *) ai; + } + | '[' a_expr ':' ']' + { + A_Indices *ai = makeNode(A_Indices); + ai->lidx = $2; + ai->uidx = NULL; + ai->lidx_default = false; + ai->uidx_default = true; $$ = (Node *) ai; } | '[' a_expr ':' a_expr ']' @@ -13200,6 +13229,8 @@ indirection_el: A_Indices *ai = makeNode(A_Indices); ai->lidx = $2; ai->uidx = $4; + ai->lidx_default = false; + ai->uidx_default = false; $$ = (Node *) ai; } ; diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 4130cbff5e..de6e0b8934 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -311,7 +311,7 @@ transformArraySubscripts(ParseState *pstate, elementType = transformArrayType(&arrayType, &arrayTypMod); /* - * A list containing only single subscripts refers to a single array + * A list containing only single subscripts (uidx) 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 @@ -322,7 +322,7 @@ transformArraySubscripts(ParseState *pstate, { A_Indices *ai = (A_Indices *) lfirst(idx); - if (ai->lidx != NULL) + if (ai->lidx != NULL || ai->lidx_default) { isSlice = true; break; @@ -335,9 +335,17 @@ transformArraySubscripts(ParseState *pstate, foreach(idx, indirection) { A_Indices *ai = (A_Indices *) lfirst(idx); - Node *subexpr; + Node *subexpr = NULL; Assert(IsA(ai, A_Indices)); + if ((ai->uidx_default || ai->lidx_default) && assignFrom != NULL) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("array subscript must have both boundaries"), + errhint("You can't omit the upper or lower" + " boundaries when updating or inserting"), + parser_errposition(pstate, exprLocation(arrayBase)))); + if (isSlice) { if (ai->lidx) @@ -356,7 +364,7 @@ transformArraySubscripts(ParseState *pstate, errmsg("array subscript must have type integer"), parser_errposition(pstate, exprLocation(ai->lidx)))); } - else + else if (ai->lidx_default == false) { /* Make a constant 1 */ subexpr = (Node *) makeConst(INT4OID, @@ -369,19 +377,26 @@ transformArraySubscripts(ParseState *pstate, } 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)))); + + if (ai->uidx_default == false) + { + 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 + subexpr = NULL; + upperIndexpr = lappend(upperIndexpr, subexpr); } diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 1b3fcd629c..df41f9fc9b 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)->lidx != NULL || ((A_Indices *) n)->lidx_default) isSlice = true; } else if (IsA(n, A_Star)) diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 9142e94b07..ac208cc533 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -358,6 +358,8 @@ typedef struct A_Indices NodeTag type; Node *lidx; /* NULL if it's a single subscript */ Node *uidx; + bool lidx_default; + bool uidx_default; } A_Indices; /* diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out index 73fb5a248b..68c14b93ae 100644 --- a/src/test/regress/expected/arrays.out +++ b/src/test/regress/expected/arrays.out @@ -2031,3 +2031,43 @@ SELECT width_bucket(5, ARRAY[3, 4, NULL]); ERROR: thresholds array must not contain NULLs SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]); ERROR: thresholds must be one-dimensional array +-- slices with empty lower and/or upper index +CREATE 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}}'); +SELECT a[:3], b[:2][:2] FROM arrtest_s; + a | b +---------+--------------- + {1,2,3} | {{1,2},{4,5}} +(1 row) + +SELECT a[2:], b[2:][2:] FROM arrtest_s; + a | b +-----------+--------------- + {2,3,4,5} | {{5,6},{8,9}} +(1 row) + +SELECT a[:], b[:] FROM arrtest_s; + a | b +-------------+--------------------------- + {1,2,3,4,5} | {{1,2,3},{4,5,6},{7,8,9}} +(1 row) + +-- errors +UPDATE arrtest_s SET a[:3] = '{11, 12, 13}', b[:2][:2] = '{{11,12}, {14, 15}}'; +ERROR: array subscript must have both boundaries +LINE 1: UPDATE arrtest_s SET a[:3] = '{11, 12, 13}', b[:2][:2] = '{{... + ^ +HINT: You can't omit the upper or lower boundaries when updating or inserting +UPDATE arrtest_s SET a[3:] = '{23, 24, 25}', b[2:][2:] = '{{25,26}, {28, 29}}'; +ERROR: array subscript must have both boundaries +LINE 1: UPDATE arrtest_s SET a[3:] = '{23, 24, 25}', b[2:][2:] = '{{... + ^ +HINT: You can't omit the upper or lower boundaries when updating or inserting +UPDATE arrtest_s SET a[:] = '{23, 24, 25}'; +ERROR: array subscript must have both boundaries +LINE 1: UPDATE arrtest_s SET a[:] = '{23, 24, 25}'; + ^ +HINT: You can't omit the upper or lower boundaries when updating or inserting diff --git a/src/test/regress/output/misc.source b/src/test/regress/output/misc.source index 5f263f9a3a..155972bc9a 100644 --- a/src/test/regress/output/misc.source +++ b/src/test/regress/output/misc.source @@ -586,6 +586,7 @@ SELECT user_relns() AS user_relns array_index_op_test array_op_test arrtest + arrtest_s b b_star bb @@ -710,7 +711,7 @@ SELECT user_relns() AS user_relns tvvmv varchar_tbl xacttest -(132 rows) +(133 rows) SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))); name diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql index b1dd651440..6a357a905e 100644 --- a/src/test/regress/sql/arrays.sql +++ b/src/test/regress/sql/arrays.sql @@ -609,3 +609,18 @@ SELECT width_bucket(5, '{}'); SELECT width_bucket('5'::text, ARRAY[3, 4]::integer[]); SELECT width_bucket(5, ARRAY[3, 4, NULL]); SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]); + +-- slices with empty lower and/or upper index +CREATE 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}}'); +SELECT a[:3], b[:2][:2] FROM arrtest_s; +SELECT a[2:], b[2:][2:] FROM arrtest_s; +SELECT a[:], b[:] FROM arrtest_s; + +-- errors +UPDATE arrtest_s SET a[:3] = '{11, 12, 13}', b[:2][:2] = '{{11,12}, {14, 15}}'; +UPDATE arrtest_s SET a[3:] = '{23, 24, 25}', b[2:][2:] = '{{25,26}, {28, 29}}'; +UPDATE arrtest_s SET a[:] = '{23, 24, 25}'; \ No newline at end of file