Make SQL arrays support null elements. This commit fixes the core array

functionality, but I still need to make another pass looking at places
that incidentally use arrays (such as ACL manipulation) to make sure they
are null-safe.  Contrib needs work too.
I have not changed the behaviors that are still under discussion about
array comparison and what to do with lower bounds.
This commit is contained in:
Tom Lane 2005-11-17 22:14:56 +00:00
parent c859308aba
commit cecb607559
35 changed files with 2149 additions and 950 deletions

View File

@ -87,7 +87,7 @@ GetPGArray(PGARRAY * p, AggState *aggstate, bool fAdd)
p = (PGARRAY *) MemoryContextAlloc(aggstate->aggcontext, cb);
p->a.size = cb;
p->a.ndim = 1;
p->a.flags = 0;
p->a.dataoffset = 0; /* we don't support nulls, for now */
p->a.elemtype = INT4OID;
p->items = 0;
p->lower = START_NUM;

View File

@ -208,12 +208,13 @@ ArrayType *
new_intArrayType(int num)
{
ArrayType *r;
int nbytes = ARR_OVERHEAD(NDIM) + sizeof(int) * num;
int nbytes = ARR_OVERHEAD_NONULLS(NDIM) + sizeof(int) * num;
r = (ArrayType *) palloc0(nbytes);
ARR_SIZE(r) = nbytes;
ARR_NDIM(r) = NDIM;
r->dataoffset = 0; /* marker for no null bitmap */
ARR_ELEMTYPE(r) = INT4OID;
*((int *) ARR_DIMS(r)) = num;
*((int *) ARR_LBOUND(r)) = 1;
@ -224,7 +225,7 @@ new_intArrayType(int num)
ArrayType *
resize_intArrayType(ArrayType *a, int num)
{
int nbytes = ARR_OVERHEAD(NDIM) + sizeof(int) * num;
int nbytes = ARR_OVERHEAD_NONULLS(NDIM) + sizeof(int) * num;
if (num == ARRNELEMS(a))
return a;

View File

@ -232,7 +232,7 @@ rewrite_accum(PG_FUNCTION_ARGS) {
if (ARR_ELEMTYPE(qa) != tsqOid)
elog(ERROR, "array should contain tsquery type");
deconstruct_array(qa, tsqOid, -1, false, 'i', &elemsp, &nelemsp);
deconstruct_array(qa, tsqOid, -1, false, 'i', &elemsp, NULL, &nelemsp);
q = (QUERYTYPE*)DatumGetPointer( elemsp[0] );
if ( q->size == 0 ) {

View File

@ -1,4 +1,4 @@
<!-- $PostgreSQL: pgsql/doc/src/sgml/array.sgml,v 1.46 2005/11/04 23:13:59 petere Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/array.sgml,v 1.47 2005/11/17 22:14:50 tgl Exp $ -->
<sect1 id="arrays">
<title>Arrays</title>
@ -110,6 +110,13 @@ CREATE TABLE tictactoe (
three subarrays of integers.
</para>
<para>
To set an element of an array constant to NULL, write <literal>NULL</>
for the element value. (Any upper- or lower-case variant of
<literal>NULL</> will do.) If you want an actual string value
<quote>NULL</>, you must put double quotes around it.
</para>
<para>
(These kinds of array constants are actually only a special case of
the generic type constants discussed in <xref
@ -121,17 +128,6 @@ CREATE TABLE tictactoe (
<para>
Now we can show some <command>INSERT</command> statements.
<programlisting>
INSERT INTO sal_emp
VALUES ('Bill',
'{10000, 10000, 10000, 10000}',
'{{"meeting", "lunch"}, {"meeting"}}');
ERROR: multidimensional arrays must have array expressions with matching dimensions
</programlisting>
Note that multidimensional arrays must have matching extents for each
dimension. A mismatch causes an error report.
<programlisting>
INSERT INTO sal_emp
VALUES ('Bill',
@ -145,15 +141,9 @@ INSERT INTO sal_emp
</programlisting>
</para>
<para>
A limitation of the present array implementation is that individual
elements of an array cannot be SQL null values. The entire array
can be set to null, but you can't have an array with some elements
null and some not. (This is likely to change in the future.)
</para>
<para>
The result of the previous two inserts looks like this:
<programlisting>
SELECT * FROM sal_emp;
name | pay_by_quarter | schedule
@ -183,6 +173,19 @@ INSERT INTO sal_emp
constructor syntax is discussed in more detail in
<xref linkend="sql-syntax-array-constructors">.
</para>
<para>
Multidimensional arrays must have matching extents for each
dimension. A mismatch causes an error report, for example:
<programlisting>
INSERT INTO sal_emp
VALUES ('Bill',
'{10000, 10000, 10000, 10000}',
'{{"meeting", "lunch"}, {"meeting"}}');
ERROR: multidimensional arrays must have array expressions with matching dimensions
</programlisting>
</para>
</sect2>
<sect2>
@ -262,14 +265,22 @@ SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
</para>
<para>
Fetching from outside the current bounds of an array yields a
SQL null value, not an error. For example, if <literal>schedule</>
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
subscript is outside the array bounds (this case does not raise an error).
For example, if <literal>schedule</>
currently has the dimensions <literal>[1:3][1:2]</> then referencing
<literal>schedule[3][3]</> yields NULL. Similarly, an array reference
with the wrong number of subscripts yields a null rather than an error.
Fetching an array slice that
is completely outside the current bounds likewise yields a null array;
but if the requested slice partially overlaps the array bounds, then it
</para>
<para>
An array slice expression likewise yields null if the array itself or
any of the subscript expressions are null. However, in other corner
cases such as selecting an array slice that
is completely outside the current array bounds, a slice expression
yields an empty (zero-dimensional) array instead of null.
If the requested slice partially overlaps the array bounds, then it
is silently reduced to just the overlapping region.
</para>
@ -349,7 +360,7 @@ UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
</para>
<para>
Array slice assignment allows creation of arrays that do not use one-based
Subscripted assignment allows creation of arrays that do not use one-based
subscripts. For example one might assign to <literal>myarray[-2:7]</> to
create an array with subscript values running from -2 to 7.
</para>
@ -442,7 +453,7 @@ SELECT array_dims(ARRAY[1,2] || ARRAY[[3,4],[5,6]]);
arrays, but <function>array_cat</function> supports multidimensional arrays.
Note that the concatenation operator discussed above is preferred over
direct use of these functions. In fact, the functions are primarily for use
direct use of these functions. In fact, the functions exist primarily for use
in implementing the concatenation operator. However, they may be directly
useful in the creation of user-defined aggregates. Some examples:
@ -544,8 +555,9 @@ SELECT * FROM sal_emp WHERE 10000 = ALL (pay_by_quarter);
<para>
The array output routine will put double quotes around element values
if they are empty strings or contain curly braces, delimiter characters,
double quotes, backslashes, or white space. Double quotes and backslashes
if they are empty strings, contain curly braces, delimiter characters,
double quotes, backslashes, or white space, or match the word
<literal>NULL</>. Double quotes and backslashes
embedded in element values will be backslash-escaped. For numeric
data types it is safe to assume that double quotes will never appear, but
for textual data types one should be prepared to cope with either presence
@ -555,35 +567,15 @@ SELECT * FROM sal_emp WHERE 10000 = ALL (pay_by_quarter);
<para>
By default, the lower bound index value of an array's dimensions is
set to one. If any of an array's dimensions has a lower bound index not
equal to one, an additional decoration that indicates the actual
array dimensions will precede the array structure decoration.
set to one. To represent arrays with other lower bounds, the array
subscript ranges can be specified explicitly before writing the
array contents.
This decoration consists of square brackets (<literal>[]</>)
around each array dimension's lower and upper bounds, with
a colon (<literal>:</>) delimiter character in between. The
array dimension decoration is followed by an equal sign (<literal>=</>).
For example:
<programlisting>
SELECT 1 || ARRAY[2,3] AS array;
array
---------------
[0:2]={1,2,3}
(1 row)
SELECT ARRAY[1,2] || ARRAY[[3,4]] AS array;
array
--------------------------
[0:1][1:2]={{1,2},{3,4}}
(1 row)
</programlisting>
</para>
<para>
This syntax can also be used to specify non-default array subscripts
in an array literal. For example:
<programlisting>
SELECT f1[1][-2][3] AS e1, f1[1][-1][5] AS e2
FROM (SELECT '[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}'::int[] AS f1) AS ss;
@ -592,6 +584,18 @@ SELECT f1[1][-2][3] AS e1, f1[1][-1][5] AS e2
1 | 6
(1 row)
</programlisting>
The array output routine will include explicit dimensions in its result
only when there are one or more lower bounds different from one.
</para>
<para>
If the value written for an element is <literal>NULL</> (in any case
variant), the element is taken to be NULL. The presence of any quotes
or backslashes disables this and allows the literal string value
<quote>NULL</> to be entered. Also, for backwards compatibility with
pre-8.2 versions of <productname>PostgreSQL</>, the <xref
linkend="guc-array-nulls"> configuration parameter may be turned
<literal>off</> to suppress recognition of <literal>NULL</> as a NULL.
</para>
<para>
@ -600,7 +604,9 @@ SELECT f1[1][-2][3] AS e1, f1[1][-1][5] AS e2
if the element value would otherwise confuse the array-value parser.
For example, elements containing curly braces, commas (or whatever the
delimiter character is), double quotes, backslashes, or leading or trailing
whitespace must be double-quoted. To put a double quote or backslash in a
whitespace must be double-quoted. Empty strings and strings matching the
word <literal>NULL</> must be quoted, too. To put a double quote or
backslash in a
quoted array element value, precede it with a backslash. Alternatively, you
can use backslash-escaping to protect all data characters that would
otherwise be taken as array syntax.

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.36 2005/11/04 23:53:18 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/config.sgml,v 1.37 2005/11/17 22:14:50 tgl Exp $
-->
<chapter Id="runtime-config">
<title>Server Configuration</title>
@ -3614,6 +3614,7 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
<sect2 id="runtime-config-compatible-version">
<title>Previous PostgreSQL Versions</title>
<variablelist>
<varlistentry id="guc-add-missing-from" xreflabel="add_missing_from">
@ -3647,40 +3648,27 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
</listitem>
</varlistentry>
<varlistentry id="guc-regex-flavor" xreflabel="regex_flavor">
<term><varname>regex_flavor</varname> (<type>string</type>)</term>
<indexterm><primary>regular expressions</></>
<varlistentry id="guc-array-nulls" xreflabel="array_nulls">
<term><varname>array_nulls</varname> (<type>boolean</type>)</term>
<indexterm>
<primary><varname>regex_flavor</> configuration parameter</primary>
<primary><varname>array_nulls</> configuration parameter</primary>
</indexterm>
<listitem>
<para>
The regular expression <quote>flavor</> can be set to
<literal>advanced</>, <literal>extended</>, or <literal>basic</>.
The default is <literal>advanced</>. The <literal>extended</>
setting may be useful for exact backwards compatibility with
pre-7.4 releases of <productname>PostgreSQL</>. See
<xref linkend="posix-syntax-details"> for details.
This controls whether the array input parser recognizes
unquoted <literal>NULL</> as specifying a NULL array element.
By default, this is <literal>on</>, allowing array values containing
NULLs to be entered. However, <productname>PostgreSQL</> versions
before 8.2 did not support NULLs in arrays, and therefore would
treat <literal>NULL</> as specifying a normal array element with
the string value <quote>NULL</>. For backwards compatibility with
applications that require the old behavior, this variable can be
turned <literal>off</>.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-sql-inheritance" xreflabel="sql_inheritance">
<term><varname>sql_inheritance</varname> (<type>boolean</type>)</term>
<indexterm>
<primary><varname>sql_inheritance</> configuration parameter</primary>
</indexterm>
<indexterm><primary>inheritance</></>
<listitem>
<para>
This controls the inheritance semantics, in particular whether
subtables are included by various commands by default. They were
not included in versions prior to 7.1. If you need the old
behavior you can set this variable to <literal>off</>, but in
the long run you are encouraged to change your applications to
use the <literal>ONLY</literal> key word to exclude subtables.
See <xref linkend="ddl-inherit"> for more information about
inheritance.
Note that it is possible to create array values containing NULLs
even when this variable is <literal>off</>.
</para>
</listitem>
</varlistentry>
@ -3736,8 +3724,47 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
</listitem>
</varlistentry>
<varlistentry id="guc-regex-flavor" xreflabel="regex_flavor">
<term><varname>regex_flavor</varname> (<type>string</type>)</term>
<indexterm><primary>regular expressions</></>
<indexterm>
<primary><varname>regex_flavor</> configuration parameter</primary>
</indexterm>
<listitem>
<para>
The regular expression <quote>flavor</> can be set to
<literal>advanced</>, <literal>extended</>, or <literal>basic</>.
The default is <literal>advanced</>. The <literal>extended</>
setting may be useful for exact backwards compatibility with
pre-7.4 releases of <productname>PostgreSQL</>. See
<xref linkend="posix-syntax-details"> for details.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-sql-inheritance" xreflabel="sql_inheritance">
<term><varname>sql_inheritance</varname> (<type>boolean</type>)</term>
<indexterm>
<primary><varname>sql_inheritance</> configuration parameter</primary>
</indexterm>
<indexterm><primary>inheritance</></>
<listitem>
<para>
This controls the inheritance semantics, in particular whether
subtables are included by various commands by default. They were
not included in versions prior to 7.1. If you need the old
behavior you can set this variable to <literal>off</>, but in
the long run you are encouraged to change your applications to
use the <literal>ONLY</literal> key word to exclude subtables.
See <xref linkend="ddl-inherit"> for more information about
inheritance.
</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
<sect2 id="runtime-config-compatible-clients">
<title>Platform and Client Compatibility</title>
<variablelist>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.292 2005/11/16 03:56:16 momjian Exp $
$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.293 2005/11/17 22:14:50 tgl Exp $
PostgreSQL documentation
-->
@ -8323,6 +8323,18 @@ AND
case where the array has zero elements).
</para>
<para>
If the array expression yields a null array, the result of
<token>ANY</token> will be null. If the left-hand expression yields null,
the result of <token>ANY</token> is ordinarily null (though a non-strict
comparison operator could possibly yield a different result).
Also, if the right-hand array contains any null elements and no true
comparison result is obtained, the result of <token>ANY</token>
will be null, not false (again, assuming a strict comparison operator).
This is in accordance with SQL's normal rules for Boolean combinations
of null values.
</para>
<para>
<token>SOME</token> is a synonym for <token>ANY</token>.
</para>
@ -8346,6 +8358,18 @@ AND
(including the special case where the array has zero elements).
The result is <quote>false</> if any false result is found.
</para>
<para>
If the array expression yields a null array, the result of
<token>ALL</token> will be null. If the left-hand expression yields null,
the result of <token>ALL</token> is ordinarily null (though a non-strict
comparison operator could possibly yield a different result).
Also, if the right-hand array contains any null elements and no false
comparison result is obtained, the result of <token>ALL</token>
will be null, not true (again, assuming a strict comparison operator).
This is in accordance with SQL's normal rules for Boolean combinations
of null values.
</para>
</sect2>
<sect2>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/insert.sgml,v 1.29 2005/01/09 05:57:45 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/insert.sgml,v 1.30 2005/11/17 22:14:51 tgl Exp $
PostgreSQL documentation
-->
@ -206,11 +206,11 @@ INSERT INTO films SELECT * FROM tmp_films WHERE date_prod &lt; '2004-05-07';
<programlisting>
-- Create an empty 3x3 gameboard for noughts-and-crosses
-- (these commands create the same board)
INSERT INTO tictactoe (game, board[1:3][1:3])
VALUES (1,'{{"","",""},{"","",""},{"","",""}}');
VALUES (1, '{{" "," "," "},{" "," "," "},{" "," "," "}}');
-- The subscripts in the above example aren't really needed
INSERT INTO tictactoe (game, board)
VALUES (2,'{{,,},{,,},{,,}}');
VALUES (2, '{{X," "," "},{" ",O," "},{" ",X," "}}');
</programlisting>
</para>
</refsect1>

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.107 2005/10/15 20:12:33 neilc Exp $
$PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.108 2005/11/17 22:14:50 tgl Exp $
-->
<sect1 id="xfunc">
@ -2790,6 +2790,7 @@ make_array(PG_FUNCTION_ARGS)
ArrayType *result;
Oid element_type = get_fn_expr_argtype(fcinfo-&gt;flinfo, 0);
Datum element;
bool isnull;
int16 typlen;
bool typbyval;
char typalign;
@ -2800,8 +2801,12 @@ make_array(PG_FUNCTION_ARGS)
if (!OidIsValid(element_type))
elog(ERROR, "could not determine data type of input");
/* get the provided element */
element = PG_GETARG_DATUM(0);
/* get the provided element, being careful in case it's NULL */
isnull = PG_ARGISNULL(0);
if (isnull)
element = (Datum) 0;
else
element = PG_GETARG_DATUM(0);
/* we have one dimension */
ndims = 1;
@ -2814,7 +2819,7 @@ make_array(PG_FUNCTION_ARGS)
get_typlenbyvalalign(element_type, &amp;typlen, &amp;typbyval, &amp;typalign);
/* now build the array */
result = construct_md_array(&amp;element, ndims, dims, lbs,
result = construct_md_array(&amp;element, &amp;isnull, ndims, dims, lbs,
element_type, typlen, typbyval, typalign);
PG_RETURN_ARRAYTYPE_P(result);
@ -2829,11 +2834,8 @@ make_array(PG_FUNCTION_ARGS)
<programlisting>
CREATE FUNCTION make_array(anyelement) RETURNS anyarray
AS '<replaceable>DIRECTORY</replaceable>/funcs', 'make_array'
LANGUAGE C STRICT;
LANGUAGE C IMMUTABLE;
</programlisting>
Note the use of <literal>STRICT</literal>; this is essential
since the code is not bothering to test for a null input.
</para>
</sect2>
</sect1>

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.135 2005/10/29 00:31:50 petere Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.136 2005/11/17 22:14:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -119,12 +119,15 @@ ProcedureCreate(const char *procedureName,
* need to use deconstruct_array() since the array data is just going
* to look like a C array of OID values.
*/
allParamCount = ARR_DIMS(DatumGetPointer(allParameterTypes))[0];
if (ARR_NDIM(DatumGetPointer(allParameterTypes)) != 1 ||
ArrayType *allParamArray = (ArrayType *) DatumGetPointer(allParameterTypes);
allParamCount = ARR_DIMS(allParamArray)[0];
if (ARR_NDIM(allParamArray) != 1 ||
allParamCount <= 0 ||
ARR_ELEMTYPE(DatumGetPointer(allParameterTypes)) != OIDOID)
ARR_HASNULL(allParamArray) ||
ARR_ELEMTYPE(allParamArray) != OIDOID)
elog(ERROR, "allParameterTypes is not a 1-D Oid array");
allParams = (Oid *) ARR_DATA_PTR(DatumGetPointer(allParameterTypes));
allParams = (Oid *) ARR_DATA_PTR(allParamArray);
Assert(allParamCount >= parameterCount);
/* we assume caller got the contents right */
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.183 2005/10/19 22:30:30 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.184 2005/11/17 22:14:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -202,16 +202,8 @@ static Datum ExecEvalRelabelType(GenericExprState *exprstate,
* if it's a simple reference, or the modified array value if it's
* an array assignment (i.e., array element or slice insertion).
*
* NOTE: if we get a NULL result from a subexpression, we return NULL when
* it's an array reference, or the unmodified source array when it's an
* array assignment. This may seem peculiar, but if we return NULL (as was
* done in versions up through 7.0) then an assignment like
* UPDATE table SET arrayfield[4] = NULL
* will result in setting the whole array to NULL, which is certainly not
* very desirable. By returning the source array we make the assignment
* into a no-op, instead. (Eventually we need to redesign arrays so that
* individual elements can be NULL, but for now, let's try to protect users
* from shooting themselves in the foot.)
* NOTE: if we get a NULL result from a subscript expression, we return NULL
* when it's an array reference, or raise an error when it's an assignment.
*
* NOTE: we deliberately refrain from applying DatumGetArrayTypeP() here,
* even though that might seem natural, because this code needs to support
@ -270,15 +262,15 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
econtext,
&eisnull,
NULL));
/* If any index expr yields NULL, result is NULL or source array */
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (!isAssignment)
{
*isNull = true;
return (Datum) NULL;
}
return PointerGetDatum(array_source);
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("array subscript in assignment must not be NULL")));
*isNull = true;
return (Datum) NULL;
}
}
@ -298,18 +290,15 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
econtext,
&eisnull,
NULL));
/*
* If any index expr yields NULL, result is NULL or source array
*/
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (!isAssignment)
{
*isNull = true;
return (Datum) NULL;
}
return PointerGetDatum(array_source);
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("array subscript in assignment must not be NULL")));
*isNull = true;
return (Datum) NULL;
}
}
/* this can't happen unless parser messed up */
@ -327,8 +316,8 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
/*
* Evaluate the value to be assigned into the array.
*
* XXX At some point we'll need to look into making the old value of the
* array element available via CaseTestExpr, as is done by
* XXX At some point we'll need to look into making the old value of
* the array element available via CaseTestExpr, as is done by
* ExecEvalFieldStore. This is not needed now but will be needed to
* support arrays of composite types; in an assignment to a field of
* an array member, the parser would generate a FieldStore that
@ -340,29 +329,23 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
NULL);
/*
* For now, can't cope with inserting NULL into an array, so make it a
* no-op per discussion above...
* For an assignment to a fixed-length array type, both the original
* array and the value to be assigned into it must be non-NULL, else
* we punt and return the original array.
*/
if (eisnull)
return PointerGetDatum(array_source);
if (astate->refattrlength > 0) /* fixed-length array? */
if (eisnull || *isNull)
return PointerGetDatum(array_source);
/*
* For an assignment, if all the subscripts and the input expression
* are non-null but the original array is null, then substitute an
* empty (zero-dimensional) array and proceed with the assignment.
* This only works for varlena arrays, though; for fixed-length array
* types we punt and return the null input array.
* For assignment to varlena arrays, we handle a NULL original array
* by substituting an empty (zero-dimensional) array; insertion of
* the new element will result in a singleton array value. It does
* not matter whether the new element is NULL.
*/
if (*isNull)
{
if (astate->refattrlength > 0) /* fixed-length array? */
return PointerGetDatum(array_source);
array_source = construct_md_array(NULL, 0, NULL, NULL,
arrayRef->refelemtype,
astate->refelemlength,
astate->refelembyval,
astate->refelemalign);
array_source = construct_empty_array(arrayRef->refelemtype);
*isNull = false;
}
@ -370,20 +353,20 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
resultArray = array_set(array_source, i,
upper.indx,
sourceData,
eisnull,
astate->refattrlength,
astate->refelemlength,
astate->refelembyval,
astate->refelemalign,
isNull);
astate->refelemalign);
else
resultArray = array_set_slice(array_source, i,
upper.indx, lower.indx,
(ArrayType *) DatumGetPointer(sourceData),
eisnull,
astate->refattrlength,
astate->refelemlength,
astate->refelembyval,
astate->refelemalign,
isNull);
astate->refelemalign);
return PointerGetDatum(resultArray);
}
@ -401,8 +384,7 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
astate->refattrlength,
astate->refelemlength,
astate->refelembyval,
astate->refelemalign,
isNull);
astate->refelemalign);
return PointerGetDatum(resultArray);
}
}
@ -1620,6 +1602,8 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
bool typbyval;
char typalign;
char *s;
bits8 *bitmap;
int bitmask;
/* Set default values for result flags: non-null, not a set result */
*isNull = false;
@ -1668,9 +1652,8 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
return BoolGetDatum(!useOr);
/*
* If the scalar is NULL, and the function is strict, return NULL. This is
* just to avoid having to test for strictness inside the loop. (XXX but
* if arrays could have null elements, we'd need a test anyway.)
* If the scalar is NULL, and the function is strict, return NULL;
* no point in iterating the loop.
*/
if (fcinfo.argnull[0] && sstate->fxprstate.func.fn_strict)
{
@ -1699,22 +1682,40 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
/* Loop over the array elements */
s = (char *) ARR_DATA_PTR(arr);
bitmap = ARR_NULLBITMAP(arr);
bitmask = 1;
for (i = 0; i < nitems; i++)
{
Datum elt;
Datum thisresult;
/* Get array element */
elt = fetch_att(s, typbyval, typlen);
s = att_addlength(s, typlen, PointerGetDatum(s));
s = (char *) att_align(s, typalign);
/* Get array element, checking for NULL */
if (bitmap && (*bitmap & bitmask) == 0)
{
fcinfo.arg[1] = (Datum) 0;
fcinfo.argnull[1] = true;
}
else
{
elt = fetch_att(s, typbyval, typlen);
s = att_addlength(s, typlen, PointerGetDatum(s));
s = (char *) att_align(s, typalign);
fcinfo.arg[1] = elt;
fcinfo.argnull[1] = false;
}
/* Call comparison function */
fcinfo.arg[1] = elt;
fcinfo.argnull[1] = false;
fcinfo.isnull = false;
thisresult = FunctionCallInvoke(&fcinfo);
if (fcinfo.argnull[1] && sstate->fxprstate.func.fn_strict)
{
fcinfo.isnull = true;
thisresult = (Datum) 0;
}
else
{
fcinfo.isnull = false;
thisresult = FunctionCallInvoke(&fcinfo);
}
/* Combine results per OR or AND semantics */
if (fcinfo.isnull)
@ -1737,6 +1738,17 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
break; /* needn't look at any more elements */
}
}
/* advance bitmap pointer if any */
if (bitmap)
{
bitmask <<= 1;
if (bitmask == 0x100)
{
bitmap++;
bitmask = 1;
}
}
}
*isNull = resultnull;
@ -2053,10 +2065,6 @@ ExecEvalCaseTestExpr(ExprState *exprstate,
/* ----------------------------------------------------------------
* ExecEvalArray - ARRAY[] expressions
*
* NOTE: currently, if any input value is NULL then we return a NULL array,
* so the ARRAY[] construct can be considered strict. Eventually this will
* change; when it does, be sure to fix contain_nonstrict_functions().
* ----------------------------------------------------------------
*/
static Datum
@ -2081,39 +2089,33 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
/* Elements are presumably of scalar type */
int nelems;
Datum *dvalues;
bool *dnulls;
int i = 0;
ndims = 1;
nelems = list_length(astate->elements);
/* Shouldn't happen here, but if length is 0, return NULL */
/* Shouldn't happen here, but if length is 0, return empty array */
if (nelems == 0)
{
*isNull = true;
return (Datum) 0;
}
return PointerGetDatum(construct_empty_array(element_type));
dvalues = (Datum *) palloc(nelems * sizeof(Datum));
dnulls = (bool *) palloc(nelems * sizeof(bool));
/* loop through and build array of datums */
foreach(element, astate->elements)
{
ExprState *e = (ExprState *) lfirst(element);
bool eisnull;
dvalues[i++] = ExecEvalExpr(e, econtext, &eisnull, NULL);
if (eisnull)
{
*isNull = true;
return (Datum) 0;
}
dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i], NULL);
i++;
}
/* setup for 1-D array of the given length */
dims[0] = nelems;
lbs[0] = 1;
result = construct_md_array(dvalues, ndims, dims, lbs,
result = construct_md_array(dvalues, dnulls, ndims, dims, lbs,
element_type,
astate->elemlength,
astate->elembyval,
@ -2122,15 +2124,28 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
else
{
/* Must be nested array expressions */
char *dat = NULL;
Size ndatabytes = 0;
int nbytes;
int outer_nelems = list_length(astate->elements);
int nbytes = 0;
int nitems = 0;
int outer_nelems = 0;
int elem_ndims = 0;
int *elem_dims = NULL;
int *elem_lbs = NULL;
bool firstone = true;
bool havenulls = false;
char **subdata;
bits8 **subbitmaps;
int *subbytes;
int *subnitems;
int i;
int32 dataoffset;
char *dat;
int iitem;
i = list_length(astate->elements);
subdata = (char **) palloc(i * sizeof(char *));
subbitmaps = (bits8 **) palloc(i * sizeof(bits8 *));
subbytes = (int *) palloc(i * sizeof(int));
subnitems = (int *) palloc(i * sizeof(int));
/* loop through and get data area from each element */
foreach(element, astate->elements)
@ -2139,14 +2154,11 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
bool eisnull;
Datum arraydatum;
ArrayType *array;
int elem_ndatabytes;
arraydatum = ExecEvalExpr(e, econtext, &eisnull, NULL);
/* ignore null subarrays */
if (eisnull)
{
*isNull = true;
return (Datum) 0;
}
continue;
array = DatumGetArrayTypeP(arraydatum);
@ -2192,16 +2204,15 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
"expressions with matching dimensions")));
}
elem_ndatabytes = ARR_SIZE(array) - ARR_OVERHEAD(elem_ndims);
ndatabytes += elem_ndatabytes;
if (dat == NULL)
dat = (char *) palloc(ndatabytes);
else
dat = (char *) repalloc(dat, ndatabytes);
memcpy(dat + (ndatabytes - elem_ndatabytes),
ARR_DATA_PTR(array),
elem_ndatabytes);
subdata[outer_nelems] = ARR_DATA_PTR(array);
subbitmaps[outer_nelems] = ARR_NULLBITMAP(array);
subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array);
nbytes += subbytes[outer_nelems];
subnitems[outer_nelems] = ArrayGetNItems(ARR_NDIM(array),
ARR_DIMS(array));
nitems += subnitems[outer_nelems];
havenulls |= ARR_HASNULL(array);
outer_nelems++;
}
/* setup for multi-D array */
@ -2213,20 +2224,37 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
lbs[i] = elem_lbs[i - 1];
}
nbytes = ndatabytes + ARR_OVERHEAD(ndims);
result = (ArrayType *) palloc(nbytes);
if (havenulls)
{
dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
nbytes += dataoffset;
}
else
{
dataoffset = 0; /* marker for no null bitmap */
nbytes += ARR_OVERHEAD_NONULLS(ndims);
}
result = (ArrayType *) palloc(nbytes);
result->size = nbytes;
result->ndim = ndims;
result->flags = 0;
result->dataoffset = dataoffset;
result->elemtype = element_type;
memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
if (ndatabytes > 0)
memcpy(ARR_DATA_PTR(result), dat, ndatabytes);
if (dat != NULL)
pfree(dat);
dat = ARR_DATA_PTR(result);
iitem = 0;
for (i = 0; i < outer_nelems; i++)
{
memcpy(dat, subdata[i], subbytes[i]);
dat += subbytes[i];
if (havenulls)
array_bitmap_copy(ARR_NULLBITMAP(result), iitem,
subbitmaps[i], 0,
subnitems[i]);
iitem += subnitems[i];
}
}
return PointerGetDatum(result);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.201 2005/10/15 02:49:21 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.202 2005/11/17 22:14:52 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -784,7 +784,7 @@ contain_nonstrict_functions_walker(Node *node, void *context)
}
if (IsA(node, ArrayRef))
{
/* array assignment is nonstrict */
/* array assignment is nonstrict, but subscripting is strict */
if (((ArrayRef *) node)->refassgnexpr != NULL)
return true;
/* else fall through to check args */
@ -842,7 +842,8 @@ contain_nonstrict_functions_walker(Node *node, void *context)
return true;
if (IsA(node, CaseWhen))
return true;
/* NB: ArrayExpr might someday be nonstrict */
if (IsA(node, ArrayExpr))
return true;
if (IsA(node, RowExpr))
return true;
if (IsA(node, CoalesceExpr))

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.127 2005/11/04 17:25:15 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.128 2005/11/17 22:14:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -352,7 +352,7 @@ allocacl(int n)
new_acl = (Acl *) palloc0(size);
new_acl->size = size;
new_acl->ndim = 1;
new_acl->flags = 0;
new_acl->dataoffset = 0; /* we never put in any nulls */
new_acl->elemtype = ACLITEMOID;
ARR_LBOUND(new_acl)[0] = 1;
ARR_DIMS(new_acl)[0] = n;

View File

@ -6,7 +6,7 @@
* Copyright (c) 2003-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.16 2005/10/15 02:49:27 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.17 2005/11/17 22:14:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -19,6 +19,7 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
/*-----------------------------------------------------------------------------
* array_push :
* push an element onto either end of a one-dimensional array
@ -29,11 +30,11 @@ array_push(PG_FUNCTION_ARGS)
{
ArrayType *v;
Datum newelem;
bool isNull;
int *dimv,
*lb;
ArrayType *result;
int indx;
bool isNull;
Oid element_type;
int16 typlen;
bool typbyval;
@ -54,15 +55,27 @@ array_push(PG_FUNCTION_ARGS)
if (arg0_elemid != InvalidOid)
{
v = PG_GETARG_ARRAYTYPE_P(0);
element_type = ARR_ELEMTYPE(v);
newelem = PG_GETARG_DATUM(1);
if (PG_ARGISNULL(0))
v = construct_empty_array(arg0_elemid);
else
v = PG_GETARG_ARRAYTYPE_P(0);
isNull = PG_ARGISNULL(1);
if (isNull)
newelem = (Datum) 0;
else
newelem = PG_GETARG_DATUM(1);
}
else if (arg1_elemid != InvalidOid)
{
v = PG_GETARG_ARRAYTYPE_P(1);
element_type = ARR_ELEMTYPE(v);
newelem = PG_GETARG_DATUM(0);
if (PG_ARGISNULL(1))
v = construct_empty_array(arg1_elemid);
else
v = PG_GETARG_ARRAYTYPE_P(1);
isNull = PG_ARGISNULL(0);
if (isNull)
newelem = (Datum) 0;
else
newelem = PG_GETARG_DATUM(0);
}
else
{
@ -73,6 +86,8 @@ array_push(PG_FUNCTION_ARGS)
PG_RETURN_NULL(); /* keep compiler quiet */
}
element_type = ARR_ELEMTYPE(v);
if (ARR_NDIM(v) == 1)
{
lb = ARR_LBOUND(v);
@ -84,11 +99,21 @@ array_push(PG_FUNCTION_ARGS)
int ub = dimv[0] + lb[0] - 1;
indx = ub + 1;
/* overflow? */
if (indx < ub)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
}
else
{
/* prepend newelem */
indx = lb[0] - 1;
/* overflow? */
if (indx > lb[0])
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("integer out of range")));
}
}
else if (ARR_NDIM(v) == 0)
@ -108,7 +133,7 @@ array_push(PG_FUNCTION_ARGS)
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
my_extra->element_type = ~element_type;
}
if (my_extra->element_type != element_type)
@ -124,8 +149,8 @@ array_push(PG_FUNCTION_ARGS)
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
result = array_set(v, 1, &indx, newelem, -1,
typlen, typbyval, typalign, &isNull);
result = array_set(v, 1, &indx, newelem, isNull,
-1, typlen, typbyval, typalign);
PG_RETURN_ARRAYTYPE_P(result);
}
@ -141,26 +166,46 @@ array_cat(PG_FUNCTION_ARGS)
{
ArrayType *v1,
*v2;
ArrayType *result;
int *dims,
*lbs,
ndims,
nitems,
ndatabytes,
nbytes;
int *dims1,
*lbs1,
ndims1,
nitems1,
ndatabytes1;
int *dims2,
*lbs2,
ndims2,
nitems2,
ndatabytes2;
int i;
char *dat1,
*dat2;
bits8 *bitmap1,
*bitmap2;
Oid element_type;
Oid element_type1;
Oid element_type2;
ArrayType *result;
int32 dataoffset;
/* Concatenating a null array is a no-op, just return the other input */
if (PG_ARGISNULL(0))
{
if (PG_ARGISNULL(1))
PG_RETURN_NULL();
result = PG_GETARG_ARRAYTYPE_P(1);
PG_RETURN_ARRAYTYPE_P(result);
}
if (PG_ARGISNULL(1))
{
result = PG_GETARG_ARRAYTYPE_P(0);
PG_RETURN_ARRAYTYPE_P(result);
}
v1 = PG_GETARG_ARRAYTYPE_P(0);
v2 = PG_GETARG_ARRAYTYPE_P(1);
@ -223,8 +268,12 @@ array_cat(PG_FUNCTION_ARGS)
dims2 = ARR_DIMS(v2);
dat1 = ARR_DATA_PTR(v1);
dat2 = ARR_DATA_PTR(v2);
ndatabytes1 = ARR_SIZE(v1) - ARR_OVERHEAD(ndims1);
ndatabytes2 = ARR_SIZE(v2) - ARR_OVERHEAD(ndims2);
bitmap1 = ARR_NULLBITMAP(v1);
bitmap2 = ARR_NULLBITMAP(v2);
nitems1 = ArrayGetNItems(ndims1, dims1);
nitems2 = ArrayGetNItems(ndims2, dims2);
ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
if (ndims1 == ndims2)
{
@ -310,20 +359,41 @@ array_cat(PG_FUNCTION_ARGS)
}
}
/* Do this mainly for overflow checking */
nitems = ArrayGetNItems(ndims, dims);
/* build the result array */
ndatabytes = ndatabytes1 + ndatabytes2;
nbytes = ndatabytes + ARR_OVERHEAD(ndims);
if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
{
dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
nbytes = ndatabytes + dataoffset;
}
else
{
dataoffset = 0; /* marker for no null bitmap */
nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
}
result = (ArrayType *) palloc(nbytes);
result->size = nbytes;
result->ndim = ndims;
result->flags = 0;
result->dataoffset = dataoffset;
result->elemtype = element_type;
memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
/* data area is arg1 then arg2 */
memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
/* handle the null bitmap if needed */
if (ARR_HASNULL(result))
{
array_bitmap_copy(ARR_NULLBITMAP(result), 0,
bitmap1, 0,
nitems1);
array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
bitmap2, 0,
nitems2);
}
PG_RETURN_ARRAYTYPE_P(result);
}
@ -347,10 +417,6 @@ create_singleton_array(FunctionCallInfo fcinfo,
int i;
ArrayMetaState *my_extra;
if (element_type == 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid array element type OID: %u", element_type)));
if (ndims < 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@ -379,7 +445,7 @@ create_singleton_array(FunctionCallInfo fcinfo,
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
my_extra->element_type = ~element_type;
}
if (my_extra->element_type != element_type)
@ -395,6 +461,6 @@ create_singleton_array(FunctionCallInfo fcinfo,
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
return construct_md_array(dvalues, ndims, dims, lbs, element_type,
return construct_md_array(dvalues, NULL, ndims, dims, lbs, element_type,
typlen, typbyval, typalign);
}

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/arrayutils.c,v 1.18 2004/12/31 22:01:21 pgsql Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/arrayutils.c,v 1.19 2005/11/17 22:14:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -16,11 +16,17 @@
#include "postgres.h"
#include "utils/array.h"
#include "utils/memutils.h"
/* Convert subscript list into linear element number (from 0) */
/*
* Convert subscript list into linear element number (from 0)
*
* We assume caller has already range-checked the dimensions and subscripts,
* so no overflow is possible.
*/
int
ArrayGetOffset(int n, int *dim, int *lb, int *indx)
ArrayGetOffset(int n, const int *dim, const int *lb, const int *indx)
{
int i,
scale = 1,
@ -34,11 +40,12 @@ ArrayGetOffset(int n, int *dim, int *lb, int *indx)
return offset;
}
/* Same, but subscripts are assumed 0-based, and use a scale array
/*
* Same, but subscripts are assumed 0-based, and use a scale array
* instead of raw dimension data (see mda_get_prod to create scale array)
*/
int
ArrayGetOffset0(int n, int *tup, int *scale)
ArrayGetOffset0(int n, const int *tup, const int *scale)
{
int i,
lin = 0;
@ -48,24 +55,66 @@ ArrayGetOffset0(int n, int *tup, int *scale)
return lin;
}
/* Convert array dimensions into number of elements */
/*
* Convert array dimensions into number of elements
*
* This must do overflow checking, since it is used to validate that a user
* dimensionality request doesn't overflow what we can handle.
*
* We limit array sizes to at most about a quarter billion elements,
* so that it's not necessary to check for overflow in quite so many
* places --- for instance when palloc'ing Datum arrays.
*
* The multiplication overflow check only works on machines that have int64
* arithmetic, but that is nearly all platforms these days, and doing check
* divides for those that don't seems way too expensive.
*/
int
ArrayGetNItems(int ndim, int *dims)
ArrayGetNItems(int ndim, const int *dims)
{
int i,
ret;
int32 ret;
int i;
#define MaxArraySize ((Size) (MaxAllocSize / sizeof(Datum)))
if (ndim <= 0)
return 0;
ret = 1;
for (i = 0; i < ndim; i++)
ret *= dims[i];
return ret;
{
int64 prod;
/* A negative dimension implies that UB-LB overflowed ... */
if (dims[i] < 0)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%d)",
(int) MaxArraySize)));
prod = (int64) ret * (int64) dims[i];
ret = (int32) prod;
if ((int64) ret != prod)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%d)",
(int) MaxArraySize)));
}
Assert(ret >= 0);
if ((Size) ret > MaxArraySize)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("array size exceeds the maximum allowed (%d)",
(int) MaxArraySize)));
return (int) ret;
}
/* Compute ranges (sub-array dimensions) for an array slice */
/*
* Compute ranges (sub-array dimensions) for an array slice
*
* We assume caller has validated slice endpoints, so overflow is impossible
*/
void
mda_get_range(int n, int *span, int *st, int *endp)
mda_get_range(int n, int *span, const int *st, const int *endp)
{
int i;
@ -73,9 +122,13 @@ mda_get_range(int n, int *span, int *st, int *endp)
span[i] = endp[i] - st[i] + 1;
}
/* Compute products of array dimensions, ie, scale factors for subscripts */
/*
* Compute products of array dimensions, ie, scale factors for subscripts
*
* We assume caller has validated dimensions, so overflow is impossible
*/
void
mda_get_prod(int n, int *range, int *prod)
mda_get_prod(int n, const int *range, int *prod)
{
int i;
@ -84,11 +137,14 @@ mda_get_prod(int n, int *range, int *prod)
prod[i] = prod[i + 1] * range[i + 1];
}
/* From products of whole-array dimensions and spans of a sub-array,
/*
* From products of whole-array dimensions and spans of a sub-array,
* compute offset distances needed to step through subarray within array
*
* We assume caller has validated dimensions, so overflow is impossible
*/
void
mda_get_offset_values(int n, int *dist, int *prod, int *span)
mda_get_offset_values(int n, int *dist, const int *prod, const int *span)
{
int i,
j;
@ -102,16 +158,18 @@ mda_get_offset_values(int n, int *dist, int *prod, int *span)
}
}
/*-----------------------------------------------------------------------------
generates the tuple that is lexicographically one greater than the current
n-tuple in "curr", with the restriction that the i-th element of "curr" is
less than the i-th element of "span".
Returns -1 if no next tuple exists, else the subscript position (0..n-1)
corresponding to the dimension to advance along.
-----------------------------------------------------------------------------
*/
/*
* Generates the tuple that is lexicographically one greater than the current
* n-tuple in "curr", with the restriction that the i-th element of "curr" is
* less than the i-th element of "span".
*
* Returns -1 if no next tuple exists, else the subscript position (0..n-1)
* corresponding to the dimension to advance along.
*
* We assume caller has validated dimensions, so overflow is impossible
*/
int
mda_next_tuple(int n, int *curr, int *span)
mda_next_tuple(int n, int *curr, const int *span)
{
int i;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/float.c,v 1.115 2005/10/15 02:49:28 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/float.c,v 1.116 2005/11/17 22:14:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1886,6 +1886,7 @@ check_float8_array(ArrayType *transarray, const char *caller)
*/
if (ARR_NDIM(transarray) != 1 ||
ARR_DIMS(transarray)[0] != 3 ||
ARR_HASNULL(transarray) ||
ARR_ELEMTYPE(transarray) != FLOAT8OID)
elog(ERROR, "%s: expected 3-element float8 array", caller);
return (float8 *) ARR_DATA_PTR(transarray);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/int.c,v 1.68 2005/10/15 02:49:28 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/int.c,v 1.69 2005/11/17 22:14:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -133,7 +133,7 @@ buildint2vector(const int2 *int2s, int n)
*/
result->size = Int2VectorSize(n);
result->ndim = 1;
result->flags = 0;
result->dataoffset = 0; /* never any nulls */
result->elemtype = INT2OID;
result->dim1 = n;
result->lbound1 = 0;
@ -171,7 +171,7 @@ int2vectorin(PG_FUNCTION_ARGS)
result->size = Int2VectorSize(n);
result->ndim = 1;
result->flags = 0;
result->dataoffset = 0; /* never any nulls */
result->elemtype = INT2OID;
result->dim1 = n;
result->lbound1 = 0;
@ -220,9 +220,9 @@ int2vectorrecv(PG_FUNCTION_ARGS)
ObjectIdGetDatum(INT2OID),
Int32GetDatum(-1)));
/* sanity checks: int2vector must be 1-D, no nulls */
if (result->ndim != 1 ||
result->flags != 0 ||
result->elemtype != INT2OID)
if (ARR_NDIM(result) != 1 ||
ARR_HASNULL(result) ||
ARR_ELEMTYPE(result) != INT2OID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid int2vector data")));

View File

@ -14,7 +14,7 @@
* Copyright (c) 1998-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.86 2005/10/15 02:49:29 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.87 2005/11/17 22:14:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -2070,7 +2070,7 @@ do_numeric_accum(ArrayType *transarray, Numeric newval)
/* We assume the input is array of numeric */
deconstruct_array(transarray,
NUMERICOID, -1, false, 'i',
&transdatums, &ndatums);
&transdatums, NULL, &ndatums);
if (ndatums != 3)
elog(ERROR, "expected 3-element numeric array");
N = transdatums[0];
@ -2161,7 +2161,7 @@ numeric_avg(PG_FUNCTION_ARGS)
/* We assume the input is array of numeric */
deconstruct_array(transarray,
NUMERICOID, -1, false, 'i',
&transdatums, &ndatums);
&transdatums, NULL, &ndatums);
if (ndatums != 3)
elog(ERROR, "expected 3-element numeric array");
N = DatumGetNumeric(transdatums[0]);
@ -2197,7 +2197,7 @@ numeric_variance(PG_FUNCTION_ARGS)
/* We assume the input is array of numeric */
deconstruct_array(transarray,
NUMERICOID, -1, false, 'i',
&transdatums, &ndatums);
&transdatums, NULL, &ndatums);
if (ndatums != 3)
elog(ERROR, "expected 3-element numeric array");
N = DatumGetNumeric(transdatums[0]);
@ -2273,7 +2273,7 @@ numeric_stddev(PG_FUNCTION_ARGS)
/* We assume the input is array of numeric */
deconstruct_array(transarray,
NUMERICOID, -1, false, 'i',
&transdatums, &ndatums);
&transdatums, NULL, &ndatums);
if (ndatums != 3)
elog(ERROR, "expected 3-element numeric array");
N = DatumGetNumeric(transdatums[0]);
@ -2511,7 +2511,8 @@ int2_avg_accum(PG_FUNCTION_ARGS)
else
transarray = PG_GETARG_ARRAYTYPE_P_COPY(0);
if (ARR_SIZE(transarray) != ARR_OVERHEAD(1) + sizeof(Int8TransTypeData))
if (ARR_HASNULL(transarray) ||
ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
elog(ERROR, "expected 2-element int8 array");
transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
@ -2538,7 +2539,8 @@ int4_avg_accum(PG_FUNCTION_ARGS)
else
transarray = PG_GETARG_ARRAYTYPE_P_COPY(0);
if (ARR_SIZE(transarray) != ARR_OVERHEAD(1) + sizeof(Int8TransTypeData))
if (ARR_HASNULL(transarray) ||
ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
elog(ERROR, "expected 2-element int8 array");
transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);
@ -2556,7 +2558,8 @@ int8_avg(PG_FUNCTION_ARGS)
Datum countd,
sumd;
if (ARR_SIZE(transarray) != ARR_OVERHEAD(1) + sizeof(Int8TransTypeData))
if (ARR_HASNULL(transarray) ||
ARR_SIZE(transarray) != ARR_OVERHEAD_NONULLS(1) + sizeof(Int8TransTypeData))
elog(ERROR, "expected 2-element int8 array");
transdata = (Int8TransTypeData *) ARR_DATA_PTR(transarray);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/oid.c,v 1.64 2005/10/15 02:49:29 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/oid.c,v 1.65 2005/11/17 22:14:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -176,7 +176,7 @@ buildoidvector(const Oid *oids, int n)
*/
result->size = OidVectorSize(n);
result->ndim = 1;
result->flags = 0;
result->dataoffset = 0; /* never any nulls */
result->elemtype = OIDOID;
result->dim1 = n;
result->lbound1 = 0;
@ -213,7 +213,7 @@ oidvectorin(PG_FUNCTION_ARGS)
result->size = OidVectorSize(n);
result->ndim = 1;
result->flags = 0;
result->dataoffset = 0; /* never any nulls */
result->elemtype = OIDOID;
result->dim1 = n;
result->lbound1 = 0;
@ -262,9 +262,9 @@ oidvectorrecv(PG_FUNCTION_ARGS)
ObjectIdGetDatum(OIDOID),
Int32GetDatum(-1)));
/* sanity checks: oidvector must be 1-D, no nulls */
if (result->ndim != 1 ||
result->flags != 0 ||
result->elemtype != OIDOID)
if (ARR_NDIM(result) != 1 ||
ARR_HASNULL(result) ||
ARR_ELEMTYPE(result) != OIDOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid oidvector data")));

View File

@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.207 2005/10/15 02:49:29 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.208 2005/11/17 22:14:53 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -1107,7 +1107,7 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
/* Extract data from array of int16 */
deconstruct_array(DatumGetArrayTypeP(column_index_array),
INT2OID, 2, true, 's',
&keys, &nKeys);
&keys, NULL, &nKeys);
for (j = 0; j < nKeys; j++)
{

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.157 2005/10/27 02:45:22 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.158 2005/11/17 22:14:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -2434,7 +2434,7 @@ interval_accum(PG_FUNCTION_ARGS)
deconstruct_array(transarray,
INTERVALOID, sizeof(Interval), false, 'd',
&transdatums, &ndatums);
&transdatums, NULL, &ndatums);
if (ndatums != 2)
elog(ERROR, "expected 2-element interval array");
@ -2475,7 +2475,7 @@ interval_avg(PG_FUNCTION_ARGS)
deconstruct_array(transarray,
INTERVALOID, sizeof(Interval), false, 'd',
&transdatums, &ndatums);
&transdatums, NULL, &ndatums);
if (ndatums != 2)
elog(ERROR, "expected 2-element interval array");

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.129 2005/10/15 02:49:31 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.130 2005/11/17 22:14:53 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@ -1896,13 +1896,13 @@ get_attstatsslot(HeapTuple statstuple,
elog(ERROR, "cache lookup failed for type %u", atttype);
typeForm = (Form_pg_type) GETSTRUCT(typeTuple);
/* Deconstruct array into Datum elements */
/* Deconstruct array into Datum elements; NULLs not expected */
deconstruct_array(statarray,
atttype,
typeForm->typlen,
typeForm->typbyval,
typeForm->typalign,
values, nvalues);
values, NULL, nvalues);
/*
* If the element type is pass-by-reference, we now have a bunch of
@ -1944,6 +1944,7 @@ get_attstatsslot(HeapTuple statstuple,
*/
narrayelem = ARR_DIMS(statarray)[0];
if (ARR_NDIM(statarray) != 1 || narrayelem <= 0 ||
ARR_HASNULL(statarray) ||
ARR_ELEMTYPE(statarray) != FLOAT4OID)
elog(ERROR, "stanumbers is not a 1-D float4 array");
*numbers = (float4 *) palloc(narrayelem * sizeof(float4));

View File

@ -7,7 +7,7 @@
* Copyright (c) 2002-2005, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.26 2005/10/15 02:49:32 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.27 2005/11/17 22:14:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -686,16 +686,18 @@ get_func_result_name(Oid functionId)
numargs = ARR_DIMS(arr)[0];
if (ARR_NDIM(arr) != 1 ||
numargs < 0 ||
ARR_HASNULL(arr) ||
ARR_ELEMTYPE(arr) != CHAROID)
elog(ERROR, "proargmodes is not a 1-D char array");
argmodes = (char *) ARR_DATA_PTR(arr);
arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */
if (ARR_NDIM(arr) != 1 ||
ARR_DIMS(arr)[0] != numargs ||
ARR_HASNULL(arr) ||
ARR_ELEMTYPE(arr) != TEXTOID)
elog(ERROR, "proargnames is not a 1-D text array");
deconstruct_array(arr, TEXTOID, -1, false, 'i',
&argnames, &nargnames);
&argnames, NULL, &nargnames);
Assert(nargnames == numargs);
/* scan for output argument(s) */
@ -818,12 +820,14 @@ build_function_result_tupdesc_d(Datum proallargtypes,
numargs = ARR_DIMS(arr)[0];
if (ARR_NDIM(arr) != 1 ||
numargs < 0 ||
ARR_HASNULL(arr) ||
ARR_ELEMTYPE(arr) != OIDOID)
elog(ERROR, "proallargtypes is not a 1-D Oid array");
argtypes = (Oid *) ARR_DATA_PTR(arr);
arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
if (ARR_NDIM(arr) != 1 ||
ARR_DIMS(arr)[0] != numargs ||
ARR_HASNULL(arr) ||
ARR_ELEMTYPE(arr) != CHAROID)
elog(ERROR, "proargmodes is not a 1-D char array");
argmodes = (char *) ARR_DATA_PTR(arr);
@ -832,10 +836,11 @@ build_function_result_tupdesc_d(Datum proallargtypes,
arr = DatumGetArrayTypeP(proargnames); /* ensure not toasted */
if (ARR_NDIM(arr) != 1 ||
ARR_DIMS(arr)[0] != numargs ||
ARR_HASNULL(arr) ||
ARR_ELEMTYPE(arr) != TEXTOID)
elog(ERROR, "proargnames is not a 1-D text array");
deconstruct_array(arr, TEXTOID, -1, false, 'i',
&argnames, &nargnames);
&argnames, NULL, &nargnames);
Assert(nargnames == numargs);
}

View File

@ -10,7 +10,7 @@
* Written by Peter Eisentraut <peter_e@gmx.net>.
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.299 2005/11/04 23:50:30 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.300 2005/11/17 22:14:54 tgl Exp $
*
*--------------------------------------------------------------------
*/
@ -876,6 +876,16 @@ static struct config_bool ConfigureNamesBool[] =
&check_function_bodies,
true, NULL, NULL
},
{
{"array_nulls", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Enable input of NULL elements in arrays."),
gettext_noop("When turned on, unquoted NULL in an array input "
"value means a NULL value; "
"otherwise it is taken literally.")
},
&Array_nulls,
true, NULL, NULL
},
{
{"default_with_oids", PGC_USERSET, COMPAT_OPTIONS_PREVIOUS,
gettext_noop("Create new tables with OIDs by default."),
@ -5383,14 +5393,13 @@ GUCArrayAdd(ArrayType *array, const char *name, const char *value)
}
}
isnull = false;
a = array_set(array, 1, &index,
datum,
-1 /* varlenarray */ ,
false,
-1 /* varlena array */ ,
-1 /* TEXT's typlen */ ,
false /* TEXT's typbyval */ ,
'i' /* TEXT's typalign */ ,
&isnull);
'i' /* TEXT's typalign */ );
}
else
a = construct_array(&datum, 1,
@ -5456,14 +5465,13 @@ GUCArrayDelete(ArrayType *array, const char *name)
/* else add it to the output array */
if (newarray)
{
isnull = false;
newarray = array_set(newarray, 1, &index,
d,
false,
-1 /* varlenarray */ ,
-1 /* TEXT's typlen */ ,
false /* TEXT's typbyval */ ,
'i' /* TEXT's typalign */ ,
&isnull);
'i' /* TEXT's typalign */ );
}
else
newarray = construct_array(&d, 1,

View File

@ -413,10 +413,11 @@
# - Previous Postgres Versions -
#add_missing_from = off
#regex_flavor = advanced # advanced, extended, or basic
#sql_inheritance = on
#array_nulls = on
#default_with_oids = off
#escape_string_warning = off
#regex_flavor = advanced # advanced, extended, or basic
#sql_inheritance = on
# - Other Platforms & Clients -

View File

@ -12,7 +12,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/c.h,v 1.190 2005/10/15 02:49:41 momjian Exp $
* $PostgreSQL: pgsql/src/include/c.h,v 1.191 2005/11/17 22:14:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -442,8 +442,8 @@ typedef struct varlena VarChar; /* var-length char, ie SQL varchar(n) */
typedef struct
{
int32 size; /* these fields must match ArrayType! */
int ndim;
int flags;
int ndim; /* always 1 for int2vector */
int32 dataoffset; /* always 0 for int2vector */
Oid elemtype;
int dim1;
int lbound1;
@ -453,8 +453,8 @@ typedef struct
typedef struct
{
int32 size; /* these fields must match ArrayType! */
int ndim;
int flags;
int ndim; /* always 1 for oidvector */
int32 dataoffset; /* always 0 for oidvector */
Oid elemtype;
int dim1;
int lbound1;

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.306 2005/11/07 17:36:46 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.307 2005/11/17 22:14:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200511071
#define CATALOG_VERSION_NO 200511171
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.388 2005/11/07 17:36:46 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.389 2005/11/17 22:14:54 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@ -995,11 +995,11 @@ DATA(insert OID = 2091 ( array_lower PGNSP PGUID 12 f f t f i 2 23 "2277 23"
DESCR("array lower dimension");
DATA(insert OID = 2092 ( array_upper PGNSP PGUID 12 f f t f i 2 23 "2277 23" _null_ _null_ _null_ array_upper - _null_ ));
DESCR("array upper dimension");
DATA(insert OID = 378 ( array_append PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" _null_ _null_ _null_ array_push - _null_ ));
DATA(insert OID = 378 ( array_append PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" _null_ _null_ _null_ array_push - _null_ ));
DESCR("append element onto end of array");
DATA(insert OID = 379 ( array_prepend PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" _null_ _null_ _null_ array_push - _null_ ));
DATA(insert OID = 379 ( array_prepend PGNSP PGUID 12 f f f f i 2 2277 "2283 2277" _null_ _null_ _null_ array_push - _null_ ));
DESCR("prepend element onto front of array");
DATA(insert OID = 383 ( array_cat PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" _null_ _null_ _null_ array_cat - _null_ ));
DATA(insert OID = 383 ( array_cat PGNSP PGUID 12 f f f f i 2 2277 "2277 2277" _null_ _null_ _null_ array_cat - _null_ ));
DESCR("concatenate two arrays");
DATA(insert OID = 384 ( array_coerce PGNSP PGUID 12 f f t f s 1 2277 "2277" _null_ _null_ _null_ array_type_coerce - _null_ ));
DESCR("coerce array to another array type");

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.86 2005/11/04 17:25:15 tgl Exp $
* $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.87 2005/11/17 22:14:55 tgl Exp $
*
* NOTES
* An ACL array is simply an array of AclItems, representing the union
@ -97,7 +97,7 @@ typedef ArrayType Acl;
#define ACL_NUM(ACL) (ARR_DIMS(ACL)[0])
#define ACL_DAT(ACL) ((AclItem *) ARR_DATA_PTR(ACL))
#define ACL_N_SIZE(N) (ARR_OVERHEAD(1) + ((N) * sizeof(AclItem)))
#define ACL_N_SIZE(N) (ARR_OVERHEAD_NONULLS(1) + ((N) * sizeof(AclItem)))
#define ACL_SIZE(ACL) ARR_SIZE(ACL)
/*
@ -107,7 +107,7 @@ typedef ArrayType IdList;
#define IDLIST_NUM(IDL) (ARR_DIMS(IDL)[0])
#define IDLIST_DAT(IDL) ((Oid *) ARR_DATA_PTR(IDL))
#define IDLIST_N_SIZE(N) (ARR_OVERHEAD(1) + ((N) * sizeof(Oid)))
#define IDLIST_N_SIZE(N) (ARR_OVERHEAD_NONULLS(1) + ((N) * sizeof(Oid)))
#define IDLIST_SIZE(IDL) ARR_SIZE(IDL)
/*

View File

@ -1,16 +1,55 @@
/*-------------------------------------------------------------------------
*
* array.h
* Utilities for the new array code. Contains prototypes from the
* following files:
* utils/adt/arrayfuncs.c
* utils/adt/arrayutils.c
* Declarations for Postgres arrays.
*
* A standard varlena array has the following internal structure:
* <size> - total number of bytes (also, TOAST info flags)
* <ndim> - number of dimensions of the array
* <dataoffset> - offset to stored data, or 0 if no nulls bitmap
* <elemtype> - element type OID
* <dimensions> - length of each array axis (C array of int)
* <lower bnds> - lower boundary of each dimension (C array of int)
* <null bitmap> - bitmap showing locations of nulls (OPTIONAL)
* <actual data> - whatever is the stored data
*
* The <dimensions> and <lower bnds> arrays each have ndim elements.
*
* The <null bitmap> may be omitted if the array contains no NULL elements.
* If it is absent, the <dataoffset> field is zero and the offset to the
* stored data must be computed on-the-fly. If the bitmap is present,
* <dataoffset> is nonzero and is equal to the offset from the array start
* to the first data element (including any alignment padding). The bitmap
* follows the same conventions as tuple null bitmaps, ie, a 1 indicates
* a non-null entry and the LSB of each bitmap byte is used first.
*
* The actual data starts on a MAXALIGN boundary. Individual items in the
* array are aligned as specified by the array element type. They are
* stored in row-major order (last subscript varies most rapidly).
*
* NOTE: it is important that array elements of toastable datatypes NOT be
* toasted, since the tupletoaster won't know they are there. (We could
* support compressed toasted items; only out-of-line items are dangerous.
* However, it seems preferable to store such items uncompressed and allow
* the toaster to compress the whole array as one input.)
*
*
* The OIDVECTOR and INT2VECTOR datatypes are storage-compatible with
* generic arrays, but they support only one-dimensional arrays with no
* nulls (and no null bitmap).
*
* There are also some "fixed-length array" datatypes, such as NAME and
* POINT. These are simply a sequence of a fixed number of items each
* of a fixed-length datatype, with no overhead; the item size must be
* a multiple of its alignment requirement, because we do no padding.
* We support subscripting on these types, but array_in() and array_out()
* only work with varlena arrays.
*
*
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/array.h,v 1.55 2005/10/15 02:49:46 momjian Exp $
* $PostgreSQL: pgsql/src/include/utils/array.h,v 1.56 2005/11/17 22:14:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -30,8 +69,7 @@ typedef struct
{
int32 size; /* total array size (varlena requirement) */
int ndim; /* # of dimensions */
int flags; /* implementation flags */
/* flags field is currently unused, always zero. */
int32 dataoffset; /* offset to data, or 0 if no bitmap */
Oid elemtype; /* element type OID */
} ArrayType;
@ -39,9 +77,10 @@ typedef struct ArrayBuildState
{
MemoryContext mcontext; /* where all the temp stuff is kept */
Datum *dvalues; /* array of accumulated Datums */
bool *dnulls; /* array of is-null flags for Datums */
/*
* The allocated size of dvalues[] is always a multiple of
* The allocated size of dvalues[] and dnulls[] is always a multiple of
* ARRAY_ELEMS_CHUNKSIZE
*/
#define ARRAY_ELEMS_CHUNKSIZE 64
@ -98,29 +137,48 @@ typedef struct ArrayMapState
*
* Unlike C, the default lower bound is 1.
*/
#define ARR_SIZE(a) (((ArrayType *) (a))->size)
#define ARR_NDIM(a) (((ArrayType *) (a))->ndim)
#define ARR_ELEMTYPE(a) (((ArrayType *) (a))->elemtype)
#define ARR_SIZE(a) ((a)->size)
#define ARR_NDIM(a) ((a)->ndim)
#define ARR_HASNULL(a) ((a)->dataoffset != 0)
#define ARR_ELEMTYPE(a) ((a)->elemtype)
#define ARR_DIMS(a) \
((int *) (((char *) (a)) + sizeof(ArrayType)))
#define ARR_LBOUND(a) \
((int *) (((char *) (a)) + sizeof(ArrayType) + \
(sizeof(int) * ARR_NDIM(a))))
sizeof(int) * ARR_NDIM(a)))
#define ARR_NULLBITMAP(a) \
(ARR_HASNULL(a) ? \
(bits8 *) (((char *) (a)) + sizeof(ArrayType) + \
2 * sizeof(int) * ARR_NDIM(a)) \
: (bits8 *) NULL)
/*
* The total array header size for an array of dimension n (in bytes).
* The total array header size (in bytes) for an array with the specified
* number of dimensions and total number of items.
*/
#define ARR_OVERHEAD(n) \
(MAXALIGN(sizeof(ArrayType) + 2 * sizeof(int) * (n)))
#define ARR_OVERHEAD_NONULLS(ndims) \
MAXALIGN(sizeof(ArrayType) + 2 * sizeof(int) * (ndims))
#define ARR_OVERHEAD_WITHNULLS(ndims, nitems) \
MAXALIGN(sizeof(ArrayType) + 2 * sizeof(int) * (ndims) + \
((nitems) + 7) / 8)
#define ARR_DATA_OFFSET(a) \
(ARR_HASNULL(a) ? (a)->dataoffset : ARR_OVERHEAD_NONULLS(ARR_NDIM(a)))
/*
* Returns a pointer to the actual array data.
*/
#define ARR_DATA_PTR(a) \
(((char *) (a)) + ARR_OVERHEAD(ARR_NDIM(a)))
(((char *) (a)) + ARR_DATA_OFFSET(a))
/*
* GUC parameter
*/
extern bool Array_nulls;
/*
* prototypes for functions defined in arrayfuncs.c
*/
@ -145,37 +203,40 @@ extern Datum array_larger(PG_FUNCTION_ARGS);
extern Datum array_smaller(PG_FUNCTION_ARGS);
extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
int arraylen, int elmlen, bool elmbyval, char elmalign,
int arraytyplen, int elmlen, bool elmbyval, char elmalign,
bool *isNull);
extern ArrayType *array_set(ArrayType *array, int nSubscripts, int *indx,
Datum dataValue,
int arraylen, int elmlen, bool elmbyval, char elmalign,
bool *isNull);
Datum dataValue, bool isNull,
int arraytyplen, int elmlen, bool elmbyval, char elmalign);
extern ArrayType *array_get_slice(ArrayType *array, int nSubscripts,
int *upperIndx, int *lowerIndx,
int arraylen, int elmlen, bool elmbyval, char elmalign,
bool *isNull);
int arraytyplen, int elmlen, bool elmbyval, char elmalign);
extern ArrayType *array_set_slice(ArrayType *array, int nSubscripts,
int *upperIndx, int *lowerIndx,
ArrayType *srcArray,
int arraylen, int elmlen, bool elmbyval, char elmalign,
bool *isNull);
ArrayType *srcArray, bool isNull,
int arraytyplen, int elmlen, bool elmbyval, char elmalign);
extern Datum array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
ArrayMapState *amstate);
extern void array_bitmap_copy(bits8 *destbitmap, int destoffset,
const bits8 *srcbitmap, int srcoffset,
int nitems);
extern ArrayType *construct_array(Datum *elems, int nelems,
Oid elmtype,
int elmlen, bool elmbyval, char elmalign);
extern ArrayType *construct_md_array(Datum *elems,
bool *nulls,
int ndims,
int *dims,
int *lbs,
Oid elmtype, int elmlen, bool elmbyval, char elmalign);
extern ArrayType *construct_empty_array(Oid elmtype);
extern void deconstruct_array(ArrayType *array,
Oid elmtype,
int elmlen, bool elmbyval, char elmalign,
Datum **elemsp, int *nelemsp);
Datum **elemsp, bool **nullsp, int *nelemsp);
extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
Datum dvalue, bool disnull,
Oid element_type,
@ -189,13 +250,13 @@ extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
* prototypes for functions defined in arrayutils.c
*/
extern int ArrayGetOffset(int n, int *dim, int *lb, int *indx);
extern int ArrayGetOffset0(int n, int *tup, int *scale);
extern int ArrayGetNItems(int ndims, int *dims);
extern void mda_get_range(int n, int *span, int *st, int *endp);
extern void mda_get_prod(int n, int *range, int *prod);
extern void mda_get_offset_values(int n, int *dist, int *prod, int *span);
extern int mda_next_tuple(int n, int *curr, int *span);
extern int ArrayGetOffset(int n, const int *dim, const int *lb, const int *indx);
extern int ArrayGetOffset0(int n, const int *tup, const int *scale);
extern int ArrayGetNItems(int ndim, const int *dims);
extern void mda_get_range(int n, int *span, const int *st, const int *endp);
extern void mda_get_prod(int n, const int *range, int *prod);
extern void mda_get_offset_values(int n, int *dist, const int *prod, const int *span);
extern int mda_next_tuple(int n, int *curr, const int *span);
/*
* prototypes for functions defined in array_userfuncs.c

View File

@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.94 2005/10/15 02:49:49 momjian Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.95 2005/11/17 22:14:55 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -787,6 +787,7 @@ fetchArgInfo(HeapTuple procTup, Oid **p_argtypes, char ***p_argnames,
numargs = ARR_DIMS(arr)[0];
if (ARR_NDIM(arr) != 1 ||
numargs < 0 ||
ARR_HASNULL(arr) ||
ARR_ELEMTYPE(arr) != OIDOID)
elog(ERROR, "proallargtypes is not a 1-D Oid array");
Assert(numargs >= procStruct->pronargs);
@ -814,7 +815,7 @@ fetchArgInfo(HeapTuple procTup, Oid **p_argtypes, char ***p_argnames,
{
deconstruct_array(DatumGetArrayTypeP(proargnames),
TEXTOID, -1, false, 'i',
&elems, &nelems);
&elems, NULL, &nelems);
if (nelems != numargs) /* should not happen */
elog(ERROR, "proargnames must have the same number of elements as the function has arguments");
*p_argnames = (char **) palloc(sizeof(char *) * numargs);
@ -834,6 +835,7 @@ fetchArgInfo(HeapTuple procTup, Oid **p_argtypes, char ***p_argnames,
arr = DatumGetArrayTypeP(proargmodes); /* ensure not toasted */
if (ARR_NDIM(arr) != 1 ||
ARR_DIMS(arr)[0] != numargs ||
ARR_HASNULL(arr) ||
ARR_ELEMTYPE(arr) != CHAROID)
elog(ERROR, "proargmodes is not a 1-D char array");
*p_argmodes = (char *) palloc(numargs * sizeof(char));

View File

@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.154 2005/10/24 15:10:22 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.155 2005/11/17 22:14:55 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -3331,11 +3331,7 @@ exec_assign_value(PLpgSQL_execstate * estate,
if (arraytyplen > 0) /* fixed-length array? */
return;
oldarrayval = construct_md_array(NULL, 0, NULL, NULL,
arrayelemtypeid,
elemtyplen,
elemtypbyval,
elemtypalign);
oldarrayval = construct_empty_array(arrayelemtypeid);
}
else
oldarrayval = (ArrayType *) DatumGetPointer(oldarraydatum);
@ -3354,18 +3350,11 @@ exec_assign_value(PLpgSQL_execstate * estate,
nsubscripts,
subscriptvals,
coerced_value,
*isNull,
arraytyplen,
elemtyplen,
elemtypbyval,
elemtypalign,
isNull);
/*
* Assign it to the base variable.
*/
exec_assign_value(estate, target,
PointerGetDatum(newarrayval),
arraytypeid, isNull);
elemtypalign);
/*
* Avoid leaking the result of exec_simple_cast_value, if it
@ -3374,6 +3363,15 @@ exec_assign_value(PLpgSQL_execstate * estate,
if (!*isNull && coerced_value != value && !elemtypbyval)
pfree(DatumGetPointer(coerced_value));
/*
* Assign the new array to the base variable. It's never
* NULL at this point.
*/
*isNull = false;
exec_assign_value(estate, target,
PointerGetDatum(newarrayval),
arraytypeid, isNull);
/*
* Avoid leaking the modified array value, too.
*/

View File

@ -63,9 +63,9 @@ SELECT a[1:3],
FROM arrtest;
a | b | c | d
------------+-----------------+-----------+---------------
{1,2,3} | {{{0,0},{1,2}}} | |
{11,12,23} | | {foobar} | {{elt1,elt2}}
| | {foo,bar} |
{1,2,3} | {{{0,0},{1,2}}} | {} | {}
{11,12,23} | {} | {foobar} | {{elt1,elt2}}
{} | {} | {foo,bar} | {}
(3 rows)
SELECT array_dims(a) AS a,array_dims(b) AS b,array_dims(c) AS c
@ -111,9 +111,36 @@ SELECT a[1:3],
FROM arrtest;
a | b | c | d
------------+-----------------------+-------------------+----------
{16,25,3} | {{{113,142},{1,147}}} | |
| | {foo,new_word} |
{16,25,23} | | {foobar,new_word} | {{elt2}}
{16,25,3} | {{{113,142},{1,147}}} | {} | {}
{} | {} | {foo,new_word} | {}
{16,25,23} | {} | {foobar,new_word} | {{elt2}}
(3 rows)
INSERT INTO arrtest(a) VALUES('{1,null,3}');
SELECT a FROM arrtest;
a
---------------
{16,25,3,4,5}
{}
{16,25,23}
{1,NULL,3}
(4 rows)
UPDATE arrtest SET a[4] = NULL WHERE a[2] IS NULL;
SELECT a FROM arrtest WHERE a[2] IS NULL;
a
-----------------
[4:4]={NULL}
{1,NULL,3,NULL}
(2 rows)
DELETE FROM arrtest WHERE a[2] IS NULL AND b IS NULL;
SELECT a,b,c FROM arrtest;
a | b | c
---------------+-----------------------+-------------------
{16,25,3,4,5} | {{{113,142},{1,147}}} | {}
{16,25,23} | {{3,4},{4,5}} | {foobar,new_word}
[4:4]={NULL} | {3,4} | {foo,new_word}
(3 rows)
--
@ -176,6 +203,19 @@ SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
{1.15,1.15,1.18,1.21,1.24,1.26,1.26,1.3,1.32}
(1 row)
-- with nulls
SELECT '{1,null,3}'::int[];
int4
------------
{1,NULL,3}
(1 row)
SELECT ARRAY[1,NULL,3];
array
------------
{1,NULL,3}
(1 row)
-- functions
SELECT array_append(array[42], 6) AS "{42,6}";
{42,6}
@ -355,6 +395,55 @@ select 33 * any ('{1,2,3}');
ERROR: op ANY/ALL (array) requires operator to yield boolean
select 33 * any (44);
ERROR: op ANY/ALL (array) requires array on right side
-- nulls
select 33 = any (null::int[]);
?column?
----------
(1 row)
select null::int = any ('{1,2,3}');
?column?
----------
(1 row)
select 33 = any ('{1,null,3}');
?column?
----------
(1 row)
select 33 = any ('{1,null,33}');
?column?
----------
t
(1 row)
select 33 = all (null::int[]);
?column?
----------
(1 row)
select null::int = all ('{1,2,3}');
?column?
----------
(1 row)
select 33 = all ('{1,null,3}');
?column?
----------
f
(1 row)
select 33 = all ('{33,null,33}');
?column?
----------
(1 row)
-- test indexes on arrays
create temp table arr_tbl (f1 int[] unique);
NOTICE: CREATE TABLE / UNIQUE will create implicit index "arr_tbl_f1_key" for table "arr_tbl"

View File

@ -91,7 +91,7 @@ select testint4arr[1], testtextarr[2:2] from domarrtest;
testint4arr | testtextarr
-------------+-------------
2 | {{c,d}}
|
| {}
2 | {{c,d}}
2 | {{c}}
| {{d,e,f}}

View File

@ -83,6 +83,13 @@ SELECT a[1:3],
d[1:1][2:2]
FROM arrtest;
INSERT INTO arrtest(a) VALUES('{1,null,3}');
SELECT a FROM arrtest;
UPDATE arrtest SET a[4] = NULL WHERE a[2] IS NULL;
SELECT a FROM arrtest WHERE a[2] IS NULL;
DELETE FROM arrtest WHERE a[2] IS NULL AND b IS NULL;
SELECT a,b,c FROM arrtest;
--
-- array expressions and operators
--
@ -128,6 +135,10 @@ SELECT ARRAY[[[[[['hello'],['world']]]]]];
SELECT ARRAY[ARRAY['hello'],ARRAY['world']];
SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
-- with nulls
SELECT '{1,null,3}'::int[];
SELECT ARRAY[1,NULL,3];
-- functions
SELECT array_append(array[42], 6) AS "{42,6}";
SELECT array_prepend(6, array[42]) AS "{6,42}";
@ -168,6 +179,15 @@ select 33.4 > all (array[1,2,3]);
-- errors
select 33 * any ('{1,2,3}');
select 33 * any (44);
-- nulls
select 33 = any (null::int[]);
select null::int = any ('{1,2,3}');
select 33 = any ('{1,null,3}');
select 33 = any ('{1,null,33}');
select 33 = all (null::int[]);
select null::int = all ('{1,2,3}');
select 33 = all ('{1,null,3}');
select 33 = all ('{33,null,33}');
-- test indexes on arrays
create temp table arr_tbl (f1 int[] unique);