Commit 5618ece8 authored by Tom Lane's avatar Tom Lane

Code review for array_fill patch: fix inadequate check for array size overflow

and bogus documentation (dimension arrays are int[] not anyarray).  Also the
errhint() messages seem to be really errdetail(), since there is nothing
heuristic about them.  Some other trivial cosmetic improvements.
parent 673a30fb
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.442 2008/07/18 03:32:51 tgl Exp $ --> <!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.443 2008/07/21 04:47:00 tgl Exp $ -->
<chapter id="functions"> <chapter id="functions">
<title>Functions and Operators</title> <title>Functions and Operators</title>
...@@ -9186,17 +9186,16 @@ SELECT NULLIF(value, '(none)') ... ...@@ -9186,17 +9186,16 @@ SELECT NULLIF(value, '(none)') ...
</sect2> </sect2>
</sect1> </sect1>
<sect1 id="functions-array"> <sect1 id="functions-array">
<title>Array Functions and Operators</title> <title>Array Functions and Operators</title>
<para> <para>
<xref linkend="array-operators-table"> shows the operators <xref linkend="array-operators-table"> shows the operators
available for <type>array</type> types. available for array types.
</para> </para>
<table id="array-operators-table"> <table id="array-operators-table">
<title><type>array</type> Operators</title> <title>Array Operators</title>
<tgroup cols="4"> <tgroup cols="4">
<thead> <thead>
<row> <row>
...@@ -9326,7 +9325,7 @@ SELECT NULLIF(value, '(none)') ... ...@@ -9326,7 +9325,7 @@ SELECT NULLIF(value, '(none)') ...
</para> </para>
<table id="array-functions-table"> <table id="array-functions-table">
<title><type>array</type> Functions</title> <title>Array Functions</title>
<tgroup cols="5"> <tgroup cols="5">
<thead> <thead>
<row> <row>
...@@ -9374,13 +9373,13 @@ SELECT NULLIF(value, '(none)') ... ...@@ -9374,13 +9373,13 @@ SELECT NULLIF(value, '(none)') ...
<row> <row>
<entry> <entry>
<literal> <literal>
<function>array_fill</function>(<type>anyelement</type>, <type>anyarray</type>, <function>array_fill</function>(<type>anyelement</type>, <type>int[]</type>,
<optional>, <type>anyarray</type></optional>) <optional>, <type>int[]</type></optional>)
</literal> </literal>
</entry> </entry>
<entry><type>anyarray</type></entry> <entry><type>anyarray</type></entry>
<entry>returns an array initialized with supplied value, <entry>returns an array initialized with supplied value and
dimensions, and lower bounds</entry> dimensions, optionally with lower bounds other than 1</entry>
<entry><literal>array_fill(7, ARRAY[3], ARRAY[2])</literal></entry> <entry><literal>array_fill(7, ARRAY[3], ARRAY[2])</literal></entry>
<entry><literal>[2:4]={7,7,7}</literal></entry> <entry><literal>[2:4]={7,7,7}</literal></entry>
</row> </row>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.146 2008/07/16 00:48:53 momjian Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.147 2008/07/21 04:47:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -97,9 +97,9 @@ static void array_insert_slice(ArrayType *destArray, ArrayType *origArray, ...@@ -97,9 +97,9 @@ static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
static int array_cmp(FunctionCallInfo fcinfo); static int array_cmp(FunctionCallInfo fcinfo);
static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbv, int nbytes, static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbv, int nbytes,
Oid elmtype, int dataoffset); Oid elmtype, int dataoffset);
static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value, static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs,
Oid elmtype, bool isnull, Datum value, bool isnull, Oid elmtype,
FunctionCallInfo fcinfo); FunctionCallInfo fcinfo);
/* /*
...@@ -4245,7 +4245,7 @@ typedef struct generate_subscripts_fctx ...@@ -4245,7 +4245,7 @@ typedef struct generate_subscripts_fctx
bool reverse; bool reverse;
} generate_subscripts_fctx; } generate_subscripts_fctx;
/* /*
* generate_subscripts(array anyarray, dim int [, reverse bool]) * generate_subscripts(array anyarray, dim int [, reverse bool])
* Returns all subscripts of the array for any dimension * Returns all subscripts of the array for any dimension
*/ */
...@@ -4335,7 +4335,7 @@ array_fill_with_lower_bounds(PG_FUNCTION_ARGS) ...@@ -4335,7 +4335,7 @@ array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
bool isnull; bool isnull;
if (PG_ARGISNULL(1) || PG_ARGISNULL(2)) if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("dimension array or low bound array cannot be NULL"))); errmsg("dimension array or low bound array cannot be NULL")));
...@@ -4353,11 +4353,11 @@ array_fill_with_lower_bounds(PG_FUNCTION_ARGS) ...@@ -4353,11 +4353,11 @@ array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
isnull = true; isnull = true;
} }
elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0); elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
if (!OidIsValid(elmtype)) if (!OidIsValid(elmtype))
elog(ERROR, "could not determine data type of input"); elog(ERROR, "could not determine data type of input");
result = array_fill_internal(dims, lbs, value, elmtype, isnull, fcinfo); result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
PG_RETURN_ARRAYTYPE_P(result); PG_RETURN_ARRAYTYPE_P(result);
} }
...@@ -4375,7 +4375,7 @@ array_fill(PG_FUNCTION_ARGS) ...@@ -4375,7 +4375,7 @@ array_fill(PG_FUNCTION_ARGS)
bool isnull; bool isnull;
if (PG_ARGISNULL(1)) if (PG_ARGISNULL(1))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("dimension array or low bound array cannot be NULL"))); errmsg("dimension array or low bound array cannot be NULL")));
...@@ -4392,17 +4392,17 @@ array_fill(PG_FUNCTION_ARGS) ...@@ -4392,17 +4392,17 @@ array_fill(PG_FUNCTION_ARGS)
isnull = true; isnull = true;
} }
elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0); elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
if (!OidIsValid(elmtype)) if (!OidIsValid(elmtype))
elog(ERROR, "could not determine data type of input"); elog(ERROR, "could not determine data type of input");
result = array_fill_internal(dims, NULL, value, elmtype, isnull, fcinfo); result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
PG_RETURN_ARRAYTYPE_P(result); PG_RETURN_ARRAYTYPE_P(result);
} }
static ArrayType * static ArrayType *
create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes, create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
Oid elmtype, int dataoffset) Oid elmtype, int dataoffset)
{ {
ArrayType *result; ArrayType *result;
...@@ -4418,9 +4418,9 @@ create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes, ...@@ -4418,9 +4418,9 @@ create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
} }
static ArrayType * static ArrayType *
array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value, array_fill_internal(ArrayType *dims, ArrayType *lbs,
Oid elmtype, bool isnull, Datum value, bool isnull, Oid elmtype,
FunctionCallInfo fcinfo) FunctionCallInfo fcinfo)
{ {
ArrayType *result; ArrayType *result;
int *dimv; int *dimv;
...@@ -4428,34 +4428,34 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value, ...@@ -4428,34 +4428,34 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value,
int ndims; int ndims;
int nitems; int nitems;
int deflbs[MAXDIM]; int deflbs[MAXDIM];
int16 elmlen; int16 elmlen;
bool elmbyval; bool elmbyval;
char elmalign; char elmalign;
ArrayMetaState *my_extra; ArrayMetaState *my_extra;
/* /*
* Params checks * Params checks
*/ */
if (ARR_NDIM(dims) != 1) if (ARR_NDIM(dims) != 1)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array subscripts"), errmsg("wrong number of array subscripts"),
errhint("Dimension array must be one dimensional."))); errdetail("Dimension array must be one dimensional.")));
if (ARR_LBOUND(dims)[0] != 1) if (ARR_LBOUND(dims)[0] != 1)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong range of array_subscripts"), errmsg("wrong range of array_subscripts"),
errhint("Lower bound of dimension array must be one."))); errdetail("Lower bound of dimension array must be one.")));
if (ARR_HASNULL(dims)) if (ARR_HASNULL(dims))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("dimension values cannot be null"))); errmsg("dimension values cannot be null")));
dimv = (int *) ARR_DATA_PTR(dims); dimv = (int *) ARR_DATA_PTR(dims);
ndims = ARR_DIMS(dims)[0]; ndims = ARR_DIMS(dims)[0];
if (ndims < 0) /* we do allow zero-dimension arrays */ if (ndims < 0) /* we do allow zero-dimension arrays */
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
...@@ -4465,23 +4465,23 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value, ...@@ -4465,23 +4465,23 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
ndims, MAXDIM))); ndims, MAXDIM)));
if (lbs != NULL) if (lbs != NULL)
{ {
if (ARR_NDIM(lbs) != 1) if (ARR_NDIM(lbs) != 1)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array subscripts"), errmsg("wrong number of array subscripts"),
errhint("Dimension array must be one dimensional."))); errdetail("Dimension array must be one dimensional.")));
if (ARR_LBOUND(lbs)[0] != 1) if (ARR_LBOUND(lbs)[0] != 1)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong range of array_subscripts"), errmsg("wrong range of array_subscripts"),
errhint("Lower bound of dimension array must be one."))); errdetail("Lower bound of dimension array must be one.")));
if (ARR_HASNULL(lbs)) if (ARR_HASNULL(lbs))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("dimension values cannot be null"))); errmsg("dimension values cannot be null")));
...@@ -4489,14 +4489,14 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value, ...@@ -4489,14 +4489,14 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value,
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array_subscripts"), errmsg("wrong number of array_subscripts"),
errhint("Low bound array has different size than dimensions array."))); errdetail("Low bound array has different size than dimensions array.")));
lbsv = (int *) ARR_DATA_PTR(lbs); lbsv = (int *) ARR_DATA_PTR(lbs);
} }
else else
{ {
int i; int i;
for (i = 0; i < MAXDIM; i++) for (i = 0; i < MAXDIM; i++)
deflbs[i] = 1; deflbs[i] = 1;
...@@ -4506,9 +4506,8 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value, ...@@ -4506,9 +4506,8 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value,
/* fast track for empty array */ /* fast track for empty array */
if (ndims == 0) if (ndims == 0)
return construct_empty_array(elmtype); return construct_empty_array(elmtype);
nitems = ArrayGetNItems(ndims, dimv);
nitems = ArrayGetNItems(ndims, dimv);
/* /*
* We arrange to look up info about element type only once per series of * We arrange to look up info about element type only once per series of
...@@ -4543,7 +4542,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value, ...@@ -4543,7 +4542,7 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value,
int i; int i;
char *p; char *p;
int nbytes; int nbytes;
Datum aux_value = value; int totbytes;
/* make sure data is not toasted */ /* make sure data is not toasted */
if (elmlen == -1) if (elmlen == -1)
...@@ -4551,40 +4550,44 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value, ...@@ -4551,40 +4550,44 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum value,
nbytes = att_addlength_datum(0, elmlen, value); nbytes = att_addlength_datum(0, elmlen, value);
nbytes = att_align_nominal(nbytes, elmalign); nbytes = att_align_nominal(nbytes, elmalign);
Assert(nbytes > 0);
nbytes *= nitems; totbytes = nbytes * nitems;
/* check for overflow of total request */
if (!AllocSizeIsValid(nbytes)) /* check for overflow of multiplication or total request */
if (totbytes / nbytes != nitems ||
!AllocSizeIsValid(totbytes))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%d)", errmsg("array size exceeds the maximum allowed (%d)",
(int) MaxAllocSize))); (int) MaxAllocSize)));
nbytes += ARR_OVERHEAD_NONULLS(ndims); /*
result = create_array_envelope(ndims, dimv, lbsv, nbytes, * This addition can't overflow, but it might cause us to go past
elmtype, 0); * MaxAllocSize. We leave it to palloc to complain in that case.
*/
totbytes += ARR_OVERHEAD_NONULLS(ndims);
result = create_array_envelope(ndims, dimv, lbsv, totbytes,
elmtype, 0);
p = ARR_DATA_PTR(result); p = ARR_DATA_PTR(result);
for (i = 0; i < nitems; i++) for (i = 0; i < nitems; i++)
p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p); p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
/* cleaning up detoasted copies of datum */
if (aux_value != value)
pfree((Pointer) value);
} }
else else
{ {
int nbytes; int nbytes;
int dataoffset; int dataoffset;
bits8 *bitmap;
dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems); dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
nbytes = dataoffset; nbytes = dataoffset;
result = create_array_envelope(ndims, dimv, lbsv, nbytes, result = create_array_envelope(ndims, dimv, lbsv, nbytes,
elmtype, dataoffset); elmtype, dataoffset);
bitmap = ARR_NULLBITMAP(result);
MemSet(bitmap, 0, (nitems + 7) / 8); /* create_array_envelope already zeroed the bitmap, so we're done */
} }
return result; return result;
} }
...@@ -988,6 +988,6 @@ select array_fill(1, array[2,2], null); ...@@ -988,6 +988,6 @@ select array_fill(1, array[2,2], null);
ERROR: dimension array or low bound array cannot be NULL ERROR: dimension array or low bound array cannot be NULL
select array_fill(1, array[3,3], array[1,1,1]); select array_fill(1, array[3,3], array[1,1,1]);
ERROR: wrong number of array_subscripts ERROR: wrong number of array_subscripts
HINT: Low bound array has different size than dimensions array. DETAIL: Low bound array has different size than dimensions array.
select array_fill(1, array[1,2,null]); select array_fill(1, array[1,2,null]);
ERROR: dimension values cannot be null ERROR: dimension values cannot be null
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