Commit 6efbded6 authored by Tom Lane's avatar Tom Lane

Allow omitting one or both boundaries in an array slice specifier.

Omitted boundaries represent the upper or lower limit of the corresponding
array subscript.  This allows simpler specification of many common
use-cases.

(Revised version of commit 9246af67)

YUriy Zhuravlev
parent 0ba3f3bc
...@@ -276,6 +276,29 @@ SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill'; ...@@ -276,6 +276,29 @@ SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
for all dimensions, e.g., <literal>[1:2][1:1]</>, not <literal>[2][1:1]</>. for all dimensions, e.g., <literal>[1:2][1:1]</>, not <literal>[2][1:1]</>.
</para> </para>
<para>
It is possible to omit the <replaceable>lower-bound</replaceable> and/or
<replaceable>upper-bound</replaceable> of a slice specifier; the missing
bound is replaced by the lower or upper limit of the array's subscripts.
For example:
<programlisting>
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)
</programlisting>
</para>
<para> <para>
An array subscript expression will return null if either the array itself or 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 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}' ...@@ -391,6 +414,10 @@ UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
WHERE name = 'Carol'; WHERE name = 'Carol';
</programlisting> </programlisting>
The slice syntaxes with omitted <replaceable>lower-bound</replaceable> and/or
<replaceable>upper-bound</replaceable> 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).
</para> </para>
<para> <para>
......
...@@ -271,6 +271,8 @@ ExecEvalArrayRef(ArrayRefExprState *astate, ...@@ -271,6 +271,8 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
j = 0; j = 0;
IntArray upper, IntArray upper,
lower; lower;
bool upperProvided[MAXDIM],
lowerProvided[MAXDIM];
int *lIndex; int *lIndex;
array_source = ExecEvalExpr(astate->refexpr, array_source = ExecEvalExpr(astate->refexpr,
...@@ -300,6 +302,15 @@ ExecEvalArrayRef(ArrayRefExprState *astate, ...@@ -300,6 +302,15 @@ 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)));
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, upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
econtext, econtext,
&eisnull, &eisnull,
...@@ -328,6 +339,14 @@ ExecEvalArrayRef(ArrayRefExprState *astate, ...@@ -328,6 +339,14 @@ 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)));
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, lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
econtext, econtext,
&eisnull, &eisnull,
...@@ -398,6 +417,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate, ...@@ -398,6 +417,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
econtext->caseValue_datum = econtext->caseValue_datum =
array_get_slice(array_source, i, array_get_slice(array_source, i,
upper.indx, lower.indx, upper.indx, lower.indx,
upperProvided, lowerProvided,
astate->refattrlength, astate->refattrlength,
astate->refelemlength, astate->refelemlength,
astate->refelembyval, astate->refelembyval,
...@@ -456,6 +476,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate, ...@@ -456,6 +476,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
else else
return array_set_slice(array_source, i, return array_set_slice(array_source, i,
upper.indx, lower.indx, upper.indx, lower.indx,
upperProvided, lowerProvided,
sourceData, sourceData,
eisnull, eisnull,
astate->refattrlength, astate->refattrlength,
...@@ -475,6 +496,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate, ...@@ -475,6 +496,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
else else
return array_get_slice(array_source, i, return array_get_slice(array_source, i,
upper.indx, lower.indx, upper.indx, lower.indx,
upperProvided, lowerProvided,
astate->refattrlength, astate->refattrlength,
astate->refelemlength, astate->refelemlength,
astate->refelembyval, astate->refelembyval,
......
...@@ -2401,6 +2401,7 @@ _copyAIndices(const A_Indices *from) ...@@ -2401,6 +2401,7 @@ _copyAIndices(const A_Indices *from)
{ {
A_Indices *newnode = makeNode(A_Indices); A_Indices *newnode = makeNode(A_Indices);
COPY_SCALAR_FIELD(is_slice);
COPY_NODE_FIELD(lidx); COPY_NODE_FIELD(lidx);
COPY_NODE_FIELD(uidx); COPY_NODE_FIELD(uidx);
......
...@@ -2151,6 +2151,7 @@ _equalAStar(const A_Star *a, const A_Star *b) ...@@ -2151,6 +2151,7 @@ _equalAStar(const A_Star *a, const A_Star *b)
static bool static bool
_equalAIndices(const A_Indices *a, const A_Indices *b) _equalAIndices(const A_Indices *a, const A_Indices *b)
{ {
COMPARE_SCALAR_FIELD(is_slice);
COMPARE_NODE_FIELD(lidx); COMPARE_NODE_FIELD(lidx);
COMPARE_NODE_FIELD(uidx); COMPARE_NODE_FIELD(uidx);
......
...@@ -2763,6 +2763,7 @@ _outA_Indices(StringInfo str, const A_Indices *node) ...@@ -2763,6 +2763,7 @@ _outA_Indices(StringInfo str, const A_Indices *node)
{ {
WRITE_NODE_TYPE("A_INDICES"); WRITE_NODE_TYPE("A_INDICES");
WRITE_BOOL_FIELD(is_slice);
WRITE_NODE_FIELD(lidx); WRITE_NODE_FIELD(lidx);
WRITE_NODE_FIELD(uidx); WRITE_NODE_FIELD(uidx);
} }
......
...@@ -434,7 +434,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); ...@@ -434,7 +434,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> columnDef columnOptions %type <node> columnDef columnOptions
%type <defelt> def_elem reloption_elem old_aggr_elem operator_def_elem %type <defelt> def_elem reloption_elem old_aggr_elem operator_def_elem
%type <node> def_arg columnElem where_clause where_or_current_clause %type <node> 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 columnref in_expr having_clause func_table array_expr
ExclusionWhereClause ExclusionWhereClause
%type <list> rowsfrom_item rowsfrom_list opt_col_def_list %type <list> rowsfrom_item rowsfrom_list opt_col_def_list
...@@ -13191,19 +13191,26 @@ indirection_el: ...@@ -13191,19 +13191,26 @@ indirection_el:
| '[' a_expr ']' | '[' a_expr ']'
{ {
A_Indices *ai = makeNode(A_Indices); A_Indices *ai = makeNode(A_Indices);
ai->is_slice = false;
ai->lidx = NULL; ai->lidx = NULL;
ai->uidx = $2; ai->uidx = $2;
$$ = (Node *) ai; $$ = (Node *) ai;
} }
| '[' a_expr ':' a_expr ']' | '[' opt_slice_bound ':' opt_slice_bound ']'
{ {
A_Indices *ai = makeNode(A_Indices); A_Indices *ai = makeNode(A_Indices);
ai->is_slice = true;
ai->lidx = $2; ai->lidx = $2;
ai->uidx = $4; ai->uidx = $4;
$$ = (Node *) ai; $$ = (Node *) ai;
} }
; ;
opt_slice_bound:
a_expr { $$ = $1; }
| /*EMPTY*/ { $$ = NULL; }
;
indirection: indirection:
indirection_el { $$ = list_make1($1); } indirection_el { $$ = list_make1($1); }
| indirection indirection_el { $$ = lappend($1, $2); } | indirection indirection_el { $$ = lappend($1, $2); }
......
...@@ -311,18 +311,18 @@ transformArraySubscripts(ParseState *pstate, ...@@ -311,18 +311,18 @@ 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 simple subscripts refers to a single array
* element. If any of the items are double subscripts (lower:upper), then * element. If any of the items are slice specifiers (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 convert any non-slice items to slices by treating the single
* single subscript. We have to prescan the indirection list to see if * subscript as the upper bound and supplying an assumed lower bound of 1.
* there are any double subscripts. * We have to prescan the list to see if there are any slice items.
*/ */
foreach(idx, indirection) foreach(idx, indirection)
{ {
A_Indices *ai = (A_Indices *) lfirst(idx); A_Indices *ai = (A_Indices *) lfirst(idx);
if (ai->lidx != NULL) if (ai->is_slice)
{ {
isSlice = true; isSlice = true;
break; break;
...@@ -356,7 +356,7 @@ transformArraySubscripts(ParseState *pstate, ...@@ -356,7 +356,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->is_slice)
{ {
/* Make a constant 1 */ /* Make a constant 1 */
subexpr = (Node *) makeConst(INT4OID, subexpr = (Node *) makeConst(INT4OID,
...@@ -367,8 +367,18 @@ transformArraySubscripts(ParseState *pstate, ...@@ -367,8 +367,18 @@ transformArraySubscripts(ParseState *pstate,
false, false,
true); /* pass by value */ true); /* pass by value */
} }
else
{
/* Slice with omitted lower bound, put NULL into the list */
subexpr = NULL;
}
lowerIndexpr = lappend(lowerIndexpr, subexpr); lowerIndexpr = lappend(lowerIndexpr, subexpr);
} }
else
Assert(ai->lidx == NULL && !ai->is_slice);
if (ai->uidx)
{
subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind); subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
/* If it's not int4 already, try to coerce */ /* If it's not int4 already, try to coerce */
subexpr = coerce_to_target_type(pstate, subexpr = coerce_to_target_type(pstate,
...@@ -382,6 +392,13 @@ transformArraySubscripts(ParseState *pstate, ...@@ -382,6 +392,13 @@ transformArraySubscripts(ParseState *pstate,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("array subscript must have type integer"), errmsg("array subscript must have type integer"),
parser_errposition(pstate, exprLocation(ai->uidx)))); 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); 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)->is_slice)
isSlice = true; isSlice = true;
} }
else if (IsA(n, A_Star)) else if (IsA(n, A_Star))
......
...@@ -1995,6 +1995,8 @@ array_get_element_expanded(Datum arraydatum, ...@@ -1995,6 +1995,8 @@ array_get_element_expanded(Datum arraydatum,
* nSubscripts: number of subscripts supplied (must be same for upper/lower) * nSubscripts: number of subscripts supplied (must be same for upper/lower)
* upperIndx[]: the upper subscript values * upperIndx[]: the upper subscript values
* lowerIndx[]: the lower 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 * arraytyplen: pg_type.typlen for the array type
* elmlen: pg_type.typlen for the array's element type * elmlen: pg_type.typlen for the array's element type
* elmbyval: pg_type.typbyval 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, ...@@ -2003,6 +2005,9 @@ array_get_element_expanded(Datum arraydatum,
* Outputs: * Outputs:
* The return value is the new array Datum (it's never NULL) * 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 * NOTE: we assume it is OK to scribble on the provided subscript arrays
* lowerIndx[] and upperIndx[]. These are generally just temporaries. * lowerIndx[] and upperIndx[]. These are generally just temporaries.
*/ */
...@@ -2011,6 +2016,8 @@ array_get_slice(Datum arraydatum, ...@@ -2011,6 +2016,8 @@ array_get_slice(Datum arraydatum,
int nSubscripts, int nSubscripts,
int *upperIndx, int *upperIndx,
int *lowerIndx, int *lowerIndx,
bool *upperProvided,
bool *lowerProvided,
int arraytyplen, int arraytyplen,
int elmlen, int elmlen,
bool elmbyval, bool elmbyval,
...@@ -2081,9 +2088,9 @@ array_get_slice(Datum arraydatum, ...@@ -2081,9 +2088,9 @@ array_get_slice(Datum arraydatum,
for (i = 0; i < nSubscripts; i++) for (i = 0; i < nSubscripts; i++)
{ {
if (lowerIndx[i] < lb[i]) if (!lowerProvided[i] || lowerIndx[i] < lb[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; upperIndx[i] = dim[i] + lb[i] - 1;
if (lowerIndx[i] > upperIndx[i]) if (lowerIndx[i] > upperIndx[i])
return PointerGetDatum(construct_empty_array(elemtype)); return PointerGetDatum(construct_empty_array(elemtype));
...@@ -2708,6 +2715,8 @@ array_set_element_expanded(Datum arraydatum, ...@@ -2708,6 +2715,8 @@ array_set_element_expanded(Datum arraydatum,
* nSubscripts: number of subscripts supplied (must be same for upper/lower) * nSubscripts: number of subscripts supplied (must be same for upper/lower)
* upperIndx[]: the upper subscript values * upperIndx[]: the upper subscript values
* lowerIndx[]: the lower 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 * srcArrayDatum: the source for the inserted values
* isNull: indicates whether srcArrayDatum is NULL * isNull: indicates whether srcArrayDatum is NULL
* arraytyplen: pg_type.typlen for the array type * arraytyplen: pg_type.typlen for the array type
...@@ -2719,6 +2728,9 @@ array_set_element_expanded(Datum arraydatum, ...@@ -2719,6 +2728,9 @@ array_set_element_expanded(Datum arraydatum,
* A new array is returned, just like the old except for the * A new array is returned, just like the old except for the
* modified range. The original array object is not changed. * 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 * For one-dimensional arrays only, we allow the array to be extended
* by assigning to positions outside the existing subscript range; any * by assigning to positions outside the existing subscript range; any
* positions between the existing elements and the new ones are set to NULLs. * positions between the existing elements and the new ones are set to NULLs.
...@@ -2735,6 +2747,8 @@ array_set_slice(Datum arraydatum, ...@@ -2735,6 +2747,8 @@ array_set_slice(Datum arraydatum,
int nSubscripts, int nSubscripts,
int *upperIndx, int *upperIndx,
int *lowerIndx, int *lowerIndx,
bool *upperProvided,
bool *lowerProvided,
Datum srcArrayDatum, Datum srcArrayDatum,
bool isNull, bool isNull,
int arraytyplen, int arraytyplen,
...@@ -2806,6 +2820,13 @@ array_set_slice(Datum arraydatum, ...@@ -2806,6 +2820,13 @@ array_set_slice(Datum arraydatum,
for (i = 0; i < nSubscripts; i++) 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]; dim[i] = 1 + upperIndx[i] - lowerIndx[i];
lb[i] = lowerIndx[i]; lb[i] = lowerIndx[i];
} }
...@@ -2839,6 +2860,10 @@ array_set_slice(Datum arraydatum, ...@@ -2839,6 +2860,10 @@ array_set_slice(Datum arraydatum,
if (ndim == 1) if (ndim == 1)
{ {
Assert(nSubscripts == 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]) if (lowerIndx[0] > upperIndx[0])
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
...@@ -2867,6 +2892,10 @@ array_set_slice(Datum arraydatum, ...@@ -2867,6 +2892,10 @@ array_set_slice(Datum arraydatum,
*/ */
for (i = 0; i < nSubscripts; i++) 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]) if (lowerIndx[i] > upperIndx[i])
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
......
...@@ -9372,10 +9372,12 @@ printSubscripts(ArrayRef *aref, deparse_context *context) ...@@ -9372,10 +9372,12 @@ printSubscripts(ArrayRef *aref, deparse_context *context)
appendStringInfoChar(buf, '['); appendStringInfoChar(buf, '[');
if (lowlist_item) if (lowlist_item)
{ {
/* If subexpression is NULL, get_rule_expr prints nothing */
get_rule_expr((Node *) lfirst(lowlist_item), context, false); get_rule_expr((Node *) lfirst(lowlist_item), context, false);
appendStringInfoChar(buf, ':'); appendStringInfoChar(buf, ':');
lowlist_item = lnext(lowlist_item); lowlist_item = lnext(lowlist_item);
} }
/* If subexpression is NULL, get_rule_expr prints nothing */
get_rule_expr((Node *) lfirst(uplist_item), context, false); get_rule_expr((Node *) lfirst(uplist_item), context, false);
appendStringInfoChar(buf, ']'); appendStringInfoChar(buf, ']');
} }
......
...@@ -157,9 +157,10 @@ typedef struct Query ...@@ -157,9 +157,10 @@ typedef struct Query
List *constraintDeps; /* a list of pg_constraint OIDs that the query List *constraintDeps; /* a list of pg_constraint OIDs that the query
* depends on to be semantically valid */ * depends on to be semantically valid */
List *withCheckOptions; /* a list of WithCheckOption's, which are List *withCheckOptions; /* a list of WithCheckOption's, which
* only added during rewrite and therefore * are only added during rewrite and
* are not written out as part of Query. */ * therefore are not written out as
* part of Query. */
} Query; } Query;
...@@ -351,13 +352,17 @@ typedef struct A_Star ...@@ -351,13 +352,17 @@ typedef struct A_Star
} 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 typedef struct A_Indices
{ {
NodeTag type; NodeTag type;
Node *lidx; /* NULL if it's a single subscript */ bool is_slice; /* true if slice (i.e., colon present) */
Node *uidx; Node *lidx; /* slice lower bound, if any */
Node *uidx; /* subscript, or slice upper bound if any */
} A_Indices; } A_Indices;
/* /*
......
...@@ -341,6 +341,9 @@ typedef struct WindowFunc ...@@ -341,6 +341,9 @@ typedef struct WindowFunc
* reflowerindexpr must be the same length as refupperindexpr when it * reflowerindexpr must be the same length as refupperindexpr when it
* is not NIL. * 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 * 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 * element; but it is the array type when doing subarray fetch or either
* type of store. * type of store.
...@@ -360,7 +363,7 @@ typedef struct ArrayRef ...@@ -360,7 +363,7 @@ typedef struct ArrayRef
List *refupperindexpr;/* expressions that evaluate to upper array List *refupperindexpr;/* expressions that evaluate to upper array
* indexes */ * indexes */
List *reflowerindexpr;/* expressions that evaluate to lower array 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 Expr *refexpr; /* the expression that evaluates to an array
* value */ * value */
Expr *refassgnexpr; /* expression for the source value, or NULL if Expr *refassgnexpr; /* expression for the source value, or NULL if
......
...@@ -377,9 +377,11 @@ extern Datum array_set_element(Datum arraydatum, int nSubscripts, int *indx, ...@@ -377,9 +377,11 @@ extern Datum array_set_element(Datum arraydatum, int nSubscripts, int *indx,
int arraytyplen, int elmlen, bool elmbyval, char elmalign); int arraytyplen, int elmlen, bool elmbyval, char elmalign);
extern Datum array_get_slice(Datum arraydatum, int nSubscripts, extern Datum array_get_slice(Datum arraydatum, int nSubscripts,
int *upperIndx, int *lowerIndx, int *upperIndx, int *lowerIndx,
bool *upperProvided, bool *lowerProvided,
int arraytyplen, int elmlen, bool elmbyval, char elmalign); int arraytyplen, int elmlen, bool elmbyval, char elmalign);
extern Datum array_set_slice(Datum arraydatum, int nSubscripts, extern Datum array_set_slice(Datum arraydatum, int nSubscripts,
int *upperIndx, int *lowerIndx, int *upperIndx, int *lowerIndx,
bool *upperProvided, bool *lowerProvided,
Datum srcArrayDatum, bool isNull, Datum srcArrayDatum, bool isNull,
int arraytyplen, int elmlen, bool elmbyval, char elmalign); int arraytyplen, int elmlen, bool elmbyval, char elmalign);
......
...@@ -125,6 +125,16 @@ SELECT a[1:3], ...@@ -125,6 +125,16 @@ SELECT a[1:3],
{16,25,23} | {} | {foobar,new_word} | {{elt2}} {16,25,23} | {} | {foobar,new_word} | {{elt2}}
(3 rows) (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}'); INSERT INTO arrtest(a) VALUES('{1,null,3}');
SELECT a FROM arrtest; SELECT a FROM arrtest;
a a
...@@ -152,6 +162,107 @@ SELECT a,b,c FROM arrtest; ...@@ -152,6 +162,107 @@ SELECT a,b,c FROM arrtest;
[4:4]={NULL} | {3,4} | {foo,new_word} [4:4]={NULL} | {3,4} | {foo,new_word}
(3 rows) (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 -- test array extension
-- --
......
...@@ -86,6 +86,10 @@ SELECT a[1:3], ...@@ -86,6 +86,10 @@ SELECT a[1:3],
d[1:1][2:2] d[1:1][2:2]
FROM arrtest; FROM arrtest;
SELECT b[1:1][2][2],
d[1:1][2]
FROM arrtest;
INSERT INTO arrtest(a) VALUES('{1,null,3}'); INSERT INTO arrtest(a) VALUES('{1,null,3}');
SELECT a FROM arrtest; SELECT a FROM arrtest;
UPDATE arrtest SET a[4] = NULL WHERE a[2] IS NULL; UPDATE arrtest SET a[4] = NULL WHERE a[2] IS NULL;
...@@ -93,6 +97,43 @@ SELECT a FROM arrtest 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; DELETE FROM arrtest WHERE a[2] IS NULL AND b IS NULL;
SELECT a,b,c FROM arrtest; 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 -- test array extension
-- --
......
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