-<!-- $PostgreSQL: pgsql/doc/src/sgml/array.sgml,v 1.51 2006/05/09 23:12:54 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/array.sgml,v 1.52 2006/09/29 21:22:21 tgl Exp $ -->
<sect1 id="arrays">
<title>Arrays</title>
</para>
<para>
- A stored array value can be enlarged by assigning to an element adjacent to
- those already present, or by assigning to a slice that is adjacent
- to or overlaps the data already present. For example, if array
- <literal>myarray</> currently has 4 elements, it will have five
- elements after an update that assigns to <literal>myarray[5]</>.
+ A stored array value can be enlarged by assigning to element(s) not already
+ present. Any positions between those previously present and the newly
+ assigned element(s) will be filled with nulls. For example, if array
+ <literal>myarray</> currently has 4 elements, it will have six
+ elements after an update that assigns to <literal>myarray[6]</>,
+ and <literal>myarray[5]</> will contain a null.
Currently, enlargement in this fashion is only allowed for one-dimensional
arrays, not multidimensional arrays.
</para>
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.131 2006/09/10 20:14:20 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.132 2006/09/29 21:22:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* modified entry. The original array object is not changed.
*
* For one-dimensional arrays only, we allow the array to be extended
- * by assigning to the position one above or one below the existing range.
- * (XXX we could be more flexible: perhaps allow NULL fill?)
+ * by assigning to a position outside the existing subscript range; any
+ * positions between the existing elements and the new one are set to NULLs.
+ * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
*
* NOTE: For assignments, we throw an error for invalid subscripts etc,
* rather than returning a NULL as the fetch operations do.
lb[MAXDIM],
offset;
char *elt_ptr;
- bool extendbefore = false;
- bool extendafter = false;
bool newhasnulls;
bits8 *oldnullbitmap;
int oldnitems,
+ newnitems,
olddatasize,
newsize,
olditemlen,
newitemlen,
overheadlen,
oldoverheadlen,
+ addedbefore,
+ addedafter,
lenbefore,
lenafter;
if (nSubscripts != 1)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("invalid array subscripts")));
+ errmsg("wrong number of array subscripts")));
if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("invalid array subscripts")));
+ errmsg("array subscript out of range")));
if (isNull)
ereport(ERROR,
if (nSubscripts <= 0 || nSubscripts > MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("invalid array subscripts")));
+ errmsg("wrong number of array subscripts")));
/* make sure item to be inserted is not toasted */
if (elmlen == -1 && !isNull)
if (ndim != nSubscripts)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("invalid array subscripts")));
+ errmsg("wrong number of array subscripts")));
/* copy dim/lb since we may modify them */
memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
+ newhasnulls = (ARR_HASNULL(array) || isNull);
+ addedbefore = addedafter = 0;
+
/*
* Check subscripts
*/
- for (i = 0; i < ndim; i++)
+ if (ndim == 1)
{
- if (indx[i] < lb[i])
+ if (indx[0] < lb[0])
{
- if (ndim == 1 && indx[i] == lb[i] - 1)
- {
- dim[i]++;
- lb[i]--;
- extendbefore = true;
- }
- else
- ereport(ERROR,
- (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("invalid array subscripts")));
+ addedbefore = lb[0] - indx[0];
+ dim[0] += addedbefore;
+ lb[0] = indx[0];
+ if (addedbefore > 1)
+ newhasnulls = true; /* will insert nulls */
}
- if (indx[i] >= (dim[i] + lb[i]))
+ if (indx[0] >= (dim[0] + lb[0]))
{
- if (ndim == 1 && indx[i] == (dim[i] + lb[i]))
- {
- dim[i]++;
- extendafter = true;
- }
- else
+ addedafter = indx[0] - (dim[0] + lb[0]) + 1;
+ dim[0] += addedafter;
+ if (addedafter > 1)
+ newhasnulls = true; /* will insert nulls */
+ }
+ }
+ else
+ {
+ /*
+ * XXX currently we do not support extending multi-dimensional
+ * arrays during assignment
+ */
+ for (i = 0; i < ndim; i++)
+ {
+ if (indx[i] < lb[i] ||
+ indx[i] >= (dim[i] + lb[i]))
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("invalid array subscripts")));
+ errmsg("array subscript out of range")));
}
}
/*
* Compute sizes of items and areas to copy
*/
- if (ARR_HASNULL(array) || isNull)
- {
- newhasnulls = true;
- overheadlen = ARR_OVERHEAD_WITHNULLS(ndim,
- ArrayGetNItems(ndim, dim));
- }
+ newnitems = ArrayGetNItems(ndim, dim);
+ if (newhasnulls)
+ overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
else
- {
- newhasnulls = false;
overheadlen = ARR_OVERHEAD_NONULLS(ndim);
- }
oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
oldnullbitmap = ARR_NULLBITMAP(array);
oldoverheadlen = ARR_DATA_OFFSET(array);
olddatasize = ARR_SIZE(array) - oldoverheadlen;
- if (extendbefore)
+ if (addedbefore)
{
offset = 0;
lenbefore = 0;
olditemlen = 0;
lenafter = olddatasize;
}
- else if (extendafter)
+ else if (addedafter)
{
offset = oldnitems;
lenbefore = olddatasize;
{
bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
- array_set_isnull(newnullbitmap, offset, isNull);
- if (extendbefore)
- array_bitmap_copy(newnullbitmap, 1,
+ /* Zero the bitmap to take care of marking inserted positions null */
+ MemSet(newnullbitmap, 0, (newnitems + 7) / 8);
+ /* Fix the inserted value */
+ if (addedafter)
+ array_set_isnull(newnullbitmap, newnitems - 1, isNull);
+ else
+ array_set_isnull(newnullbitmap, offset, isNull);
+ /* Fix the copied range(s) */
+ if (addedbefore)
+ array_bitmap_copy(newnullbitmap, addedbefore,
oldnullbitmap, 0,
oldnitems);
else
array_bitmap_copy(newnullbitmap, 0,
oldnullbitmap, 0,
offset);
- if (!extendafter)
+ if (addedafter == 0)
array_bitmap_copy(newnullbitmap, offset + 1,
oldnullbitmap, offset + 1,
oldnitems - offset - 1);
* A new array is returned, just like the old except for the
* modified range. The original array object is not changed.
*
+ * 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.
+ * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
+ *
* NOTE: we assume it is OK to scribble on the provided index arrays
* lowerIndx[] and upperIndx[]. These are generally just temporaries.
*
newitemsize,
overheadlen,
oldoverheadlen,
+ addedbefore,
+ addedafter,
lenbefore,
lenafter,
itemsbefore,
if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("invalid array subscripts")));
+ errmsg("wrong number of array subscripts")));
/* copy dim/lb since we may modify them */
memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
+ newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
+ addedbefore = addedafter = 0;
+
/*
- * Check provided subscripts. A slice exceeding the current array limits
- * throws an error, *except* in the 1-D case where we will extend the
- * array as long as no hole is created. An empty slice is an error, too.
+ * Check subscripts
*/
- for (i = 0; i < nSubscripts; i++)
+ if (ndim == 1)
{
- if (lowerIndx[i] > upperIndx[i])
+ Assert(nSubscripts == 1);
+ if (lowerIndx[0] > upperIndx[0])
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("invalid array subscripts")));
- if (lowerIndx[i] < lb[i])
+ errmsg("upper bound cannot be less than lower bound")));
+ if (lowerIndx[0] < lb[0])
{
- if (ndim == 1 && upperIndx[i] >= lb[i] - 1)
- {
- dim[i] += lb[i] - lowerIndx[i];
- lb[i] = lowerIndx[i];
- }
- else
+ if (upperIndx[0] < lb[0] - 1)
+ newhasnulls = true; /* will insert nulls */
+ addedbefore = lb[0] - lowerIndx[0];
+ dim[0] += addedbefore;
+ lb[0] = lowerIndx[0];
+ }
+ if (upperIndx[0] >= (dim[0] + lb[0]))
+ {
+ if (lowerIndx[0] > (dim[0] + lb[0]))
+ newhasnulls = true; /* will insert nulls */
+ addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
+ dim[0] += addedafter;
+ }
+ }
+ else
+ {
+ /*
+ * XXX currently we do not support extending multi-dimensional
+ * arrays during assignment
+ */
+ for (i = 0; i < nSubscripts; i++)
+ {
+ if (lowerIndx[i] > upperIndx[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("upper bound cannot be less than lower bound")));
+ if (lowerIndx[i] < lb[i] ||
+ upperIndx[i] >= (dim[i] + lb[i]))
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("invalid array subscripts")));
+ errmsg("array subscript out of range")));
}
- if (upperIndx[i] >= (dim[i] + lb[i]))
+ /* fill any missing subscript positions with full array range */
+ for (; i < ndim; i++)
{
- if (ndim == 1 && lowerIndx[i] <= (dim[i] + lb[i]))
- dim[i] = upperIndx[i] - lb[i] + 1;
- else
+ lowerIndx[i] = lb[i];
+ upperIndx[i] = dim[i] + lb[i] - 1;
+ if (lowerIndx[i] > upperIndx[i])
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("invalid array subscripts")));
+ errmsg("upper bound cannot be less than lower bound")));
}
}
- /* fill any missing subscript positions with full array range */
- for (; i < ndim; i++)
- {
- lowerIndx[i] = lb[i];
- upperIndx[i] = dim[i] + lb[i] - 1;
- if (lowerIndx[i] > upperIndx[i])
- ereport(ERROR,
- (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("invalid array subscripts")));
- }
/* Do this mainly to check for overflow */
nitems = ArrayGetNItems(ndim, dim);
* Compute space occupied by new entries, space occupied by replaced
* entries, and required space for new array.
*/
- if (ARR_HASNULL(array) || ARR_HASNULL(srcArray))
- {
- newhasnulls = true;
+ if (newhasnulls)
overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
- }
else
- {
- newhasnulls = false;
overheadlen = ARR_OVERHEAD_NONULLS(ndim);
- }
newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
ARR_NULLBITMAP(srcArray), nsrcitems,
elmlen, elmbyval, elmalign);
char *oldarraydata = ARR_DATA_PTR(array);
bits8 *oldarraybitmap = ARR_NULLBITMAP(array);
- itemsbefore = slicelb - oldlb;
+ itemsbefore = Min(slicelb, oldub + 1) - oldlb;
lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
itemsbefore,
elmlen, elmbyval, elmalign);
bits8 *newnullbitmap = ARR_NULLBITMAP(newarray);
bits8 *oldnullbitmap = ARR_NULLBITMAP(array);
- array_bitmap_copy(newnullbitmap, 0,
+ /* Zero the bitmap to handle marking inserted positions null */
+ MemSet(newnullbitmap, 0, (nitems + 7) / 8);
+ array_bitmap_copy(newnullbitmap, addedbefore,
oldnullbitmap, 0,
itemsbefore);
- array_bitmap_copy(newnullbitmap, itemsbefore,
+ array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
ARR_NULLBITMAP(srcArray), 0,
nsrcitems);
- array_bitmap_copy(newnullbitmap, itemsbefore + nsrcitems,
+ array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
oldnullbitmap, itemsbefore + nolditems,
itemsafter);
}
[4:4]={NULL} | {3,4} | {foo,new_word}
(3 rows)
+--
+-- test array extension
+--
+CREATE TEMP TABLE arrtest1 (i int[], t text[]);
+insert into arrtest1 values(array[1,2,null,4], array['one','two',null,'four']);
+select * from arrtest1;
+ i | t
+--------------+---------------------
+ {1,2,NULL,4} | {one,two,NULL,four}
+(1 row)
+
+update arrtest1 set i[2] = 22, t[2] = 'twenty-two';
+select * from arrtest1;
+ i | t
+---------------+----------------------------
+ {1,22,NULL,4} | {one,twenty-two,NULL,four}
+(1 row)
+
+update arrtest1 set i[5] = 5, t[5] = 'five';
+select * from arrtest1;
+ i | t
+-----------------+---------------------------------
+ {1,22,NULL,4,5} | {one,twenty-two,NULL,four,five}
+(1 row)
+
+update arrtest1 set i[8] = 8, t[8] = 'eight';
+select * from arrtest1;
+ i | t
+-----------------------------+-------------------------------------------------
+ {1,22,NULL,4,5,NULL,NULL,8} | {one,twenty-two,NULL,four,five,NULL,NULL,eight}
+(1 row)
+
+update arrtest1 set i[0] = 0, t[0] = 'zero';
+select * from arrtest1;
+ i | t
+-------------------------------------+------------------------------------------------------------
+ [0:8]={0,1,22,NULL,4,5,NULL,NULL,8} | [0:8]={zero,one,twenty-two,NULL,four,five,NULL,NULL,eight}
+(1 row)
+
+update arrtest1 set i[-3] = -3, t[-3] = 'minus-three';
+select * from arrtest1;
+ i | t
+---------------------------------------------------+-----------------------------------------------------------------------------------
+ [-3:8]={-3,NULL,NULL,0,1,22,NULL,4,5,NULL,NULL,8} | [-3:8]={minus-three,NULL,NULL,zero,one,twenty-two,NULL,four,five,NULL,NULL,eight}
+(1 row)
+
+update arrtest1 set i[0:2] = array[10,11,12], t[0:2] = array['ten','eleven','twelve'];
+select * from arrtest1;
+ i | t
+-----------------------------------------------------+---------------------------------------------------------------------------------
+ [-3:8]={-3,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,8} | [-3:8]={minus-three,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,eight}
+(1 row)
+
+update arrtest1 set i[8:10] = array[18,null,20], t[8:10] = array['p18',null,'p20'];
+select * from arrtest1;
+ i | t
+---------------------------------------------------------------+-----------------------------------------------------------------------------------------
+ [-3:10]={-3,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,18,NULL,20} | [-3:10]={minus-three,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,p18,NULL,p20}
+(1 row)
+
+update arrtest1 set i[11:12] = array[null,22], t[11:12] = array[null,'p22'];
+select * from arrtest1;
+ i | t
+-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------------
+ [-3:12]={-3,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,18,NULL,20,NULL,22} | [-3:12]={minus-three,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,p18,NULL,p20,NULL,p22}
+(1 row)
+
+update arrtest1 set i[15:16] = array[null,26], t[15:16] = array[null,'p26'];
+select * from arrtest1;
+ i | t
+-----------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------
+ [-3:16]={-3,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,18,NULL,20,NULL,22,NULL,NULL,NULL,26} | [-3:16]={minus-three,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,p18,NULL,p20,NULL,p22,NULL,NULL,NULL,p26}
+(1 row)
+
+update arrtest1 set i[-5:-3] = array[-15,-14,-13], t[-5:-3] = array['m15','m14','m13'];
+select * from arrtest1;
+ i | t
+--------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------
+ [-5:16]={-15,-14,-13,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,18,NULL,20,NULL,22,NULL,NULL,NULL,26} | [-5:16]={m15,m14,m13,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,p18,NULL,p20,NULL,p22,NULL,NULL,NULL,p26}
+(1 row)
+
+update arrtest1 set i[-7:-6] = array[-17,null], t[-7:-6] = array['m17',null];
+select * from arrtest1;
+ i | t
+-----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------
+ [-7:16]={-17,NULL,-15,-14,-13,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,18,NULL,20,NULL,22,NULL,NULL,NULL,26} | [-7:16]={m17,NULL,m15,m14,m13,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,p18,NULL,p20,NULL,p22,NULL,NULL,NULL,p26}
+(1 row)
+
+update arrtest1 set i[-12:-10] = array[-22,null,-20], t[-12:-10] = array['m22',null,'m20'];
+select * from arrtest1;
+ i | t
+-----------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------
+ [-12:16]={-22,NULL,-20,NULL,NULL,-17,NULL,-15,-14,-13,NULL,NULL,10,11,12,NULL,4,5,NULL,NULL,18,NULL,20,NULL,22,NULL,NULL,NULL,26} | [-12:16]={m22,NULL,m20,NULL,NULL,m17,NULL,m15,m14,m13,NULL,NULL,ten,eleven,twelve,NULL,four,five,NULL,NULL,p18,NULL,p20,NULL,p22,NULL,NULL,NULL,p26}
+(1 row)
+
+delete from arrtest1;
+insert into arrtest1 values(array[1,2,null,4], array['one','two',null,'four']);
+select * from arrtest1;
+ i | t
+--------------+---------------------
+ {1,2,NULL,4} | {one,two,NULL,four}
+(1 row)
+
+update arrtest1 set i[0:5] = array[0,1,2,null,4,5], t[0:5] = array['z','p1','p2',null,'p4','p5'];
+select * from arrtest1;
+ i | t
+------------------------+----------------------------
+ [0:5]={0,1,2,NULL,4,5} | [0:5]={z,p1,p2,NULL,p4,p5}
+(1 row)
+
--
-- array expressions and operators
--
DELETE FROM arrtest WHERE a[2] IS NULL AND b IS NULL;
SELECT a,b,c FROM arrtest;
+--
+-- test array extension
+--
+CREATE TEMP TABLE arrtest1 (i int[], t text[]);
+insert into arrtest1 values(array[1,2,null,4], array['one','two',null,'four']);
+select * from arrtest1;
+update arrtest1 set i[2] = 22, t[2] = 'twenty-two';
+select * from arrtest1;
+update arrtest1 set i[5] = 5, t[5] = 'five';
+select * from arrtest1;
+update arrtest1 set i[8] = 8, t[8] = 'eight';
+select * from arrtest1;
+update arrtest1 set i[0] = 0, t[0] = 'zero';
+select * from arrtest1;
+update arrtest1 set i[-3] = -3, t[-3] = 'minus-three';
+select * from arrtest1;
+update arrtest1 set i[0:2] = array[10,11,12], t[0:2] = array['ten','eleven','twelve'];
+select * from arrtest1;
+update arrtest1 set i[8:10] = array[18,null,20], t[8:10] = array['p18',null,'p20'];
+select * from arrtest1;
+update arrtest1 set i[11:12] = array[null,22], t[11:12] = array[null,'p22'];
+select * from arrtest1;
+update arrtest1 set i[15:16] = array[null,26], t[15:16] = array[null,'p26'];
+select * from arrtest1;
+update arrtest1 set i[-5:-3] = array[-15,-14,-13], t[-5:-3] = array['m15','m14','m13'];
+select * from arrtest1;
+update arrtest1 set i[-7:-6] = array[-17,null], t[-7:-6] = array['m17',null];
+select * from arrtest1;
+update arrtest1 set i[-12:-10] = array[-22,null,-20], t[-12:-10] = array['m22',null,'m20'];
+select * from arrtest1;
+delete from arrtest1;
+insert into arrtest1 values(array[1,2,null,4], array['one','two',null,'four']);
+select * from arrtest1;
+update arrtest1 set i[0:5] = array[0,1,2,null,4,5], t[0:5] = array['z','p1','p2',null,'p4','p5'];
+select * from arrtest1;
+
--
-- array expressions and operators
--