summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTeodor Sigaev2015-12-18 12:18:58 +0000
committerTeodor Sigaev2015-12-18 12:18:58 +0000
commit9246af6799819847faa33baf441251003acbb8fe (patch)
tree4ec56f3add9dc6e3bb583a9c3d617385390109fe
parent33bd250f6c4cc309f4eeb657da80f1e7743b3e5c (diff)
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
-rw-r--r--doc/src/sgml/array.sgml20
-rw-r--r--src/backend/executor/execQual.c44
-rw-r--r--src/backend/nodes/copyfuncs.c2
-rw-r--r--src/backend/nodes/equalfuncs.c2
-rw-r--r--src/backend/nodes/outfuncs.c2
-rw-r--r--src/backend/parser/gram.y31
-rw-r--r--src/backend/parser/parse_node.c49
-rw-r--r--src/backend/parser/parse_target.c2
-rw-r--r--src/include/nodes/parsenodes.h2
-rw-r--r--src/test/regress/expected/arrays.out40
-rw-r--r--src/test/regress/output/misc.source3
-rw-r--r--src/test/regress/sql/arrays.sql15
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
@@ -257,6 +257,26 @@ SELECT schedule[1:2][1:1] FROM sal_emp WHERE name = 'Bill';
(1 row)
</programlisting>
+ Possible to skip the <literal><replaceable>lower-bound</replaceable></literal> or
+ <literal><replaceable>upper-bound</replaceable></literal>
+ for get first or last element in slice.
+
+<programlisting>
+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)
+</programlisting>
+
If any dimension is written as a slice, i.e., contains a colon, then all
dimensions are treated as slices. Any dimension that has only a single
number (no colon) is treated as being from 1
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