Create real array comparison functions (that use the element datatype's

comparison functions), replacing the highly bogus bitwise array_eq.  Create
a btree index opclass for ANYARRAY --- it is now possible to create indexes
on array columns.
Arrange to cache the results of catalog lookups across multiple array
operations, instead of repeating the lookups on every call.
Add string_to_array and array_to_string functions.
Remove singleton_array, array_accum, array_assign, and array_subscript
functions, since these were for proof-of-concept and not intended to become
supported functions.
Minor adjustments to behavior in some corner cases with empty or
zero-dimensional arrays.

Joe Conway (with some editorializing by Tom Lane).
This commit is contained in:
Tom Lane 2003-06-27 00:33:26 +00:00
parent 0c985ab5a8
commit b3c0551eda
28 changed files with 1731 additions and 557 deletions

View File

@ -1,4 +1,4 @@
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/array.sgml,v 1.27 2003/06/25 21:30:25 momjian Exp $ -->
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/array.sgml,v 1.28 2003/06/27 00:33:25 tgl Exp $ -->
<sect1 id="arrays">
<title>Arrays</title>
@ -60,14 +60,74 @@ 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.
</para>
<para>
This can lead to surprising results. For example, the result of the
previous two inserts looks like this:
<programlisting>
SELECT * FROM sal_emp;
name | pay_by_quarter | schedule
-------+---------------------------+--------------------
Bill | {10000,10000,10000,10000} | {{meeting},{""}}
Carol | {20000,25000,25000,25000} | {{talk},{meeting}}
(2 rows)
</programlisting>
Because the <literal>[2][2]</literal> element of
<structfield>schedule</structfield> is missing in each of the
<command>INSERT</command> statements, the <literal>[1][2]</literal>
element is discarded.
</para>
<note>
<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. Fixing this is on the to-do list.
Fixing this is on the to-do list.
</para>
</note>
<para>
The <command>ARRAY</command> expression syntax may also be used:
<programlisting>
INSERT INTO sal_emp
VALUES ('Bill',
ARRAY[10000, 10000, 10000, 10000],
ARRAY[['meeting', 'lunch'], ['','']]);
INSERT INTO sal_emp
VALUES ('Carol',
ARRAY[20000, 25000, 25000, 25000],
ARRAY[['talk', 'consult'], ['meeting', '']]);
SELECT * FROM sal_emp;
name | pay_by_quarter | schedule
-------+---------------------------+-------------------------------
Bill | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}}
Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}}
(2 rows)
</programlisting>
Note that with this syntax, multidimensional arrays must have matching
extents for each dimension. This eliminates the missing-array-elements
problem above. For example:
<programlisting>
INSERT INTO sal_emp
VALUES ('Carol',
ARRAY[20000, 25000, 25000, 25000],
ARRAY[['talk', 'consult'], ['meeting']]);
ERROR: Multidimensional arrays must have array expressions with matching dimensions
</programlisting>
Also notice that string literals are single quoted instead of double quoted.
</para>
<note>
<para>
The examples in the rest of this section are based on the
<command>ARRAY</command> expression syntax <command>INSERT</command>s.
</para>
</note>
</sect2>
<sect2>
@ -132,11 +192,30 @@ SELECT schedule[1:2][1] FROM sal_emp WHERE name = 'Bill';
</programlisting>
with the same result. An array subscripting operation is always taken to
represent an array slice if any of the subscripts are written in the
form
represent an array slice if any of the subscripts are written in the form
<literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
A lower bound of 1 is assumed for any subscript where only one value
is specified.
is specified. Another example follows:
<programlisting>
SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
schedule
---------------------------
{{meeting,lunch},{"",""}}
(1 row)
</programlisting>
</para>
<para>
Additionally, we can also access a single arbitrary array element of
a one-dimensional array with the <function>array_subscript</function>
function:
<programlisting>
SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
array_subscript
-----------------
10000
(1 row)
</programlisting>
</para>
<para>
@ -147,7 +226,23 @@ UPDATE sal_emp SET pay_by_quarter = '{25000,25000,27000,27000}'
WHERE name = 'Carol';
</programlisting>
or updated at a single element:
or using the <command>ARRAY</command> expression syntax:
<programlisting>
UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
WHERE name = 'Carol';
</programlisting>
<note>
<para>
Anywhere you can use the <quote>curly braces</quote> array syntax,
you can also use the <command>ARRAY</command> expression syntax. The
remainder of this section will illustrate only one or the other, but
not both.
</para>
</note>
An array may also be updated at a single element:
<programlisting>
UPDATE sal_emp SET pay_by_quarter[4] = 15000
@ -160,6 +255,14 @@ UPDATE sal_emp SET pay_by_quarter[4] = 15000
UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
WHERE name = 'Carol';
</programlisting>
A one-dimensional array may also be updated with the
<function>array_assign</function> function:
<programlisting>
UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000)
WHERE name = 'Bill';
</programListing>
</para>
<para>
@ -178,6 +281,88 @@ UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
create an array with subscript values running from -2 to 7.
</para>
<para>
An array can also be enlarged by using the concatenation operator,
<command>||</command>.
<programlisting>
SELECT ARRAY[1,2] || ARRAY[3,4];
?column?
---------------
{{1,2},{3,4}}
(1 row)
SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]];
?column?
---------------------
{{5,6},{1,2},{3,4}}
(1 row)
</programlisting>
The concatenation operator allows a single element to be pushed on to the
beginning or end of a one-dimensional array. It also allows two
<replaceable>N</>-dimensional arrays, or an <replaceable>N</>-dimensional
and an <replaceable>N+1</>-dimensional array. In the former case, the two
<replaceable>N</>-dimension arrays become outer elements of an
<replaceable>N+1</>-dimensional array. In the latter, the
<replaceable>N</>-dimensional array is added as either the first or last
outer element of the <replaceable>N+1</>-dimensional array.
The array is extended in the direction of the push. Hence, by pushing
onto the beginning of an array with a one-based subscript, a zero-based
subscript array is created:
<programlisting>
SELECT array_dims(t.f) FROM (SELECT 1 || ARRAY[2,3] AS f) AS t;
array_dims
------------
[0:2]
(1 row)
</programlisting>
</para>
<para>
An array can also be enlarged by using the functions
<function>array_prepend</function>, <function>array_append</function>,
or <function>array_cat</function>. The first two only support one-dimensional
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
in implementing the concatenation operator. However, they may be directly
useful in the creation of user-defined aggregates. Some examples:
<programlisting>
SELECT array_prepend(1, ARRAY[2,3]);
array_prepend
---------------
{1,2,3}
(1 row)
SELECT array_append(ARRAY[1,2], 3);
array_append
--------------
{1,2,3}
(1 row)
SELECT array_cat(ARRAY[1,2], ARRAY[3,4]);
array_cat
---------------
{{1,2},{3,4}}
(1 row)
SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]);
array_cat
---------------------
{{1,2},{3,4},{5,6}}
(1 row)
SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]);
array_cat
---------------------
{{5,6},{1,2},{3,4}}
</programlisting>
</para>
<para>
The syntax for <command>CREATE TABLE</command> allows fixed-length
arrays to be defined:
@ -193,6 +378,16 @@ CREATE TABLE tictactoe (
length.
</para>
<para>
An alternative syntax for one-dimensional arrays may be used.
<structfield>pay_by_quarter</structfield> could have been defined as:
<programlisting>
pay_by_quarter integer ARRAY[4],
</programlisting>
This syntax may <emphasis>only</emphasis> be used with the integer
constant to denote the array size.
</para>
<para>
Actually, the current implementation does not enforce the declared
number of dimensions either. Arrays of a particular element type are
@ -300,6 +495,72 @@ SELECT * FROM sal_emp WHERE pay_by_quarter **= 10000;
is not ignored, however: after skipping leading whitespace, everything
up to the next right brace or delimiter is taken as the item value.
</para>
<para>
As illustrated earlier in this chapter, arrays may also be represented
using the <command>ARRAY</command> expression syntax. This representation
of an array value consists of items that are interpreted according to the
I/O conversion rules for the array's element type, plus decoration that
indicates the array structure. The decoration consists of the keyword
<command>ARRAY</command> and square brackets (<literal>[</> and
<literal>]</>) around the array values, plus delimiter characters between
adjacent items. The delimiter character is always a comma (<literal>,</>).
When representing multidimensional arrays, the keyword
<command>ARRAY</command> is only necessary for the outer level. For example,
<literal>'{{"hello world", "happy birthday"}}'</literal> could be written as:
<programlisting>
SELECT ARRAY[['hello world', 'happy birthday']];
array
------------------------------------
{{"hello world","happy birthday"}}
(1 row)
</programlisting>
or it also could be written as:
<programlisting>
SELECT ARRAY[ARRAY['hello world', 'happy birthday']];
array
------------------------------------
{{"hello world","happy birthday"}}
(1 row)
</programlisting>
</para>
<para>
A final method to represent an array, is through an
<command>ARRAY</command> sub-select expression. For example:
<programlisting>
SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%');
?column?
-------------------------------------------------------------
{2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31}
(1 row)
</programlisting>
The sub-select may <emphasis>only</emphasis> return a single column. The
resulting one-dimensional array will have an element for each row in the
sub-select result, with an element type matching that of the sub-select's
target column.
</para>
<para>
Arrays may be cast from one type to another in similar fashion to other
data types:
<programlisting>
SELECT ARRAY[1,2,3]::oid[];
array
---------
{1,2,3}
(1 row)
SELECT CAST(ARRAY[1,2,3] AS float8[]);
array
---------
{1,2,3}
(1 row)
</programlisting>
</para>
</sect2>
<sect2>
@ -317,6 +578,14 @@ SELECT * FROM sal_emp WHERE pay_by_quarter **= 10000;
that would otherwise be taken as array syntax or ignorable white space.
</para>
<note>
<para>
The discussion in the preceding paragraph with respect to double quoting does
not pertain to the <command>ARRAY</command> expression syntax. In that case,
each element is quoted exactly as any other literal value of the element type.
</para>
</note>
<para>
The array output routine will put double quotes around element values
if they are empty strings or contain curly braces, delimiter characters,

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.156 2003/06/25 21:30:25 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.157 2003/06/27 00:33:25 tgl Exp $
PostgreSQL documentation
-->
@ -6962,6 +6962,203 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
</sect1>
<sect1 id="functions-array">
<title>Array Functions</title>
<para>
<xref linkend="array-operators-table"> shows the operators
available for the <type>array</type> types.
</para>
<table id="array-operators-table">
<title><type>array</type> Operators</title>
<tgroup cols="4">
<thead>
<row>
<entry>Operator</entry>
<entry>Description</entry>
<entry>Example</entry>
<entry>Result</entry>
</row>
</thead>
<tbody>
<row>
<entry> <literal>=</literal> </entry>
<entry>equals</entry>
<entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry>
<entry><literal>t</literal></entry>
</row>
<row>
<entry> <literal>||</literal> </entry>
<entry>array-to-array concatenation</entry>
<entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry>
<entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
</row>
<row>
<entry> <literal>||</literal> </entry>
<entry>array-to-array concatenation</entry>
<entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry>
<entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry>
</row>
<row>
<entry> <literal>||</literal> </entry>
<entry>element-to-array concatenation</entry>
<entry><literal>3 || ARRAY[4,5,6]</literal></entry>
<entry><literal>{3,4,5,6}</literal></entry>
</row>
<row>
<entry> <literal>||</literal> </entry>
<entry>array-to-element concatenation</entry>
<entry><literal>ARRAY[4,5,6] || 7</literal></entry>
<entry><literal>{4,5,6,7}</literal></entry>
</row>
</tbody>
</tgroup>
</table>
<para>
<xref linkend="array-functions-table"> shows the functions
available for use with array types. See <xref linkend="arrays">
for more discussion and examples for the use of these functions.
</para>
<table id="array-functions-table">
<title><type>array</type> Functions</title>
<tgroup cols="5">
<thead>
<row>
<entry>Function</entry>
<entry>Return Type</entry>
<entry>Description</entry>
<entry>Example</entry>
<entry>Result</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<literal>
<function>array_append</function>
(<type>anyarray</type>, <type>anyelement</type>)
</literal>
</entry>
<entry><type>anyarray</type></entry>
<entry>
append an element to the end of an array, returning
<literal>NULL</literal> for <literal>NULL</literal> inputs
</entry>
<entry><literal>array_append(ARRAY[1,2], 3)</literal></entry>
<entry><literal>{1,2,3}</literal></entry>
</row>
<row>
<entry>
<literal>
<function>array_cat</function>
(<type>anyarray</type>, <type>anyarray</type>)
</literal>
</entry>
<entry><type>anyarray</type></entry>
<entry>
concatenate two arrays, returning <literal>NULL</literal>
for <literal>NULL</literal> inputs
</entry>
<entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry>
<entry><literal>{{1,2,3},{4,5,6}}</literal></entry>
</row>
<row>
<entry>
<literal>
<function>array_dims</function>
(<type>anyarray</type>)
</literal>
</entry>
<entry><type>text</type></entry>
<entry>
returns a text representation of array dimension lower and upper bounds,
generating an ERROR for <literal>NULL</literal> inputs
</entry>
<entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry>
<entry><literal>[1:2][1:3]</literal></entry>
</row>
<row>
<entry>
<literal>
<function>array_lower</function>
(<type>anyarray</type>, <type>integer</type>)
</literal>
</entry>
<entry><type>integer</type></entry>
<entry>
returns lower bound of the requested array dimension, returning
<literal>NULL</literal> for <literal>NULL</literal> inputs
</entry>
<entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry>
<entry><literal>0</literal></entry>
</row>
<row>
<entry>
<literal>
<function>array_prepend</function>
(<type>anyelement</type>, <type>anyarray</type>)
</literal>
</entry>
<entry><type>anyarray</type></entry>
<entry>
append an element to the beginning of an array, returning
<literal>NULL</literal> for <literal>NULL</literal> inputs
</entry>
<entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry>
<entry><literal>{1,2,3}</literal></entry>
</row>
<row>
<entry>
<literal>
<function>array_to_string</function>
(<type>anyarray</type>, <type>text</type>)
</literal>
</entry>
<entry><type>text</type></entry>
<entry>
concatenates array elements using provided delimiter, returning
<literal>NULL</literal> for <literal>NULL</literal> inputs
</entry>
<entry><literal>array_to_string(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry>
<entry><literal>1.10~^~2.20~^~3.30</literal></entry>
</row>
<row>
<entry>
<literal>
<function>array_upper</function>
(<type>anyarray</type>, <type>integer</type>)
</literal>
</entry>
<entry><type>integer</type></entry>
<entry>
returns upper bound of the requested array dimension, returning
<literal>NULL</literal> for <literal>NULL</literal> inputs
</entry>
<entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry>
<entry><literal>4</literal></entry>
</row>
<row>
<entry>
<literal>
<function>string_to_array</function>
(<type>text</type>, <type>text</type>)
</literal>
</entry>
<entry><type>text[]</type></entry>
<entry>
splits string into array elements using provided delimiter, returning
<literal>NULL</literal> for <literal>NULL</literal> inputs
</entry>
<entry><literal>string_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry>
<entry><literal>{1.1,2.2,3.3}</literal></entry>
</row>
</tbody>
</tgroup>
</table>
</sect1>
<sect1 id="functions-aggregate">
<title>Aggregate Functions</title>

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.132 2003/06/25 21:30:28 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.133 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1528,17 +1528,17 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
{
/* Check other sub-arrays are compatible */
if (elem_ndims != ARR_NDIM(array))
elog(ERROR, "Multiple dimension arrays must have array "
elog(ERROR, "Multidimensional arrays must have array "
"expressions with matching number of dimensions");
if (memcmp(elem_dims, ARR_DIMS(array),
elem_ndims * sizeof(int)) != 0)
elog(ERROR, "Multiple dimension arrays must have array "
elog(ERROR, "Multidimensional arrays must have array "
"expressions with matching dimensions");
if (memcmp(elem_lbs, ARR_LBOUND(array),
elem_ndims * sizeof(int)) != 0)
elog(ERROR, "Multiple dimension arrays must have array "
elog(ERROR, "Multidimensional arrays must have array "
"expressions with matching dimensions");
}

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.49 2003/06/25 21:30:29 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.50 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -29,22 +29,6 @@
#include "utils/lsyscache.h"
typedef struct ArrayBuildState
{
MemoryContext mcontext; /* where all the temp stuff is kept */
Datum *dvalues; /* array of accumulated Datums */
/*
* The allocated size of dvalues[] is always a multiple of
* ARRAY_ELEMS_CHUNKSIZE
*/
#define ARRAY_ELEMS_CHUNKSIZE 64
int nelems; /* number of valid Datums in dvalues[] */
Oid element_type; /* data type of the Datums */
int16 typlen; /* needed info about datatype */
bool typbyval;
char typalign;
} ArrayBuildState;
static Datum ExecHashSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull);
@ -54,12 +38,6 @@ static Datum ExecScanSubPlan(SubPlanState *node,
static void buildSubPlanHash(SubPlanState *node);
static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
static bool tupleAllNulls(HeapTuple tuple);
static ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
Datum dvalue, bool disnull,
Oid element_type,
MemoryContext rcontext);
static Datum makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext);
/* ----------------------------------------------------------------
@ -1099,101 +1077,3 @@ ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
parent->chgParam = bms_add_member(parent->chgParam, paramid);
}
}
/*
* accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
*
* astate is working state (NULL on first call)
* rcontext is where to keep working state
*/
static ArrayBuildState *
accumArrayResult(ArrayBuildState *astate,
Datum dvalue, bool disnull,
Oid element_type,
MemoryContext rcontext)
{
MemoryContext arr_context,
oldcontext;
if (astate == NULL)
{
/* First time through --- initialize */
/* Make a temporary context to hold all the junk */
arr_context = AllocSetContextCreate(rcontext,
"ARRAY_SUBLINK Result",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcontext = MemoryContextSwitchTo(arr_context);
astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
astate->mcontext = arr_context;
astate->dvalues = (Datum *)
palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
astate->nelems = 0;
astate->element_type = element_type;
get_typlenbyvalalign(element_type,
&astate->typlen,
&astate->typbyval,
&astate->typalign);
}
else
{
oldcontext = MemoryContextSwitchTo(astate->mcontext);
Assert(astate->element_type == element_type);
/* enlarge dvalues[] if needed */
if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
astate->dvalues = (Datum *)
repalloc(astate->dvalues,
(astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
}
if (disnull)
elog(ERROR, "NULL elements not allowed in Arrays");
/* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
astate->dvalues[astate->nelems++] =
datumCopy(dvalue, astate->typbyval, astate->typlen);
MemoryContextSwitchTo(oldcontext);
return astate;
}
/*
* makeArrayResult - produce final result of ARRAY_SUBLINK
*
* astate is working state (not NULL)
* rcontext is where to construct result
*/
static Datum
makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext)
{
ArrayType *result;
int dims[1];
int lbs[1];
MemoryContext oldcontext;
/* Build the final array result in rcontext */
oldcontext = MemoryContextSwitchTo(rcontext);
dims[0] = astate->nelems;
lbs[0] = 1;
result = construct_md_array(astate->dvalues,
1,
dims,
lbs,
astate->element_type,
astate->typlen,
astate->typbyval,
astate->typalign);
MemoryContextSwitchTo(oldcontext);
/* Clean up all the junk */
MemoryContextDelete(astate->mcontext);
return PointerGetDatum(result);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.100 2003/06/25 21:30:31 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.101 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1145,7 +1145,8 @@ IsPreferredType(CATEGORY category, Oid type)
* invokable, no-function-needed pg_cast entry. Also, a domain is always
* binary-coercible to its base type, though *not* vice versa (in the other
* direction, one must apply domain constraint checks before accepting the
* value as legitimate).
* value as legitimate). We also need to special-case the polymorphic
* ANYARRAY type.
*
* This function replaces IsBinaryCompatible(), which was an inherently
* symmetric test. Since the pg_cast entries aren't necessarily symmetric,
@ -1170,6 +1171,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (srctype == targettype)
return true;
/* Also accept any array type as coercible to ANYARRAY */
if (targettype == ANYARRAYOID)
if (get_element_type(srctype) != InvalidOid)
return true;
/* Else look in pg_cast */
tuple = SearchSysCache(CASTSOURCETARGET,
ObjectIdGetDatum(srctype),

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.150 2003/06/25 21:30:31 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.151 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -743,7 +743,7 @@ transformExpr(ParseState *pstate, Node *expr)
ArrayExpr *e = (ArrayExpr *) lfirst(element);
if (!IsA(e, ArrayExpr))
elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
if (ndims == 0)
ndims = e->ndims;
else if (e->ndims != ndims)

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.66 2003/06/25 21:30:32 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.67 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -137,28 +137,50 @@ Operator
equality_oper(Oid argtype, bool noError)
{
Operator optup;
Oid elem_type;
/*
* Look for an "=" operator for the datatype. We require it to be
* an exact or binary-compatible match, since most callers are not
* prepared to cope with adding any run-time type coercion steps.
* If the datatype is an array, then we can use array_eq ... but only
* if there is a suitable equality operator for the element type.
* (We must run this test first, since compatible_oper will find
* array_eq, but would not notice the lack of an element operator.)
*/
optup = compatible_oper(makeList1(makeString("=")),
argtype, argtype, true);
if (optup != NULL)
elem_type = get_element_type(argtype);
if (OidIsValid(elem_type))
{
optup = equality_oper(elem_type, true);
if (optup != NULL)
{
ReleaseSysCache(optup);
return SearchSysCache(OPEROID,
ObjectIdGetDatum(ARRAY_EQ_OP),
0, 0, 0);
}
}
else
{
/*
* Only believe that it's equality if it's mergejoinable,
* hashjoinable, or uses eqsel() as oprrest.
* Look for an "=" operator for the datatype. We require it to be
* an exact or binary-compatible match, since most callers are not
* prepared to cope with adding any run-time type coercion steps.
*/
Form_pg_operator pgopform = (Form_pg_operator) GETSTRUCT(optup);
optup = compatible_oper(makeList1(makeString("=")),
argtype, argtype, true);
if (optup != NULL)
{
/*
* Only believe that it's equality if it's mergejoinable,
* hashjoinable, or uses eqsel() as oprrest.
*/
Form_pg_operator pgopform = (Form_pg_operator) GETSTRUCT(optup);
if (OidIsValid(pgopform->oprlsortop) ||
pgopform->oprcanhash ||
pgopform->oprrest == F_EQSEL)
return optup;
if (OidIsValid(pgopform->oprlsortop) ||
pgopform->oprcanhash ||
pgopform->oprrest == F_EQSEL)
return optup;
ReleaseSysCache(optup);
ReleaseSysCache(optup);
}
}
if (!noError)
elog(ERROR, "Unable to identify an equality operator for type %s",
@ -175,27 +197,50 @@ Operator
ordering_oper(Oid argtype, bool noError)
{
Operator optup;
Oid elem_type;
/*
* Find the type's equality operator, and use its lsortop (it *must*
* be mergejoinable). We use this definition because for sorting and
* grouping purposes, it's important that the equality and ordering
* operators are consistent.
* If the datatype is an array, then we can use array_lt ... but only
* if there is a suitable ordering operator for the element type.
* (We must run this test first, since the code below would find
* array_lt if there's an element = operator, but would not notice the
* lack of an element < operator.)
*/
optup = equality_oper(argtype, noError);
if (optup != NULL)
elem_type = get_element_type(argtype);
if (OidIsValid(elem_type))
{
Oid lsortop = ((Form_pg_operator) GETSTRUCT(optup))->oprlsortop;
ReleaseSysCache(optup);
if (OidIsValid(lsortop))
optup = ordering_oper(elem_type, true);
if (optup != NULL)
{
optup = SearchSysCache(OPEROID,
ObjectIdGetDatum(lsortop),
0, 0, 0);
if (optup != NULL)
return optup;
ReleaseSysCache(optup);
return SearchSysCache(OPEROID,
ObjectIdGetDatum(ARRAY_LT_OP),
0, 0, 0);
}
}
else
{
/*
* Find the type's equality operator, and use its lsortop (it *must*
* be mergejoinable). We use this definition because for sorting and
* grouping purposes, it's important that the equality and ordering
* operators are consistent.
*/
optup = equality_oper(argtype, noError);
if (optup != NULL)
{
Oid lsortop;
lsortop = ((Form_pg_operator) GETSTRUCT(optup))->oprlsortop;
ReleaseSysCache(optup);
if (OidIsValid(lsortop))
{
optup = SearchSysCache(OPEROID,
ObjectIdGetDatum(lsortop),
0, 0, 0);
if (optup != NULL)
return optup;
}
}
}
if (!noError)
@ -237,6 +282,21 @@ ordering_oper_opid(Oid argtype)
return result;
}
/*
* ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
*/
Oid
ordering_oper_funcid(Oid argtype)
{
Operator optup;
Oid result;
optup = ordering_oper(argtype, false);
result = oprfuncid(optup);
ReleaseSysCache(optup);
return result;
}
/* given operator tuple, return the operator OID */
Oid

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.90 2003/06/25 21:30:32 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.91 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -34,7 +34,7 @@ static const char *getid(const char *s, char *n);
static void putid(char *p, const char *s);
static Acl *allocacl(int n);
static const char *aclparse(const char *s, AclItem *aip);
static bool aclitemeq(const AclItem *a1, const AclItem *a2);
static bool aclitem_match(const AclItem *a1, const AclItem *a2);
static Acl *recursive_revoke(Acl *acl, AclId grantee,
AclMode revoke_privs, DropBehavior behavior);
@ -415,18 +415,33 @@ aclitemout(PG_FUNCTION_ARGS)
}
/*
* aclitemeq
* Two AclItems are considered equal iff they have the same
* aclitem_match
* Two AclItems are considered to match iff they have the same
* grantee and grantor; the privileges are ignored.
*/
static bool
aclitemeq(const AclItem *a1, const AclItem *a2)
aclitem_match(const AclItem *a1, const AclItem *a2)
{
return ACLITEM_GET_IDTYPE(*a1) == ACLITEM_GET_IDTYPE(*a2) &&
a1->ai_grantee == a2->ai_grantee &&
a1->ai_grantor == a2->ai_grantor;
}
/*
* aclitem equality operator
*/
Datum
aclitem_eq(PG_FUNCTION_ARGS)
{
AclItem *a1 = PG_GETARG_ACLITEM_P(0);
AclItem *a2 = PG_GETARG_ACLITEM_P(1);
bool result;
result = a1->ai_privs == a2->ai_privs &&
a1->ai_grantee == a2->ai_grantee &&
a1->ai_grantor == a2->ai_grantor;
PG_RETURN_BOOL(result);
}
/*
* acldefault() --- create an ACL describing default access permissions
@ -535,7 +550,7 @@ aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg, DropBeh
for (dst = 0; dst < num; ++dst)
{
if (aclitemeq(mod_aip, old_aip + dst))
if (aclitem_match(mod_aip, old_aip + dst))
{
/* found a match, so modify existing item */
new_acl = allocacl(num);
@ -685,8 +700,10 @@ aclremove(PG_FUNCTION_ARGS)
old_aip = ACL_DAT(old_acl);
/* Search for the matching entry */
for (dst = 0; dst < old_num && !aclitemeq(mod_aip, old_aip + dst); ++dst)
;
for (dst = 0;
dst < old_num && !aclitem_match(mod_aip, old_aip + dst);
++dst)
/* continue */ ;
if (dst >= old_num)
{

View File

@ -6,7 +6,7 @@
* Copyright (c) 2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.3 2003/06/25 21:30:32 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.4 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,35 +18,6 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
/*-----------------------------------------------------------------------------
* singleton_array :
* Form a multi-dimensional array given one starting element.
*
* - first argument is the datum with which to build the array
* - second argument is the number of dimensions the array should have;
* defaults to 1 if no second argument is provided
*----------------------------------------------------------------------------
*/
Datum
singleton_array(PG_FUNCTION_ARGS)
{
Oid elem_type = get_fn_expr_argtype(fcinfo, 0);
int ndims;
if (elem_type == InvalidOid)
elog(ERROR, "Cannot determine input datatype");
if (PG_NARGS() == 2)
ndims = PG_GETARG_INT32(1);
else
ndims = 1;
PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type,
PG_GETARG_DATUM(0),
ndims));
}
/*-----------------------------------------------------------------------------
* array_push :
* push an element onto either end of a one-dimensional array
@ -70,6 +41,7 @@ array_push(PG_FUNCTION_ARGS)
Oid arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
Oid arg0_elemid;
Oid arg1_elemid;
ArrayMetaState *my_extra;
if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
elog(ERROR, "array_push: cannot determine input data types");
@ -95,25 +67,54 @@ array_push(PG_FUNCTION_ARGS)
PG_RETURN_NULL(); /* keep compiler quiet */
}
/* Sanity check: do we have a one-dimensional array */
if (ARR_NDIM(v) != 1)
elog(ERROR, "Arrays greater than one-dimension are not supported");
lb = ARR_LBOUND(v);
dimv = ARR_DIMS(v);
if (arg0_elemid != InvalidOid)
if (ARR_NDIM(v) == 1)
{
/* append newelem */
int ub = dimv[0] + lb[0] - 1;
indx = ub + 1;
lb = ARR_LBOUND(v);
dimv = ARR_DIMS(v);
if (arg0_elemid != InvalidOid)
{
/* append newelem */
int ub = dimv[0] + lb[0] - 1;
indx = ub + 1;
}
else
{
/* prepend newelem */
indx = lb[0] - 1;
}
}
else if (ARR_NDIM(v) == 0)
indx = 1;
else
elog(ERROR, "only empty and one-dimensional arrays are supported");
/*
* We arrange to look up info about element type only once per series
* of calls, assuming the element type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
/* prepend newelem */
indx = lb[0] - 1;
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
if (my_extra->element_type != element_type)
{
/* Get info about element type */
get_typlenbyvalalign(element_type,
&my_extra->typlen,
&my_extra->typbyval,
&my_extra->typalign);
my_extra->element_type = element_type;
}
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
result = array_set(v, 1, &indx, newelem, -1,
typlen, typbyval, typalign, &isNull);
@ -145,13 +146,28 @@ array_cat(PG_FUNCTION_ARGS)
/*
* We must have one of the following combinations of inputs:
* 1) two arrays with ndims1 == ndims2
* 2) ndims1 == ndims2 - 1
* 3) ndims1 == ndims2 + 1
* 1) one empty array, and one non-empty array
* 2) both arrays empty
* 3) two arrays with ndims1 == ndims2
* 4) ndims1 == ndims2 - 1
* 5) ndims1 == ndims2 + 1
*/
ndims1 = ARR_NDIM(v1);
ndims2 = ARR_NDIM(v2);
/*
* short circuit - if one input array is empty, and the other is not,
* we return the non-empty one as the result
*
* if both are empty, return the first one
*/
if (ndims1 == 0 && ndims2 > 0)
PG_RETURN_ARRAYTYPE_P(v2);
if (ndims2 == 0)
PG_RETURN_ARRAYTYPE_P(v1);
/* the rest fall into combo 2, 3, or 4 */
if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
"%d dimensions", ndims1, ndims2);
@ -266,147 +282,15 @@ array_cat(PG_FUNCTION_ARGS)
PG_RETURN_ARRAYTYPE_P(result);
}
/*----------------------------------------------------------------------------
* array_accum :
* accumulator to build a 1-D array from input values -- this can be used
* to create custom aggregates.
*
* This function is not marked strict, so we have to be careful about nulls.
*----------------------------------------------------------------------------
*/
Datum
array_accum(PG_FUNCTION_ARGS)
{
/* return NULL if both arguments are NULL */
if (PG_ARGISNULL(0) && PG_ARGISNULL(1))
PG_RETURN_NULL();
/* create a new 1-D array from the new element if the array is NULL */
if (PG_ARGISNULL(0))
{
Oid tgt_type = get_fn_expr_rettype(fcinfo);
Oid tgt_elem_type;
if (tgt_type == InvalidOid)
elog(ERROR, "Cannot determine target array type");
tgt_elem_type = get_element_type(tgt_type);
if (tgt_elem_type == InvalidOid)
elog(ERROR, "Target type is not an array");
PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type,
PG_GETARG_DATUM(1),
1));
}
/* return the array if the new element is NULL */
if (PG_ARGISNULL(1))
PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0));
/*
* Otherwise this is equivalent to array_push. We hack the call a little
* so that array_push can see the fn_expr information.
*/
return array_push(fcinfo);
}
/*-----------------------------------------------------------------------------
* array_assign :
* assign an element of an array to a new value and return the
* redefined array
*----------------------------------------------------------------------------
*/
Datum
array_assign(PG_FUNCTION_ARGS)
{
ArrayType *v;
int idx_to_chg;
Datum newelem;
int *dimv,
*lb, ub;
ArrayType *result;
bool isNull;
Oid element_type;
int16 typlen;
bool typbyval;
char typalign;
v = PG_GETARG_ARRAYTYPE_P(0);
idx_to_chg = PG_GETARG_INT32(1);
newelem = PG_GETARG_DATUM(2);
/* Sanity check: do we have a one-dimensional array */
if (ARR_NDIM(v) != 1)
elog(ERROR, "Arrays greater than one-dimension are not supported");
lb = ARR_LBOUND(v);
dimv = ARR_DIMS(v);
ub = dimv[0] + lb[0] - 1;
if (idx_to_chg < lb[0] || idx_to_chg > ub)
elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg);
element_type = ARR_ELEMTYPE(v);
/* Sanity check: do we have a non-zero element type */
if (element_type == 0)
elog(ERROR, "Invalid array element type: %u", element_type);
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
result = array_set(v, 1, &idx_to_chg, newelem, -1,
typlen, typbyval, typalign, &isNull);
PG_RETURN_ARRAYTYPE_P(result);
}
/*-----------------------------------------------------------------------------
* array_subscript :
* return specific element of an array
*----------------------------------------------------------------------------
*/
Datum
array_subscript(PG_FUNCTION_ARGS)
{
ArrayType *v;
int idx;
int *dimv,
*lb, ub;
Datum result;
bool isNull;
Oid element_type;
int16 typlen;
bool typbyval;
char typalign;
v = PG_GETARG_ARRAYTYPE_P(0);
idx = PG_GETARG_INT32(1);
/* Sanity check: do we have a one-dimensional array */
if (ARR_NDIM(v) != 1)
elog(ERROR, "Arrays greater than one-dimension are not supported");
lb = ARR_LBOUND(v);
dimv = ARR_DIMS(v);
ub = dimv[0] + lb[0] - 1;
if (idx < lb[0] || idx > ub)
elog(ERROR, "Cannot return nonexistent array element: %d", idx);
element_type = ARR_ELEMTYPE(v);
/* Sanity check: do we have a non-zero element type */
if (element_type == 0)
elog(ERROR, "Invalid array element type: %u", element_type);
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull);
PG_RETURN_DATUM(result);
}
/*
* actually does the work for singleton_array(), and array_accum() if it is
* given a null input array.
* used by text_to_array() in varlena.c
*/
ArrayType *
create_singleton_array(Oid element_type, Datum element, int ndims)
create_singleton_array(FunctionCallInfo fcinfo,
Oid element_type,
Datum element,
int ndims)
{
Datum dvalues[1];
int16 typlen;
@ -415,6 +299,7 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
int dims[MAXDIM];
int lbs[MAXDIM];
int i;
ArrayMetaState *my_extra;
if (element_type == 0)
elog(ERROR, "Invalid array element type: %u", element_type);
@ -429,7 +314,31 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
lbs[i] = 1;
}
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
/*
* We arrange to look up info about element type only once per series
* of calls, assuming the element type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
/* Get info about element type */
get_typlenbyvalalign(element_type,
&my_extra->typlen,
&my_extra->typbyval,
&my_extra->typalign);
my_extra->element_type = element_type;
}
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
return construct_md_array(dvalues, ndims, dims, lbs, element_type,
typlen, typbyval, typalign);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.91 2003/06/25 21:30:32 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.92 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -21,8 +21,10 @@
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "parser/parse_coerce.h"
#include "parser/parse_oper.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/memutils.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@ -70,16 +72,6 @@
#define RETURN_NULL(type) do { *isNull = true; return (type) 0; } while (0)
/* I/O function selector for system_cache_lookup */
typedef enum IOFuncSelector
{
IOFunc_input,
IOFunc_output,
IOFunc_receive,
IOFunc_send
} IOFuncSelector;
static int ArrayCount(char *str, int *dim, char typdelim);
static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
FmgrInfo *inputproc, Oid typelem, int32 typmod,
@ -93,10 +85,6 @@ static Datum *ReadArrayBinary(StringInfo buf, int nitems,
static void CopyArrayEls(char *p, Datum *values, int nitems,
int typlen, bool typbyval, char typalign,
bool freedata);
static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
int *typlen, bool *typbyval,
char *typdelim, Oid *typelem,
Oid *proc, char *typalign);
static Datum ArrayCast(char *value, bool byval, int len);
static int ArrayCastAndSet(Datum src,
int typlen, bool typbyval, char typalign,
@ -119,7 +107,7 @@ static void array_insert_slice(int ndim, int *dim, int *lb,
char *destPtr,
int *st, int *endp, char *srcPtr,
int typlen, bool typbyval, char typalign);
static int array_cmp(FunctionCallInfo fcinfo);
/*---------------------------------------------------------------------
* array_in :
@ -139,12 +127,11 @@ array_in(PG_FUNCTION_ARGS)
* elements */
int typlen;
bool typbyval;
char typalign;
char typdelim;
Oid typinput;
Oid typelem;
char *string_save,
*p;
FmgrInfo inputproc;
int i,
nitems;
int32 nbytes;
@ -153,13 +140,38 @@ array_in(PG_FUNCTION_ARGS)
int ndim,
dim[MAXDIM],
lBound[MAXDIM];
char typalign;
ArrayMetaState *my_extra;
/* Get info about element type, including its input conversion proc */
system_cache_lookup(element_type, IOFunc_input,
&typlen, &typbyval, &typdelim,
&typelem, &typinput, &typalign);
fmgr_info(typinput, &inputproc);
/*
* We arrange to look up info about element type, including its input
* conversion proc, only once per series of calls, assuming the element
* type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
/* Get info about element type, including its input conversion proc */
get_type_io_data(element_type, IOFunc_input,
&my_extra->typlen, &my_extra->typbyval,
&my_extra->typalign, &my_extra->typdelim,
&my_extra->typelem, &my_extra->typiofunc);
fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
fcinfo->flinfo->fn_mcxt);
my_extra->element_type = element_type;
}
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
typdelim = my_extra->typdelim;
typelem = my_extra->typelem;
/* Make a modifiable copy of the input */
/* XXX why are we allocating an extra 2 bytes here? */
@ -262,7 +274,7 @@ array_in(PG_FUNCTION_ARGS)
if (*p != '{')
elog(ERROR, "array_in: missing left brace");
dataPtr = ReadArrayStr(p, nitems, ndim, dim, &inputproc, typelem,
dataPtr = ReadArrayStr(p, nitems, ndim, dim, &my_extra->proc, typelem,
typmod, typdelim, typlen, typbyval, typalign,
&nbytes);
nbytes += ARR_OVERHEAD(ndim);
@ -618,11 +630,9 @@ array_out(PG_FUNCTION_ARGS)
Oid element_type;
int typlen;
bool typbyval;
char typdelim;
Oid typoutput,
typelem;
FmgrInfo outputproc;
char typalign;
char typdelim;
Oid typelem;
char *p,
*tmp,
*retval,
@ -636,12 +646,40 @@ array_out(PG_FUNCTION_ARGS)
indx[MAXDIM];
int ndim,
*dim;
ArrayMetaState *my_extra;
element_type = ARR_ELEMTYPE(v);
system_cache_lookup(element_type, IOFunc_output,
&typlen, &typbyval, &typdelim,
&typelem, &typoutput, &typalign);
fmgr_info(typoutput, &outputproc);
/*
* We arrange to look up info about element type, including its output
* conversion proc, only once per series of calls, assuming the element
* type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
/* Get info about element type, including its output conversion proc */
get_type_io_data(element_type, IOFunc_output,
&my_extra->typlen, &my_extra->typbyval,
&my_extra->typalign, &my_extra->typdelim,
&my_extra->typelem, &my_extra->typiofunc);
fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
fcinfo->flinfo->fn_mcxt);
my_extra->element_type = element_type;
}
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
typdelim = my_extra->typdelim;
typelem = my_extra->typelem;
ndim = ARR_NDIM(v);
dim = ARR_DIMS(v);
@ -668,7 +706,7 @@ array_out(PG_FUNCTION_ARGS)
bool nq;
itemvalue = fetch_att(p, typbyval, typlen);
values[i] = DatumGetCString(FunctionCall3(&outputproc,
values[i] = DatumGetCString(FunctionCall3(&my_extra->proc,
itemvalue,
ObjectIdGetDatum(typelem),
Int32GetDatum(-1)));
@ -786,10 +824,8 @@ array_recv(PG_FUNCTION_ARGS)
Oid element_type;
int typlen;
bool typbyval;
char typdelim;
Oid typreceive;
char typalign;
Oid typelem;
FmgrInfo receiveproc;
int i,
nitems;
int32 nbytes;
@ -799,7 +835,7 @@ array_recv(PG_FUNCTION_ARGS)
flags,
dim[MAXDIM],
lBound[MAXDIM];
char typalign;
ArrayMetaState *my_extra;
/* Get the array header information */
ndim = pq_getmsgint(buf, 4);
@ -831,16 +867,40 @@ array_recv(PG_FUNCTION_ARGS)
PG_RETURN_ARRAYTYPE_P(retval);
}
/* Get info about element type, including its receive conversion proc */
system_cache_lookup(element_type, IOFunc_receive,
&typlen, &typbyval, &typdelim,
&typelem, &typreceive, &typalign);
if (!OidIsValid(typreceive))
elog(ERROR, "No binary input function available for type %s",
format_type_be(element_type));
fmgr_info(typreceive, &receiveproc);
/*
* We arrange to look up info about element type, including its receive
* conversion proc, only once per series of calls, assuming the element
* type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
if (my_extra->element_type != element_type)
{
/* Get info about element type, including its receive proc */
get_type_io_data(element_type, IOFunc_receive,
&my_extra->typlen, &my_extra->typbyval,
&my_extra->typalign, &my_extra->typdelim,
&my_extra->typelem, &my_extra->typiofunc);
if (!OidIsValid(my_extra->typiofunc))
elog(ERROR, "No binary input function available for type %s",
format_type_be(element_type));
fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
fcinfo->flinfo->fn_mcxt);
my_extra->element_type = element_type;
}
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
typelem = my_extra->typelem;
dataPtr = ReadArrayBinary(buf, nitems, &my_extra->proc, typelem,
typlen, typbyval, typalign,
&nbytes);
nbytes += ARR_OVERHEAD(ndim);
@ -965,26 +1025,51 @@ array_send(PG_FUNCTION_ARGS)
Oid element_type;
int typlen;
bool typbyval;
char typdelim;
Oid typsend,
typelem;
FmgrInfo sendproc;
char typalign;
Oid typelem;
char *p;
int nitems,
i;
int ndim,
*dim;
StringInfoData buf;
ArrayMetaState *my_extra;
/* Get information about the element type and the array dimensions */
element_type = ARR_ELEMTYPE(v);
system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
&typdelim, &typelem, &typsend, &typalign);
if (!OidIsValid(typsend))
elog(ERROR, "No binary output function available for type %s",
format_type_be(element_type));
fmgr_info(typsend, &sendproc);
/*
* We arrange to look up info about element type, including its send
* conversion proc, only once per series of calls, assuming the element
* type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
/* Get info about element type, including its send proc */
get_type_io_data(element_type, IOFunc_send,
&my_extra->typlen, &my_extra->typbyval,
&my_extra->typalign, &my_extra->typdelim,
&my_extra->typelem, &my_extra->typiofunc);
if (!OidIsValid(my_extra->typiofunc))
elog(ERROR, "No binary output function available for type %s",
format_type_be(element_type));
fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
fcinfo->flinfo->fn_mcxt);
my_extra->element_type = element_type;
}
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
typelem = my_extra->typelem;
ndim = ARR_NDIM(v);
dim = ARR_DIMS(v);
@ -1011,7 +1096,7 @@ array_send(PG_FUNCTION_ARGS)
itemvalue = fetch_att(p, typbyval, typlen);
outputbytes = DatumGetByteaP(FunctionCall2(&sendproc,
outputbytes = DatumGetByteaP(FunctionCall2(&my_extra->proc,
itemvalue,
ObjectIdGetDatum(typelem)));
/* We assume the result will not have been toasted */
@ -1476,6 +1561,26 @@ array_set(ArrayType *array,
array = DatumGetArrayTypeP(PointerGetDatum(array));
ndim = ARR_NDIM(array);
/*
* if number of dims is zero, i.e. an empty array, create an array
* with nSubscripts dimensions, and set the lower bounds to the supplied
* subscripts
*/
if (ndim == 0)
{
Oid elmtype = ARR_ELEMTYPE(array);
for (i = 0; i < nSubscripts; i++)
{
dim[i] = 1;
lb[i] = indx[i];
}
return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype,
elmlen, elmbyval, elmalign);
}
if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
elog(ERROR, "Invalid array subscripts");
@ -1632,6 +1737,31 @@ array_set_slice(ArrayType *array,
/* note: we assume srcArray contains no toasted elements */
ndim = ARR_NDIM(array);
/*
* if number of dims is zero, i.e. an empty array, create an array
* with nSubscripts dimensions, and set the upper and lower bounds
* to the supplied subscripts
*/
if (ndim == 0)
{
Datum *dvalues;
int nelems;
Oid elmtype = ARR_ELEMTYPE(array);
deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
&dvalues, &nelems);
for (i = 0; i < nSubscripts; i++)
{
dim[i] = 1 + upperIndx[i] - lowerIndx[i];
lb[i] = lowerIndx[i];
}
return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype,
elmlen, elmbyval, elmalign);
}
if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
elog(ERROR, "Invalid array subscripts");
@ -1807,10 +1937,14 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
int typlen;
bool typbyval;
char typalign;
char typdelim;
Oid typelem;
Oid proc;
char *s;
typedef struct {
ArrayMetaState inp_extra;
ArrayMetaState ret_extra;
} am_extra;
am_extra *my_extra;
ArrayMetaState *inp_extra;
ArrayMetaState *ret_extra;
/* Get input array */
if (fcinfo->nargs < 1)
@ -1829,11 +1963,51 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
if (nitems <= 0)
PG_RETURN_ARRAYTYPE_P(v);
/* Lookup source and result types. Unneeded variables are reused. */
system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
&typdelim, &typelem, &proc, &inp_typalign);
system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
&typdelim, &typelem, &proc, &typalign);
/*
* We arrange to look up info about input and return element types only
* once per series of calls, assuming the element type doesn't change
* underneath us.
*/
my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(am_extra));
my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
inp_extra = &my_extra->inp_extra;
inp_extra->element_type = InvalidOid;
ret_extra = &my_extra->ret_extra;
ret_extra->element_type = InvalidOid;
}
else
{
inp_extra = &my_extra->inp_extra;
ret_extra = &my_extra->ret_extra;
}
if (inp_extra->element_type != inpType)
{
get_typlenbyvalalign(inpType,
&inp_extra->typlen,
&inp_extra->typbyval,
&inp_extra->typalign);
inp_extra->element_type = inpType;
}
inp_typlen = inp_extra->typlen;
inp_typbyval = inp_extra->typbyval;
inp_typalign = inp_extra->typalign;
if (ret_extra->element_type != retType)
{
get_typlenbyvalalign(retType,
&ret_extra->typlen,
&ret_extra->typbyval,
&ret_extra->typalign);
ret_extra->element_type = retType;
}
typlen = ret_extra->typlen;
typbyval = ret_extra->typbyval;
typalign = ret_extra->typalign;
/* Allocate temporary array for new values */
values = (Datum *) palloc(nitems * sizeof(Datum));
@ -2049,8 +2223,6 @@ deconstruct_array(ArrayType *array,
* compares two arrays for equality
* result :
* returns true if the arrays are equal, false otherwise.
*
* XXX bitwise equality is pretty bogus ...
*-----------------------------------------------------------------------------
*/
Datum
@ -2058,12 +2230,101 @@ array_eq(PG_FUNCTION_ARGS)
{
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
char *p1 = (char *) ARR_DATA_PTR(array1);
char *p2 = (char *) ARR_DATA_PTR(array2);
int ndims1 = ARR_NDIM(array1);
int ndims2 = ARR_NDIM(array2);
int *dims1 = ARR_DIMS(array1);
int *dims2 = ARR_DIMS(array2);
int nitems1 = ArrayGetNItems(ndims1, dims1);
int nitems2 = ArrayGetNItems(ndims2, dims2);
Oid element_type = ARR_ELEMTYPE(array1);
FmgrInfo *ae_fmgr_info = fcinfo->flinfo;
bool result = true;
int typlen;
bool typbyval;
char typalign;
int i;
ArrayMetaState *my_extra;
FunctionCallInfoData locfcinfo;
if (ARR_SIZE(array1) != ARR_SIZE(array2))
result = false;
else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
if (element_type != ARR_ELEMTYPE(array2))
elog(ERROR, "cannot compare arrays of different element types");
/* fast path if the arrays do not have the same number of elements */
if (nitems1 != nitems2)
result = false;
else
{
/*
* We arrange to look up the equality function only once per series of
* calls, assuming the element type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
if (my_extra == NULL)
{
ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
Oid opfuncid = equality_oper_funcid(element_type);
get_typlenbyvalalign(element_type,
&my_extra->typlen,
&my_extra->typbyval,
&my_extra->typalign);
fmgr_info_cxt(opfuncid, &my_extra->proc,
ae_fmgr_info->fn_mcxt);
my_extra->element_type = element_type;
}
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
/*
* apply the operator to each pair of array elements.
*/
MemSet(&locfcinfo, 0, sizeof(locfcinfo));
locfcinfo.flinfo = &my_extra->proc;
locfcinfo.nargs = 2;
/* Loop over source data */
for (i = 0; i < nitems1; i++)
{
Datum elt1;
Datum elt2;
bool oprresult;
/* Get element pair */
elt1 = fetch_att(p1, typbyval, typlen);
elt2 = fetch_att(p2, typbyval, typlen);
p1 = att_addlength(p1, typlen, PointerGetDatum(p1));
p1 = (char *) att_align(p1, typalign);
p2 = att_addlength(p2, typlen, PointerGetDatum(p2));
p2 = (char *) att_align(p2, typalign);
/*
* Apply the operator to the element pair
*/
locfcinfo.arg[0] = elt1;
locfcinfo.arg[1] = elt2;
locfcinfo.argnull[0] = false;
locfcinfo.argnull[1] = false;
locfcinfo.isnull = false;
oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
if (!oprresult)
{
result = false;
break;
}
}
}
/* Avoid leaking memory when handed toasted input. */
PG_FREE_IF_COPY(array1, 0);
@ -2073,53 +2334,171 @@ array_eq(PG_FUNCTION_ARGS)
}
/*-----------------------------------------------------------------------------
* array-array bool operators:
* Given two arrays, iterate comparison operators
* over the array. Uses logic similar to text comparison
* functions, except element-by-element instead of
* character-by-character.
*----------------------------------------------------------------------------
*/
Datum
array_ne(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
}
Datum
array_lt(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
}
Datum
array_gt(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
}
Datum
array_le(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
}
Datum
array_ge(PG_FUNCTION_ARGS)
{
PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
}
Datum
btarraycmp(PG_FUNCTION_ARGS)
{
PG_RETURN_INT32(array_cmp(fcinfo));
}
/*
* array_cmp()
* Internal comparison function for arrays.
*
* Returns -1, 0 or 1
*/
static int
array_cmp(FunctionCallInfo fcinfo)
{
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
FmgrInfo *ac_fmgr_info = fcinfo->flinfo;
Datum opresult;
int result = 0;
Oid element_type = InvalidOid;
int typlen;
bool typbyval;
char typalign;
Datum *dvalues1;
int nelems1;
Datum *dvalues2;
int nelems2;
int min_nelems;
int i;
typedef struct
{
Oid element_type;
int16 typlen;
bool typbyval;
char typalign;
FmgrInfo eqproc;
FmgrInfo ordproc;
} ac_extra;
ac_extra *my_extra;
element_type = ARR_ELEMTYPE(array1);
if (element_type != ARR_ELEMTYPE(array2))
elog(ERROR, "cannot compare arrays of different element types");
/*
* We arrange to look up the element type info and related functions
* only once per series of calls, assuming the element type doesn't
* change underneath us.
*/
my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
if (my_extra == NULL)
{
ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
sizeof(ac_extra));
my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
Oid eqfuncid = equality_oper_funcid(element_type);
Oid ordfuncid = ordering_oper_funcid(element_type);
get_typlenbyvalalign(element_type,
&my_extra->typlen,
&my_extra->typbyval,
&my_extra->typalign);
fmgr_info_cxt(eqfuncid, &my_extra->eqproc,
ac_fmgr_info->fn_mcxt);
fmgr_info_cxt(ordfuncid, &my_extra->ordproc,
ac_fmgr_info->fn_mcxt);
my_extra->element_type = element_type;
}
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
/* extract a C array of arg array datums */
deconstruct_array(array1, element_type, typlen, typbyval, typalign,
&dvalues1, &nelems1);
deconstruct_array(array2, element_type, typlen, typbyval, typalign,
&dvalues2, &nelems2);
min_nelems = Min(nelems1, nelems2);
for (i = 0; i < min_nelems; i++)
{
/* are they equal */
opresult = FunctionCall2(&my_extra->eqproc,
dvalues1[i], dvalues2[i]);
if (!DatumGetBool(opresult))
{
/* nope, see if arg1 is less than arg2 */
opresult = FunctionCall2(&my_extra->ordproc,
dvalues1[i], dvalues2[i]);
if (DatumGetBool(opresult))
{
/* arg1 is less than arg2 */
result = -1;
break;
}
else
{
/* arg1 is greater than arg2 */
result = 1;
break;
}
}
}
if ((result == 0) && (nelems1 != nelems2))
result = (nelems1 < nelems2) ? -1 : 1;
/* Avoid leaking memory when handed toasted input. */
PG_FREE_IF_COPY(array1, 0);
PG_FREE_IF_COPY(array2, 1);
return result;
}
/***************************************************************************/
/******************| Support Routines |*****************/
/***************************************************************************/
static void
system_cache_lookup(Oid element_type,
IOFuncSelector which_func,
int *typlen,
bool *typbyval,
char *typdelim,
Oid *typelem,
Oid *proc,
char *typalign)
{
HeapTuple typeTuple;
Form_pg_type typeStruct;
typeTuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(element_type),
0, 0, 0);
if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "cache lookup failed for type %u", element_type);
typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
*typlen = typeStruct->typlen;
*typbyval = typeStruct->typbyval;
*typdelim = typeStruct->typdelim;
*typelem = typeStruct->typelem;
*typalign = typeStruct->typalign;
switch (which_func)
{
case IOFunc_input:
*proc = typeStruct->typinput;
break;
case IOFunc_output:
*proc = typeStruct->typoutput;
break;
case IOFunc_receive:
*proc = typeStruct->typreceive;
break;
case IOFunc_send:
*proc = typeStruct->typsend;
break;
}
ReleaseSysCache(typeTuple);
}
/*
* Fetch array element at pointer, converted correctly to a Datum
*/
@ -2423,6 +2802,18 @@ array_type_coerce(PG_FUNCTION_ARGS)
if (tgt_elem_type == InvalidOid)
elog(ERROR, "Target type is not an array");
/*
* We don't deal with domain constraints yet, so bail out.
* This isn't currently a problem, because we also don't
* support arrays of domain type elements either. But in the
* future we might. At that point consideration should be given
* to removing the check below and adding a domain constraints
* check to the coercion.
*/
if (getBaseType(tgt_elem_type) != tgt_elem_type)
elog(ERROR, "array coercion to domain type elements not " \
"currently supported");
if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
COERCION_EXPLICIT, &funcId))
{
@ -2439,10 +2830,16 @@ array_type_coerce(PG_FUNCTION_ARGS)
}
/*
* If it's binary-compatible, return the array unmodified.
* If it's binary-compatible, modify the element type in the array header,
* but otherwise leave the array as we received it.
*/
if (my_extra->coerce_finfo.fn_oid == InvalidOid)
PG_RETURN_ARRAYTYPE_P(src);
{
ArrayType *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
ARR_ELEMTYPE(result) = my_extra->desttype;
PG_RETURN_ARRAYTYPE_P(result);
}
/*
* Use array_map to apply the function to each array element.
@ -2454,3 +2851,121 @@ array_type_coerce(PG_FUNCTION_ARGS)
return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
}
/*
* accumArrayResult - accumulate one (more) Datum for an array result
*
* astate is working state (NULL on first call)
* rcontext is where to keep working state
*/
ArrayBuildState *
accumArrayResult(ArrayBuildState *astate,
Datum dvalue, bool disnull,
Oid element_type,
MemoryContext rcontext)
{
MemoryContext arr_context,
oldcontext;
if (astate == NULL)
{
/* First time through --- initialize */
/* Make a temporary context to hold all the junk */
arr_context = AllocSetContextCreate(rcontext,
"accumArrayResult",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldcontext = MemoryContextSwitchTo(arr_context);
astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
astate->mcontext = arr_context;
astate->dvalues = (Datum *)
palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum));
astate->nelems = 0;
astate->element_type = element_type;
get_typlenbyvalalign(element_type,
&astate->typlen,
&astate->typbyval,
&astate->typalign);
}
else
{
oldcontext = MemoryContextSwitchTo(astate->mcontext);
Assert(astate->element_type == element_type);
/* enlarge dvalues[] if needed */
if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0)
astate->dvalues = (Datum *)
repalloc(astate->dvalues,
(astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum));
}
if (disnull)
elog(ERROR, "NULL elements not allowed in Arrays");
/* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */
astate->dvalues[astate->nelems++] =
datumCopy(dvalue, astate->typbyval, astate->typlen);
MemoryContextSwitchTo(oldcontext);
return astate;
}
/*
* makeArrayResult - produce 1-D final result of accumArrayResult
*
* astate is working state (not NULL)
* rcontext is where to construct result
*/
Datum
makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext)
{
int dims[1];
int lbs[1];
dims[0] = astate->nelems;
lbs[0] = 1;
return makeMdArrayResult(astate, 1, dims, lbs, rcontext);
}
/*
* makeMdArrayResult - produce multi-D final result of accumArrayResult
*
* beware: no check that specified dimensions match the number of values
* accumulated.
*
* astate is working state (not NULL)
* rcontext is where to construct result
*/
Datum
makeMdArrayResult(ArrayBuildState *astate,
int ndims,
int *dims,
int *lbs,
MemoryContext rcontext)
{
ArrayType *result;
MemoryContext oldcontext;
/* Build the final array result in rcontext */
oldcontext = MemoryContextSwitchTo(rcontext);
result = construct_md_array(astate->dvalues,
ndims,
dims,
lbs,
astate->element_type,
astate->typlen,
astate->typbyval,
astate->typalign);
MemoryContextSwitchTo(oldcontext);
/* Clean up all the junk */
MemoryContextDelete(astate->mcontext);
return PointerGetDatum(result);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.100 2003/06/25 21:30:32 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.101 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -19,11 +19,14 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "access/tuptoaster.h"
#include "catalog/pg_type.h"
#include "lib/stringinfo.h"
#include "libpq/crypt.h"
#include "libpq/pqformat.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/pg_locale.h"
#include "utils/lsyscache.h"
typedef struct varlena unknown;
@ -1983,8 +1986,7 @@ split_text(PG_FUNCTION_ARGS)
if (fldnum == 1) /* first field - just return the input
* string */
PG_RETURN_TEXT_P(inputstring);
else
/* otherwise return an empty string */
else /* otherwise return an empty string */
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
}
@ -2004,8 +2006,7 @@ split_text(PG_FUNCTION_ARGS)
if (fldnum == 1) /* first field - just return the input
* string */
PG_RETURN_TEXT_P(inputstring);
else
/* otherwise return an empty string */
else /* otherwise return an empty string */
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
}
else if ((start_posn != 0) && (end_posn == 0))
@ -2028,6 +2029,176 @@ split_text(PG_FUNCTION_ARGS)
}
}
/*
* text_to_array
* parse input string
* return text array of elements
* based on provided field separator
*/
Datum
text_to_array(PG_FUNCTION_ARGS)
{
text *inputstring = PG_GETARG_TEXT_P(0);
int inputstring_len = TEXTLEN(inputstring);
text *fldsep = PG_GETARG_TEXT_P(1);
int fldsep_len = TEXTLEN(fldsep);
int fldnum;
int start_posn = 0;
int end_posn = 0;
text *result_text = NULL;
ArrayBuildState *astate = NULL;
MemoryContext oldcontext = CurrentMemoryContext;
/* return NULL for empty input string */
if (inputstring_len < 1)
PG_RETURN_NULL();
/* empty field separator
* return one element, 1D, array using the input string */
if (fldsep_len < 1)
PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
CStringGetDatum(inputstring), 1));
/* start with end position holding the initial start position */
end_posn = 0;
for (fldnum=1;;fldnum++) /* field number is 1 based */
{
Datum dvalue;
bool disnull = false;
start_posn = end_posn;
end_posn = text_position(PointerGetDatum(inputstring),
PointerGetDatum(fldsep),
fldnum);
if ((start_posn == 0) && (end_posn == 0)) /* fldsep not found */
{
if (fldnum == 1)
{
/* first element
* return one element, 1D, array using the input string */
PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
CStringGetDatum(inputstring), 1));
}
else
{
/* otherwise create array and exit */
PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
}
}
else if ((start_posn != 0) && (end_posn == 0))
{
/* last field requested */
result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
}
else if ((start_posn == 0) && (end_posn != 0))
{
/* first field requested */
result_text = LEFT(inputstring, fldsep);
}
else
{
/* prior to last field requested */
result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn - fldsep_len, false);
}
/* stash away current value */
dvalue = PointerGetDatum(result_text);
astate = accumArrayResult(astate, dvalue,
disnull, TEXTOID, oldcontext);
}
/* never reached -- keep compiler quiet */
PG_RETURN_NULL();
}
/*
* array_to_text
* concatenate Cstring representation of input array elements
* using provided field separator
*/
Datum
array_to_text(PG_FUNCTION_ARGS)
{
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
char *fldsep = PG_TEXTARG_GET_STR(1);
int nitems, *dims, ndims;
char *p;
Oid element_type;
int typlen;
bool typbyval;
char typalign;
Oid typelem;
StringInfo result_str = makeStringInfo();
int i;
ArrayMetaState *my_extra;
p = ARR_DATA_PTR(v);
ndims = ARR_NDIM(v);
dims = ARR_DIMS(v);
nitems = ArrayGetNItems(ndims, dims);
/* if there are no elements, return an empty string */
if (nitems == 0)
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
element_type = ARR_ELEMTYPE(v);
/*
* We arrange to look up info about element type, including its output
* conversion proc, only once per series of calls, assuming the element
* type doesn't change underneath us.
*/
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL)
{
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(ArrayMetaState));
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
my_extra->element_type = InvalidOid;
}
if (my_extra->element_type != element_type)
{
/* Get info about element type, including its output conversion proc */
get_type_io_data(element_type, IOFunc_output,
&my_extra->typlen, &my_extra->typbyval,
&my_extra->typalign, &my_extra->typdelim,
&my_extra->typelem, &my_extra->typiofunc);
fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
fcinfo->flinfo->fn_mcxt);
my_extra->element_type = element_type;
}
typlen = my_extra->typlen;
typbyval = my_extra->typbyval;
typalign = my_extra->typalign;
typelem = my_extra->typelem;
for (i = 0; i < nitems; i++)
{
Datum itemvalue;
char *value;
itemvalue = fetch_att(p, typbyval, typlen);
value = DatumGetCString(FunctionCall3(&my_extra->proc,
itemvalue,
ObjectIdGetDatum(typelem),
Int32GetDatum(-1)));
if (i > 0)
appendStringInfo(result_str, "%s%s", fldsep, value);
else
appendStringInfo(result_str, "%s", value);
p = att_addlength(p, typlen, PointerGetDatum(p));
p = (char *) att_align(p, typalign);
}
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data));
}
#define HEXBASE 16
/*
* Convert a int32 to a string containing a base 16 (hex) representation of

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.99 2003/06/25 21:30:32 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.100 2003/06/27 00:33:25 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@ -1090,6 +1090,56 @@ get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
ReleaseSysCache(tp);
}
/*
* get_type_io_data
*
* A six-fer: given the type OID, return typlen, typbyval, typalign,
* typdelim, typelem, and IO function OID. The IO function
* returned is controlled by IOFuncSelector
*/
void
get_type_io_data(Oid typid,
IOFuncSelector which_func,
int16 *typlen,
bool *typbyval,
char *typalign,
char *typdelim,
Oid *typelem,
Oid *func)
{
HeapTuple typeTuple;
Form_pg_type typeStruct;
typeTuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(typid),
0, 0, 0);
if (!HeapTupleIsValid(typeTuple))
elog(ERROR, "cache lookup failed for type %u", typid);
typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
*typlen = typeStruct->typlen;
*typbyval = typeStruct->typbyval;
*typalign = typeStruct->typalign;
*typdelim = typeStruct->typdelim;
*typelem = typeStruct->typelem;
switch (which_func)
{
case IOFunc_input:
*func = typeStruct->typinput;
break;
case IOFunc_output:
*func = typeStruct->typoutput;
break;
case IOFunc_receive:
*func = typeStruct->typreceive;
break;
case IOFunc_send:
*func = typeStruct->typsend;
break;
}
ReleaseSysCache(typeTuple);
}
#ifdef NOT_USED
char
get_typalign(Oid typid)

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.200 2003/06/25 01:26:16 momjian Exp $
* $Id: catversion.h,v 1.201 2003/06/27 00:33:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200306241
#define CATALOG_VERSION_NO 200306261
#endif

View File

@ -16,7 +16,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_amop.h,v 1.52 2003/06/25 21:30:32 momjian Exp $
* $Id: pg_amop.h,v 1.53 2003/06/27 00:33:25 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -418,6 +418,15 @@ DATA(insert ( 2098 3 f 2334 ));
DATA(insert ( 2098 4 f 2335 ));
DATA(insert ( 2098 5 f 2336 ));
/*
* btree array_ops
*/
DATA(insert ( 397 1 f 1072 ));
DATA(insert ( 397 2 f 1074 ));
DATA(insert ( 397 3 f 1070 ));
DATA(insert ( 397 4 f 1075 ));
DATA(insert ( 397 5 f 1073 ));
/*
* hash index _ops

View File

@ -14,7 +14,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_amproc.h,v 1.40 2003/06/25 21:30:32 momjian Exp $
* $Id: pg_amproc.h,v 1.41 2003/06/27 00:33:25 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -78,6 +78,7 @@ DATA(insert ( 1993 3 199 ));
/* btree */
DATA(insert ( 397 1 382 ));
DATA(insert ( 421 1 357 ));
DATA(insert ( 423 1 1596 ));
DATA(insert ( 424 1 1693 ));

View File

@ -26,7 +26,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_opclass.h,v 1.53 2003/06/25 21:30:32 momjian Exp $
* $Id: pg_opclass.h,v 1.54 2003/06/27 00:33:25 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -87,6 +87,8 @@ typedef FormData_pg_opclass *Form_pg_opclass;
*/
DATA(insert OID = 421 ( 403 abstime_ops PGNSP PGUID 702 t 0 ));
DATA(insert OID = 397 ( 403 array_ops PGNSP PGUID 2277 t 0 ));
#define ARRAY_BTREE_OPS_OID 397
DATA(insert OID = 422 ( 402 bigbox_ops PGNSP PGUID 603 f 0 ));
DATA(insert OID = 423 ( 403 bit_ops PGNSP PGUID 1560 t 0 ));
DATA(insert OID = 424 ( 403 bool_ops PGNSP PGUID 16 t 0 ));

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_operator.h,v 1.117 2003/06/25 21:30:32 momjian Exp $
* $Id: pg_operator.h,v 1.118 2003/06/27 00:33:25 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -116,7 +116,6 @@ DATA(insert OID = 96 ( "=" PGNSP PGUID b t 23 23 16 96 518 97 97 97 521 int
DATA(insert OID = 97 ( "<" PGNSP PGUID b f 23 23 16 521 525 0 0 0 0 int4lt scalarltsel scalarltjoinsel ));
DATA(insert OID = 98 ( "=" PGNSP PGUID b t 25 25 16 98 531 664 664 664 666 texteq eqsel eqjoinsel ));
DATA(insert OID = 329 ( "=" PGNSP PGUID b f 2277 2277 16 329 0 0 0 0 0 array_eq eqsel eqjoinsel ));
DATA(insert OID = 349 ( "||" PGNSP PGUID b f 2277 2283 2277 0 0 0 0 0 0 array_append - - ));
DATA(insert OID = 374 ( "||" PGNSP PGUID b f 2283 2277 2277 0 0 0 0 0 0 array_prepend - - ));
DATA(insert OID = 375 ( "||" PGNSP PGUID b f 2277 2277 2277 0 0 0 0 0 0 array_cat - - ));
@ -425,6 +424,7 @@ DATA(insert OID = 965 ( "^" PGNSP PGUID b f 701 701 701 0 0 0 0 0 0 dpow -
DATA(insert OID = 966 ( "+" PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
DATA(insert OID = 967 ( "-" PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
DATA(insert OID = 968 ( "~" PGNSP PGUID b f 1034 1033 16 0 0 0 0 0 0 aclcontains - - ));
DATA(insert OID = 974 ( "=" PGNSP PGUID b f 1033 1033 16 974 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));
/* additional geometric operators - thomas 1997-07-09 */
DATA(insert OID = 969 ( "@@" PGNSP PGUID l f 0 601 600 0 0 0 0 0 0 lseg_center - - ));
@ -441,6 +441,16 @@ DATA(insert OID = 1059 ( "<=" PGNSP PGUID b f 1042 1042 16 1061 1060 0 0 0
DATA(insert OID = 1060 ( ">" PGNSP PGUID b f 1042 1042 16 1058 1059 0 0 0 0 bpchargt scalargtsel scalargtjoinsel ));
DATA(insert OID = 1061 ( ">=" PGNSP PGUID b f 1042 1042 16 1059 1058 0 0 0 0 bpcharge scalargtsel scalargtjoinsel ));
/* generic array comparison operators */
DATA(insert OID = 1070 ( "=" PGNSP PGUID b f 2277 2277 16 1070 1071 1072 1072 1072 1073 array_eq eqsel eqjoinsel ));
#define ARRAY_EQ_OP 1070
DATA(insert OID = 1071 ( "<>" PGNSP PGUID b f 2277 2277 16 1071 1070 0 0 0 0 array_ne neqsel neqjoinsel ));
DATA(insert OID = 1072 ( "<" PGNSP PGUID b f 2277 2277 16 1073 1075 0 0 0 0 array_lt scalarltsel scalarltjoinsel ));
#define ARRAY_LT_OP 1072
DATA(insert OID = 1073 ( ">" PGNSP PGUID b f 2277 2277 16 1072 1074 0 0 0 0 array_gt scalargtsel scalargtjoinsel ));
DATA(insert OID = 1074 ( "<=" PGNSP PGUID b f 2277 2277 16 1075 1073 0 0 0 0 array_le scalarltsel scalarltjoinsel ));
DATA(insert OID = 1075 ( ">=" PGNSP PGUID b f 2277 2277 16 1074 1072 0 0 0 0 array_ge scalargtsel scalargtjoinsel ));
/* date operators */
DATA(insert OID = 1076 ( "+" PGNSP PGUID b f 1082 1186 1114 0 0 0 0 0 0 date_pl_interval - - ));
DATA(insert OID = 1077 ( "-" PGNSP PGUID b f 1082 1186 1114 0 0 0 0 0 0 date_mi_interval - - ));

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_proc.h,v 1.307 2003/06/25 21:30:32 momjian Exp $
* $Id: pg_proc.h,v 1.308 2003/06/27 00:33:25 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@ -758,6 +758,8 @@ DATA(insert OID = 359 ( btnamecmp PGNSP PGUID 12 f f t f i 2 23 "19 19" btn
DESCR("btree less-equal-greater");
DATA(insert OID = 360 ( bttextcmp PGNSP PGUID 12 f f t f i 2 23 "25 25" bttextcmp - _null_ ));
DESCR("btree less-equal-greater");
DATA(insert OID = 382 ( btarraycmp PGNSP PGUID 12 f f t f i 2 23 "2277 2277" btarraycmp - _null_ ));
DESCR("btree less-equal-greater");
DATA(insert OID = 361 ( lseg_distance PGNSP PGUID 12 f f t f i 2 701 "601 601" lseg_distance - _null_ ));
DESCR("distance between");
@ -988,14 +990,23 @@ DESCR("greater-than");
DATA(insert OID = 743 ( text_ge PGNSP PGUID 12 f f t f i 2 16 "25 25" text_ge - _null_ ));
DESCR("greater-than-or-equal");
DATA(insert OID = 744 ( array_eq PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
DESCR("array equal");
DATA(insert OID = 745 ( current_user PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
DESCR("current user name");
DATA(insert OID = 746 ( session_user PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
DESCR("session user name");
DATA(insert OID = 744 ( array_eq PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ ));
DESCR("array equal");
DATA(insert OID = 390 ( array_ne PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ ));
DESCR("array not equal");
DATA(insert OID = 391 ( array_lt PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ ));
DESCR("array less than");
DATA(insert OID = 392 ( array_gt PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ ));
DESCR("array greater than");
DATA(insert OID = 393 ( array_le PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ ));
DESCR("array less than or equal");
DATA(insert OID = 396 ( array_ge PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ ));
DESCR("array greater than or equal");
DATA(insert OID = 747 ( array_dims PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
DESCR("array dimensions");
DATA(insert OID = 750 ( array_in PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23" array_in - _null_ ));
@ -1006,22 +1017,18 @@ 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" array_upper - _null_ ));
DESCR("array upper dimension");
DATA(insert OID = 377 ( singleton_array PGNSP PGUID 12 f f t f i 1 2277 "2283" singleton_array - _null_ ));
DESCR("create array from single element");
DATA(insert OID = 378 ( array_append PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" 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" array_push - _null_ ));
DESCR("prepend element onto front of array");
DATA(insert OID = 380 ( array_accum PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ ));
DESCR("push element onto end of array, creating array if needed");
DATA(insert OID = 381 ( array_assign PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_ ));
DESCR("assign specific array element");
DATA(insert OID = 382 ( array_subscript PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ ));
DESCR("return specific array element");
DATA(insert OID = 383 ( array_cat PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ ));
DESCR("concatenate two arrays");
DATA(insert OID = 384 ( array_coerce PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ ));
DESCR("coerce array type to another array type");
DATA(insert OID = 394 ( string_to_array PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ ));
DESCR("split delimited text into text[]");
DATA(insert OID = 395 ( array_to_string PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ ));
DESCR("concatenate array elements, using delimiter, into text");
DATA(insert OID = 760 ( smgrin PGNSP PGUID 12 f f t f s 1 210 "2275" smgrin - _null_ ));
DESCR("I/O");
@ -1322,6 +1329,8 @@ DATA(insert OID = 1036 ( aclremove PGNSP PGUID 12 f f t f s 2 1034 "1034 10
DESCR("remove ACL item");
DATA(insert OID = 1037 ( aclcontains PGNSP PGUID 12 f f t f s 2 16 "1034 1033" aclcontains - _null_ ));
DESCR("does ACL contain item?");
DATA(insert OID = 1062 ( aclitemeq PGNSP PGUID 12 f f t f s 2 16 "1033 1033" aclitem_eq - _null_ ));
DESCR("equality operator for ACL items");
DATA(insert OID = 1365 ( makeaclitem PGNSP PGUID 12 f f t f s 5 1033 "23 23 23 25 16" makeaclitem - _null_ ));
DESCR("make ACL item");
DATA(insert OID = 1038 ( seteval PGNSP PGUID 12 f f t t v 1 23 "26" seteval - _null_ ));

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_oper.h,v 1.27 2003/06/25 21:30:33 momjian Exp $
* $Id: parse_oper.h,v 1.28 2003/06/27 00:33:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -45,6 +45,7 @@ extern Operator ordering_oper(Oid argtype, bool noError);
extern Oid compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
extern Oid equality_oper_funcid(Oid argtype);
extern Oid ordering_oper_opid(Oid argtype);
extern Oid ordering_oper_funcid(Oid argtype);
/* Extract operator OID or underlying-function OID from an Operator tuple */
extern Oid oprid(Operator op);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: acl.h,v 1.54 2003/06/25 21:30:33 momjian Exp $
* $Id: acl.h,v 1.55 2003/06/27 00:33:26 tgl Exp $
*
* NOTES
* For backward-compatibility purposes we have to allow there
@ -192,6 +192,7 @@ extern Datum aclinsert(PG_FUNCTION_ARGS);
extern Datum aclremove(PG_FUNCTION_ARGS);
extern Datum aclcontains(PG_FUNCTION_ARGS);
extern Datum makeaclitem(PG_FUNCTION_ARGS);
extern Datum aclitem_eq(PG_FUNCTION_ARGS);
/*
* prototypes for functions in aclchk.c

View File

@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: array.h,v 1.40 2003/06/25 21:30:33 momjian Exp $
* $Id: array.h,v 1.41 2003/06/27 00:33:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -32,6 +32,37 @@ typedef struct
Oid elemtype; /* element type OID */
} ArrayType;
typedef struct ArrayBuildState
{
MemoryContext mcontext; /* where all the temp stuff is kept */
Datum *dvalues; /* array of accumulated Datums */
/*
* The allocated size of dvalues[] is always a multiple of
* ARRAY_ELEMS_CHUNKSIZE
*/
#define ARRAY_ELEMS_CHUNKSIZE 64
int nelems; /* number of valid Datums in dvalues[] */
Oid element_type; /* data type of the Datums */
int16 typlen; /* needed info about datatype */
bool typbyval;
char typalign;
} ArrayBuildState;
/*
* structure to cache type metadata needed for array manipulation
*/
typedef struct ArrayMetaState
{
Oid element_type;
int16 typlen;
bool typbyval;
char typalign;
char typdelim;
Oid typelem;
Oid typiofunc;
FmgrInfo proc;
} ArrayMetaState;
/*
* fmgr macros for array objects
*/
@ -86,11 +117,15 @@ extern Datum array_recv(PG_FUNCTION_ARGS);
extern Datum array_send(PG_FUNCTION_ARGS);
extern Datum array_length_coerce(PG_FUNCTION_ARGS);
extern Datum array_eq(PG_FUNCTION_ARGS);
extern Datum array_ne(PG_FUNCTION_ARGS);
extern Datum array_lt(PG_FUNCTION_ARGS);
extern Datum array_gt(PG_FUNCTION_ARGS);
extern Datum array_le(PG_FUNCTION_ARGS);
extern Datum array_ge(PG_FUNCTION_ARGS);
extern Datum btarraycmp(PG_FUNCTION_ARGS);
extern Datum array_dims(PG_FUNCTION_ARGS);
extern Datum array_lower(PG_FUNCTION_ARGS);
extern Datum array_upper(PG_FUNCTION_ARGS);
extern Datum array_assign(PG_FUNCTION_ARGS);
extern Datum array_subscript(PG_FUNCTION_ARGS);
extern Datum array_type_coerce(PG_FUNCTION_ARGS);
extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
@ -124,7 +159,14 @@ extern void deconstruct_array(ArrayType *array,
Oid elmtype,
int elmlen, bool elmbyval, char elmalign,
Datum **elemsp, int *nelemsp);
extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate,
Datum dvalue, bool disnull,
Oid element_type,
MemoryContext rcontext);
extern Datum makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext);
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
int *dims, int *lbs, MemoryContext rcontext);
/*
* prototypes for functions defined in arrayutils.c
@ -141,12 +183,11 @@ extern int mda_next_tuple(int n, int *curr, int *span);
/*
* prototypes for functions defined in array_userfuncs.c
*/
extern Datum singleton_array(PG_FUNCTION_ARGS);
extern Datum array_push(PG_FUNCTION_ARGS);
extern Datum array_accum(PG_FUNCTION_ARGS);
extern Datum array_cat(PG_FUNCTION_ARGS);
extern ArrayType *create_singleton_array(Oid element_type,
extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
Oid element_type,
Datum element,
int ndims);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: builtins.h,v 1.222 2003/06/25 21:30:33 momjian Exp $
* $Id: builtins.h,v 1.223 2003/06/27 00:33:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -530,6 +530,8 @@ extern bool SplitIdentifierString(char *rawstring, char separator,
List **namelist);
extern Datum replace_text(PG_FUNCTION_ARGS);
extern Datum split_text(PG_FUNCTION_ARGS);
extern Datum text_to_array(PG_FUNCTION_ARGS);
extern Datum array_to_text(PG_FUNCTION_ARGS);
extern Datum to_hex32(PG_FUNCTION_ARGS);
extern Datum to_hex64(PG_FUNCTION_ARGS);
extern Datum md5_text(PG_FUNCTION_ARGS);

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: lsyscache.h,v 1.74 2003/06/25 21:30:33 momjian Exp $
* $Id: lsyscache.h,v 1.75 2003/06/27 00:33:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -15,6 +15,15 @@
#include "access/htup.h"
/* I/O function selector for get_type_io_data */
typedef enum IOFuncSelector
{
IOFunc_input,
IOFunc_output,
IOFunc_receive,
IOFunc_send
} IOFuncSelector;
extern bool op_in_opclass(Oid opno, Oid opclass);
extern bool op_requires_recheck(Oid opno, Oid opclass);
extern Oid get_opclass_member(Oid opclass, int16 strategy);
@ -56,6 +65,14 @@ extern bool get_typbyval(Oid typid);
extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
char *typalign);
extern void get_type_io_data(Oid typid,
IOFuncSelector which_func,
int16 *typlen,
bool *typbyval,
char *typalign,
char *typdelim,
Oid *typelem,
Oid *func);
extern char get_typstorage(Oid typid);
extern int32 get_typtypmod(Oid typid);
extern Node *get_typdefault(Oid typid);

View File

@ -1,4 +1,4 @@
/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.240 2003/06/26 11:37:05 meskes Exp $ */
/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.241 2003/06/27 00:33:26 tgl Exp $ */
/* Copyright comment */
%{
@ -4597,7 +4597,7 @@ type_declaration: S_TYPEDEF
$3.type_enum != ECPGt_char &&
$3.type_enum != ECPGt_unsigned_char &&
atoi(this->type->type_index) >= 0)
mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
types = this;
}
@ -5417,7 +5417,7 @@ ECPGTypedef: TYPE_P
$5.type_enum != ECPGt_char &&
$5.type_enum != ECPGt_unsigned_char &&
atoi(this->type->type_index) >= 0)
mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
types = this;
}
@ -5484,7 +5484,7 @@ ECPGVar: SQL_VAR
default:
if (atoi(length) >= 0)
mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
if (atoi(dimension) < 0)
type = ECPGmake_simple_type($5.type_enum, make_str("1"));

View File

@ -504,7 +504,7 @@ ECPGfree_type(struct ECPGtype * type)
switch (type->u.element->type)
{
case ECPGt_array:
yyerror("internal error, found multi-dimensional array\n");
yyerror("internal error, found multidimensional array\n");
break;
case ECPGt_struct:
case ECPGt_union:

View File

@ -436,7 +436,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
if (atoi(type_index) >= 0)
{
if (atoi(*length) >= 0)
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
*length = type_index;
}
@ -444,7 +444,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
if (atoi(type_dimension) >= 0)
{
if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
if (atoi(*dimension) >= 0)
*length = *dimension;
@ -463,10 +463,10 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
switch (type_enum)
{
@ -480,7 +480,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
}
if (atoi(*length) >= 0)
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");
mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");
break;
case ECPGt_varchar:
@ -525,7 +525,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
}
if (atoi(*length) >= 0)
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");
mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");
break;
}

View File

@ -178,19 +178,13 @@ SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
(1 row)
-- functions
SELECT singleton_array(42) AS "{42}";
{42}
------
{42}
(1 row)
SELECT array_append(singleton_array(42), 6) AS "{42,6}";
SELECT array_append(array[42], 6) AS "{42,6}";
{42,6}
--------
{42,6}
(1 row)
SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
SELECT array_prepend(6, array[42]) AS "{6,42}";
{6,42}
--------
{6,42}
@ -214,24 +208,6 @@ SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
{{3,4},{5,6},{1,2}}
(1 row)
SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
1.2
-----
1.2
(1 row)
SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
{1.1,9.99,1.3}
----------------
{1.1,9.99,1.3}
(1 row)
SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
9.99
------
9.99
(1 row)
-- operators
SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
a
@ -318,3 +294,24 @@ SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}"
{{{{{{a,bb,ccc}}}}}}
(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'
insert into arr_tbl values ('{1,2,3}');
insert into arr_tbl values ('{1,2}');
-- failure expected:
insert into arr_tbl values ('{1,2,3}');
ERROR: Cannot insert a duplicate key into unique index arr_tbl_f1_key
insert into arr_tbl values ('{2,3,4}');
insert into arr_tbl values ('{1,5,3}');
insert into arr_tbl values ('{1,2,10}');
set enable_seqscan to off;
select * from arr_tbl where f1 > '{1,2,3}' and f1 <= '{1,5,3}';
f1
----------
{1,2,10}
{1,5,3}
(2 rows)
-- note: if above select doesn't produce the expected tuple order,
-- then you didn't get an indexscan plan, and something is busted.

View File

@ -130,15 +130,11 @@ SELECT ARRAY[ARRAY['hello'],ARRAY['world']];
SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
-- functions
SELECT singleton_array(42) AS "{42}";
SELECT array_append(singleton_array(42), 6) AS "{42,6}";
SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
SELECT array_append(array[42], 6) AS "{42,6}";
SELECT array_prepend(6, array[42]) AS "{6,42}";
SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}";
SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}";
SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2;
SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2;
SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2;
-- operators
SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
@ -157,3 +153,17 @@ SELECT ARRAY[1,2,3]::text[]::int[]::float8[] is of (float8[]) as "TRUE";
SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] AS "{{a,bc},{def,hijk}}";
SELECT ARRAY[['a','bc'],['def','hijk']]::text[]::varchar[] is of (varchar[]) as "TRUE";
SELECT CAST(ARRAY[[[[[['a','bb','ccc']]]]]] as text[]) as "{{{{{{a,bb,ccc}}}}}}";
-- test indexes on arrays
create temp table arr_tbl (f1 int[] unique);
insert into arr_tbl values ('{1,2,3}');
insert into arr_tbl values ('{1,2}');
-- failure expected:
insert into arr_tbl values ('{1,2,3}');
insert into arr_tbl values ('{2,3,4}');
insert into arr_tbl values ('{1,5,3}');
insert into arr_tbl values ('{1,2,10}');
set enable_seqscan to off;
select * from arr_tbl where f1 > '{1,2,3}' and f1 <= '{1,5,3}';
-- note: if above select doesn't produce the expected tuple order,
-- then you didn't get an indexscan plan, and something is busted.