Re: array support patch phase 1 patch

From: Bruce Momjian <pgman(at)candle(dot)pha(dot)pa(dot)us>
To: Joe Conway <mail(at)joeconway(dot)com>
Cc: "Patches (PostgreSQL)" <pgsql-patches(at)postgresql(dot)org>, Peter Eisentraut <peter_e(at)gmx(dot)net>, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
Subject: Re: array support patch phase 1 patch
Date: 2003-06-24 03:11:54
Message-ID: 200306240311.h5O3BsJ14022@candle.pha.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-patches


Your patch has been added to the PostgreSQL unapplied patches list at:

http://momjian.postgresql.org/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

---------------------------------------------------------------------------

Joe Conway wrote:
> Bruce Momjian wrote:
> > Your patch has been added to the PostgreSQL unapplied patches list at:
> [...snip...]
> >>Joe Conway wrote:
> >>>The attached patch addresses Peter's concerns, subject to our agreements
> >>>above. I.e, the changes are:
> >>
> >>The previous patch was no longer applying cleanly, so here is an update.
> >>Applies and compiles clean on cvs tip, passes all regression tests.
> >>Please apply.
>
> Here's another update against cvs tip. The previous version now has a
> duplicate oid conflict -- fixed.
>
> Joe

> Index: doc/src/sgml/array.sgml
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v
> retrieving revision 1.25
> diff -c -r1.25 array.sgml
> *** doc/src/sgml/array.sgml 13 Mar 2003 01:30:26 -0000 1.25
> --- doc/src/sgml/array.sgml 24 Jun 2003 02:24:06 -0000
> ***************
> *** 60,73 ****
> </programlisting>
> </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.
> </para>
> </note>
> </sect2>
>
> <sect2>
> --- 60,133 ----
> </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>
> + 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,142 ****
> </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
> <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
> A lower bound of 1 is assumed for any subscript where only one value
> ! is specified.
> </para>
>
> <para>
> --- 192,221 ----
> </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
> <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
> A lower bound of 1 is assumed for any subscript where only one value
> ! 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,153 ****
> WHERE name = 'Carol';
> </programlisting>
>
> ! or updated at a single element:
>
> <programlisting>
> UPDATE sal_emp SET pay_by_quarter[4] = 15000
> --- 226,248 ----
> WHERE name = 'Carol';
> </programlisting>
>
> ! 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,165 ****
> --- 255,268 ----
> 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>
> ***************
> *** 179,184 ****
> --- 282,369 ----
> </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:
>
> ***************
> *** 194,199 ****
> --- 379,394 ----
> </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
> all considered to be of the same type, regardless of size or number
> ***************
> *** 300,305 ****
> --- 495,566 ----
> 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>
> ***************
> *** 316,321 ****
> --- 577,590 ----
> Alternatively, you can use backslash-escaping to protect all data characters
> 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
> Index: doc/src/sgml/func.sgml
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v
> retrieving revision 1.154
> diff -c -r1.154 func.sgml
> *** doc/src/sgml/func.sgml 5 May 2003 15:08:49 -0000 1.154
> --- doc/src/sgml/func.sgml 24 Jun 2003 02:24:06 -0000
> ***************
> *** 6962,6967 ****
> --- 6962,7164 ----
>
> </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>
> Index: src/backend/catalog/pg_aggregate.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v
> retrieving revision 1.56
> diff -c -r1.56 pg_aggregate.c
> *** src/backend/catalog/pg_aggregate.c 18 Sep 2002 21:35:20 -0000 1.56
> --- src/backend/catalog/pg_aggregate.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 50,59 ****
> Oid finalfn = InvalidOid; /* can be omitted */
> Oid finaltype;
> Oid fnArgs[FUNC_MAX_ARGS];
> ! int nargs;
> Oid procOid;
> TupleDesc tupDesc;
> int i;
> ObjectAddress myself,
> referenced;
>
> --- 50,65 ----
> Oid finalfn = InvalidOid; /* can be omitted */
> Oid finaltype;
> Oid fnArgs[FUNC_MAX_ARGS];
> ! int nargs_transfn;
> ! int nargs_finalfn;
> Oid procOid;
> TupleDesc tupDesc;
> int i;
> + Oid rettype;
> + Oid *true_oid_array_transfn;
> + Oid *true_oid_array_finalfn;
> + bool retset;
> + FuncDetailCode fdresult;
> ObjectAddress myself,
> referenced;
>
> ***************
> *** 68,91 ****
> MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
> fnArgs[0] = aggTransType;
> if (aggBaseType == ANYOID)
> ! nargs = 1;
> else
> {
> fnArgs[1] = aggBaseType;
> ! nargs = 2;
> }
> ! transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
> if (!OidIsValid(transfn))
> ! func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
> tup = SearchSysCache(PROCOID,
> ObjectIdGetDatum(transfn),
> 0, 0, 0);
> if (!HeapTupleIsValid(tup))
> ! func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
> proc = (Form_pg_proc) GETSTRUCT(tup);
> - if (proc->prorettype != aggTransType)
> - elog(ERROR, "return type of transition function %s is not %s",
> - NameListToString(aggtransfnName), format_type_be(aggTransType));
>
> /*
> * If the transfn is strict and the initval is NULL, make sure input
> --- 74,122 ----
> MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
> fnArgs[0] = aggTransType;
> if (aggBaseType == ANYOID)
> ! nargs_transfn = 1;
> else
> {
> fnArgs[1] = aggBaseType;
> ! nargs_transfn = 2;
> }
> !
> ! /*
> ! * func_get_detail looks up the function in the catalogs, does
> ! * disambiguation for polymorphic functions, handles inheritance, and
> ! * returns the funcid and type and set or singleton status of the
> ! * function's return value. it also returns the true argument types
> ! * to the function.
> ! */
> ! fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs,
> ! &transfn, &rettype, &retset,
> ! &true_oid_array_transfn);
> !
> ! /* only valid case is a normal function */
> ! if (fdresult != FUNCDETAIL_NORMAL)
> ! func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
> !
> if (!OidIsValid(transfn))
> ! func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL);
> !
> ! /*
> ! * enforce consistency with ANYARRAY and ANYELEMENT argument
> ! * and return types, possibly modifying return type along the way
> ! */
> ! rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn,
> ! nargs_transfn, rettype);
> !
> ! if (rettype != aggTransType)
> ! elog(ERROR, "return type of transition function %s is not %s",
> ! NameListToString(aggtransfnName), format_type_be(aggTransType));
> !
> tup = SearchSysCache(PROCOID,
> ObjectIdGetDatum(transfn),
> 0, 0, 0);
> if (!HeapTupleIsValid(tup))
> ! func_error("AggregateCreate", aggtransfnName,
> ! nargs_transfn, fnArgs, NULL);
> proc = (Form_pg_proc) GETSTRUCT(tup);
>
> /*
> * If the transfn is strict and the initval is NULL, make sure input
> ***************
> *** 105,121 ****
> {
> MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
> fnArgs[0] = aggTransType;
> ! finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
> ! if (!OidIsValid(finalfn))
> func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> ! tup = SearchSysCache(PROCOID,
> ! ObjectIdGetDatum(finalfn),
> ! 0, 0, 0);
> ! if (!HeapTupleIsValid(tup))
> func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> ! proc = (Form_pg_proc) GETSTRUCT(tup);
> ! finaltype = proc->prorettype;
> ! ReleaseSysCache(tup);
> }
> else
> {
> --- 136,161 ----
> {
> MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
> fnArgs[0] = aggTransType;
> ! nargs_finalfn = 1;
> !
> ! fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs,
> ! &finalfn, &rettype, &retset,
> ! &true_oid_array_finalfn);
> !
> ! /* only valid case is a normal function */
> ! if (fdresult != FUNCDETAIL_NORMAL)
> func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !
> ! if (!OidIsValid(finalfn))
> func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
> !
> ! /*
> ! * enforce consistency with ANYARRAY and ANYELEMENT argument
> ! * and return types, possibly modifying return type along the way
> ! */
> ! finaltype = enforce_generic_type_consistency(fnArgs,
> ! true_oid_array_finalfn,
> ! nargs_finalfn, rettype);
> }
> else
> {
> ***************
> *** 125,130 ****
> --- 165,191 ----
> finaltype = aggTransType;
> }
> Assert(OidIsValid(finaltype));
> +
> + /*
> + * special disallowed cases:
> + * 1) if finaltype is polymorphic, basetype cannot be ANY
> + * 2) if finaltype is polymorphic, both args to transfn must be
> + * polymorphic
> + */
> + if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
> + {
> + if (aggBaseType == ANYOID)
> + elog(ERROR, "aggregate with base type ANY must have a " \
> + "non-polymorphic return type");
> +
> + if (nargs_transfn > 1 && (
> + (true_oid_array_transfn[0] != ANYARRAYOID &&
> + true_oid_array_transfn[0] != ANYELEMENTOID) ||
> + (true_oid_array_transfn[1] != ANYARRAYOID &&
> + true_oid_array_transfn[1] != ANYELEMENTOID)))
> + elog(ERROR, "aggregate with polymorphic return type requires " \
> + "state function with both arguments polymorphic");
> + }
>
> /*
> * Everything looks okay. Try to create the pg_proc entry for the
> Index: src/backend/commands/aggregatecmds.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v
> retrieving revision 1.5
> diff -c -r1.5 aggregatecmds.c
> *** src/backend/commands/aggregatecmds.c 4 Sep 2002 20:31:14 -0000 1.5
> --- src/backend/commands/aggregatecmds.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 119,125 ****
> baseTypeId = typenameTypeId(baseType);
>
> transTypeId = typenameTypeId(transType);
> ! if (get_typtype(transTypeId) == 'p')
> elog(ERROR, "Aggregate transition datatype cannot be %s",
> format_type_be(transTypeId));
>
> --- 119,127 ----
> baseTypeId = typenameTypeId(baseType);
>
> transTypeId = typenameTypeId(transType);
> ! if (get_typtype(transTypeId) == 'p' &&
> ! transTypeId != ANYARRAYOID &&
> ! transTypeId != ANYELEMENTOID)
> elog(ERROR, "Aggregate transition datatype cannot be %s",
> format_type_be(transTypeId));
>
> Index: src/backend/executor/execQual.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execQual.c,v
> retrieving revision 1.130
> diff -c -r1.130 execQual.c
> *** src/backend/executor/execQual.c 28 May 2003 22:32:49 -0000 1.130
> --- src/backend/executor/execQual.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 1528,1544 ****
> {
> /* Check other sub-arrays are compatible */
> if (elem_ndims != ARR_NDIM(array))
> ! elog(ERROR, "Multiple dimension 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 "
> "expressions with matching dimensions");
>
> if (memcmp(elem_lbs, ARR_LBOUND(array),
> elem_ndims * sizeof(int)) != 0)
> ! elog(ERROR, "Multiple dimension arrays must have array "
> "expressions with matching dimensions");
> }
>
> --- 1528,1544 ----
> {
> /* Check other sub-arrays are compatible */
> if (elem_ndims != ARR_NDIM(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, "Multidimensional arrays must have array "
> "expressions with matching dimensions");
>
> if (memcmp(elem_lbs, ARR_LBOUND(array),
> elem_ndims * sizeof(int)) != 0)
> ! elog(ERROR, "Multidimensional arrays must have array "
> "expressions with matching dimensions");
> }
>
> Index: src/backend/executor/nodeAgg.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v
> retrieving revision 1.107
> diff -c -r1.107 nodeAgg.c
> *** src/backend/executor/nodeAgg.c 22 Jun 2003 22:04:54 -0000 1.107
> --- src/backend/executor/nodeAgg.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 58,63 ****
> --- 58,64 ----
> #include "executor/executor.h"
> #include "executor/nodeAgg.h"
> #include "miscadmin.h"
> + #include "nodes/makefuncs.h"
> #include "optimizer/clauses.h"
> #include "parser/parse_coerce.h"
> #include "parser/parse_expr.h"
> ***************
> *** 212,218 ****
> static void agg_fill_hash_table(AggState *aggstate);
> static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
> static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
> !
>
> /*
> * Initialize all aggregates for a new group of input values.
> --- 213,219 ----
> static void agg_fill_hash_table(AggState *aggstate);
> static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
> static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
> ! static Oid resolve_type(Oid type_to_resolve, Oid context_type);
>
> /*
> * Initialize all aggregates for a new group of input values.
> ***************
> *** 351,364 ****
> fcinfo.context = NULL;
> fcinfo.resultinfo = NULL;
> fcinfo.isnull = false;
> -
> fcinfo.flinfo = &peraggstate->transfn;
> fcinfo.nargs = 2;
> fcinfo.arg[0] = pergroupstate->transValue;
> fcinfo.argnull[0] = pergroupstate->transValueIsNull;
> fcinfo.arg[1] = newVal;
> fcinfo.argnull[1] = isNull;
> -
> newVal = FunctionCallInvoke(&fcinfo);
>
> /*
> --- 352,363 ----
> ***************
> *** 1187,1193 ****
> --- 1186,1206 ----
> AclResult aclresult;
> Oid transfn_oid,
> finalfn_oid;
> + FuncExpr *transfnexpr,
> + *finalfnexpr;
> Datum textInitVal;
> + List *fargs;
> + Oid agg_rt_type;
> + Oid *transfn_arg_types;
> + List *transfn_args = NIL;
> + int transfn_nargs;
> + Oid transfn_ret_type;
> + Oid *finalfn_arg_types = NULL;
> + List *finalfn_args = NIL;
> + Oid finalfn_ret_type = InvalidOid;
> + int finalfn_nargs = 0;
> + Node *arg0;
> + Node *arg1;
> int i;
>
> /* Planner should have assigned aggregate to correct level */
> ***************
> *** 1238,1243 ****
> --- 1251,1416 ----
> &peraggstate->transtypeLen,
> &peraggstate->transtypeByVal);
>
> + peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
> + peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
> +
> + /* get the runtime aggregate argument type */
> + fargs = aggref->args;
> + agg_rt_type = exprType((Node *) nth(0, fargs));
> +
> + /* get the transition function argument and return types */
> + transfn_ret_type = get_func_rettype(transfn_oid);
> + transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs);
> +
> + /* resolve any polymorphic types */
> + if (transfn_nargs == 2)
> + /* base type was not ANY */
> + {
> + if (transfn_arg_types[1] == ANYARRAYOID ||
> + transfn_arg_types[1] == ANYELEMENTOID)
> + transfn_arg_types[1] = agg_rt_type;
> +
> + transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
> + agg_rt_type);
> +
> + /*
> + * Build arg list to use on the transfn FuncExpr node. We really
> + * only care that the node type is correct so that the transfn
> + * can discover the actual argument types at runtime using
> + * get_fn_expr_argtype()
> + */
> + arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
> + -1, COERCE_DONTCARE);
> + arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1],
> + -1, COERCE_DONTCARE);
> + transfn_args = makeList2(arg0, arg1);
> +
> + /*
> + * the state transition function always returns the same type
> + * as its first argument
> + */
> + if (transfn_ret_type == ANYARRAYOID ||
> + transfn_ret_type == ANYELEMENTOID)
> + transfn_ret_type = transfn_arg_types[0];
> + }
> + else if (transfn_nargs == 1)
> + /*
> + * base type was ANY, therefore the aggregate return type should
> + * be non-polymorphic
> + */
> + {
> + Oid finaltype = get_func_rettype(aggref->aggfnoid);
> +
> + /*
> + * this should have been prevented in AggregateCreate,
> + * but check anyway
> + */
> + if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)
> + elog(ERROR, "aggregate with base type ANY must have a " \
> + "non-polymorphic return type");
> +
> + /* see if we have a final function */
> + if (OidIsValid(finalfn_oid))
> + {
> + finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
> + if (finalfn_nargs != 1)
> + elog(ERROR, "final function takes unexpected number " \
> + "of arguments: %d", finalfn_nargs);
> +
> + /*
> + * final function argument is always the same as the state
> + * function return type
> + */
> + if (finalfn_arg_types[0] != ANYARRAYOID &&
> + finalfn_arg_types[0] != ANYELEMENTOID)
> + {
> + /* if it is not ambiguous, use it */
> + transfn_ret_type = finalfn_arg_types[0];
> + }
> + else
> + {
> + /* if it is ambiguous, try to derive it */
> + finalfn_ret_type = finaltype;
> + finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0],
> + finalfn_ret_type);
> + transfn_ret_type = finalfn_arg_types[0];
> + }
> + }
> + else
> + transfn_ret_type = finaltype;
> +
> + transfn_arg_types[0] = resolve_type(transfn_arg_types[0],
> + transfn_ret_type);
> +
> + /*
> + * Build arg list to use on the transfn FuncExpr node. We really
> + * only care that the node type is correct so that the transfn
> + * can discover the actual argument types at runtime using
> + * get_fn_expr_argtype()
> + */
> + arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0],
> + -1, COERCE_DONTCARE);
> + transfn_args = makeList1(arg0);
> + }
> + else
> + elog(ERROR, "state transition function takes unexpected number " \
> + "of arguments: %d", transfn_nargs);
> +
> + if (OidIsValid(finalfn_oid))
> + {
> + /* get the final function argument and return types */
> + if (finalfn_ret_type == InvalidOid)
> + finalfn_ret_type = get_func_rettype(finalfn_oid);
> +
> + if (!finalfn_arg_types)
> + {
> + finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs);
> + if (finalfn_nargs != 1)
> + elog(ERROR, "final function takes unexpected number " \
> + "of arguments: %d", finalfn_nargs);
> + }
> +
> + /*
> + * final function argument is always the same as the state
> + * function return type, which by now should have been resolved
> + */
> + if (finalfn_arg_types[0] == ANYARRAYOID ||
> + finalfn_arg_types[0] == ANYELEMENTOID)
> + finalfn_arg_types[0] = transfn_ret_type;
> +
> + /*
> + * Build arg list to use on the finalfn FuncExpr node. We really
> + * only care that the node type is correct so that the finalfn
> + * can discover the actual argument type at runtime using
> + * get_fn_expr_argtype()
> + */
> + arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0],
> + -1, COERCE_DONTCARE);
> + finalfn_args = makeList1(arg0);
> +
> + finalfn_ret_type = resolve_type(finalfn_ret_type,
> + finalfn_arg_types[0]);
> + }
> +
> + fmgr_info(transfn_oid, &peraggstate->transfn);
> + transfnexpr = (FuncExpr *) make_funcclause(transfn_oid,
> + transfn_ret_type,
> + false, /* cannot be a set */
> + COERCE_DONTCARE, /* to match any user expr */
> + transfn_args);
> + peraggstate->transfn.fn_expr = (Node *) transfnexpr;
> +
> + if (OidIsValid(finalfn_oid))
> + {
> + fmgr_info(finalfn_oid, &peraggstate->finalfn);
> + finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid,
> + finalfn_ret_type,
> + false, /* cannot be a set */
> + COERCE_DONTCARE, /* to match any user expr */
> + finalfn_args);
> + peraggstate->finalfn.fn_expr = (Node *) finalfnexpr;
> + }
> +
> /*
> * initval is potentially null, so don't try to access it as a
> * struct field. Must do it the hard way with SysCacheGetAttr.
> ***************
> *** 1250,1263 ****
> peraggstate->initValue = (Datum) 0;
> else
> peraggstate->initValue = GetAggInitVal(textInitVal,
> ! aggform->aggtranstype);
> !
> ! peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
> ! peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
> !
> ! fmgr_info(transfn_oid, &peraggstate->transfn);
> ! if (OidIsValid(finalfn_oid))
> ! fmgr_info(finalfn_oid, &peraggstate->finalfn);
>
> /*
> * If the transfn is strict and the initval is NULL, make sure
> --- 1423,1429 ----
> peraggstate->initValue = (Datum) 0;
> else
> peraggstate->initValue = GetAggInitVal(textInitVal,
> ! transfn_arg_types[0]);
>
> /*
> * If the transfn is strict and the initval is NULL, make sure
> ***************
> *** 1468,1471 ****
> --- 1634,1670 ----
> elog(ERROR, "Aggregate function %u called as normal function",
> fcinfo->flinfo->fn_oid);
> return (Datum) 0; /* keep compiler quiet */
> + }
> +
> + static Oid
> + resolve_type(Oid type_to_resolve, Oid context_type)
> + {
> + Oid resolved_type;
> +
> + if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID)
> + resolved_type = type_to_resolve;
> + else if (type_to_resolve == ANYARRAYOID)
> + /* any array */
> + {
> + Oid context_type_arraytype = get_array_type(context_type);
> +
> + if (context_type_arraytype != InvalidOid)
> + resolved_type = context_type_arraytype;
> + else
> + resolved_type = context_type;
> + }
> + else if (type_to_resolve == ANYELEMENTOID)
> + /* any element */
> + {
> + Oid context_type_elemtype = get_element_type(context_type);
> +
> + if (context_type_elemtype != InvalidOid)
> + resolved_type = context_type_elemtype;
> + else
> + resolved_type = context_type;
> + }
> + else
> + resolved_type = type_to_resolve;
> +
> + return resolved_type;
> }
> Index: src/backend/executor/nodeSubplan.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v
> retrieving revision 1.47
> diff -c -r1.47 nodeSubplan.c
> *** src/backend/executor/nodeSubplan.c 22 Jun 2003 22:04:54 -0000 1.47
> --- src/backend/executor/nodeSubplan.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 28,50 ****
> #include "utils/datum.h"
> #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);
> --- 28,33 ----
> ***************
> *** 54,66 ****
> 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);
> -
>
> /* ----------------------------------------------------------------
> * ExecSubPlan
> --- 37,42 ----
> ***************
> *** 224,229 ****
> --- 200,206 ----
> PlanState *planstate = node->planstate;
> SubLinkType subLinkType = subplan->subLinkType;
> bool useOr = subplan->useOr;
> + bool isExpr = subplan->isExpr;
> MemoryContext oldcontext;
> TupleTableSlot *slot;
> Datum result;
> ***************
> *** 294,299 ****
> --- 271,281 ----
> bool rownull = false;
> int col = 1;
> List *plst;
> + int numelems;
> + int elemnum;
> + Datum dvalue;
> + Datum *dvalues = NULL;
> + bool disnull;
>
> if (subLinkType == EXISTS_SUBLINK)
> {
> ***************
> *** 331,339 ****
>
> if (subLinkType == ARRAY_SUBLINK)
> {
> - Datum dvalue;
> - bool disnull;
> -
> found = true;
> /* stash away current value */
> dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> --- 313,318 ----
> ***************
> *** 351,448 ****
> found = true;
>
> /*
> ! * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
> ! * operators for columns of tuple.
> */
> ! plst = subplan->paramIds;
> ! foreach(lst, node->exprs)
> {
> ! ExprState *exprstate = (ExprState *) lfirst(lst);
> ! int paramid = lfirsti(plst);
> ! ParamExecData *prmdata;
> ! Datum expresult;
> ! bool expnull;
> !
> ! /*
> ! * Load up the Param representing this column of the sub-select.
> ! */
> ! prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
> ! Assert(prmdata->execPlan == NULL);
> ! prmdata->value = heap_getattr(tup, col, tdesc,
> ! &(prmdata->isnull));
>
> ! /*
> ! * Now we can eval the combining operator for this column.
> ! */
> ! expresult = ExecEvalExprSwitchContext(exprstate, econtext,
> ! &expnull, NULL);
> !
> ! /*
> ! * Combine the result into the row result as appropriate.
> ! */
> ! if (col == 1)
> {
> ! rowresult = expresult;
> ! rownull = expnull;
> }
> ! else if (useOr)
> {
> ! /* combine within row per OR semantics */
> ! if (expnull)
> ! rownull = true;
> ! else if (DatumGetBool(expresult))
> {
> ! rowresult = BoolGetDatum(true);
> ! rownull = false;
> ! break; /* needn't look at any more columns */
> }
> }
> else
> {
> ! /* combine within row per AND semantics */
> ! if (expnull)
> ! rownull = true;
> ! else if (!DatumGetBool(expresult))
> ! {
> ! rowresult = BoolGetDatum(false);
> ! rownull = false;
> ! break; /* needn't look at any more columns */
> ! }
> }
>
> - plst = lnext(plst);
> - col++;
> }
>
> ! if (subLinkType == ANY_SUBLINK)
> {
> ! /* combine across rows per OR semantics */
> ! if (rownull)
> ! *isNull = true;
> ! else if (DatumGetBool(rowresult))
> {
> ! result = BoolGetDatum(true);
> ! *isNull = false;
> ! break; /* needn't look at any more rows */
> }
> ! }
> ! else if (subLinkType == ALL_SUBLINK)
> ! {
> ! /* combine across rows per AND semantics */
> ! if (rownull)
> ! *isNull = true;
> ! else if (!DatumGetBool(rowresult))
> {
> ! result = BoolGetDatum(false);
> ! *isNull = false;
> ! break; /* needn't look at any more rows */
> }
> - }
> - else
> - {
> - /* must be MULTIEXPR_SUBLINK */
> - result = rowresult;
> - *isNull = rownull;
> }
> }
>
> --- 330,492 ----
> found = true;
>
> /*
> ! * When isExpr is true, we have either a scalar expression or an
> ! * array. In the former case, this is no different than the !isExpr
> ! * case. In the latter case, iterate over the elements as if they
> ! * were from multiple input tuples.
> */
> ! if (!isExpr)
> ! numelems = 1;
> ! else
> {
> ! Oid expr_typeid = tdesc->attrs[0]->atttypid;
>
> ! if (expr_typeid != subplan->exprtype)
> {
> ! subplan->exprtype = expr_typeid;
> ! subplan->elemtype = get_element_type(expr_typeid);
> !
> ! if (subplan->elemtype != InvalidOid)
> ! get_typlenbyvalalign(subplan->elemtype,
> ! &subplan->elmlen,
> ! &subplan->elmbyval,
> ! &subplan->elmalign);
> }
> !
> ! /* get current value */
> ! dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> !
> ! /* XXX this will need work if/when arrays support NULL elements */
> ! if (!disnull)
> {
> ! if (subplan->elemtype != InvalidOid)
> ! {
> ! ArrayType *v = DatumGetArrayTypeP(dvalue);
> !
> ! deconstruct_array(v, subplan->elemtype, subplan->elmlen,
> ! subplan->elmbyval, subplan->elmalign,
> ! &dvalues, &numelems);
> ! }
> ! else
> {
> ! numelems = 1;
> ! dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> ! dvalues[0] = dvalue;
> }
> }
> else
> {
> ! numelems = 1;
> ! dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> ! dvalues[0] = (Datum) 0;
> }
>
> }
>
> ! for (elemnum = 0; elemnum < numelems; elemnum++)
> {
> ! /*
> ! * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
> ! * operators for columns of tuple.
> ! */
> ! col = 1;
> ! plst = subplan->paramIds;
> ! foreach(lst, node->exprs)
> {
> ! ExprState *exprstate = (ExprState *) lfirst(lst);
> ! int paramid = lfirsti(plst);
> ! ParamExecData *prmdata;
> ! Datum expresult;
> ! bool expnull;
> !
> ! /*
> ! * Load up the Param representing this column of the sub-select.
> ! */
> ! prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
> ! Assert(prmdata->execPlan == NULL);
> !
> ! if (!isExpr)
> ! prmdata->value = heap_getattr(tup, col, tdesc,
> ! &(prmdata->isnull));
> ! else
> ! {
> ! prmdata->value = dvalues[elemnum];
> ! prmdata->isnull = disnull;
> ! }
> !
> ! /*
> ! * Now we can eval the combining operator for this column.
> ! */
> ! expresult = ExecEvalExprSwitchContext(exprstate, econtext,
> ! &expnull, NULL);
> !
> ! /*
> ! * Combine the result into the row result as appropriate.
> ! */
> ! if (col == 1)
> ! {
> ! rowresult = expresult;
> ! rownull = expnull;
> ! }
> ! else if (useOr)
> ! {
> ! /* combine within row per OR semantics */
> ! if (expnull)
> ! rownull = true;
> ! else if (DatumGetBool(expresult))
> ! {
> ! rowresult = BoolGetDatum(true);
> ! rownull = false;
> ! break; /* needn't look at any more columns */
> ! }
> ! }
> ! else
> ! {
> ! /* combine within row per AND semantics */
> ! if (expnull)
> ! rownull = true;
> ! else if (!DatumGetBool(expresult))
> ! {
> ! rowresult = BoolGetDatum(false);
> ! rownull = false;
> ! break; /* needn't look at any more columns */
> ! }
> ! }
> !
> ! plst = lnext(plst);
> ! col++;
> }
> !
> ! if (subLinkType == ANY_SUBLINK)
> {
> ! /* combine across rows per OR semantics */
> ! if (rownull)
> ! *isNull = true;
> ! else if (DatumGetBool(rowresult))
> ! {
> ! result = BoolGetDatum(true);
> ! *isNull = false;
> ! break; /* needn't look at any more rows */
> ! }
> ! }
> ! else if (subLinkType == ALL_SUBLINK)
> ! {
> ! /* combine across rows per AND semantics */
> ! if (rownull)
> ! *isNull = true;
> ! else if (!DatumGetBool(rowresult))
> ! {
> ! result = BoolGetDatum(false);
> ! *isNull = false;
> ! break; /* needn't look at any more rows */
> ! }
> ! }
> ! else
> ! {
> ! /* must be MULTIEXPR_SUBLINK */
> ! result = rowresult;
> ! *isNull = rownull;
> }
> }
> }
>
> ***************
> *** 480,485 ****
> --- 524,530 ----
> buildSubPlanHash(SubPlanState *node)
> {
> SubPlan *subplan = (SubPlan *) node->xprstate.expr;
> + bool isExpr = subplan->isExpr;
> PlanState *planstate = node->planstate;
> int ncols = length(node->exprs);
> ExprContext *innerecontext = node->innerecontext;
> ***************
> *** 487,492 ****
> --- 532,538 ----
> MemoryContext oldcontext;
> int nbuckets;
> TupleTableSlot *slot;
> + TupleTableSlot *arrslot = NULL;
>
> Assert(subplan->subLinkType == ANY_SUBLINK);
> Assert(!subplan->useOr);
> ***************
> *** 566,608 ****
> {
> HeapTuple tup = slot->val;
> TupleDesc tdesc = slot->ttc_tupleDescriptor;
> ! int col = 1;
> List *plst;
> bool isnew;
>
> /*
> ! * Load up the Params representing the raw sub-select outputs,
> ! * then form the projection tuple to store in the hashtable.
> */
> ! foreach(plst, subplan->paramIds)
> {
> ! int paramid = lfirsti(plst);
> ! ParamExecData *prmdata;
>
> ! prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
> ! Assert(prmdata->execPlan == NULL);
> ! prmdata->value = heap_getattr(tup, col, tdesc,
> ! &(prmdata->isnull));
> ! col++;
> ! }
> ! slot = ExecProject(node->projRight, NULL);
> ! tup = slot->val;
>
> - /*
> - * If result contains any nulls, store separately or not at all.
> - * (Since we know the projection tuple has no junk columns, we
> - * can just look at the overall hasnull info bit, instead of
> - * groveling through the columns.)
> - */
> - if (HeapTupleNoNulls(tup))
> - {
> - (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
> - node->havehashrows = true;
> }
> ! else if (node->hashnulls)
> {
> ! (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
> ! node->havenullrows = true;
> }
>
> /*
> --- 612,750 ----
> {
> HeapTuple tup = slot->val;
> TupleDesc tdesc = slot->ttc_tupleDescriptor;
> ! TupleDesc arrtdesc = NULL;
> List *plst;
> bool isnew;
> + int numelems;
> + int elemnum;
> + Datum dvalue;
> + Datum *dvalues = NULL;
> + bool disnull;
>
> /*
> ! * When isExpr is true, we have either a scalar expression or an
> ! * array. In the former case, this is no different than the !isExpr
> ! * case. In the latter case, iterate over the elements as if they
> ! * were from multiple input tuples.
> */
> ! if (!isExpr)
> ! numelems = 1;
> ! else
> {
> ! Oid expr_typeid = tdesc->attrs[0]->atttypid;
>
> ! if (expr_typeid != subplan->exprtype)
> ! {
> ! subplan->exprtype = expr_typeid;
> ! subplan->elemtype = get_element_type(expr_typeid);
> !
> ! if (subplan->elemtype != InvalidOid)
> ! get_typlenbyvalalign(subplan->elemtype,
> ! &subplan->elmlen,
> ! &subplan->elmbyval,
> ! &subplan->elmalign);
> ! }
> !
> ! /* get current value */
> ! dvalue = heap_getattr(tup, 1, tdesc, &disnull);
> !
> ! if (subplan->elemtype != InvalidOid)
> ! {
> ! TupleTable tupleTable;
> ! ArrayType *v = DatumGetArrayTypeP(dvalue);
> !
> ! arrtdesc = CreateTemplateTupleDesc(1, false);
> ! TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype,
> ! -1, 0, false);
> !
> ! tupleTable = ExecCreateTupleTable(1);
> ! arrslot = ExecAllocTableSlot(tupleTable);
> ! ExecSetSlotDescriptor(arrslot, arrtdesc, true);
> !
> ! /* XXX this will need work if/when arrays support NULL elements */
> ! if (!disnull)
> ! {
> ! deconstruct_array(v, subplan->elemtype, subplan->elmlen,
> ! subplan->elmbyval, subplan->elmalign,
> ! &dvalues, &numelems);
> ! }
> ! else
> ! {
> ! numelems = 1;
> ! dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> ! dvalues[0] = (Datum) 0;
> ! }
> ! }
> ! else
> ! {
> ! numelems = 1;
> ! dvalues = (Datum *) palloc(numelems * sizeof(Datum));
> ! dvalues[0] = dvalue;
> ! }
>
> }
> !
> ! for (elemnum = 0; elemnum < numelems; elemnum++)
> {
> ! int col = 1;
> !
> ! if (!isExpr || subplan->elemtype == InvalidOid)
> ! {
> ! /*
> ! * Load up the Params representing the raw sub-select outputs,
> ! * then form the projection tuple to store in the hashtable.
> ! */
> ! foreach(plst, subplan->paramIds)
> ! {
> ! int paramid = lfirsti(plst);
> ! ParamExecData *prmdata;
> !
> ! prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
> ! Assert(prmdata->execPlan == NULL);
> !
> ! prmdata->value = heap_getattr(tup, col, tdesc,
> ! &(prmdata->isnull));
> !
> ! col++;
> ! }
> ! slot = ExecProject(node->projRight, NULL);
> ! tup = slot->val;
> ! }
> ! else
> ! {
> ! /*
> ! * For array type expressions, we need to build up our own
> ! * tuple and slot
> ! */
> ! char nullflag;
> !
> ! nullflag = disnull ? 'n' : ' ';
> ! tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag);
> ! arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true);
> ! }
> !
> ! /*
> ! * If result contains any nulls, store separately or not at all.
> ! * (Since we know the projection tuple has no junk columns, we
> ! * can just look at the overall hasnull info bit, instead of
> ! * groveling through the columns.)
> ! */
> ! if (HeapTupleNoNulls(tup))
> ! {
> ! if (!isExpr)
> ! (void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
> ! else
> ! (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
> ! node->havehashrows = true;
> ! }
> ! else if (node->hashnulls)
> ! {
> ! if (!isExpr)
> ! (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
> ! else
> ! (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
> ! node->havenullrows = true;
> ! }
> }
>
> /*
> ***************
> *** 619,624 ****
> --- 761,768 ----
> * have the potential for a double free attempt.
> */
> ExecClearTuple(node->projRight->pi_slot);
> + if (arrslot)
> + ExecClearTuple(arrslot);
>
> MemoryContextSwitchTo(oldcontext);
> }
> ***************
> *** 1098,1199 ****
> prm->execPlan = node;
> 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);
> }
> --- 1242,1245 ----
> Index: src/backend/nodes/copyfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v
> retrieving revision 1.252
> diff -c -r1.252 copyfuncs.c
> *** src/backend/nodes/copyfuncs.c 6 Jun 2003 15:04:02 -0000 1.252
> --- src/backend/nodes/copyfuncs.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 728,733 ****
> --- 728,734 ----
> COPY_SCALAR_FIELD(agglevelsup);
> COPY_SCALAR_FIELD(aggstar);
> COPY_SCALAR_FIELD(aggdistinct);
> + COPY_NODE_FIELD(args);
>
> return newnode;
> }
> ***************
> *** 826,831 ****
> --- 827,833 ----
>
> COPY_SCALAR_FIELD(subLinkType);
> COPY_SCALAR_FIELD(useOr);
> + COPY_SCALAR_FIELD(isExpr);
> COPY_NODE_FIELD(lefthand);
> COPY_NODE_FIELD(operName);
> COPY_OIDLIST_FIELD(operOids);
> ***************
> *** 844,849 ****
> --- 846,857 ----
>
> COPY_SCALAR_FIELD(subLinkType);
> COPY_SCALAR_FIELD(useOr);
> + COPY_SCALAR_FIELD(isExpr);
> + COPY_SCALAR_FIELD(exprtype);
> + COPY_SCALAR_FIELD(elemtype);
> + COPY_SCALAR_FIELD(elmlen);
> + COPY_SCALAR_FIELD(elmbyval);
> + COPY_SCALAR_FIELD(elmalign);
> COPY_NODE_FIELD(exprs);
> COPY_INTLIST_FIELD(paramIds);
> COPY_NODE_FIELD(plan);
> Index: src/backend/nodes/equalfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v
> retrieving revision 1.195
> diff -c -r1.195 equalfuncs.c
> *** src/backend/nodes/equalfuncs.c 6 Jun 2003 15:04:02 -0000 1.195
> --- src/backend/nodes/equalfuncs.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 205,210 ****
> --- 205,211 ----
> COMPARE_SCALAR_FIELD(agglevelsup);
> COMPARE_SCALAR_FIELD(aggstar);
> COMPARE_SCALAR_FIELD(aggdistinct);
> + COMPARE_NODE_FIELD(args);
>
> return true;
> }
> ***************
> *** 301,306 ****
> --- 302,308 ----
> {
> COMPARE_SCALAR_FIELD(subLinkType);
> COMPARE_SCALAR_FIELD(useOr);
> + COMPARE_SCALAR_FIELD(isExpr);
> COMPARE_NODE_FIELD(lefthand);
> COMPARE_NODE_FIELD(operName);
> COMPARE_OIDLIST_FIELD(operOids);
> ***************
> *** 314,319 ****
> --- 316,327 ----
> {
> COMPARE_SCALAR_FIELD(subLinkType);
> COMPARE_SCALAR_FIELD(useOr);
> + COMPARE_SCALAR_FIELD(isExpr);
> + COMPARE_SCALAR_FIELD(exprtype);
> + COMPARE_SCALAR_FIELD(elemtype);
> + COMPARE_SCALAR_FIELD(elmlen);
> + COMPARE_SCALAR_FIELD(elmbyval);
> + COMPARE_SCALAR_FIELD(elmalign);
> COMPARE_NODE_FIELD(exprs);
> COMPARE_INTLIST_FIELD(paramIds);
> /* should compare plans, but have to settle for comparing plan IDs */
> Index: src/backend/nodes/outfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v
> retrieving revision 1.208
> diff -c -r1.208 outfuncs.c
> *** src/backend/nodes/outfuncs.c 15 Jun 2003 22:51:45 -0000 1.208
> --- src/backend/nodes/outfuncs.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 616,621 ****
> --- 616,622 ----
> WRITE_UINT_FIELD(agglevelsup);
> WRITE_BOOL_FIELD(aggstar);
> WRITE_BOOL_FIELD(aggdistinct);
> + WRITE_NODE_FIELD(args);
> }
>
> static void
> ***************
> *** 701,706 ****
> --- 702,708 ----
>
> WRITE_ENUM_FIELD(subLinkType, SubLinkType);
> WRITE_BOOL_FIELD(useOr);
> + WRITE_BOOL_FIELD(isExpr);
> WRITE_NODE_FIELD(lefthand);
> WRITE_NODE_FIELD(operName);
> WRITE_OIDLIST_FIELD(operOids);
> ***************
> *** 714,719 ****
> --- 716,727 ----
>
> WRITE_ENUM_FIELD(subLinkType, SubLinkType);
> WRITE_BOOL_FIELD(useOr);
> + WRITE_BOOL_FIELD(isExpr);
> + WRITE_OID_FIELD(exprtype);
> + WRITE_OID_FIELD(elemtype);
> + WRITE_INT_FIELD(elmlen);
> + WRITE_BOOL_FIELD(elmbyval);
> + WRITE_CHAR_FIELD(elmalign);
> WRITE_NODE_FIELD(exprs);
> WRITE_INTLIST_FIELD(paramIds);
> WRITE_NODE_FIELD(plan);
> Index: src/backend/nodes/readfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v
> retrieving revision 1.154
> diff -c -r1.154 readfuncs.c
> *** src/backend/nodes/readfuncs.c 6 Jun 2003 15:04:02 -0000 1.154
> --- src/backend/nodes/readfuncs.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 416,421 ****
> --- 416,422 ----
> READ_UINT_FIELD(agglevelsup);
> READ_BOOL_FIELD(aggstar);
> READ_BOOL_FIELD(aggdistinct);
> + READ_NODE_FIELD(args);
>
> READ_DONE();
> }
> ***************
> *** 545,550 ****
> --- 546,552 ----
>
> READ_ENUM_FIELD(subLinkType, SubLinkType);
> READ_BOOL_FIELD(useOr);
> + READ_BOOL_FIELD(isExpr);
> READ_NODE_FIELD(lefthand);
> READ_NODE_FIELD(operName);
> READ_OIDLIST_FIELD(operOids);
> Index: src/backend/optimizer/plan/subselect.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v
> retrieving revision 1.76
> diff -c -r1.76 subselect.c
> *** src/backend/optimizer/plan/subselect.c 6 Jun 2003 15:04:02 -0000 1.76
> --- src/backend/optimizer/plan/subselect.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 83,89 ****
>
> static List *convert_sublink_opers(List *lefthand, List *operOids,
> List *targetlist, int rtindex,
> ! List **righthandIds);
> static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
> static Node *replace_correlation_vars_mutator(Node *node, void *context);
> static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
> --- 83,89 ----
>
> static List *convert_sublink_opers(List *lefthand, List *operOids,
> List *targetlist, int rtindex,
> ! bool isExpr, List **righthandIds);
> static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
> static Node *replace_correlation_vars_mutator(Node *node, void *context);
> static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
> ***************
> *** 299,304 ****
> --- 299,310 ----
> */
> node->subLinkType = slink->subLinkType;
> node->useOr = slink->useOr;
> + node->isExpr = slink->isExpr;
> + node->exprtype = InvalidOid;
> + node->elemtype = InvalidOid;
> + node->elmlen = 0;
> + node->elmbyval = false;
> + node->elmalign = '\0';
> node->exprs = NIL;
> node->paramIds = NIL;
> node->useHashTable = false;
> ***************
> *** 374,380 ****
> exprs = convert_sublink_opers(lefthand,
> slink->operOids,
> plan->targetlist,
> ! 0,
> &node->paramIds);
> node->setParam = listCopy(node->paramIds);
> PlannerInitPlan = lappend(PlannerInitPlan, node);
> --- 380,386 ----
> exprs = convert_sublink_opers(lefthand,
> slink->operOids,
> plan->targetlist,
> ! 0, node->isExpr,
> &node->paramIds);
> node->setParam = listCopy(node->paramIds);
> PlannerInitPlan = lappend(PlannerInitPlan, node);
> ***************
> *** 457,463 ****
> node->exprs = convert_sublink_opers(lefthand,
> slink->operOids,
> plan->targetlist,
> ! 0,
> &node->paramIds);
>
> /*
> --- 463,469 ----
> node->exprs = convert_sublink_opers(lefthand,
> slink->operOids,
> plan->targetlist,
> ! 0, node->isExpr,
> &node->paramIds);
>
> /*
> ***************
> *** 499,505 ****
> static List *
> convert_sublink_opers(List *lefthand, List *operOids,
> List *targetlist, int rtindex,
> ! List **righthandIds)
> {
> List *result = NIL;
> List *lst;
> --- 505,511 ----
> static List *
> convert_sublink_opers(List *lefthand, List *operOids,
> List *targetlist, int rtindex,
> ! bool isExpr, List **righthandIds)
> {
> List *result = NIL;
> List *lst;
> ***************
> *** 554,566 ****
> * are not expecting to have to resolve unknown Params, so
> * it's okay to pass a null pstate.)
> */
> ! result = lappend(result,
> ! make_op_expr(NULL,
> ! tup,
> ! leftop,
> ! rightop,
> ! exprType(leftop),
> ! te->resdom->restype));
>
> ReleaseSysCache(tup);
>
> --- 560,597 ----
> * are not expecting to have to resolve unknown Params, so
> * it's okay to pass a null pstate.)
> */
> ! if (!isExpr)
> ! {
> ! result = lappend(result,
> ! make_op_expr(NULL,
> ! tup,
> ! leftop,
> ! rightop,
> ! exprType(leftop),
> ! te->resdom->restype));
> ! }
> ! else
> ! {
> ! Oid exprtype = te->resdom->restype;
> ! Oid elemtype = get_element_type(exprtype);
> !
> ! if (elemtype != InvalidOid)
> ! result = lappend(result,
> ! make_op_expr(NULL,
> ! tup,
> ! leftop,
> ! rightop,
> ! exprType(leftop),
> ! elemtype));
> ! else
> ! result = lappend(result,
> ! make_op_expr(NULL,
> ! tup,
> ! leftop,
> ! rightop,
> ! exprType(leftop),
> ! exprtype));
> ! }
>
> ReleaseSysCache(tup);
>
> ***************
> *** 671,683 ****
> /*
> * The sublink type must be "= ANY" --- that is, an IN operator.
> * (We require the operator name to be unqualified, which may be
> ! * overly paranoid, or may not be.)
> */
> if (sublink->subLinkType != ANY_SUBLINK)
> return NULL;
> if (length(sublink->operName) != 1 ||
> strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
> return NULL;
> /*
> * The sub-select must not refer to any Vars of the parent query.
> * (Vars of higher levels should be okay, though.)
> --- 702,718 ----
> /*
> * The sublink type must be "= ANY" --- that is, an IN operator.
> * (We require the operator name to be unqualified, which may be
> ! * overly paranoid, or may not be.) It must not be an Expression
> ! * sublink.
> */
> if (sublink->subLinkType != ANY_SUBLINK)
> return NULL;
> if (length(sublink->operName) != 1 ||
> strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
> return NULL;
> + if (sublink->isExpr)
> + return NULL;
> +
> /*
> * The sub-select must not refer to any Vars of the parent query.
> * (Vars of higher levels should be okay, though.)
> ***************
> *** 730,736 ****
> exprs = convert_sublink_opers(sublink->lefthand,
> sublink->operOids,
> subselect->targetList,
> ! rtindex,
> &ininfo->sub_targetlist);
> return (Node *) make_ands_explicit(exprs);
> }
> --- 765,771 ----
> exprs = convert_sublink_opers(sublink->lefthand,
> sublink->operOids,
> subselect->targetList,
> ! rtindex, sublink->isExpr,
> &ininfo->sub_targetlist);
> return (Node *) make_ands_explicit(exprs);
> }
> Index: src/backend/optimizer/util/clauses.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v
> retrieving revision 1.139
> diff -c -r1.139 clauses.c
> *** src/backend/optimizer/util/clauses.c 6 Jun 2003 15:04:02 -0000 1.139
> --- src/backend/optimizer/util/clauses.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 133,138 ****
> --- 133,160 ----
> }
>
> /*****************************************************************************
> + * FUNCTION clause functions
> + *****************************************************************************/
> +
> + /*
> + * make_funcclause
> + * Creates a function clause given its function info and argument list.
> + */
> + Expr *
> + make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
> + CoercionForm funcformat, List *funcargs)
> + {
> + FuncExpr *expr = makeNode(FuncExpr);
> +
> + expr->funcid = funcid;
> + expr->funcresulttype = funcresulttype;
> + expr->funcretset = funcretset;
> + expr->funcformat = funcformat;
> + expr->args = funcargs;
> + return (Expr *) expr;
> + }
> +
> + /*****************************************************************************
> * NOT clause functions
> *****************************************************************************/
>
> Index: src/backend/parser/gram.y
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v
> retrieving revision 2.417
> diff -c -r2.417 gram.y
> *** src/backend/parser/gram.y 17 Jun 2003 23:12:36 -0000 2.417
> --- src/backend/parser/gram.y 24 Jun 2003 02:24:06 -0000
> ***************
> *** 5490,5495 ****
> --- 5490,5496 ----
> {
> SubLink *n = makeNode(SubLink);
> n->subLinkType = ANY_SUBLINK;
> + n->isExpr = false;
> n->lefthand = $1;
> n->operName = makeList1(makeString("="));
> n->subselect = $3;
> ***************
> *** 5500,5505 ****
> --- 5501,5507 ----
> /* Make an IN node */
> SubLink *n = makeNode(SubLink);
> n->subLinkType = ANY_SUBLINK;
> + n->isExpr = false;
> n->lefthand = $1;
> n->operName = makeList1(makeString("="));
> n->subselect = $4;
> ***************
> *** 5511,5516 ****
> --- 5513,5519 ----
> {
> SubLink *n = makeNode(SubLink);
> n->subLinkType = $3;
> + n->isExpr = false;
> n->lefthand = $1;
> n->operName = $2;
> n->subselect = $4;
> ***************
> *** 5521,5526 ****
> --- 5524,5530 ----
> {
> SubLink *n = makeNode(SubLink);
> n->subLinkType = MULTIEXPR_SUBLINK;
> + n->isExpr = false;
> n->lefthand = $1;
> n->operName = $2;
> n->subselect = $3;
> ***************
> *** 5904,5909 ****
> --- 5908,5914 ----
> {
> SubLink *n = (SubLink *)$3;
> n->subLinkType = ANY_SUBLINK;
> + n->isExpr = false;
> n->lefthand = makeList1($1);
> n->operName = makeList1(makeString("="));
> $$ = (Node *)n;
> ***************
> *** 5931,5936 ****
> --- 5936,5942 ----
> {
> /* Make an IN node */
> SubLink *n = (SubLink *)$4;
> + n->isExpr = false;
> n->subLinkType = ANY_SUBLINK;
> n->lefthand = makeList1($1);
> n->operName = makeList1(makeString("="));
> ***************
> *** 5957,5967 ****
> --- 5963,6000 ----
> {
> SubLink *n = makeNode(SubLink);
> n->subLinkType = $3;
> + n->isExpr = false;
> n->lefthand = makeList1($1);
> n->operName = $2;
> n->subselect = $4;
> $$ = (Node *)n;
> }
> + | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op
> + {
> + SubLink *n = makeNode(SubLink);
> + SelectStmt *s = makeNode(SelectStmt);
> + ResTarget *r = makeNode(ResTarget);
> +
> + r->name = NULL;
> + r->indirection = NIL;
> + r->val = (Node *)$5;
> +
> + s->distinctClause = NIL;
> + s->targetList = makeList1(r);
> + s->into = NULL;
> + s->intoColNames = NIL;
> + s->fromClause = NIL;
> + s->whereClause = NULL;
> + s->groupClause = NIL;
> + s->havingClause = NULL;
> +
> + n->subLinkType = $3;
> + n->isExpr = true;
> + n->lefthand = makeList1($1);
> + n->operName = $2;
> + n->subselect = (Node *) s;
> + $$ = (Node *)n;
> + }
> | UNIQUE select_with_parens %prec Op
> {
> /* Not sure how to get rid of the parentheses
> ***************
> *** 6538,6543 ****
> --- 6571,6577 ----
> {
> SubLink *n = makeNode(SubLink);
> n->subLinkType = EXPR_SUBLINK;
> + n->isExpr = false;
> n->lefthand = NIL;
> n->operName = NIL;
> n->subselect = $1;
> ***************
> *** 6547,6552 ****
> --- 6581,6587 ----
> {
> SubLink *n = makeNode(SubLink);
> n->subLinkType = EXISTS_SUBLINK;
> + n->isExpr = false;
> n->lefthand = NIL;
> n->operName = NIL;
> n->subselect = $2;
> ***************
> *** 6556,6561 ****
> --- 6591,6597 ----
> {
> SubLink *n = makeNode(SubLink);
> n->subLinkType = ARRAY_SUBLINK;
> + n->isExpr = false;
> n->lefthand = NIL;
> n->operName = NIL;
> n->subselect = $2;
> ***************
> *** 6730,6735 ****
> --- 6766,6772 ----
> in_expr: select_with_parens
> {
> SubLink *n = makeNode(SubLink);
> + n->isExpr = false;
> n->subselect = $1;
> /* other fields will be filled later */
> $$ = (Node *)n;
> Index: src/backend/parser/parse_coerce.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v
> retrieving revision 2.97
> diff -c -r2.97 parse_coerce.c
> *** src/backend/parser/parse_coerce.c 26 May 2003 00:11:27 -0000 2.97
> --- src/backend/parser/parse_coerce.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 859,865 ****
> /* Get the element type based on the array type, if we have one */
> if (OidIsValid(array_typeid))
> {
> ! array_typelem = get_element_type(array_typeid);
> if (!OidIsValid(array_typelem))
> elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
> format_type_be(array_typeid));
> --- 859,869 ----
> /* Get the element type based on the array type, if we have one */
> if (OidIsValid(array_typeid))
> {
> ! if (array_typeid != ANYARRAYOID)
> ! array_typelem = get_element_type(array_typeid);
> ! else
> ! array_typelem = ANYELEMENTOID;
> !
> if (!OidIsValid(array_typelem))
> elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
> format_type_be(array_typeid));
> ***************
> *** 919,925 ****
> {
> if (!OidIsValid(array_typeid))
> {
> ! array_typeid = get_array_type(elem_typeid);
> if (!OidIsValid(array_typeid))
> elog(ERROR, "Cannot find array type for datatype %s",
> format_type_be(elem_typeid));
> --- 923,933 ----
> {
> if (!OidIsValid(array_typeid))
> {
> ! if (elem_typeid != ANYELEMENTOID)
> ! array_typeid = get_array_type(elem_typeid);
> ! else
> ! array_typeid = ANYARRAYOID;
> !
> if (!OidIsValid(array_typeid))
> elog(ERROR, "Cannot find array type for datatype %s",
> format_type_be(elem_typeid));
> ***************
> *** 1169,1174 ****
> --- 1177,1187 ----
> /* Somewhat-fast path for domain -> base type case */
> if (srctype == targettype)
> return true;
> +
> + /* Last of the fast-paths: check for matching polymorphic arrays */
> + if (targettype == ANYARRAYOID)
> + if (get_element_type(srctype) != InvalidOid)
> + return true;
>
> /* Else look in pg_cast */
> tuple = SearchSysCache(CASTSOURCETARGET,
> Index: src/backend/parser/parse_expr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v
> retrieving revision 1.148
> diff -c -r1.148 parse_expr.c
> *** src/backend/parser/parse_expr.c 29 Apr 2003 22:13:10 -0000 1.148
> --- src/backend/parser/parse_expr.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 436,441 ****
> --- 436,442 ----
> sublink->operName = NIL;
> sublink->operOids = NIL;
> sublink->useOr = FALSE;
> + sublink->isExpr = FALSE;
> }
> else if (sublink->subLinkType == EXPR_SUBLINK ||
> sublink->subLinkType == ARRAY_SUBLINK)
> ***************
> *** 463,468 ****
> --- 464,470 ----
> sublink->operName = NIL;
> sublink->operOids = NIL;
> sublink->useOr = FALSE;
> + sublink->isExpr = FALSE;
> }
> else
> {
> ***************
> *** 538,547 ****
> * here, because make_subplan() will insert type
> * coercion calls if needed.
> */
> ! optup = oper(op,
> ! exprType(lexpr),
> ! exprType((Node *) tent->expr),
> ! false);
> opform = (Form_pg_operator) GETSTRUCT(optup);
>
> if (opform->oprresult != BOOLOID)
> --- 540,569 ----
> * here, because make_subplan() will insert type
> * coercion calls if needed.
> */
> ! if (!sublink->isExpr)
> ! {
> ! optup = oper(op,
> ! exprType(lexpr),
> ! exprType((Node *) tent->expr),
> ! false);
> ! }
> ! else
> ! {
> ! Oid exprtype = exprType((Node *) tent->expr);
> ! Oid elemtype = get_element_type(exprtype);
> !
> ! if (elemtype != InvalidOid)
> ! optup = oper(op,
> ! exprType(lexpr),
> ! elemtype,
> ! false);
> ! else
> ! optup = oper(op,
> ! exprType(lexpr),
> ! exprtype,
> ! false);
> ! }
> !
> opform = (Form_pg_operator) GETSTRUCT(optup);
>
> if (opform->oprresult != BOOLOID)
> ***************
> *** 743,749 ****
> ArrayExpr *e = (ArrayExpr *) lfirst(element);
>
> if (!IsA(e, ArrayExpr))
> ! elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
> if (ndims == 0)
> ndims = e->ndims;
> else if (e->ndims != ndims)
> --- 765,771 ----
> ArrayExpr *e = (ArrayExpr *) lfirst(element);
>
> if (!IsA(e, ArrayExpr))
> ! elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
> if (ndims == 0)
> ndims = e->ndims;
> else if (e->ndims != ndims)
> Index: src/backend/parser/parse_func.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v
> retrieving revision 1.149
> diff -c -r1.149 parse_func.c
> *** src/backend/parser/parse_func.c 6 Jun 2003 15:04:02 -0000 1.149
> --- src/backend/parser/parse_func.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 336,341 ****
> --- 336,342 ----
> aggref->target = lfirst(fargs);
> aggref->aggstar = agg_star;
> aggref->aggdistinct = agg_distinct;
> + aggref->args = fargs;
>
> /* parse_agg.c does additional aggregate-specific processing */
> transformAggregateCall(pstate, aggref);
> Index: src/backend/parser/parse_oper.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v
> retrieving revision 1.64
> diff -c -r1.64 parse_oper.c
> *** src/backend/parser/parse_oper.c 26 May 2003 00:11:27 -0000 1.64
> --- src/backend/parser/parse_oper.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 137,142 ****
> --- 137,169 ----
> equality_oper(Oid argtype, bool noError)
> {
> Operator optup;
> + Oid elem_type = get_element_type(argtype);
> +
> + if (OidIsValid(elem_type))
> + {
> + bool found = false;
> + /*
> + * If the datatype is an array, look for an "=" operator for the
> + * element 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.
> + */
> + optup = equality_oper(elem_type, true);
> + if (optup != NULL)
> + {
> + found = true;
> + ReleaseSysCache(optup);
> + }
> +
> + if (!found)
> + {
> + if (!noError)
> + elog(ERROR, "Unable to identify an equality operator for " \
> + "array type's element type %s",
> + format_type_be(elem_type));
> + return NULL;
> + }
> + }
>
> /*
> * Look for an "=" operator for the datatype. We require it to be
> ***************
> *** 175,180 ****
> --- 202,234 ----
> ordering_oper(Oid argtype, bool noError)
> {
> Operator optup;
> + Oid elem_type = get_element_type(argtype);
> +
> + if (OidIsValid(elem_type))
> + {
> + bool found = false;
> + /*
> + * If the datatype is an array, find the array element 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 = ordering_oper(elem_type, true);
> + if (optup != NULL)
> + {
> + found = true;
> + ReleaseSysCache(optup);
> + }
> +
> + if (!found)
> + {
> + if (!noError)
> + elog(ERROR, "Unable to identify an ordering operator for " \
> + "array type's element type %s",
> + format_type_be(elem_type));
> + return NULL;
> + }
> + }
>
> /*
> * Find the type's equality operator, and use its lsortop (it *must*
> ***************
> *** 215,220 ****
> --- 269,289 ----
> Oid result;
>
> optup = equality_oper(argtype, false);
> + result = oprfuncid(optup);
> + ReleaseSysCache(optup);
> + 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;
> Index: src/backend/utils/adt/acl.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v
> retrieving revision 1.88
> diff -c -r1.88 acl.c
> *** src/backend/utils/adt/acl.c 11 Jun 2003 09:23:55 -0000 1.88
> --- src/backend/utils/adt/acl.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 427,432 ****
> --- 427,441 ----
> a1->ai_grantor == a2->ai_grantor;
> }
>
> + /*
> + * user-facing version of aclitemeq() for use as the
> + * aclitem equality operator
> + */
> + Datum
> + aclitem_eq(PG_FUNCTION_ARGS)
> + {
> + PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1)));
> + }
>
> /*
> * acldefault() --- create an ACL describing default access permissions
> Index: src/backend/utils/adt/array_userfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v
> retrieving revision 1.1
> diff -c -r1.1 array_userfuncs.c
> *** src/backend/utils/adt/array_userfuncs.c 8 Apr 2003 23:20:02 -0000 1.1
> --- src/backend/utils/adt/array_userfuncs.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 18,52 ****
> #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
> --- 18,23 ----
> ***************
> *** 70,75 ****
> --- 41,47 ----
> 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,122 ****
> 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)
> {
> ! /* append newelem */
> ! int ub = dimv[0] + lb[0] - 1;
> ! indx = ub + 1;
> }
> else
> {
> ! /* prepend newelem */
> ! indx = lb[0] - 1;
> }
>
> ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
> ! result = array_set(v, 1, &indx, newelem, -1,
> ! typlen, typbyval, typalign, &isNull);
>
> PG_RETURN_ARRAYTYPE_P(result);
> }
> --- 67,127 ----
> PG_RETURN_NULL(); /* keep compiler quiet */
> }
>
> ! if (ARR_NDIM(v) == 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)
> {
> ! 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, &typlen, &typbyval, &typalign);
>
> ! my_extra->element_type = element_type;
> ! my_extra->typlen = typlen;
> ! my_extra->typbyval = typbyval;
> ! my_extra->typalign = typalign;
> ! }
> ! else
> ! {
> ! typlen = my_extra->typlen;
> ! typbyval = my_extra->typbyval;
> ! typalign = my_extra->typalign;
> ! }
> !
> ! result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval,
> ! typalign, &isNull);
>
> PG_RETURN_ARRAYTYPE_P(result);
> }
> ***************
> *** 145,157 ****
>
> /*
> * We must have one of the following combinations of inputs:
> ! * 1) two arrays with ndims1 == ndims2
> ! * 2) ndims1 == ndims2 - 1
> ! * 3) ndims1 == ndims2 + 1
> */
> ndims1 = ARR_NDIM(v1);
> ndims2 = ARR_NDIM(v2);
>
> if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
> elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
> "%d dimensions", ndims1, ndims2);
> --- 150,177 ----
>
> /*
> * We must have one of the following combinations of inputs:
> ! * 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,412 ****
> 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.
> */
> ArrayType *
> ! create_singleton_array(Oid element_type, Datum element, int ndims)
> {
> Datum dvalues[1];
> int16 typlen;
> --- 286,300 ----
> PG_RETURN_ARRAYTYPE_P(result);
> }
>
>
> /*
> ! * used by text_to_array() in varlena.c
> */
> ArrayType *
> ! create_singleton_array(FunctionCallInfo fcinfo,
> ! Oid element_type,
> ! Datum element,
> ! int ndims)
> {
> Datum dvalues[1];
> int16 typlen;
> ***************
> *** 415,420 ****
> --- 303,309 ----
> 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,435 ****
> lbs[i] = 1;
> }
>
> ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
>
> return construct_md_array(dvalues, ndims, dims, lbs, element_type,
> typlen, typbyval, typalign);
> --- 318,352 ----
> lbs[i] = 1;
> }
>
> ! /*
> ! * 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, &typlen, &typbyval, &typalign);
> !
> ! my_extra->element_type = element_type;
> ! my_extra->typlen = typlen;
> ! my_extra->typbyval = typbyval;
> ! my_extra->typalign = typalign;
> ! }
> ! else
> ! {
> ! 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);
> Index: src/backend/utils/adt/arrayfuncs.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v
> retrieving revision 1.89
> diff -c -r1.89 arrayfuncs.c
> *** src/backend/utils/adt/arrayfuncs.c 9 May 2003 23:01:45 -0000 1.89
> --- src/backend/utils/adt/arrayfuncs.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 21,28 ****
> --- 21,30 ----
> #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,85 ****
>
> #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,
> --- 72,77 ----
> ***************
> *** 93,102 ****
> 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,
> --- 85,90 ----
> ***************
> *** 119,125 ****
> char *destPtr,
> int *st, int *endp, char *srcPtr,
> int typlen, bool typbyval, char typalign);
> !
>
> /*---------------------------------------------------------------------
> * array_in :
> --- 107,113 ----
> char *destPtr,
> int *st, int *endp, char *srcPtr,
> int typlen, bool typbyval, char typalign);
> ! static int array_cmp(FunctionCallInfo fcinfo);
>
> /*---------------------------------------------------------------------
> * array_in :
> ***************
> *** 154,165 ****
> dim[MAXDIM],
> lBound[MAXDIM];
> char typalign;
>
> ! /* 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);
>
> /* Make a modifiable copy of the input */
> /* XXX why are we allocating an extra 2 bytes here? */
> --- 142,190 ----
> dim[MAXDIM],
> lBound[MAXDIM];
> char typalign;
> + ArrayMetaState *my_extra;
>
> ! /*
> ! * 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_metadata(element_type, IOFunc_input,
> ! &typlen, &typbyval, &typdelim,
> ! &typelem, &typinput, &typalign);
> ! fmgr_info(typinput, &inputproc);
> !
> ! my_extra->element_type = element_type;
> ! my_extra->typlen = typlen;
> ! my_extra->typbyval = typbyval;
> ! my_extra->typdelim = typdelim;
> ! my_extra->typelem = typelem;
> ! my_extra->typiofunc = typinput;
> ! my_extra->typalign = typalign;
> ! my_extra->proc = inputproc;
> ! }
> ! else
> ! {
> ! typlen = my_extra->typlen;
> ! typbyval = my_extra->typbyval;
> ! typdelim = my_extra->typdelim;
> ! typelem = my_extra->typelem;
> ! typinput = my_extra->typiofunc;
> ! typalign = my_extra->typalign;
> ! inputproc = my_extra->proc;
> ! }
>
> /* Make a modifiable copy of the input */
> /* XXX why are we allocating an extra 2 bytes here? */
> ***************
> *** 636,647 ****
> indx[MAXDIM];
> int ndim,
> *dim;
>
> element_type = ARR_ELEMTYPE(v);
> ! system_cache_lookup(element_type, IOFunc_output,
> ! &typlen, &typbyval, &typdelim,
> ! &typelem, &typoutput, &typalign);
> ! fmgr_info(typoutput, &outputproc);
>
> ndim = ARR_NDIM(v);
> dim = ARR_DIMS(v);
> --- 661,711 ----
> indx[MAXDIM];
> int ndim,
> *dim;
> + ArrayMetaState *my_extra;
>
> element_type = ARR_ELEMTYPE(v);
> !
> ! /*
> ! * 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 output conversion proc */
> ! get_type_metadata(element_type, IOFunc_output,
> ! &typlen, &typbyval, &typdelim,
> ! &typelem, &typoutput, &typalign);
> ! fmgr_info(typoutput, &outputproc);
> !
> ! my_extra->element_type = element_type;
> ! my_extra->typlen = typlen;
> ! my_extra->typbyval = typbyval;
> ! my_extra->typdelim = typdelim;
> ! my_extra->typelem = typelem;
> ! my_extra->typiofunc = typoutput;
> ! my_extra->typalign = typalign;
> ! my_extra->proc = outputproc;
> ! }
> ! else
> ! {
> ! typlen = my_extra->typlen;
> ! typbyval = my_extra->typbyval;
> ! typdelim = my_extra->typdelim;
> ! typelem = my_extra->typelem;
> ! typoutput = my_extra->typiofunc;
> ! typalign = my_extra->typalign;
> ! outputproc = my_extra->proc;
> ! }
>
> ndim = ARR_NDIM(v);
> dim = ARR_DIMS(v);
> ***************
> *** 800,805 ****
> --- 864,870 ----
> dim[MAXDIM],
> lBound[MAXDIM];
> char typalign;
> + ArrayMetaState *my_extra;
>
> /* Get the array header information */
> ndim = pq_getmsgint(buf, 4);
> ***************
> *** 831,844 ****
> 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);
>
> dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
> typlen, typbyval, typalign,
> --- 896,945 ----
> PG_RETURN_ARRAYTYPE_P(retval);
> }
>
> ! /*
> ! * 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;
> ! }
> !
> ! if (my_extra->element_type != element_type)
> ! {
> ! /* Get info about element type, including its receive conversion proc */
> ! get_type_metadata(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);
> !
> ! my_extra->element_type = element_type;
> ! my_extra->typlen = typlen;
> ! my_extra->typbyval = typbyval;
> ! my_extra->typdelim = typdelim;
> ! my_extra->typelem = typelem;
> ! my_extra->typiofunc = typreceive;
> ! my_extra->typalign = typalign;
> ! my_extra->proc = receiveproc;
> ! }
> ! else
> ! {
> ! typlen = my_extra->typlen;
> ! typbyval = my_extra->typbyval;
> ! typdelim = my_extra->typdelim;
> ! typelem = my_extra->typelem;
> ! typreceive = my_extra->typiofunc;
> ! typalign = my_extra->typalign;
> ! receiveproc = my_extra->proc;
> ! }
>
> dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
> typlen, typbyval, typalign,
> ***************
> *** 976,990 ****
> int ndim,
> *dim;
> StringInfoData buf;
>
> /* 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);
>
> ndim = ARR_NDIM(v);
> dim = ARR_DIMS(v);
> --- 1077,1130 ----
> int ndim,
> *dim;
> StringInfoData buf;
> + ArrayMetaState *my_extra;
>
> /* Get information about the element type and the array dimensions */
> element_type = ARR_ELEMTYPE(v);
> !
> ! /*
> ! * We arrange to look up info about element type, including its send
> ! * 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_metadata(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);
> !
> ! my_extra->element_type = element_type;
> ! my_extra->typlen = typlen;
> ! my_extra->typbyval = typbyval;
> ! my_extra->typdelim = typdelim;
> ! my_extra->typelem = typelem;
> ! my_extra->typiofunc = typsend;
> ! my_extra->typalign = typalign;
> ! my_extra->proc = sendproc;
> ! }
> ! else
> ! {
> ! typlen = my_extra->typlen;
> ! typbyval = my_extra->typbyval;
> ! typdelim = my_extra->typdelim;
> ! typelem = my_extra->typelem;
> ! typsend = my_extra->typiofunc;
> ! typalign = my_extra->typalign;
> ! sendproc = my_extra->proc;
> ! }
>
> ndim = ARR_NDIM(v);
> dim = ARR_DIMS(v);
> ***************
> *** 1476,1481 ****
> --- 1616,1641 ----
> 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,1637 ****
> --- 1792,1822 ----
> /* 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");
>
> ***************
> *** 1811,1816 ****
> --- 1996,2008 ----
> 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,1839 ****
> 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);
>
> /* Allocate temporary array for new values */
> values = (Datum *) palloc(nitems * sizeof(Datum));
> --- 2021,2101 ----
> if (nitems <= 0)
> PG_RETURN_ARRAYTYPE_P(v);
>
> ! /*
> ! * 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;
> !
> ! my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> ! sizeof(ArrayMetaState));
> ! inp_extra = my_extra->inp_extra;
> ! inp_extra->element_type = InvalidOid;
> !
> ! my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
> ! sizeof(ArrayMetaState));
> ! 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)
> ! {
> ! /* Lookup source and result types. Unneeded variables are reused. */
> ! get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
> ! &typdelim, &typelem, &proc, &inp_typalign);
> !
> ! inp_extra->element_type = inpType;
> ! inp_extra->typlen = inp_typlen;
> ! inp_extra->typbyval = inp_typbyval;
> ! inp_extra->typdelim = typdelim;
> ! inp_extra->typelem = typelem;
> ! inp_extra->typiofunc = proc;
> ! inp_extra->typalign = inp_typalign;
> ! }
> ! else
> ! {
> ! inp_typlen = inp_extra->typlen;
> ! inp_typbyval = inp_extra->typbyval;
> ! typdelim = inp_extra->typdelim;
> ! typelem = inp_extra->typelem;
> ! proc = inp_extra->typiofunc;
> ! inp_typalign = inp_extra->typalign;
> ! }
> !
> ! if (ret_extra->element_type != retType)
> ! {
> ! /* Lookup source and result types. Unneeded variables are reused. */
> ! get_type_metadata(retType, IOFunc_input, &typlen, &typbyval,
> ! &typdelim, &typelem, &proc, &typalign);
> !
> ! ret_extra->element_type = retType;
> ! ret_extra->typlen = typlen;
> ! ret_extra->typbyval = typbyval;
> ! ret_extra->typdelim = typdelim;
> ! ret_extra->typelem = typelem;
> ! ret_extra->typiofunc = proc;
> ! ret_extra->typalign = typalign;
> ! }
> ! else
> ! {
> ! typlen = ret_extra->typlen;
> ! typbyval = ret_extra->typbyval;
> ! typdelim = ret_extra->typdelim;
> ! typelem = ret_extra->typelem;
> ! proc = ret_extra->typiofunc;
> ! typalign = ret_extra->typalign;
> ! }
>
> /* Allocate temporary array for new values */
> values = (Datum *) palloc(nitems * sizeof(Datum));
> ***************
> *** 2049,2056 ****
> * compares two arrays for equality
> * result :
> * returns true if the arrays are equal, false otherwise.
> - *
> - * XXX bitwise equality is pretty bogus ...
> *-----------------------------------------------------------------------------
> */
> Datum
> --- 2311,2316 ----
> ***************
> *** 2058,2069 ****
> {
> ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
> ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
> bool result = true;
>
> ! if (ARR_SIZE(array1) != ARR_SIZE(array2))
> ! result = false;
> ! else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
> result = false;
>
> /* Avoid leaking memory when handed toasted input. */
> PG_FREE_IF_COPY(array1, 0);
> --- 2318,2435 ----
> {
> 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 typdelim;
> + Oid typelem;
> + char typalign;
> + Oid typiofunc;
> + int i;
> + ArrayMetaState *my_extra;
> + FunctionCallInfoData locfcinfo;
>
> ! /* 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);
> +
> + if (OidIsValid(opfuncid))
> + fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt);
> + else
> + elog(ERROR,
> + "array_eq: cannot find equality operator for type: %u",
> + element_type);
> +
> + get_type_metadata(element_type, IOFunc_output,
> + &typlen, &typbyval, &typdelim,
> + &typelem, &typiofunc, &typalign);
> +
> + my_extra->element_type = element_type;
> + my_extra->typlen = typlen;
> + my_extra->typbyval = typbyval;
> + my_extra->typdelim = typdelim;
> + my_extra->typelem = typelem;
> + my_extra->typiofunc = typiofunc;
> + my_extra->typalign = typalign;
> + }
> + else
> + {
> + typlen = my_extra->typlen;
> + typbyval = my_extra->typbyval;
> + typdelim = my_extra->typdelim;
> + typelem = my_extra->typelem;
> + typiofunc = my_extra->typiofunc;
> + 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,2125 ****
> }
>
>
> ! /***************************************************************************/
> ! /******************| 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
> */
> --- 2439,2628 ----
> }
>
>
> ! /*-----------------------------------------------------------------------------
> ! * 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 typdelim;
> ! Oid typelem;
> ! char typalign;
> ! Oid typiofunc;
> ! Datum *dvalues1;
> ! int nelems1;
> ! Datum *dvalues2;
> ! int nelems2;
> ! int min_nelems;
> ! int i;
> ! typedef struct
> ! {
> ! Oid element_type;
> ! int typlen;
> ! bool typbyval;
> ! char typdelim;
> ! Oid typelem;
> ! Oid typiofunc;
> ! char typalign;
> ! FmgrInfo eqproc;
> ! FmgrInfo ordproc;
> ! } ac_extra;
> ! ac_extra *my_extra;
> !
> ! element_type = ARR_ELEMTYPE(array1);
> !
> ! /*
> ! * We arrange to look up the element type operator function only once
> ! * per series of calls, assuming the element type and opname don'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);
> !
> ! fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt);
> ! fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt);
> !
> ! if (my_extra->eqproc.fn_nargs != 2)
> ! elog(ERROR, "Equality operator does not take 2 arguments: %u",
> ! eqfuncid);
> ! if (my_extra->ordproc.fn_nargs != 2)
> ! elog(ERROR, "Ordering operator does not take 2 arguments: %u",
> ! ordfuncid);
> !
> ! get_type_metadata(element_type, IOFunc_output,
> ! &typlen, &typbyval, &typdelim,
> ! &typelem, &typiofunc, &typalign);
> !
> ! my_extra->element_type = element_type;
> ! my_extra->typlen = typlen;
> ! my_extra->typbyval = typbyval;
> ! my_extra->typdelim = typdelim;
> ! my_extra->typelem = typelem;
> ! my_extra->typiofunc = InvalidOid;
> ! my_extra->typalign = typalign;
> ! }
> ! else
> ! {
> ! 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 |*****************/
> + /***************************************************************************/
> +
> /*
> * Fetch array element at pointer, converted correctly to a Datum
> */
> ***************
> *** 2423,2428 ****
> --- 2926,2943 ----
> 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,2448 ****
> }
>
> /*
> ! * If it's binary-compatible, return the array unmodified.
> */
> if (my_extra->coerce_finfo.fn_oid == InvalidOid)
> ! PG_RETURN_ARRAYTYPE_P(src);
>
> /*
> * Use array_map to apply the function to each array element.
> --- 2954,2969 ----
> }
>
> /*
> ! * 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)
> ! {
> ! 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.
> ***************
> *** 2453,2456 ****
> --- 2974,3092 ----
> locfcinfo.arg[0] = PointerGetDatum(src);
>
> return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
> + }
> +
> + /*
> + * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK
> + *
> + * 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 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 md final result of accumArrayResult
> + *
> + * 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);
> }
> Index: src/backend/utils/adt/varlena.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v
> retrieving revision 1.98
> diff -c -r1.98 varlena.c
> *** src/backend/utils/adt/varlena.c 15 May 2003 15:50:19 -0000 1.98
> --- src/backend/utils/adt/varlena.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 19,29 ****
> --- 19,32 ----
> #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,1990 ****
> if (fldnum == 1) /* first field - just return the input
> * string */
> PG_RETURN_TEXT_P(inputstring);
> ! else
> ! /* otherwise return an empty string */
> PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
> }
>
> --- 1986,1992 ----
> if (fldnum == 1) /* first field - just return the input
> * string */
> PG_RETURN_TEXT_P(inputstring);
> ! else /* otherwise return an empty string */
> PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
> }
>
> ***************
> *** 2004,2011 ****
> if (fldnum == 1) /* first field - just return the input
> * string */
> PG_RETURN_TEXT_P(inputstring);
> ! else
> ! /* otherwise return an empty string */
> PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
> }
> else if ((start_posn != 0) && (end_posn == 0))
> --- 2006,2012 ----
> if (fldnum == 1) /* first field - just return the input
> * string */
> PG_RETURN_TEXT_P(inputstring);
> ! else /* otherwise return an empty string */
> PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
> }
> else if ((start_posn != 0) && (end_posn == 0))
> ***************
> *** 2026,2031 ****
> --- 2027,2217 ----
> result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn - fldsep_len, false);
> PG_RETURN_TEXT_P(result_text);
> }
> + }
> +
> + /*
> + * 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 typdelim;
> + Oid typoutput,
> + typelem;
> + FmgrInfo outputproc;
> + char typalign;
> + 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_metadata(element_type, IOFunc_output,
> + &typlen, &typbyval, &typdelim,
> + &typelem, &typoutput, &typalign);
> + fmgr_info(typoutput, &outputproc);
> +
> + my_extra->element_type = element_type;
> + my_extra->typlen = typlen;
> + my_extra->typbyval = typbyval;
> + my_extra->typdelim = typdelim;
> + my_extra->typelem = typelem;
> + my_extra->typiofunc = typoutput;
> + my_extra->typalign = typalign;
> + my_extra->proc = outputproc;
> + }
> + else
> + {
> + typlen = my_extra->typlen;
> + typbyval = my_extra->typbyval;
> + typdelim = my_extra->typdelim;
> + typelem = my_extra->typelem;
> + typoutput = my_extra->typiofunc;
> + typalign = my_extra->typalign;
> + outputproc = my_extra->proc;
> + }
> +
> + for (i = 0; i < nitems; i++)
> + {
> + Datum itemvalue;
> + char *value;
> +
> + itemvalue = fetch_att(p, typbyval, typlen);
> +
> + value = DatumGetCString(FunctionCall3(&outputproc,
> + 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
> Index: src/backend/utils/cache/lsyscache.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v
> retrieving revision 1.96
> diff -c -r1.96 lsyscache.c
> *** src/backend/utils/cache/lsyscache.c 22 Jun 2003 22:04:54 -0000 1.96
> --- src/backend/utils/cache/lsyscache.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 719,724 ****
> --- 719,758 ----
> }
>
> /*
> + * get_func_argtypes
> + * Given procedure id, return the function's argument types.
> + * Also pass back the number of arguments.
> + */
> + Oid *
> + get_func_argtypes(Oid funcid, int *nargs)
> + {
> + HeapTuple tp;
> + Form_pg_proc procstruct;
> + Oid *result = NULL;
> + int i;
> +
> + tp = SearchSysCache(PROCOID,
> + ObjectIdGetDatum(funcid),
> + 0, 0, 0);
> + if (!HeapTupleIsValid(tp))
> + elog(ERROR, "Function OID %u does not exist", funcid);
> +
> + procstruct = (Form_pg_proc) GETSTRUCT(tp);
> + *nargs = (int) procstruct->pronargs;
> +
> + if (*nargs > 0)
> + {
> + result = (Oid *) palloc(*nargs * sizeof(Oid));
> +
> + for (i = 0; i < *nargs; i++)
> + result[i] = procstruct->proargtypes[i];
> + }
> +
> + ReleaseSysCache(tp);
> + return result;
> + }
> +
> + /*
> * get_func_retset
> * Given procedure id, return the function's proretset flag.
> */
> ***************
> *** 1088,1093 ****
> --- 1122,1177 ----
> *typbyval = typtup->typbyval;
> *typalign = typtup->typalign;
> ReleaseSysCache(tp);
> + }
> +
> + /*
> + * get_type_metadata
> + *
> + * A six-fer: given the type OID, return typlen, typbyval, typalign,
> + * typdelim, typelem, IO function Oid. The IO function
> + * returned is controlled by IOFuncSelector
> + */
> + void
> + get_type_metadata(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);
> }
>
> #ifdef NOT_USED
> Index: src/backend/utils/fmgr/fmgr.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v
> retrieving revision 1.68
> diff -c -r1.68 fmgr.c
> *** src/backend/utils/fmgr/fmgr.c 8 Apr 2003 23:20:02 -0000 1.68
> --- src/backend/utils/fmgr/fmgr.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 1673,1675 ****
> --- 1673,1701 ----
>
> return exprType((Node *) nth(argnum, args));
> }
> +
> + /*
> + * Get the OID of the function or operator
> + *
> + * Returns InvalidOid if information is not available
> + */
> + Oid
> + get_fn_expr_functype(FunctionCallInfo fcinfo)
> + {
> + Node *expr;
> +
> + /*
> + * can't return anything useful if we have no FmgrInfo or if
> + * its fn_expr node has not been initialized
> + */
> + if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr)
> + return InvalidOid;
> +
> + expr = fcinfo->flinfo->fn_expr;
> + if (IsA(expr, FuncExpr))
> + return ((FuncExpr *) expr)->funcid;
> + else if (IsA(expr, OpExpr))
> + return ((OpExpr *) expr)->opno;
> + else
> + return InvalidOid;
> + }
> Index: src/include/fmgr.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v
> retrieving revision 1.27
> diff -c -r1.27 fmgr.h
> *** src/include/fmgr.h 8 Apr 2003 23:20:04 -0000 1.27
> --- src/include/fmgr.h 24 Jun 2003 02:24:06 -0000
> ***************
> *** 18,23 ****
> --- 18,24 ----
> #ifndef FMGR_H
> #define FMGR_H
>
> + #include "nodes/nodes.h"
>
> /*
> * All functions that can be called directly by fmgr must have this signature.
> ***************
> *** 372,385 ****
> Datum arg6, Datum arg7, Datum arg8,
> Datum arg9);
>
> -
> /*
> * Routines in fmgr.c
> */
> extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
> ! extern Oid fmgr_internal_function(const char *proname);
> ! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
> ! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
>
> /*
> * Routines in dfmgr.c
> --- 373,386 ----
> Datum arg6, Datum arg7, Datum arg8,
> Datum arg9);
>
> /*
> * Routines in fmgr.c
> */
> extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
> ! extern Oid fmgr_internal_function(const char *proname);
> ! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
> ! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
> ! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);
>
> /*
> * Routines in dfmgr.c
> Index: src/include/catalog/pg_amop.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amop.h,v
> retrieving revision 1.50
> diff -c -r1.50 pg_amop.h
> *** src/include/catalog/pg_amop.h 22 Jun 2003 22:04:55 -0000 1.50
> --- src/include/catalog/pg_amop.h 24 Jun 2003 02:24:06 -0000
> ***************
> *** 418,423 ****
> --- 418,432 ----
> 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
> Index: src/include/catalog/pg_amproc.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amproc.h,v
> retrieving revision 1.38
> diff -c -r1.38 pg_amproc.h
> *** src/include/catalog/pg_amproc.h 22 Jun 2003 22:04:55 -0000 1.38
> --- src/include/catalog/pg_amproc.h 24 Jun 2003 02:35:24 -0000
> ***************
> *** 78,83 ****
> --- 78,84 ----
>
>
> /* btree */
> + DATA(insert ( 397 1 382 ));
> DATA(insert ( 421 1 357 ));
> DATA(insert ( 423 1 1596 ));
> DATA(insert ( 424 1 1693 ));
> Index: src/include/catalog/pg_opclass.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_opclass.h,v
> retrieving revision 1.51
> diff -c -r1.51 pg_opclass.h
> *** src/include/catalog/pg_opclass.h 22 Jun 2003 22:04:55 -0000 1.51
> --- src/include/catalog/pg_opclass.h 24 Jun 2003 02:24:06 -0000
> ***************
> *** 87,92 ****
> --- 87,94 ----
> */
>
> 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 ));
> Index: src/include/catalog/pg_operator.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v
> retrieving revision 1.115
> diff -c -r1.115 pg_operator.h
> *** src/include/catalog/pg_operator.h 22 Jun 2003 22:04:55 -0000 1.115
> --- src/include/catalog/pg_operator.h 24 Jun 2003 02:24:06 -0000
> ***************
> *** 116,125 ****
> 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 - - ));
>
> DATA(insert OID = 352 ( "=" PGNSP PGUID b t 28 28 16 352 0 0 0 0 0 xideq eqsel eqjoinsel ));
> DATA(insert OID = 353 ( "=" PGNSP PGUID b f 28 23 16 0 0 0 0 0 0 xideqint4 eqsel eqjoinsel ));
> --- 116,130 ----
> 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 = 1070 ( "=" PGNSP PGUID b f 2277 2277 16 1070 1071 1072 1072 1072 1073 array_eq eqsel eqjoinsel ));
> ! 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 ));
> ! 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 ));
> ! 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 - - ));
>
> DATA(insert OID = 352 ( "=" PGNSP PGUID b t 28 28 16 352 0 0 0 0 0 xideq eqsel eqjoinsel ));
> DATA(insert OID = 353 ( "=" PGNSP PGUID b f 28 23 16 0 0 0 0 0 0 xideqint4 eqsel eqjoinsel ));
> ***************
> *** 425,430 ****
> --- 430,436 ----
> 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 0 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 - - ));
> Index: src/include/catalog/pg_proc.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v
> retrieving revision 1.304
> diff -c -r1.304 pg_proc.h
> *** src/include/catalog/pg_proc.h 22 Jun 2003 22:04:55 -0000 1.304
> --- src/include/catalog/pg_proc.h 24 Jun 2003 02:35:46 -0000
> ***************
> *** 758,763 ****
> --- 758,765 ----
> 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,1001 ****
> 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 = 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_ ));
> --- 990,1012 ----
> 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 = 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,1027 ****
> 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 = 760 ( smgrin PGNSP PGUID 12 f f t f s 1 210 "2275" smgrin - _null_ ));
> DESCR("I/O");
> --- 1017,1034 ----
> 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 = 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 = 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,1327 ****
> --- 1329,1336 ----
> 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_ ));
> Index: src/include/nodes/primnodes.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v
> retrieving revision 1.83
> diff -c -r1.83 primnodes.h
> *** src/include/nodes/primnodes.h 6 Jun 2003 15:04:03 -0000 1.83
> --- src/include/nodes/primnodes.h 24 Jun 2003 02:24:06 -0000
> ***************
> *** 226,231 ****
> --- 226,232 ----
> Index agglevelsup; /* > 0 if agg belongs to outer query */
> bool aggstar; /* TRUE if argument was really '*' */
> bool aggdistinct; /* TRUE if it's agg(DISTINCT ...) */
> + List *args; /* arguments to the aggregate */
> } Aggref;
>
> /* ----------------
> ***************
> *** 358,372 ****
> /* ----------------
> * SubLink
> *
> ! * A SubLink represents a subselect appearing in an expression, and in some
> ! * cases also the combining operator(s) just above it. The subLinkType
> ! * indicates the form of the expression represented:
> * EXISTS_SUBLINK EXISTS(SELECT ...)
> * ALL_SUBLINK (lefthand) op ALL (SELECT ...)
> * ANY_SUBLINK (lefthand) op ANY (SELECT ...)
> * MULTIEXPR_SUBLINK (lefthand) op (SELECT ...)
> * EXPR_SUBLINK (SELECT with single targetlist item ...)
> * ARRAY_SUBLINK ARRAY(SELECT with single targetlist item ...)
> * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
> * same length as the subselect's targetlist. MULTIEXPR will *always* have
> * a list with more than one entry; if the subselect has just one target
> --- 359,377 ----
> /* ----------------
> * SubLink
> *
> ! * A SubLink represents a subselect, or an expression, appearing in an
> ! * expression, and in some cases also the combining operator(s) just above
> ! * it. The subLinkType indicates the form of the expression represented:
> * EXISTS_SUBLINK EXISTS(SELECT ...)
> * ALL_SUBLINK (lefthand) op ALL (SELECT ...)
> * ANY_SUBLINK (lefthand) op ANY (SELECT ...)
> * MULTIEXPR_SUBLINK (lefthand) op (SELECT ...)
> * EXPR_SUBLINK (SELECT with single targetlist item ...)
> * ARRAY_SUBLINK ARRAY(SELECT with single targetlist item ...)
> + * If an expression is used in place of the subselect, it is transformed
> + * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be
> + * used as if they were the result of a single column subselect. If the
> + * expression is scalar, it is treated as a one element array.
> * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the
> * same length as the subselect's targetlist. MULTIEXPR will *always* have
> * a list with more than one entry; if the subselect has just one target
> ***************
> *** 415,420 ****
> --- 420,427 ----
> SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
> bool useOr; /* TRUE to combine column results with
> * "OR" not "AND" */
> + bool isExpr; /* TRUE if the subselect is really derived
> + * from a single expression */
> List *lefthand; /* list of outer-query expressions on the
> * left */
> List *operName; /* originally specified operator name */
> ***************
> *** 456,461 ****
> --- 463,477 ----
> SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
> bool useOr; /* TRUE to combine column results with
> * "OR" not "AND" */
> + bool isExpr; /* TRUE if the subselect is really derived
> + * from a single expression */
> + /* runtime cache for single array expressions */
> + Oid exprtype; /* array and element type, and other info
> + * needed deconstruct the array */
> + Oid elemtype;
> + int16 elmlen;
> + bool elmbyval;
> + char elmalign;
> /* The combining operators, transformed to executable expressions: */
> List *exprs; /* list of OpExpr expression trees */
> List *paramIds; /* IDs of Params embedded in the above */
> Index: src/include/optimizer/clauses.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v
> retrieving revision 1.63
> diff -c -r1.63 clauses.h
> *** src/include/optimizer/clauses.h 28 May 2003 16:04:02 -0000 1.63
> --- src/include/optimizer/clauses.h 24 Jun 2003 02:24:06 -0000
> ***************
> *** 28,33 ****
> --- 28,36 ----
> extern Node *get_leftop(Expr *clause);
> extern Node *get_rightop(Expr *clause);
>
> + extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
> + CoercionForm funcformat, List *funcargs);
> +
> extern bool not_clause(Node *clause);
> extern Expr *make_notclause(Expr *notclause);
> extern Expr *get_notclausearg(Expr *notclause);
> Index: src/include/parser/parse_oper.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_oper.h,v
> retrieving revision 1.25
> diff -c -r1.25 parse_oper.h
> *** src/include/parser/parse_oper.h 29 Apr 2003 22:13:11 -0000 1.25
> --- src/include/parser/parse_oper.h 24 Jun 2003 02:24:06 -0000
> ***************
> *** 44,49 ****
> --- 44,50 ----
> /* Convenience routines for common calls on the above */
> extern Oid compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
> extern Oid equality_oper_funcid(Oid argtype);
> + extern Oid ordering_oper_funcid(Oid argtype);
> extern Oid ordering_oper_opid(Oid argtype);
>
> /* Extract operator OID or underlying-function OID from an Operator tuple */
> Index: src/include/utils/acl.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v
> retrieving revision 1.52
> diff -c -r1.52 acl.h
> *** src/include/utils/acl.h 11 Jun 2003 09:23:55 -0000 1.52
> --- src/include/utils/acl.h 24 Jun 2003 02:24:06 -0000
> ***************
> *** 192,197 ****
> --- 192,198 ----
> 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
> Index: src/include/utils/array.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v
> retrieving revision 1.38
> diff -c -r1.38 array.h
> *** src/include/utils/array.h 8 May 2003 22:19:57 -0000 1.38
> --- src/include/utils/array.h 24 Jun 2003 02:24:06 -0000
> ***************
> *** 32,37 ****
> --- 32,68 ----
> 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;
> + int typlen;
> + bool typbyval;
> + char typdelim;
> + Oid typelem;
> + Oid typiofunc;
> + char typalign;
> + FmgrInfo proc;
> + } ArrayMetaState;
> +
> /*
> * fmgr macros for array objects
> */
> ***************
> *** 86,96 ****
> 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_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,
> --- 117,131 ----
> 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_type_coerce(PG_FUNCTION_ARGS);
>
> extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
> ***************
> *** 124,130 ****
> Oid elmtype,
> int elmlen, bool elmbyval, char elmalign,
> Datum **elemsp, int *nelemsp);
> !
>
> /*
> * prototypes for functions defined in arrayutils.c
> --- 159,172 ----
> 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,152 ****
> /*
> * 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,
> Datum element,
> int ndims);
>
> --- 183,193 ----
> /*
> * prototypes for functions defined in array_userfuncs.c
> */
> extern Datum array_push(PG_FUNCTION_ARGS);
> extern Datum array_cat(PG_FUNCTION_ARGS);
>
> ! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
> ! Oid element_type,
> Datum element,
> int ndims);
>
> Index: src/include/utils/builtins.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v
> retrieving revision 1.219
> diff -c -r1.219 builtins.h
> *** src/include/utils/builtins.h 26 May 2003 00:11:28 -0000 1.219
> --- src/include/utils/builtins.h 24 Jun 2003 02:24:06 -0000
> ***************
> *** 530,535 ****
> --- 530,537 ----
> 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);
> Index: src/include/utils/lsyscache.h
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v
> retrieving revision 1.71
> diff -c -r1.71 lsyscache.h
> *** src/include/utils/lsyscache.h 22 Jun 2003 22:04:55 -0000 1.71
> --- src/include/utils/lsyscache.h 24 Jun 2003 02:24:06 -0000
> ***************
> *** 15,20 ****
> --- 15,29 ----
>
> #include "access/htup.h"
>
> + /* I/O function selector for system_cache_lookup */
> + 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);
> ***************
> *** 41,46 ****
> --- 50,56 ----
> extern RegProcedure get_oprjoin(Oid opno);
> extern char *get_func_name(Oid funcid);
> extern Oid get_func_rettype(Oid funcid);
> + extern Oid *get_func_argtypes(Oid funcid, int *nargs);
> extern bool get_func_retset(Oid funcid);
> extern bool func_strict(Oid funcid);
> extern char func_volatile(Oid funcid);
> ***************
> *** 56,61 ****
> --- 66,79 ----
> 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_metadata(Oid element_type,
> + IOFuncSelector which_func,
> + int *typlen,
> + bool *typbyval,
> + char *typdelim,
> + Oid *typelem,
> + Oid *proc,
> + char *typalign);
> extern char get_typstorage(Oid typid);
> extern int32 get_typtypmod(Oid typid);
> extern Node *get_typdefault(Oid typid);
> Index: src/interfaces/ecpg/preproc/preproc.y
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/preproc.y,v
> retrieving revision 1.236
> diff -c -r1.236 preproc.y
> *** src/interfaces/ecpg/preproc/preproc.y 20 Jun 2003 13:36:34 -0000 1.236
> --- src/interfaces/ecpg/preproc/preproc.y 24 Jun 2003 02:24:06 -0000
> ***************
> *** 4595,4601 ****
> $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");
>
> types = this;
> }
> --- 4595,4601 ----
> $3.type_enum != ECPGt_char &&
> $3.type_enum != ECPGt_unsigned_char &&
> atoi(this->type->type_index) >= 0)
> ! mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
>
> types = this;
> }
> ***************
> *** 5415,5421 ****
> $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");
>
> types = this;
> }
> --- 5415,5421 ----
> $5.type_enum != ECPGt_char &&
> $5.type_enum != ECPGt_unsigned_char &&
> atoi(this->type->type_index) >= 0)
> ! mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
>
> types = this;
> }
> ***************
> *** 5482,5488 ****
>
> default:
> if (atoi(length) >= 0)
> ! mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
>
> if (atoi(dimension) < 0)
> type = ECPGmake_simple_type($5.type_enum, make_str("1"));
> --- 5482,5488 ----
>
> default:
> if (atoi(length) >= 0)
> ! 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"));
> Index: src/interfaces/ecpg/preproc/type.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/type.c,v
> retrieving revision 1.52
> diff -c -r1.52 type.c
> *** src/interfaces/ecpg/preproc/type.c 20 Jun 2003 12:00:59 -0000 1.52
> --- src/interfaces/ecpg/preproc/type.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 504,510 ****
> switch (type->u.element->type)
> {
> case ECPGt_array:
> ! yyerror("internal error, found multi-dimensional array\n");
> break;
> case ECPGt_struct:
> case ECPGt_union:
> --- 504,510 ----
> switch (type->u.element->type)
> {
> case ECPGt_array:
> ! yyerror("internal error, found multidimensional array\n");
> break;
> case ECPGt_struct:
> case ECPGt_union:
> Index: src/interfaces/ecpg/preproc/variable.c
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/variable.c,v
> retrieving revision 1.21
> diff -c -r1.21 variable.c
> *** src/interfaces/ecpg/preproc/variable.c 11 Jun 2003 06:39:13 -0000 1.21
> --- src/interfaces/ecpg/preproc/variable.c 24 Jun 2003 02:24:06 -0000
> ***************
> *** 436,442 ****
> if (atoi(type_index) >= 0)
> {
> if (atoi(*length) >= 0)
> ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
> *length = type_index;
> }
> --- 436,442 ----
> if (atoi(type_index) >= 0)
> {
> if (atoi(*length) >= 0)
> ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
> *length = type_index;
> }
> ***************
> *** 444,450 ****
> if (atoi(type_dimension) >= 0)
> {
> if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
> ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
> if (atoi(*dimension) >= 0)
> *length = *dimension;
> --- 444,450 ----
> if (atoi(type_dimension) >= 0)
> {
> if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
> ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
> if (atoi(*dimension) >= 0)
> *length = *dimension;
> ***************
> *** 463,472 ****
> 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");
>
> if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
> ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
>
> switch (type_enum)
> {
> --- 463,472 ----
> 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 multidimensional array support");
>
> if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
> ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
>
> switch (type_enum)
> {
> ***************
> *** 480,486 ****
> }
>
> if (atoi(*length) >= 0)
> ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");
>
> break;
> case ECPGt_varchar:
> --- 480,486 ----
> }
>
> if (atoi(*length) >= 0)
> ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");
>
> break;
> case ECPGt_varchar:
> ***************
> *** 525,531 ****
> }
>
> if (atoi(*length) >= 0)
> ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");
>
> break;
> }
> --- 525,531 ----
> }
>
> if (atoi(*length) >= 0)
> ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");
>
> break;
> }
> Index: src/test/regress/expected/arrays.out
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/arrays.out,v
> retrieving revision 1.11
> diff -c -r1.11 arrays.out
> *** src/test/regress/expected/arrays.out 8 Apr 2003 23:20:04 -0000 1.11
> --- src/test/regress/expected/arrays.out 24 Jun 2003 02:24:06 -0000
> ***************
> *** 178,196 ****
> (1 row)
>
> -- functions
> ! SELECT singleton_array(42) AS "{42}";
> ! {42}
> ! ------
> ! {42}
> ! (1 row)
> !
> ! SELECT array_append(singleton_array(42), 6) AS "{42,6}";
> {42,6}
> --------
> {42,6}
> (1 row)
>
> ! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
> {6,42}
> --------
> {6,42}
> --- 178,190 ----
> (1 row)
>
> -- functions
> ! SELECT array_append(array[42], 6) AS "{42,6}";
> {42,6}
> --------
> {42,6}
> (1 row)
>
> ! SELECT array_prepend(6, array[42]) AS "{6,42}";
> {6,42}
> --------
> {6,42}
> ***************
> *** 212,235 ****
> {{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
> --- 206,211 ----
> Index: src/test/regress/sql/arrays.sql
> ===================================================================
> RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/arrays.sql,v
> retrieving revision 1.10
> diff -c -r1.10 arrays.sql
> *** src/test/regress/sql/arrays.sql 8 Apr 2003 23:20:04 -0000 1.10
> --- src/test/regress/sql/arrays.sql 24 Jun 2003 02:24:06 -0000
> ***************
> *** 130,144 ****
> 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_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]]];
> --- 130,140 ----
> SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
>
> -- functions
> ! 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}}";
>
> -- operators
> SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];

--
Bruce Momjian | http://candle.pha.pa.us
pgman(at)candle(dot)pha(dot)pa(dot)us | (610) 359-1001
+ If your life is a hard drive, | 13 Roberts Road
+ Christ can be your backup. | Newtown Square, Pennsylvania 19073

In response to

Browse pgsql-patches by date

  From Date Subject
Next Message Bruce Momjian 2003-06-24 03:31:01 Re: [HACKERS] Subtraction carry bug in xlog.c in 7.3 and 7.4
Previous Message Joe Conway 2003-06-24 03:01:58 Re: array support patch phase 1 patch