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
This commit is contained in:
Teodor Sigaev 2015-12-18 15:18:58 +03:00
parent 33bd250f6c
commit 9246af6799
12 changed files with 184 additions and 28 deletions

View File

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

View File

@ -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,
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,
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,
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,
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)
{ {

View File

@ -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;
} }

View File

@ -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;
} }

View File

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

View File

@ -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:
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;
} }
; ;

View File

@ -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,
{ {
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,
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,
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,
} }
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);
} }

View File

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

View File

@ -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;
/* /*

View File

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

View File

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

View File

@ -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}';