Commit 9246af67 authored by Teodor Sigaev's avatar Teodor Sigaev

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
parent 33bd250f
...@@ -255,6 +255,26 @@ SELECT schedule[1:2][1:1] FROM sal_emp WHERE name = 'Bill'; ...@@ -255,6 +255,26 @@ SELECT schedule[1:2][1:1] FROM sal_emp WHERE name = 'Bill';
------------------------ ------------------------
{{meeting},{training}} {{meeting},{training}}
(1 row) (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> </programlisting>
If any dimension is written as a slice, i.e., contains a colon, then all If any dimension is written as a slice, i.e., contains a colon, then all
......
...@@ -268,10 +268,12 @@ ExecEvalArrayRef(ArrayRefExprState *astate, ...@@ -268,10 +268,12 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
bool eisnull; bool eisnull;
ListCell *l; ListCell *l;
int i = 0, int i = 0,
j = 0; j = 0,
indexexpr;
IntArray upper, IntArray upper,
lower; lower;
int *lIndex; int *lIndex;
AnyArrayType *arrays;
array_source = ExecEvalExpr(astate->refexpr, array_source = ExecEvalExpr(astate->refexpr,
econtext, econtext,
...@@ -293,6 +295,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate, ...@@ -293,6 +295,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
foreach(l, astate->refupperindexpr) foreach(l, astate->refupperindexpr)
{ {
ExprState *eltstate = (ExprState *) lfirst(l); ExprState *eltstate = (ExprState *) lfirst(l);
eisnull = false;
if (i >= MAXDIM) if (i >= MAXDIM)
ereport(ERROR, ereport(ERROR,
...@@ -300,10 +303,23 @@ ExecEvalArrayRef(ArrayRefExprState *astate, ...@@ -300,10 +303,23 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
i + 1, MAXDIM))); i + 1, MAXDIM)));
upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, if (eltstate == NULL && astate->refattrlength <= 0)
econtext, {
&eisnull, if (isAssignment)
NULL)); 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 any index expr yields NULL, result is NULL or error */
if (eisnull) if (eisnull)
{ {
...@@ -321,6 +337,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate, ...@@ -321,6 +337,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
foreach(l, astate->reflowerindexpr) foreach(l, astate->reflowerindexpr)
{ {
ExprState *eltstate = (ExprState *) lfirst(l); ExprState *eltstate = (ExprState *) lfirst(l);
eisnull = false;
if (j >= MAXDIM) if (j >= MAXDIM)
ereport(ERROR, ereport(ERROR,
...@@ -328,10 +345,19 @@ ExecEvalArrayRef(ArrayRefExprState *astate, ...@@ -328,10 +345,19 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
j + 1, MAXDIM))); j + 1, MAXDIM)));
lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate, if (eltstate == NULL)
econtext, {
&eisnull, arrays = (AnyArrayType *)DatumGetArrayTypeP(array_source);
NULL)); 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 any index expr yields NULL, result is NULL or error */
if (eisnull) if (eisnull)
{ {
......
...@@ -2403,6 +2403,8 @@ _copyAIndices(const A_Indices *from) ...@@ -2403,6 +2403,8 @@ _copyAIndices(const A_Indices *from)
COPY_NODE_FIELD(lidx); COPY_NODE_FIELD(lidx);
COPY_NODE_FIELD(uidx); COPY_NODE_FIELD(uidx);
COPY_SCALAR_FIELD(lidx_default);
COPY_SCALAR_FIELD(uidx_default);
return newnode; return newnode;
} }
......
...@@ -2153,6 +2153,8 @@ _equalAIndices(const A_Indices *a, const A_Indices *b) ...@@ -2153,6 +2153,8 @@ _equalAIndices(const A_Indices *a, const A_Indices *b)
{ {
COMPARE_NODE_FIELD(lidx); COMPARE_NODE_FIELD(lidx);
COMPARE_NODE_FIELD(uidx); COMPARE_NODE_FIELD(uidx);
COMPARE_SCALAR_FIELD(lidx_default);
COMPARE_SCALAR_FIELD(uidx_default);
return true; return true;
} }
......
...@@ -2765,6 +2765,8 @@ _outA_Indices(StringInfo str, const A_Indices *node) ...@@ -2765,6 +2765,8 @@ _outA_Indices(StringInfo str, const A_Indices *node)
WRITE_NODE_FIELD(lidx); WRITE_NODE_FIELD(lidx);
WRITE_NODE_FIELD(uidx); WRITE_NODE_FIELD(uidx);
WRITE_BOOL_FIELD(lidx_default);
WRITE_BOOL_FIELD(uidx_default);
} }
static void static void
......
...@@ -13193,6 +13193,35 @@ indirection_el: ...@@ -13193,6 +13193,35 @@ indirection_el:
A_Indices *ai = makeNode(A_Indices); A_Indices *ai = makeNode(A_Indices);
ai->lidx = NULL; ai->lidx = NULL;
ai->uidx = $2; 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; $$ = (Node *) ai;
} }
| '[' a_expr ':' a_expr ']' | '[' a_expr ':' a_expr ']'
...@@ -13200,6 +13229,8 @@ indirection_el: ...@@ -13200,6 +13229,8 @@ indirection_el:
A_Indices *ai = makeNode(A_Indices); A_Indices *ai = makeNode(A_Indices);
ai->lidx = $2; ai->lidx = $2;
ai->uidx = $4; ai->uidx = $4;
ai->lidx_default = false;
ai->uidx_default = false;
$$ = (Node *) ai; $$ = (Node *) ai;
} }
; ;
......
...@@ -311,7 +311,7 @@ transformArraySubscripts(ParseState *pstate, ...@@ -311,7 +311,7 @@ transformArraySubscripts(ParseState *pstate,
elementType = transformArrayType(&arrayType, &arrayTypMod); 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 * element. If any of the items are double subscripts (lower:upper), then
* the subscript expression means an array slice operation. In this case, * 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 * we supply a default lower bound of 1 for any items that contain only a
...@@ -322,7 +322,7 @@ transformArraySubscripts(ParseState *pstate, ...@@ -322,7 +322,7 @@ transformArraySubscripts(ParseState *pstate,
{ {
A_Indices *ai = (A_Indices *) lfirst(idx); A_Indices *ai = (A_Indices *) lfirst(idx);
if (ai->lidx != NULL) if (ai->lidx != NULL || ai->lidx_default)
{ {
isSlice = true; isSlice = true;
break; break;
...@@ -335,9 +335,17 @@ transformArraySubscripts(ParseState *pstate, ...@@ -335,9 +335,17 @@ transformArraySubscripts(ParseState *pstate,
foreach(idx, indirection) foreach(idx, indirection)
{ {
A_Indices *ai = (A_Indices *) lfirst(idx); A_Indices *ai = (A_Indices *) lfirst(idx);
Node *subexpr; Node *subexpr = NULL;
Assert(IsA(ai, A_Indices)); 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 (isSlice)
{ {
if (ai->lidx) if (ai->lidx)
...@@ -356,7 +364,7 @@ transformArraySubscripts(ParseState *pstate, ...@@ -356,7 +364,7 @@ transformArraySubscripts(ParseState *pstate,
errmsg("array subscript must have type integer"), errmsg("array subscript must have type integer"),
parser_errposition(pstate, exprLocation(ai->lidx)))); parser_errposition(pstate, exprLocation(ai->lidx))));
} }
else else if (ai->lidx_default == false)
{ {
/* Make a constant 1 */ /* Make a constant 1 */
subexpr = (Node *) makeConst(INT4OID, subexpr = (Node *) makeConst(INT4OID,
...@@ -369,19 +377,26 @@ transformArraySubscripts(ParseState *pstate, ...@@ -369,19 +377,26 @@ transformArraySubscripts(ParseState *pstate,
} }
lowerIndexpr = lappend(lowerIndexpr, subexpr); lowerIndexpr = lappend(lowerIndexpr, subexpr);
} }
subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
/* If it's not int4 already, try to coerce */ if (ai->uidx_default == false)
subexpr = coerce_to_target_type(pstate, {
subexpr, exprType(subexpr), subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
INT4OID, -1, /* If it's not int4 already, try to coerce */
COERCION_ASSIGNMENT, subexpr = coerce_to_target_type(pstate,
COERCE_IMPLICIT_CAST, subexpr, exprType(subexpr),
-1); INT4OID, -1,
if (subexpr == NULL) COERCION_ASSIGNMENT,
ereport(ERROR, COERCE_IMPLICIT_CAST,
(errcode(ERRCODE_DATATYPE_MISMATCH), -1);
errmsg("array subscript must have type integer"), if (subexpr == NULL)
parser_errposition(pstate, exprLocation(ai->uidx)))); 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); upperIndexpr = lappend(upperIndexpr, subexpr);
} }
......
...@@ -650,7 +650,7 @@ transformAssignmentIndirection(ParseState *pstate, ...@@ -650,7 +650,7 @@ transformAssignmentIndirection(ParseState *pstate,
if (IsA(n, A_Indices)) if (IsA(n, A_Indices))
{ {
subscripts = lappend(subscripts, n); subscripts = lappend(subscripts, n);
if (((A_Indices *) n)->lidx != NULL) if (((A_Indices *) n)->lidx != NULL || ((A_Indices *) n)->lidx_default)
isSlice = true; isSlice = true;
} }
else if (IsA(n, A_Star)) else if (IsA(n, A_Star))
......
...@@ -358,6 +358,8 @@ typedef struct A_Indices ...@@ -358,6 +358,8 @@ typedef struct A_Indices
NodeTag type; NodeTag type;
Node *lidx; /* NULL if it's a single subscript */ Node *lidx; /* NULL if it's a single subscript */
Node *uidx; Node *uidx;
bool lidx_default;
bool uidx_default;
} A_Indices; } A_Indices;
/* /*
......
...@@ -2031,3 +2031,43 @@ SELECT width_bucket(5, ARRAY[3, 4, NULL]); ...@@ -2031,3 +2031,43 @@ SELECT width_bucket(5, ARRAY[3, 4, NULL]);
ERROR: thresholds array must not contain NULLs ERROR: thresholds array must not contain NULLs
SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]); SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]);
ERROR: thresholds must be one-dimensional array 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
...@@ -586,6 +586,7 @@ SELECT user_relns() AS user_relns ...@@ -586,6 +586,7 @@ SELECT user_relns() AS user_relns
array_index_op_test array_index_op_test
array_op_test array_op_test
arrtest arrtest
arrtest_s
b b
b_star b_star
bb bb
...@@ -710,7 +711,7 @@ SELECT user_relns() AS user_relns ...@@ -710,7 +711,7 @@ SELECT user_relns() AS user_relns
tvvmv tvvmv
varchar_tbl varchar_tbl
xacttest xacttest
(132 rows) (133 rows)
SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))); SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
name name
......
...@@ -609,3 +609,18 @@ SELECT width_bucket(5, '{}'); ...@@ -609,3 +609,18 @@ SELECT width_bucket(5, '{}');
SELECT width_bucket('5'::text, ARRAY[3, 4]::integer[]); SELECT width_bucket('5'::text, ARRAY[3, 4]::integer[]);
SELECT width_bucket(5, ARRAY[3, 4, NULL]); SELECT width_bucket(5, ARRAY[3, 4, NULL]);
SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]); 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment