aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2015-12-22 21:05:16 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2015-12-22 21:05:29 -0500
commit6efbded6e4672c597a6f0dc0f09263e7db7369ff (patch)
tree2ee78e250058cdbc4021f24c6ae0bf4e92098c4b /src/backend
parent0ba3f3bc65f1176250b942e14fd9e4975a5d3913 (diff)
downloadpostgresql-6efbded6e4672c597a6f0dc0f09263e7db7369ff.tar.gz
postgresql-6efbded6e4672c597a6f0dc0f09263e7db7369ff.zip
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 9246af6799819847faa33baf441251003acbb8fe) YUriy Zhuravlev
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/executor/execQual.c22
-rw-r--r--src/backend/nodes/copyfuncs.c1
-rw-r--r--src/backend/nodes/equalfuncs.c1
-rw-r--r--src/backend/nodes/outfuncs.c1
-rw-r--r--src/backend/parser/gram.y11
-rw-r--r--src/backend/parser/parse_node.c59
-rw-r--r--src/backend/parser/parse_target.c2
-rw-r--r--src/backend/utils/adt/arrayfuncs.c33
-rw-r--r--src/backend/utils/adt/ruleutils.c2
9 files changed, 106 insertions, 26 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 29f058ce5cb..d4dc2dcd21a 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -271,6 +271,8 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
j = 0;
IntArray upper,
lower;
+ bool upperProvided[MAXDIM],
+ lowerProvided[MAXDIM];
int *lIndex;
array_source = ExecEvalExpr(astate->refexpr,
@@ -300,6 +302,15 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
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,
econtext,
&eisnull,
@@ -328,6 +339,14 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
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,
econtext,
&eisnull,
@@ -398,6 +417,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
econtext->caseValue_datum =
array_get_slice(array_source, i,
upper.indx, lower.indx,
+ upperProvided, lowerProvided,
astate->refattrlength,
astate->refelemlength,
astate->refelembyval,
@@ -456,6 +476,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
else
return array_set_slice(array_source, i,
upper.indx, lower.indx,
+ upperProvided, lowerProvided,
sourceData,
eisnull,
astate->refattrlength,
@@ -475,6 +496,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
else
return array_get_slice(array_source, i,
upper.indx, lower.indx,
+ upperProvided, lowerProvided,
astate->refattrlength,
astate->refelemlength,
astate->refelembyval,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index ba04b7227ca..4cf14b6f71f 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2401,6 +2401,7 @@ _copyAIndices(const A_Indices *from)
{
A_Indices *newnode = makeNode(A_Indices);
+ COPY_SCALAR_FIELD(is_slice);
COPY_NODE_FIELD(lidx);
COPY_NODE_FIELD(uidx);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 356fcafeb49..a13d83181b9 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2151,6 +2151,7 @@ _equalAStar(const A_Star *a, const A_Star *b)
static bool
_equalAIndices(const A_Indices *a, const A_Indices *b)
{
+ COMPARE_SCALAR_FIELD(is_slice);
COMPARE_NODE_FIELD(lidx);
COMPARE_NODE_FIELD(uidx);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 63fae82aba0..fe2c643c348 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2763,6 +2763,7 @@ _outA_Indices(StringInfo str, const A_Indices *node)
{
WRITE_NODE_TYPE("A_INDICES");
+ WRITE_BOOL_FIELD(is_slice);
WRITE_NODE_FIELD(lidx);
WRITE_NODE_FIELD(uidx);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c4bed8a5ef7..223ef175dee 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -434,7 +434,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> columnDef columnOptions
%type <defelt> def_elem reloption_elem old_aggr_elem operator_def_elem
%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
ExclusionWhereClause
%type <list> rowsfrom_item rowsfrom_list opt_col_def_list
@@ -13191,19 +13191,26 @@ indirection_el:
| '[' a_expr ']'
{
A_Indices *ai = makeNode(A_Indices);
+ ai->is_slice = false;
ai->lidx = NULL;
ai->uidx = $2;
$$ = (Node *) ai;
}
- | '[' a_expr ':' a_expr ']'
+ | '[' opt_slice_bound ':' opt_slice_bound ']'
{
A_Indices *ai = makeNode(A_Indices);
+ ai->is_slice = true;
ai->lidx = $2;
ai->uidx = $4;
$$ = (Node *) ai;
}
;
+opt_slice_bound:
+ a_expr { $$ = $1; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
indirection:
indirection_el { $$ = list_make1($1); }
| indirection indirection_el { $$ = lappend($1, $2); }
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 4130cbff5ed..591a1f3a681 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -311,18 +311,18 @@ transformArraySubscripts(ParseState *pstate,
elementType = transformArrayType(&arrayType, &arrayTypMod);
/*
- * A list containing only single subscripts 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
- * single subscript. We have to prescan the indirection list to see if
- * there are any double subscripts.
+ * A list containing only simple subscripts refers to a single array
+ * element. If any of the items are slice specifiers (lower:upper), then
+ * the subscript expression means an array slice operation. In this case,
+ * we convert any non-slice items to slices by treating the single
+ * subscript as the upper bound and supplying an assumed lower bound of 1.
+ * We have to prescan the list to see if there are any slice items.
*/
foreach(idx, indirection)
{
A_Indices *ai = (A_Indices *) lfirst(idx);
- if (ai->lidx != NULL)
+ if (ai->is_slice)
{
isSlice = true;
break;
@@ -356,7 +356,7 @@ transformArraySubscripts(ParseState *pstate,
errmsg("array subscript must have type integer"),
parser_errposition(pstate, exprLocation(ai->lidx))));
}
- else
+ else if (!ai->is_slice)
{
/* Make a constant 1 */
subexpr = (Node *) makeConst(INT4OID,
@@ -367,21 +367,38 @@ transformArraySubscripts(ParseState *pstate,
false,
true); /* pass by value */
}
+ else
+ {
+ /* Slice with omitted lower bound, put NULL into the list */
+ subexpr = NULL;
+ }
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))));
+ else
+ Assert(ai->lidx == NULL && !ai->is_slice);
+
+ if (ai->uidx)
+ {
+ 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
+ {
+ /* Slice with omitted upper bound, put NULL into the list */
+ Assert(isSlice && ai->is_slice);
+ subexpr = NULL;
+ }
upperIndexpr = lappend(upperIndexpr, subexpr);
}
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 1b3fcd629c1..8c2c38dbe67 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)->is_slice)
isSlice = true;
}
else if (IsA(n, A_Star))
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 67c9b357c85..359fb1462bc 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -1995,6 +1995,8 @@ array_get_element_expanded(Datum arraydatum,
* nSubscripts: number of subscripts supplied (must be same for upper/lower)
* upperIndx[]: the upper 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
* elmlen: pg_type.typlen 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,
* Outputs:
* 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
* lowerIndx[] and upperIndx[]. These are generally just temporaries.
*/
@@ -2011,6 +2016,8 @@ array_get_slice(Datum arraydatum,
int nSubscripts,
int *upperIndx,
int *lowerIndx,
+ bool *upperProvided,
+ bool *lowerProvided,
int arraytyplen,
int elmlen,
bool elmbyval,
@@ -2081,9 +2088,9 @@ array_get_slice(Datum arraydatum,
for (i = 0; i < nSubscripts; i++)
{
- if (lowerIndx[i] < lb[i])
+ if (!lowerProvided[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;
if (lowerIndx[i] > upperIndx[i])
return PointerGetDatum(construct_empty_array(elemtype));
@@ -2708,6 +2715,8 @@ array_set_element_expanded(Datum arraydatum,
* nSubscripts: number of subscripts supplied (must be same for upper/lower)
* upperIndx[]: the upper 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
* isNull: indicates whether srcArrayDatum is NULL
* arraytyplen: pg_type.typlen for the array type
@@ -2719,6 +2728,9 @@ array_set_element_expanded(Datum arraydatum,
* A new array is returned, just like the old except for the
* 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
* by assigning to positions outside the existing subscript range; any
* positions between the existing elements and the new ones are set to NULLs.
@@ -2735,6 +2747,8 @@ array_set_slice(Datum arraydatum,
int nSubscripts,
int *upperIndx,
int *lowerIndx,
+ bool *upperProvided,
+ bool *lowerProvided,
Datum srcArrayDatum,
bool isNull,
int arraytyplen,
@@ -2806,6 +2820,13 @@ array_set_slice(Datum arraydatum,
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];
lb[i] = lowerIndx[i];
}
@@ -2839,6 +2860,10 @@ array_set_slice(Datum arraydatum,
if (ndim == 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])
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
@@ -2867,6 +2892,10 @@ array_set_slice(Datum arraydatum,
*/
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])
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 0ab839dc736..280808ae4f9 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -9372,10 +9372,12 @@ printSubscripts(ArrayRef *aref, deparse_context *context)
appendStringInfoChar(buf, '[');
if (lowlist_item)
{
+ /* If subexpression is NULL, get_rule_expr prints nothing */
get_rule_expr((Node *) lfirst(lowlist_item), context, false);
appendStringInfoChar(buf, ':');
lowlist_item = lnext(lowlist_item);
}
+ /* If subexpression is NULL, get_rule_expr prints nothing */
get_rule_expr((Node *) lfirst(uplist_item), context, false);
appendStringInfoChar(buf, ']');
}