parent
50e53236af
commit
46bf651480
|
@ -1,4 +1,4 @@
|
||||||
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/array.sgml,v 1.25 2003/03/13 01:30:26 petere Exp $ -->
|
<!-- $Header: /cvsroot/pgsql/doc/src/sgml/array.sgml,v 1.26 2003/06/24 23:14:42 momjian Exp $ -->
|
||||||
|
|
||||||
<sect1 id="arrays">
|
<sect1 id="arrays">
|
||||||
<title>Arrays</title>
|
<title>Arrays</title>
|
||||||
|
@ -60,14 +60,74 @@ INSERT INTO sal_emp
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<note>
|
|
||||||
<para>
|
<para>
|
||||||
A limitation of the present array implementation is that individual
|
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
|
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
|
to null, but you can't have an array with some elements null and some
|
||||||
not. Fixing this is on the to-do list.
|
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>
|
</para>
|
||||||
</note>
|
</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>
|
||||||
|
|
||||||
<sect2>
|
<sect2>
|
||||||
|
@ -132,11 +192,30 @@ SELECT schedule[1:2][1] FROM sal_emp WHERE name = 'Bill';
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
with the same result. An array subscripting operation is always taken to
|
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
|
represent an array slice if any of the subscripts are written in the form
|
||||||
form
|
|
||||||
<literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
|
<literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>.
|
||||||
A lower bound of 1 is assumed for any subscript where only one value
|
A lower bound of 1 is assumed for any subscript where only one value
|
||||||
is specified.
|
is specified; another example follows:
|
||||||
|
<programlisting>
|
||||||
|
SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill';
|
||||||
|
schedule
|
||||||
|
---------------------------
|
||||||
|
{{meeting,lunch},{"",""}}
|
||||||
|
(1 row)
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Additionally, we can also access a single arbitrary array element of
|
||||||
|
a one-dimensional array with the <function>array_subscript</function>
|
||||||
|
function:
|
||||||
|
<programlisting>
|
||||||
|
SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill';
|
||||||
|
array_subscript
|
||||||
|
-----------------
|
||||||
|
10000
|
||||||
|
(1 row)
|
||||||
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -147,7 +226,23 @@ UPDATE sal_emp SET pay_by_quarter = '{25000,25000,27000,27000}'
|
||||||
WHERE name = 'Carol';
|
WHERE name = 'Carol';
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
or updated at a single element:
|
or using the <command>ARRAY</command> expression syntax:
|
||||||
|
|
||||||
|
<programlisting>
|
||||||
|
UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
|
||||||
|
WHERE name = 'Carol';
|
||||||
|
</programlisting>
|
||||||
|
|
||||||
|
<note>
|
||||||
|
<para>
|
||||||
|
Anywhere you can use the <quote>curly braces</quote> array syntax,
|
||||||
|
you can also use the <command>ARRAY</command> expression syntax. The
|
||||||
|
remainder of this section will illustrate only one or the other, but
|
||||||
|
not both.
|
||||||
|
</para>
|
||||||
|
</note>
|
||||||
|
|
||||||
|
An array may also be updated at a single element:
|
||||||
|
|
||||||
<programlisting>
|
<programlisting>
|
||||||
UPDATE sal_emp SET pay_by_quarter[4] = 15000
|
UPDATE sal_emp SET pay_by_quarter[4] = 15000
|
||||||
|
@ -160,6 +255,14 @@ UPDATE sal_emp SET pay_by_quarter[4] = 15000
|
||||||
UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
|
UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
|
||||||
WHERE name = 'Carol';
|
WHERE name = 'Carol';
|
||||||
</programlisting>
|
</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>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@ -178,6 +281,88 @@ UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}'
|
||||||
create an array with subscript values running from -2 to 7.
|
create an array with subscript values running from -2 to 7.
|
||||||
</para>
|
</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>
|
<para>
|
||||||
The syntax for <command>CREATE TABLE</command> allows fixed-length
|
The syntax for <command>CREATE TABLE</command> allows fixed-length
|
||||||
arrays to be defined:
|
arrays to be defined:
|
||||||
|
@ -193,6 +378,16 @@ CREATE TABLE tictactoe (
|
||||||
length.
|
length.
|
||||||
</para>
|
</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>
|
<para>
|
||||||
Actually, the current implementation does not enforce the declared
|
Actually, the current implementation does not enforce the declared
|
||||||
number of dimensions either. Arrays of a particular element type are
|
number of dimensions either. Arrays of a particular element type are
|
||||||
|
@ -300,6 +495,72 @@ SELECT * FROM sal_emp WHERE pay_by_quarter **= 10000;
|
||||||
is not ignored, however: after skipping leading whitespace, everything
|
is not ignored, however: after skipping leading whitespace, everything
|
||||||
up to the next right brace or delimiter is taken as the item value.
|
up to the next right brace or delimiter is taken as the item value.
|
||||||
</para>
|
</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>
|
||||||
|
|
||||||
<sect2>
|
<sect2>
|
||||||
|
@ -317,6 +578,14 @@ SELECT * FROM sal_emp WHERE pay_by_quarter **= 10000;
|
||||||
that would otherwise be taken as array syntax or ignorable white space.
|
that would otherwise be taken as array syntax or ignorable white space.
|
||||||
</para>
|
</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>
|
<para>
|
||||||
The array output routine will put double quotes around element values
|
The array output routine will put double quotes around element values
|
||||||
if they are empty strings or contain curly braces, delimiter characters,
|
if they are empty strings or contain curly braces, delimiter characters,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.154 2003/05/05 15:08:49 tgl Exp $
|
$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.155 2003/06/24 23:14:42 momjian Exp $
|
||||||
PostgreSQL documentation
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
@ -6962,6 +6962,203 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
|
||||||
|
|
||||||
</sect1>
|
</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">
|
<sect1 id="functions-aggregate">
|
||||||
<title>Aggregate Functions</title>
|
<title>Aggregate Functions</title>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.56 2002/09/18 21:35:20 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.57 2003/06/24 23:14:42 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -50,10 +50,16 @@ AggregateCreate(const char *aggName,
|
||||||
Oid finalfn = InvalidOid; /* can be omitted */
|
Oid finalfn = InvalidOid; /* can be omitted */
|
||||||
Oid finaltype;
|
Oid finaltype;
|
||||||
Oid fnArgs[FUNC_MAX_ARGS];
|
Oid fnArgs[FUNC_MAX_ARGS];
|
||||||
int nargs;
|
int nargs_transfn;
|
||||||
|
int nargs_finalfn;
|
||||||
Oid procOid;
|
Oid procOid;
|
||||||
TupleDesc tupDesc;
|
TupleDesc tupDesc;
|
||||||
int i;
|
int i;
|
||||||
|
Oid rettype;
|
||||||
|
Oid *true_oid_array_transfn;
|
||||||
|
Oid *true_oid_array_finalfn;
|
||||||
|
bool retset;
|
||||||
|
FuncDetailCode fdresult;
|
||||||
ObjectAddress myself,
|
ObjectAddress myself,
|
||||||
referenced;
|
referenced;
|
||||||
|
|
||||||
|
@ -68,24 +74,49 @@ AggregateCreate(const char *aggName,
|
||||||
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
||||||
fnArgs[0] = aggTransType;
|
fnArgs[0] = aggTransType;
|
||||||
if (aggBaseType == ANYOID)
|
if (aggBaseType == ANYOID)
|
||||||
nargs = 1;
|
nargs_transfn = 1;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fnArgs[1] = aggBaseType;
|
fnArgs[1] = aggBaseType;
|
||||||
nargs = 2;
|
nargs_transfn = 2;
|
||||||
}
|
}
|
||||||
transfn = LookupFuncName(aggtransfnName, nargs, fnArgs);
|
|
||||||
|
/*
|
||||||
|
* 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))
|
if (!OidIsValid(transfn))
|
||||||
func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
|
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,
|
tup = SearchSysCache(PROCOID,
|
||||||
ObjectIdGetDatum(transfn),
|
ObjectIdGetDatum(transfn),
|
||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(tup))
|
if (!HeapTupleIsValid(tup))
|
||||||
func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL);
|
func_error("AggregateCreate", aggtransfnName,
|
||||||
|
nargs_transfn, fnArgs, NULL);
|
||||||
proc = (Form_pg_proc) GETSTRUCT(tup);
|
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
|
* If the transfn is strict and the initval is NULL, make sure input
|
||||||
|
@ -105,17 +136,26 @@ AggregateCreate(const char *aggName,
|
||||||
{
|
{
|
||||||
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
||||||
fnArgs[0] = aggTransType;
|
fnArgs[0] = aggTransType;
|
||||||
finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs);
|
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))
|
if (!OidIsValid(finalfn))
|
||||||
func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
|
func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
|
||||||
tup = SearchSysCache(PROCOID,
|
|
||||||
ObjectIdGetDatum(finalfn),
|
/*
|
||||||
0, 0, 0);
|
* enforce consistency with ANYARRAY and ANYELEMENT argument
|
||||||
if (!HeapTupleIsValid(tup))
|
* and return types, possibly modifying return type along the way
|
||||||
func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL);
|
*/
|
||||||
proc = (Form_pg_proc) GETSTRUCT(tup);
|
finaltype = enforce_generic_type_consistency(fnArgs,
|
||||||
finaltype = proc->prorettype;
|
true_oid_array_finalfn,
|
||||||
ReleaseSysCache(tup);
|
nargs_finalfn, rettype);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -126,6 +166,27 @@ AggregateCreate(const char *aggName,
|
||||||
}
|
}
|
||||||
Assert(OidIsValid(finaltype));
|
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
|
* Everything looks okay. Try to create the pg_proc entry for the
|
||||||
* aggregate. (This could fail if there's already a conflicting
|
* aggregate. (This could fail if there's already a conflicting
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.5 2002/09/04 20:31:14 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.6 2003/06/24 23:14:43 momjian Exp $
|
||||||
*
|
*
|
||||||
* DESCRIPTION
|
* DESCRIPTION
|
||||||
* The "DefineFoo" routines take the parse tree and pick out the
|
* The "DefineFoo" routines take the parse tree and pick out the
|
||||||
|
@ -119,7 +119,9 @@ DefineAggregate(List *names, List *parameters)
|
||||||
baseTypeId = typenameTypeId(baseType);
|
baseTypeId = typenameTypeId(baseType);
|
||||||
|
|
||||||
transTypeId = typenameTypeId(transType);
|
transTypeId = typenameTypeId(transType);
|
||||||
if (get_typtype(transTypeId) == 'p')
|
if (get_typtype(transTypeId) == 'p' &&
|
||||||
|
transTypeId != ANYARRAYOID &&
|
||||||
|
transTypeId != ANYELEMENTOID)
|
||||||
elog(ERROR, "Aggregate transition datatype cannot be %s",
|
elog(ERROR, "Aggregate transition datatype cannot be %s",
|
||||||
format_type_be(transTypeId));
|
format_type_be(transTypeId));
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.130 2003/05/28 22:32:49 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.131 2003/06/24 23:14:43 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1528,17 +1528,17 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
|
||||||
{
|
{
|
||||||
/* Check other sub-arrays are compatible */
|
/* Check other sub-arrays are compatible */
|
||||||
if (elem_ndims != ARR_NDIM(array))
|
if (elem_ndims != ARR_NDIM(array))
|
||||||
elog(ERROR, "Multiple dimension arrays must have array "
|
elog(ERROR, "Multidimensional arrays must have array "
|
||||||
"expressions with matching number of dimensions");
|
"expressions with matching number of dimensions");
|
||||||
|
|
||||||
if (memcmp(elem_dims, ARR_DIMS(array),
|
if (memcmp(elem_dims, ARR_DIMS(array),
|
||||||
elem_ndims * sizeof(int)) != 0)
|
elem_ndims * sizeof(int)) != 0)
|
||||||
elog(ERROR, "Multiple dimension arrays must have array "
|
elog(ERROR, "Multidimensional arrays must have array "
|
||||||
"expressions with matching dimensions");
|
"expressions with matching dimensions");
|
||||||
|
|
||||||
if (memcmp(elem_lbs, ARR_LBOUND(array),
|
if (memcmp(elem_lbs, ARR_LBOUND(array),
|
||||||
elem_ndims * sizeof(int)) != 0)
|
elem_ndims * sizeof(int)) != 0)
|
||||||
elog(ERROR, "Multiple dimension arrays must have array "
|
elog(ERROR, "Multidimensional arrays must have array "
|
||||||
"expressions with matching dimensions");
|
"expressions with matching dimensions");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.107 2003/06/22 22:04:54 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.108 2003/06/24 23:14:43 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -58,6 +58,7 @@
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "executor/nodeAgg.h"
|
#include "executor/nodeAgg.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
#include "nodes/makefuncs.h"
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
#include "parser/parse_coerce.h"
|
#include "parser/parse_coerce.h"
|
||||||
#include "parser/parse_expr.h"
|
#include "parser/parse_expr.h"
|
||||||
|
@ -212,7 +213,7 @@ static TupleTableSlot *agg_retrieve_direct(AggState *aggstate);
|
||||||
static void agg_fill_hash_table(AggState *aggstate);
|
static void agg_fill_hash_table(AggState *aggstate);
|
||||||
static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
|
static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
|
||||||
static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
|
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.
|
* Initialize all aggregates for a new group of input values.
|
||||||
|
@ -351,14 +352,12 @@ advance_transition_function(AggState *aggstate,
|
||||||
fcinfo.context = NULL;
|
fcinfo.context = NULL;
|
||||||
fcinfo.resultinfo = NULL;
|
fcinfo.resultinfo = NULL;
|
||||||
fcinfo.isnull = false;
|
fcinfo.isnull = false;
|
||||||
|
|
||||||
fcinfo.flinfo = &peraggstate->transfn;
|
fcinfo.flinfo = &peraggstate->transfn;
|
||||||
fcinfo.nargs = 2;
|
fcinfo.nargs = 2;
|
||||||
fcinfo.arg[0] = pergroupstate->transValue;
|
fcinfo.arg[0] = pergroupstate->transValue;
|
||||||
fcinfo.argnull[0] = pergroupstate->transValueIsNull;
|
fcinfo.argnull[0] = pergroupstate->transValueIsNull;
|
||||||
fcinfo.arg[1] = newVal;
|
fcinfo.arg[1] = newVal;
|
||||||
fcinfo.argnull[1] = isNull;
|
fcinfo.argnull[1] = isNull;
|
||||||
|
|
||||||
newVal = FunctionCallInvoke(&fcinfo);
|
newVal = FunctionCallInvoke(&fcinfo);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1187,7 +1186,21 @@ ExecInitAgg(Agg *node, EState *estate)
|
||||||
AclResult aclresult;
|
AclResult aclresult;
|
||||||
Oid transfn_oid,
|
Oid transfn_oid,
|
||||||
finalfn_oid;
|
finalfn_oid;
|
||||||
|
FuncExpr *transfnexpr,
|
||||||
|
*finalfnexpr;
|
||||||
Datum textInitVal;
|
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;
|
int i;
|
||||||
|
|
||||||
/* Planner should have assigned aggregate to correct level */
|
/* Planner should have assigned aggregate to correct level */
|
||||||
|
@ -1238,6 +1251,166 @@ ExecInitAgg(Agg *node, EState *estate)
|
||||||
&peraggstate->transtypeLen,
|
&peraggstate->transtypeLen,
|
||||||
&peraggstate->transtypeByVal);
|
&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
|
* initval is potentially null, so don't try to access it as a
|
||||||
* struct field. Must do it the hard way with SysCacheGetAttr.
|
* struct field. Must do it the hard way with SysCacheGetAttr.
|
||||||
|
@ -1250,14 +1423,7 @@ ExecInitAgg(Agg *node, EState *estate)
|
||||||
peraggstate->initValue = (Datum) 0;
|
peraggstate->initValue = (Datum) 0;
|
||||||
else
|
else
|
||||||
peraggstate->initValue = GetAggInitVal(textInitVal,
|
peraggstate->initValue = GetAggInitVal(textInitVal,
|
||||||
aggform->aggtranstype);
|
transfn_arg_types[0]);
|
||||||
|
|
||||||
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
|
* If the transfn is strict and the initval is NULL, make sure
|
||||||
|
@ -1469,3 +1635,36 @@ aggregate_dummy(PG_FUNCTION_ARGS)
|
||||||
fcinfo->flinfo->fn_oid);
|
fcinfo->flinfo->fn_oid);
|
||||||
return (Datum) 0; /* keep compiler quiet */
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.47 2003/06/22 22:04:54 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.48 2003/06/24 23:14:43 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -28,23 +28,6 @@
|
||||||
#include "utils/datum.h"
|
#include "utils/datum.h"
|
||||||
#include "utils/lsyscache.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,
|
static Datum ExecHashSubPlan(SubPlanState *node,
|
||||||
ExprContext *econtext,
|
ExprContext *econtext,
|
||||||
bool *isNull);
|
bool *isNull);
|
||||||
|
@ -54,13 +37,6 @@ static Datum ExecScanSubPlan(SubPlanState *node,
|
||||||
static void buildSubPlanHash(SubPlanState *node);
|
static void buildSubPlanHash(SubPlanState *node);
|
||||||
static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
|
static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot);
|
||||||
static bool tupleAllNulls(HeapTuple tuple);
|
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
|
* ExecSubPlan
|
||||||
|
@ -224,6 +200,7 @@ ExecScanSubPlan(SubPlanState *node,
|
||||||
PlanState *planstate = node->planstate;
|
PlanState *planstate = node->planstate;
|
||||||
SubLinkType subLinkType = subplan->subLinkType;
|
SubLinkType subLinkType = subplan->subLinkType;
|
||||||
bool useOr = subplan->useOr;
|
bool useOr = subplan->useOr;
|
||||||
|
bool isExpr = subplan->isExpr;
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
Datum result;
|
Datum result;
|
||||||
|
@ -294,6 +271,11 @@ ExecScanSubPlan(SubPlanState *node,
|
||||||
bool rownull = false;
|
bool rownull = false;
|
||||||
int col = 1;
|
int col = 1;
|
||||||
List *plst;
|
List *plst;
|
||||||
|
int numelems;
|
||||||
|
int elemnum;
|
||||||
|
Datum dvalue;
|
||||||
|
Datum *dvalues = NULL;
|
||||||
|
bool disnull;
|
||||||
|
|
||||||
if (subLinkType == EXISTS_SUBLINK)
|
if (subLinkType == EXISTS_SUBLINK)
|
||||||
{
|
{
|
||||||
|
@ -331,9 +313,6 @@ ExecScanSubPlan(SubPlanState *node,
|
||||||
|
|
||||||
if (subLinkType == ARRAY_SUBLINK)
|
if (subLinkType == ARRAY_SUBLINK)
|
||||||
{
|
{
|
||||||
Datum dvalue;
|
|
||||||
bool disnull;
|
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
/* stash away current value */
|
/* stash away current value */
|
||||||
dvalue = heap_getattr(tup, 1, tdesc, &disnull);
|
dvalue = heap_getattr(tup, 1, tdesc, &disnull);
|
||||||
|
@ -350,10 +329,67 @@ ExecScanSubPlan(SubPlanState *node,
|
||||||
|
|
||||||
found = true;
|
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
|
* For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
|
||||||
* operators for columns of tuple.
|
* operators for columns of tuple.
|
||||||
*/
|
*/
|
||||||
|
col = 1;
|
||||||
plst = subplan->paramIds;
|
plst = subplan->paramIds;
|
||||||
foreach(lst, node->exprs)
|
foreach(lst, node->exprs)
|
||||||
{
|
{
|
||||||
|
@ -368,8 +404,15 @@ ExecScanSubPlan(SubPlanState *node,
|
||||||
*/
|
*/
|
||||||
prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
|
prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
|
||||||
Assert(prmdata->execPlan == NULL);
|
Assert(prmdata->execPlan == NULL);
|
||||||
|
|
||||||
|
if (!isExpr)
|
||||||
prmdata->value = heap_getattr(tup, col, tdesc,
|
prmdata->value = heap_getattr(tup, col, tdesc,
|
||||||
&(prmdata->isnull));
|
&(prmdata->isnull));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prmdata->value = dvalues[elemnum];
|
||||||
|
prmdata->isnull = disnull;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now we can eval the combining operator for this column.
|
* Now we can eval the combining operator for this column.
|
||||||
|
@ -445,6 +488,7 @@ ExecScanSubPlan(SubPlanState *node,
|
||||||
*isNull = rownull;
|
*isNull = rownull;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
|
@ -480,6 +524,7 @@ static void
|
||||||
buildSubPlanHash(SubPlanState *node)
|
buildSubPlanHash(SubPlanState *node)
|
||||||
{
|
{
|
||||||
SubPlan *subplan = (SubPlan *) node->xprstate.expr;
|
SubPlan *subplan = (SubPlan *) node->xprstate.expr;
|
||||||
|
bool isExpr = subplan->isExpr;
|
||||||
PlanState *planstate = node->planstate;
|
PlanState *planstate = node->planstate;
|
||||||
int ncols = length(node->exprs);
|
int ncols = length(node->exprs);
|
||||||
ExprContext *innerecontext = node->innerecontext;
|
ExprContext *innerecontext = node->innerecontext;
|
||||||
|
@ -487,6 +532,7 @@ buildSubPlanHash(SubPlanState *node)
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
int nbuckets;
|
int nbuckets;
|
||||||
TupleTableSlot *slot;
|
TupleTableSlot *slot;
|
||||||
|
TupleTableSlot *arrslot = NULL;
|
||||||
|
|
||||||
Assert(subplan->subLinkType == ANY_SUBLINK);
|
Assert(subplan->subLinkType == ANY_SUBLINK);
|
||||||
Assert(!subplan->useOr);
|
Assert(!subplan->useOr);
|
||||||
|
@ -566,10 +612,84 @@ buildSubPlanHash(SubPlanState *node)
|
||||||
{
|
{
|
||||||
HeapTuple tup = slot->val;
|
HeapTuple tup = slot->val;
|
||||||
TupleDesc tdesc = slot->ttc_tupleDescriptor;
|
TupleDesc tdesc = slot->ttc_tupleDescriptor;
|
||||||
int col = 1;
|
TupleDesc arrtdesc = NULL;
|
||||||
List *plst;
|
List *plst;
|
||||||
bool isnew;
|
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,
|
* Load up the Params representing the raw sub-select outputs,
|
||||||
* then form the projection tuple to store in the hashtable.
|
* then form the projection tuple to store in the hashtable.
|
||||||
|
@ -581,12 +701,27 @@ buildSubPlanHash(SubPlanState *node)
|
||||||
|
|
||||||
prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
|
prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]);
|
||||||
Assert(prmdata->execPlan == NULL);
|
Assert(prmdata->execPlan == NULL);
|
||||||
|
|
||||||
prmdata->value = heap_getattr(tup, col, tdesc,
|
prmdata->value = heap_getattr(tup, col, tdesc,
|
||||||
&(prmdata->isnull));
|
&(prmdata->isnull));
|
||||||
|
|
||||||
col++;
|
col++;
|
||||||
}
|
}
|
||||||
slot = ExecProject(node->projRight, NULL);
|
slot = ExecProject(node->projRight, NULL);
|
||||||
tup = slot->val;
|
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.
|
* If result contains any nulls, store separately or not at all.
|
||||||
|
@ -596,14 +731,21 @@ buildSubPlanHash(SubPlanState *node)
|
||||||
*/
|
*/
|
||||||
if (HeapTupleNoNulls(tup))
|
if (HeapTupleNoNulls(tup))
|
||||||
{
|
{
|
||||||
|
if (!isExpr)
|
||||||
(void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
|
(void) LookupTupleHashEntry(node->hashtable, slot, &isnew);
|
||||||
|
else
|
||||||
|
(void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew);
|
||||||
node->havehashrows = true;
|
node->havehashrows = true;
|
||||||
}
|
}
|
||||||
else if (node->hashnulls)
|
else if (node->hashnulls)
|
||||||
{
|
{
|
||||||
|
if (!isExpr)
|
||||||
(void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
|
(void) LookupTupleHashEntry(node->hashnulls, slot, &isnew);
|
||||||
|
else
|
||||||
|
(void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew);
|
||||||
node->havenullrows = true;
|
node->havenullrows = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset innerecontext after each inner tuple to free any memory
|
* Reset innerecontext after each inner tuple to free any memory
|
||||||
|
@ -619,6 +761,8 @@ buildSubPlanHash(SubPlanState *node)
|
||||||
* have the potential for a double free attempt.
|
* have the potential for a double free attempt.
|
||||||
*/
|
*/
|
||||||
ExecClearTuple(node->projRight->pi_slot);
|
ExecClearTuple(node->projRight->pi_slot);
|
||||||
|
if (arrslot)
|
||||||
|
ExecClearTuple(arrslot);
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
}
|
}
|
||||||
|
@ -1099,101 +1243,3 @@ ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
|
||||||
parent->chgParam = bms_add_member(parent->chgParam, paramid);
|
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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.252 2003/06/06 15:04:02 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.253 2003/06/24 23:14:43 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -728,6 +728,7 @@ _copyAggref(Aggref *from)
|
||||||
COPY_SCALAR_FIELD(agglevelsup);
|
COPY_SCALAR_FIELD(agglevelsup);
|
||||||
COPY_SCALAR_FIELD(aggstar);
|
COPY_SCALAR_FIELD(aggstar);
|
||||||
COPY_SCALAR_FIELD(aggdistinct);
|
COPY_SCALAR_FIELD(aggdistinct);
|
||||||
|
COPY_NODE_FIELD(args);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
@ -826,6 +827,7 @@ _copySubLink(SubLink *from)
|
||||||
|
|
||||||
COPY_SCALAR_FIELD(subLinkType);
|
COPY_SCALAR_FIELD(subLinkType);
|
||||||
COPY_SCALAR_FIELD(useOr);
|
COPY_SCALAR_FIELD(useOr);
|
||||||
|
COPY_SCALAR_FIELD(isExpr);
|
||||||
COPY_NODE_FIELD(lefthand);
|
COPY_NODE_FIELD(lefthand);
|
||||||
COPY_NODE_FIELD(operName);
|
COPY_NODE_FIELD(operName);
|
||||||
COPY_OIDLIST_FIELD(operOids);
|
COPY_OIDLIST_FIELD(operOids);
|
||||||
|
@ -844,6 +846,12 @@ _copySubPlan(SubPlan *from)
|
||||||
|
|
||||||
COPY_SCALAR_FIELD(subLinkType);
|
COPY_SCALAR_FIELD(subLinkType);
|
||||||
COPY_SCALAR_FIELD(useOr);
|
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_NODE_FIELD(exprs);
|
||||||
COPY_INTLIST_FIELD(paramIds);
|
COPY_INTLIST_FIELD(paramIds);
|
||||||
COPY_NODE_FIELD(plan);
|
COPY_NODE_FIELD(plan);
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.195 2003/06/06 15:04:02 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.196 2003/06/24 23:14:43 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -205,6 +205,7 @@ _equalAggref(Aggref *a, Aggref *b)
|
||||||
COMPARE_SCALAR_FIELD(agglevelsup);
|
COMPARE_SCALAR_FIELD(agglevelsup);
|
||||||
COMPARE_SCALAR_FIELD(aggstar);
|
COMPARE_SCALAR_FIELD(aggstar);
|
||||||
COMPARE_SCALAR_FIELD(aggdistinct);
|
COMPARE_SCALAR_FIELD(aggdistinct);
|
||||||
|
COMPARE_NODE_FIELD(args);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -301,6 +302,7 @@ _equalSubLink(SubLink *a, SubLink *b)
|
||||||
{
|
{
|
||||||
COMPARE_SCALAR_FIELD(subLinkType);
|
COMPARE_SCALAR_FIELD(subLinkType);
|
||||||
COMPARE_SCALAR_FIELD(useOr);
|
COMPARE_SCALAR_FIELD(useOr);
|
||||||
|
COMPARE_SCALAR_FIELD(isExpr);
|
||||||
COMPARE_NODE_FIELD(lefthand);
|
COMPARE_NODE_FIELD(lefthand);
|
||||||
COMPARE_NODE_FIELD(operName);
|
COMPARE_NODE_FIELD(operName);
|
||||||
COMPARE_OIDLIST_FIELD(operOids);
|
COMPARE_OIDLIST_FIELD(operOids);
|
||||||
|
@ -314,6 +316,12 @@ _equalSubPlan(SubPlan *a, SubPlan *b)
|
||||||
{
|
{
|
||||||
COMPARE_SCALAR_FIELD(subLinkType);
|
COMPARE_SCALAR_FIELD(subLinkType);
|
||||||
COMPARE_SCALAR_FIELD(useOr);
|
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_NODE_FIELD(exprs);
|
||||||
COMPARE_INTLIST_FIELD(paramIds);
|
COMPARE_INTLIST_FIELD(paramIds);
|
||||||
/* should compare plans, but have to settle for comparing plan IDs */
|
/* should compare plans, but have to settle for comparing plan IDs */
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.208 2003/06/15 22:51:45 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.209 2003/06/24 23:14:43 momjian Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Every node type that can appear in stored rules' parsetrees *must*
|
* Every node type that can appear in stored rules' parsetrees *must*
|
||||||
|
@ -616,6 +616,7 @@ _outAggref(StringInfo str, Aggref *node)
|
||||||
WRITE_UINT_FIELD(agglevelsup);
|
WRITE_UINT_FIELD(agglevelsup);
|
||||||
WRITE_BOOL_FIELD(aggstar);
|
WRITE_BOOL_FIELD(aggstar);
|
||||||
WRITE_BOOL_FIELD(aggdistinct);
|
WRITE_BOOL_FIELD(aggdistinct);
|
||||||
|
WRITE_NODE_FIELD(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -701,6 +702,7 @@ _outSubLink(StringInfo str, SubLink *node)
|
||||||
|
|
||||||
WRITE_ENUM_FIELD(subLinkType, SubLinkType);
|
WRITE_ENUM_FIELD(subLinkType, SubLinkType);
|
||||||
WRITE_BOOL_FIELD(useOr);
|
WRITE_BOOL_FIELD(useOr);
|
||||||
|
WRITE_BOOL_FIELD(isExpr);
|
||||||
WRITE_NODE_FIELD(lefthand);
|
WRITE_NODE_FIELD(lefthand);
|
||||||
WRITE_NODE_FIELD(operName);
|
WRITE_NODE_FIELD(operName);
|
||||||
WRITE_OIDLIST_FIELD(operOids);
|
WRITE_OIDLIST_FIELD(operOids);
|
||||||
|
@ -714,6 +716,12 @@ _outSubPlan(StringInfo str, SubPlan *node)
|
||||||
|
|
||||||
WRITE_ENUM_FIELD(subLinkType, SubLinkType);
|
WRITE_ENUM_FIELD(subLinkType, SubLinkType);
|
||||||
WRITE_BOOL_FIELD(useOr);
|
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_NODE_FIELD(exprs);
|
||||||
WRITE_INTLIST_FIELD(paramIds);
|
WRITE_INTLIST_FIELD(paramIds);
|
||||||
WRITE_NODE_FIELD(plan);
|
WRITE_NODE_FIELD(plan);
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.154 2003/06/06 15:04:02 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.155 2003/06/24 23:14:43 momjian Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Path and Plan nodes do not have any readfuncs support, because we
|
* Path and Plan nodes do not have any readfuncs support, because we
|
||||||
|
@ -416,6 +416,7 @@ _readAggref(void)
|
||||||
READ_UINT_FIELD(agglevelsup);
|
READ_UINT_FIELD(agglevelsup);
|
||||||
READ_BOOL_FIELD(aggstar);
|
READ_BOOL_FIELD(aggstar);
|
||||||
READ_BOOL_FIELD(aggdistinct);
|
READ_BOOL_FIELD(aggdistinct);
|
||||||
|
READ_NODE_FIELD(args);
|
||||||
|
|
||||||
READ_DONE();
|
READ_DONE();
|
||||||
}
|
}
|
||||||
|
@ -545,6 +546,7 @@ _readSubLink(void)
|
||||||
|
|
||||||
READ_ENUM_FIELD(subLinkType, SubLinkType);
|
READ_ENUM_FIELD(subLinkType, SubLinkType);
|
||||||
READ_BOOL_FIELD(useOr);
|
READ_BOOL_FIELD(useOr);
|
||||||
|
READ_BOOL_FIELD(isExpr);
|
||||||
READ_NODE_FIELD(lefthand);
|
READ_NODE_FIELD(lefthand);
|
||||||
READ_NODE_FIELD(operName);
|
READ_NODE_FIELD(operName);
|
||||||
READ_OIDLIST_FIELD(operOids);
|
READ_OIDLIST_FIELD(operOids);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.76 2003/06/06 15:04:02 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.77 2003/06/24 23:14:43 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -83,7 +83,7 @@ typedef struct finalize_primnode_context
|
||||||
|
|
||||||
static List *convert_sublink_opers(List *lefthand, List *operOids,
|
static List *convert_sublink_opers(List *lefthand, List *operOids,
|
||||||
List *targetlist, int rtindex,
|
List *targetlist, int rtindex,
|
||||||
List **righthandIds);
|
bool isExpr, List **righthandIds);
|
||||||
static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
|
static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
|
||||||
static Node *replace_correlation_vars_mutator(Node *node, void *context);
|
static Node *replace_correlation_vars_mutator(Node *node, void *context);
|
||||||
static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
|
static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
|
||||||
|
@ -299,6 +299,12 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
|
||||||
*/
|
*/
|
||||||
node->subLinkType = slink->subLinkType;
|
node->subLinkType = slink->subLinkType;
|
||||||
node->useOr = slink->useOr;
|
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->exprs = NIL;
|
||||||
node->paramIds = NIL;
|
node->paramIds = NIL;
|
||||||
node->useHashTable = false;
|
node->useHashTable = false;
|
||||||
|
@ -374,7 +380,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
|
||||||
exprs = convert_sublink_opers(lefthand,
|
exprs = convert_sublink_opers(lefthand,
|
||||||
slink->operOids,
|
slink->operOids,
|
||||||
plan->targetlist,
|
plan->targetlist,
|
||||||
0,
|
0, node->isExpr,
|
||||||
&node->paramIds);
|
&node->paramIds);
|
||||||
node->setParam = listCopy(node->paramIds);
|
node->setParam = listCopy(node->paramIds);
|
||||||
PlannerInitPlan = lappend(PlannerInitPlan, node);
|
PlannerInitPlan = lappend(PlannerInitPlan, node);
|
||||||
|
@ -457,7 +463,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
|
||||||
node->exprs = convert_sublink_opers(lefthand,
|
node->exprs = convert_sublink_opers(lefthand,
|
||||||
slink->operOids,
|
slink->operOids,
|
||||||
plan->targetlist,
|
plan->targetlist,
|
||||||
0,
|
0, node->isExpr,
|
||||||
&node->paramIds);
|
&node->paramIds);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -499,7 +505,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
|
||||||
static List *
|
static List *
|
||||||
convert_sublink_opers(List *lefthand, List *operOids,
|
convert_sublink_opers(List *lefthand, List *operOids,
|
||||||
List *targetlist, int rtindex,
|
List *targetlist, int rtindex,
|
||||||
List **righthandIds)
|
bool isExpr, List **righthandIds)
|
||||||
{
|
{
|
||||||
List *result = NIL;
|
List *result = NIL;
|
||||||
List *lst;
|
List *lst;
|
||||||
|
@ -554,6 +560,8 @@ convert_sublink_opers(List *lefthand, List *operOids,
|
||||||
* are not expecting to have to resolve unknown Params, so
|
* are not expecting to have to resolve unknown Params, so
|
||||||
* it's okay to pass a null pstate.)
|
* it's okay to pass a null pstate.)
|
||||||
*/
|
*/
|
||||||
|
if (!isExpr)
|
||||||
|
{
|
||||||
result = lappend(result,
|
result = lappend(result,
|
||||||
make_op_expr(NULL,
|
make_op_expr(NULL,
|
||||||
tup,
|
tup,
|
||||||
|
@ -561,6 +569,29 @@ convert_sublink_opers(List *lefthand, List *operOids,
|
||||||
rightop,
|
rightop,
|
||||||
exprType(leftop),
|
exprType(leftop),
|
||||||
te->resdom->restype));
|
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);
|
ReleaseSysCache(tup);
|
||||||
|
|
||||||
|
@ -671,13 +702,17 @@ convert_IN_to_join(Query *parse, SubLink *sublink)
|
||||||
/*
|
/*
|
||||||
* The sublink type must be "= ANY" --- that is, an IN operator.
|
* The sublink type must be "= ANY" --- that is, an IN operator.
|
||||||
* (We require the operator name to be unqualified, which may be
|
* (We require the operator name to be unqualified, which may be
|
||||||
* overly paranoid, or may not be.)
|
* overly paranoid, or may not be.) It must not be an Expression
|
||||||
|
* sublink.
|
||||||
*/
|
*/
|
||||||
if (sublink->subLinkType != ANY_SUBLINK)
|
if (sublink->subLinkType != ANY_SUBLINK)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (length(sublink->operName) != 1 ||
|
if (length(sublink->operName) != 1 ||
|
||||||
strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
|
strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
if (sublink->isExpr)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The sub-select must not refer to any Vars of the parent query.
|
* The sub-select must not refer to any Vars of the parent query.
|
||||||
* (Vars of higher levels should be okay, though.)
|
* (Vars of higher levels should be okay, though.)
|
||||||
|
@ -730,7 +765,7 @@ convert_IN_to_join(Query *parse, SubLink *sublink)
|
||||||
exprs = convert_sublink_opers(sublink->lefthand,
|
exprs = convert_sublink_opers(sublink->lefthand,
|
||||||
sublink->operOids,
|
sublink->operOids,
|
||||||
subselect->targetList,
|
subselect->targetList,
|
||||||
rtindex,
|
rtindex, sublink->isExpr,
|
||||||
&ininfo->sub_targetlist);
|
&ininfo->sub_targetlist);
|
||||||
return (Node *) make_ands_explicit(exprs);
|
return (Node *) make_ands_explicit(exprs);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.139 2003/06/06 15:04:02 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.140 2003/06/24 23:14:43 momjian Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
|
@ -132,6 +132,28 @@ get_rightop(Expr *clause)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* 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
|
* NOT clause functions
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.417 2003/06/17 23:12:36 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.418 2003/06/24 23:14:43 momjian Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
|
@ -5490,6 +5490,7 @@ r_expr: row IN_P select_with_parens
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->subLinkType = ANY_SUBLINK;
|
n->subLinkType = ANY_SUBLINK;
|
||||||
|
n->isExpr = false;
|
||||||
n->lefthand = $1;
|
n->lefthand = $1;
|
||||||
n->operName = makeList1(makeString("="));
|
n->operName = makeList1(makeString("="));
|
||||||
n->subselect = $3;
|
n->subselect = $3;
|
||||||
|
@ -5500,6 +5501,7 @@ r_expr: row IN_P select_with_parens
|
||||||
/* Make an IN node */
|
/* Make an IN node */
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->subLinkType = ANY_SUBLINK;
|
n->subLinkType = ANY_SUBLINK;
|
||||||
|
n->isExpr = false;
|
||||||
n->lefthand = $1;
|
n->lefthand = $1;
|
||||||
n->operName = makeList1(makeString("="));
|
n->operName = makeList1(makeString("="));
|
||||||
n->subselect = $4;
|
n->subselect = $4;
|
||||||
|
@ -5511,6 +5513,7 @@ r_expr: row IN_P select_with_parens
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->subLinkType = $3;
|
n->subLinkType = $3;
|
||||||
|
n->isExpr = false;
|
||||||
n->lefthand = $1;
|
n->lefthand = $1;
|
||||||
n->operName = $2;
|
n->operName = $2;
|
||||||
n->subselect = $4;
|
n->subselect = $4;
|
||||||
|
@ -5521,6 +5524,7 @@ r_expr: row IN_P select_with_parens
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->subLinkType = MULTIEXPR_SUBLINK;
|
n->subLinkType = MULTIEXPR_SUBLINK;
|
||||||
|
n->isExpr = false;
|
||||||
n->lefthand = $1;
|
n->lefthand = $1;
|
||||||
n->operName = $2;
|
n->operName = $2;
|
||||||
n->subselect = $3;
|
n->subselect = $3;
|
||||||
|
@ -5904,6 +5908,7 @@ a_expr: c_expr { $$ = $1; }
|
||||||
{
|
{
|
||||||
SubLink *n = (SubLink *)$3;
|
SubLink *n = (SubLink *)$3;
|
||||||
n->subLinkType = ANY_SUBLINK;
|
n->subLinkType = ANY_SUBLINK;
|
||||||
|
n->isExpr = false;
|
||||||
n->lefthand = makeList1($1);
|
n->lefthand = makeList1($1);
|
||||||
n->operName = makeList1(makeString("="));
|
n->operName = makeList1(makeString("="));
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
|
@ -5931,6 +5936,7 @@ a_expr: c_expr { $$ = $1; }
|
||||||
{
|
{
|
||||||
/* Make an IN node */
|
/* Make an IN node */
|
||||||
SubLink *n = (SubLink *)$4;
|
SubLink *n = (SubLink *)$4;
|
||||||
|
n->isExpr = false;
|
||||||
n->subLinkType = ANY_SUBLINK;
|
n->subLinkType = ANY_SUBLINK;
|
||||||
n->lefthand = makeList1($1);
|
n->lefthand = makeList1($1);
|
||||||
n->operName = makeList1(makeString("="));
|
n->operName = makeList1(makeString("="));
|
||||||
|
@ -5957,11 +5963,38 @@ a_expr: c_expr { $$ = $1; }
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->subLinkType = $3;
|
n->subLinkType = $3;
|
||||||
|
n->isExpr = false;
|
||||||
n->lefthand = makeList1($1);
|
n->lefthand = makeList1($1);
|
||||||
n->operName = $2;
|
n->operName = $2;
|
||||||
n->subselect = $4;
|
n->subselect = $4;
|
||||||
$$ = (Node *)n;
|
$$ = (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
|
| UNIQUE select_with_parens %prec Op
|
||||||
{
|
{
|
||||||
/* Not sure how to get rid of the parentheses
|
/* Not sure how to get rid of the parentheses
|
||||||
|
@ -6538,6 +6571,7 @@ c_expr: columnref { $$ = (Node *) $1; }
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->subLinkType = EXPR_SUBLINK;
|
n->subLinkType = EXPR_SUBLINK;
|
||||||
|
n->isExpr = false;
|
||||||
n->lefthand = NIL;
|
n->lefthand = NIL;
|
||||||
n->operName = NIL;
|
n->operName = NIL;
|
||||||
n->subselect = $1;
|
n->subselect = $1;
|
||||||
|
@ -6547,6 +6581,7 @@ c_expr: columnref { $$ = (Node *) $1; }
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->subLinkType = EXISTS_SUBLINK;
|
n->subLinkType = EXISTS_SUBLINK;
|
||||||
|
n->isExpr = false;
|
||||||
n->lefthand = NIL;
|
n->lefthand = NIL;
|
||||||
n->operName = NIL;
|
n->operName = NIL;
|
||||||
n->subselect = $2;
|
n->subselect = $2;
|
||||||
|
@ -6556,6 +6591,7 @@ c_expr: columnref { $$ = (Node *) $1; }
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
n->subLinkType = ARRAY_SUBLINK;
|
n->subLinkType = ARRAY_SUBLINK;
|
||||||
|
n->isExpr = false;
|
||||||
n->lefthand = NIL;
|
n->lefthand = NIL;
|
||||||
n->operName = NIL;
|
n->operName = NIL;
|
||||||
n->subselect = $2;
|
n->subselect = $2;
|
||||||
|
@ -6730,6 +6766,7 @@ trim_list: a_expr FROM expr_list { $$ = lappend($3, $1); }
|
||||||
in_expr: select_with_parens
|
in_expr: select_with_parens
|
||||||
{
|
{
|
||||||
SubLink *n = makeNode(SubLink);
|
SubLink *n = makeNode(SubLink);
|
||||||
|
n->isExpr = false;
|
||||||
n->subselect = $1;
|
n->subselect = $1;
|
||||||
/* other fields will be filled later */
|
/* other fields will be filled later */
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.97 2003/05/26 00:11:27 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.98 2003/06/24 23:14:45 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -859,7 +859,11 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
|
||||||
/* Get the element type based on the array type, if we have one */
|
/* Get the element type based on the array type, if we have one */
|
||||||
if (OidIsValid(array_typeid))
|
if (OidIsValid(array_typeid))
|
||||||
{
|
{
|
||||||
|
if (array_typeid != ANYARRAYOID)
|
||||||
array_typelem = get_element_type(array_typeid);
|
array_typelem = get_element_type(array_typeid);
|
||||||
|
else
|
||||||
|
array_typelem = ANYELEMENTOID;
|
||||||
|
|
||||||
if (!OidIsValid(array_typelem))
|
if (!OidIsValid(array_typelem))
|
||||||
elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
|
elog(ERROR, "Argument declared ANYARRAY is not an array: %s",
|
||||||
format_type_be(array_typeid));
|
format_type_be(array_typeid));
|
||||||
|
@ -919,7 +923,11 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
|
||||||
{
|
{
|
||||||
if (!OidIsValid(array_typeid))
|
if (!OidIsValid(array_typeid))
|
||||||
{
|
{
|
||||||
|
if (elem_typeid != ANYELEMENTOID)
|
||||||
array_typeid = get_array_type(elem_typeid);
|
array_typeid = get_array_type(elem_typeid);
|
||||||
|
else
|
||||||
|
array_typeid = ANYARRAYOID;
|
||||||
|
|
||||||
if (!OidIsValid(array_typeid))
|
if (!OidIsValid(array_typeid))
|
||||||
elog(ERROR, "Cannot find array type for datatype %s",
|
elog(ERROR, "Cannot find array type for datatype %s",
|
||||||
format_type_be(elem_typeid));
|
format_type_be(elem_typeid));
|
||||||
|
@ -1170,6 +1178,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
|
||||||
if (srctype == targettype)
|
if (srctype == targettype)
|
||||||
return true;
|
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 */
|
/* Else look in pg_cast */
|
||||||
tuple = SearchSysCache(CASTSOURCETARGET,
|
tuple = SearchSysCache(CASTSOURCETARGET,
|
||||||
ObjectIdGetDatum(srctype),
|
ObjectIdGetDatum(srctype),
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.148 2003/04/29 22:13:10 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.149 2003/06/24 23:14:45 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -436,6 +436,7 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||||
sublink->operName = NIL;
|
sublink->operName = NIL;
|
||||||
sublink->operOids = NIL;
|
sublink->operOids = NIL;
|
||||||
sublink->useOr = FALSE;
|
sublink->useOr = FALSE;
|
||||||
|
sublink->isExpr = FALSE;
|
||||||
}
|
}
|
||||||
else if (sublink->subLinkType == EXPR_SUBLINK ||
|
else if (sublink->subLinkType == EXPR_SUBLINK ||
|
||||||
sublink->subLinkType == ARRAY_SUBLINK)
|
sublink->subLinkType == ARRAY_SUBLINK)
|
||||||
|
@ -463,6 +464,7 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||||
sublink->operName = NIL;
|
sublink->operName = NIL;
|
||||||
sublink->operOids = NIL;
|
sublink->operOids = NIL;
|
||||||
sublink->useOr = FALSE;
|
sublink->useOr = FALSE;
|
||||||
|
sublink->isExpr = FALSE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -538,10 +540,30 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||||
* here, because make_subplan() will insert type
|
* here, because make_subplan() will insert type
|
||||||
* coercion calls if needed.
|
* coercion calls if needed.
|
||||||
*/
|
*/
|
||||||
|
if (!sublink->isExpr)
|
||||||
|
{
|
||||||
optup = oper(op,
|
optup = oper(op,
|
||||||
exprType(lexpr),
|
exprType(lexpr),
|
||||||
exprType((Node *) tent->expr),
|
exprType((Node *) tent->expr),
|
||||||
false);
|
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);
|
opform = (Form_pg_operator) GETSTRUCT(optup);
|
||||||
|
|
||||||
if (opform->oprresult != BOOLOID)
|
if (opform->oprresult != BOOLOID)
|
||||||
|
@ -743,7 +765,7 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||||
ArrayExpr *e = (ArrayExpr *) lfirst(element);
|
ArrayExpr *e = (ArrayExpr *) lfirst(element);
|
||||||
|
|
||||||
if (!IsA(e, ArrayExpr))
|
if (!IsA(e, ArrayExpr))
|
||||||
elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions");
|
elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions");
|
||||||
if (ndims == 0)
|
if (ndims == 0)
|
||||||
ndims = e->ndims;
|
ndims = e->ndims;
|
||||||
else if (e->ndims != ndims)
|
else if (e->ndims != ndims)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.149 2003/06/06 15:04:02 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.150 2003/06/24 23:14:45 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -336,6 +336,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
|
||||||
aggref->target = lfirst(fargs);
|
aggref->target = lfirst(fargs);
|
||||||
aggref->aggstar = agg_star;
|
aggref->aggstar = agg_star;
|
||||||
aggref->aggdistinct = agg_distinct;
|
aggref->aggdistinct = agg_distinct;
|
||||||
|
aggref->args = fargs;
|
||||||
|
|
||||||
/* parse_agg.c does additional aggregate-specific processing */
|
/* parse_agg.c does additional aggregate-specific processing */
|
||||||
transformAggregateCall(pstate, aggref);
|
transformAggregateCall(pstate, aggref);
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.64 2003/05/26 00:11:27 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.65 2003/06/24 23:14:45 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -137,6 +137,33 @@ Operator
|
||||||
equality_oper(Oid argtype, bool noError)
|
equality_oper(Oid argtype, bool noError)
|
||||||
{
|
{
|
||||||
Operator optup;
|
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
|
* Look for an "=" operator for the datatype. We require it to be
|
||||||
|
@ -175,6 +202,33 @@ Operator
|
||||||
ordering_oper(Oid argtype, bool noError)
|
ordering_oper(Oid argtype, bool noError)
|
||||||
{
|
{
|
||||||
Operator optup;
|
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*
|
* Find the type's equality operator, and use its lsortop (it *must*
|
||||||
|
@ -220,6 +274,21 @@ equality_oper_funcid(Oid argtype)
|
||||||
return result;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ordering_oper_opid - convenience routine for oprid(ordering_oper())
|
* ordering_oper_opid - convenience routine for oprid(ordering_oper())
|
||||||
*
|
*
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.88 2003/06/11 09:23:55 petere Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.89 2003/06/24 23:14:45 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -427,6 +427,15 @@ aclitemeq(const AclItem *a1, const AclItem *a2)
|
||||||
a1->ai_grantor == a2->ai_grantor;
|
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
|
* acldefault() --- create an ACL describing default access permissions
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Copyright (c) 2003, PostgreSQL Global Development Group
|
* Copyright (c) 2003, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.1 2003/04/08 23:20:02 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.2 2003/06/24 23:14:45 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -18,35 +18,6 @@
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/syscache.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 :
|
* array_push :
|
||||||
* push an element onto either end of a one-dimensional array
|
* push an element onto either end of a one-dimensional array
|
||||||
|
@ -70,6 +41,7 @@ array_push(PG_FUNCTION_ARGS)
|
||||||
Oid arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
|
Oid arg1_typeid = get_fn_expr_argtype(fcinfo, 1);
|
||||||
Oid arg0_elemid;
|
Oid arg0_elemid;
|
||||||
Oid arg1_elemid;
|
Oid arg1_elemid;
|
||||||
|
ArrayMetaState *my_extra;
|
||||||
|
|
||||||
if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
|
if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid)
|
||||||
elog(ERROR, "array_push: cannot determine input data types");
|
elog(ERROR, "array_push: cannot determine input data types");
|
||||||
|
@ -95,12 +67,11 @@ array_push(PG_FUNCTION_ARGS)
|
||||||
PG_RETURN_NULL(); /* keep compiler quiet */
|
PG_RETURN_NULL(); /* keep compiler quiet */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sanity check: do we have a one-dimensional array */
|
if (ARR_NDIM(v) == 1)
|
||||||
if (ARR_NDIM(v) != 1)
|
{
|
||||||
elog(ERROR, "Arrays greater than one-dimension are not supported");
|
|
||||||
|
|
||||||
lb = ARR_LBOUND(v);
|
lb = ARR_LBOUND(v);
|
||||||
dimv = ARR_DIMS(v);
|
dimv = ARR_DIMS(v);
|
||||||
|
|
||||||
if (arg0_elemid != InvalidOid)
|
if (arg0_elemid != InvalidOid)
|
||||||
{
|
{
|
||||||
/* append newelem */
|
/* append newelem */
|
||||||
|
@ -112,11 +83,45 @@ array_push(PG_FUNCTION_ARGS)
|
||||||
/* prepend newelem */
|
/* prepend newelem */
|
||||||
indx = lb[0] - 1;
|
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);
|
get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign);
|
||||||
|
|
||||||
result = array_set(v, 1, &indx, newelem, -1,
|
my_extra->element_type = element_type;
|
||||||
typlen, typbyval, typalign, &isNull);
|
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);
|
PG_RETURN_ARRAYTYPE_P(result);
|
||||||
}
|
}
|
||||||
|
@ -145,13 +150,28 @@ array_cat(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We must have one of the following combinations of inputs:
|
* We must have one of the following combinations of inputs:
|
||||||
* 1) two arrays with ndims1 == ndims2
|
* 1) one empty array, and one non-empty array
|
||||||
* 2) ndims1 == ndims2 - 1
|
* 2) both arrays empty
|
||||||
* 3) ndims1 == ndims2 + 1
|
* 3) two arrays with ndims1 == ndims2
|
||||||
|
* 4) ndims1 == ndims2 - 1
|
||||||
|
* 5) ndims1 == ndims2 + 1
|
||||||
*/
|
*/
|
||||||
ndims1 = ARR_NDIM(v1);
|
ndims1 = ARR_NDIM(v1);
|
||||||
ndims2 = ARR_NDIM(v2);
|
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)
|
if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1)
|
||||||
elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
|
elog(ERROR, "Cannot concatenate incompatible arrays of %d and "
|
||||||
"%d dimensions", ndims1, ndims2);
|
"%d dimensions", ndims1, ndims2);
|
||||||
|
@ -266,147 +286,15 @@ array_cat(PG_FUNCTION_ARGS)
|
||||||
PG_RETURN_ARRAYTYPE_P(result);
|
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
|
* used by text_to_array() in varlena.c
|
||||||
* 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 *
|
ArrayType *
|
||||||
create_singleton_array(Oid element_type, Datum element, int ndims)
|
create_singleton_array(FunctionCallInfo fcinfo,
|
||||||
|
Oid element_type,
|
||||||
|
Datum element,
|
||||||
|
int ndims)
|
||||||
{
|
{
|
||||||
Datum dvalues[1];
|
Datum dvalues[1];
|
||||||
int16 typlen;
|
int16 typlen;
|
||||||
|
@ -415,6 +303,7 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
|
||||||
int dims[MAXDIM];
|
int dims[MAXDIM];
|
||||||
int lbs[MAXDIM];
|
int lbs[MAXDIM];
|
||||||
int i;
|
int i;
|
||||||
|
ArrayMetaState *my_extra;
|
||||||
|
|
||||||
if (element_type == 0)
|
if (element_type == 0)
|
||||||
elog(ERROR, "Invalid array element type: %u", element_type);
|
elog(ERROR, "Invalid array element type: %u", element_type);
|
||||||
|
@ -429,8 +318,36 @@ create_singleton_array(Oid element_type, Datum element, int ndims)
|
||||||
lbs[i] = 1;
|
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);
|
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,
|
return construct_md_array(dvalues, ndims, dims, lbs, element_type,
|
||||||
typlen, typbyval, typalign);
|
typlen, typbyval, typalign);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.89 2003/05/09 23:01:45 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.90 2003/06/24 23:14:45 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -21,8 +21,10 @@
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
#include "parser/parse_coerce.h"
|
#include "parser/parse_coerce.h"
|
||||||
|
#include "parser/parse_oper.h"
|
||||||
#include "utils/array.h"
|
#include "utils/array.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/datum.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
@ -70,16 +72,6 @@
|
||||||
|
|
||||||
#define RETURN_NULL(type) do { *isNull = true; return (type) 0; } while (0)
|
#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 int ArrayCount(char *str, int *dim, char typdelim);
|
||||||
static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
|
static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
|
||||||
FmgrInfo *inputproc, Oid typelem, int32 typmod,
|
FmgrInfo *inputproc, Oid typelem, int32 typmod,
|
||||||
|
@ -93,10 +85,6 @@ static Datum *ReadArrayBinary(StringInfo buf, int nitems,
|
||||||
static void CopyArrayEls(char *p, Datum *values, int nitems,
|
static void CopyArrayEls(char *p, Datum *values, int nitems,
|
||||||
int typlen, bool typbyval, char typalign,
|
int typlen, bool typbyval, char typalign,
|
||||||
bool freedata);
|
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 Datum ArrayCast(char *value, bool byval, int len);
|
||||||
static int ArrayCastAndSet(Datum src,
|
static int ArrayCastAndSet(Datum src,
|
||||||
int typlen, bool typbyval, char typalign,
|
int typlen, bool typbyval, char typalign,
|
||||||
|
@ -119,7 +107,7 @@ static void array_insert_slice(int ndim, int *dim, int *lb,
|
||||||
char *destPtr,
|
char *destPtr,
|
||||||
int *st, int *endp, char *srcPtr,
|
int *st, int *endp, char *srcPtr,
|
||||||
int typlen, bool typbyval, char typalign);
|
int typlen, bool typbyval, char typalign);
|
||||||
|
static int array_cmp(FunctionCallInfo fcinfo);
|
||||||
|
|
||||||
/*---------------------------------------------------------------------
|
/*---------------------------------------------------------------------
|
||||||
* array_in :
|
* array_in :
|
||||||
|
@ -154,13 +142,50 @@ array_in(PG_FUNCTION_ARGS)
|
||||||
dim[MAXDIM],
|
dim[MAXDIM],
|
||||||
lBound[MAXDIM];
|
lBound[MAXDIM];
|
||||||
char typalign;
|
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 info about element type, including its input conversion proc */
|
||||||
system_cache_lookup(element_type, IOFunc_input,
|
get_type_metadata(element_type, IOFunc_input,
|
||||||
&typlen, &typbyval, &typdelim,
|
&typlen, &typbyval, &typdelim,
|
||||||
&typelem, &typinput, &typalign);
|
&typelem, &typinput, &typalign);
|
||||||
fmgr_info(typinput, &inputproc);
|
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 */
|
/* Make a modifiable copy of the input */
|
||||||
/* XXX why are we allocating an extra 2 bytes here? */
|
/* XXX why are we allocating an extra 2 bytes here? */
|
||||||
string_save = (char *) palloc(strlen(string) + 3);
|
string_save = (char *) palloc(strlen(string) + 3);
|
||||||
|
@ -636,13 +661,52 @@ array_out(PG_FUNCTION_ARGS)
|
||||||
indx[MAXDIM];
|
indx[MAXDIM];
|
||||||
int ndim,
|
int ndim,
|
||||||
*dim;
|
*dim;
|
||||||
|
ArrayMetaState *my_extra;
|
||||||
|
|
||||||
element_type = ARR_ELEMTYPE(v);
|
element_type = ARR_ELEMTYPE(v);
|
||||||
system_cache_lookup(element_type, IOFunc_output,
|
|
||||||
|
/*
|
||||||
|
* 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,
|
&typlen, &typbyval, &typdelim,
|
||||||
&typelem, &typoutput, &typalign);
|
&typelem, &typoutput, &typalign);
|
||||||
fmgr_info(typoutput, &outputproc);
|
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);
|
ndim = ARR_NDIM(v);
|
||||||
dim = ARR_DIMS(v);
|
dim = ARR_DIMS(v);
|
||||||
nitems = ArrayGetNItems(ndim, dim);
|
nitems = ArrayGetNItems(ndim, dim);
|
||||||
|
@ -800,6 +864,7 @@ array_recv(PG_FUNCTION_ARGS)
|
||||||
dim[MAXDIM],
|
dim[MAXDIM],
|
||||||
lBound[MAXDIM];
|
lBound[MAXDIM];
|
||||||
char typalign;
|
char typalign;
|
||||||
|
ArrayMetaState *my_extra;
|
||||||
|
|
||||||
/* Get the array header information */
|
/* Get the array header information */
|
||||||
ndim = pq_getmsgint(buf, 4);
|
ndim = pq_getmsgint(buf, 4);
|
||||||
|
@ -831,8 +896,24 @@ array_recv(PG_FUNCTION_ARGS)
|
||||||
PG_RETURN_ARRAYTYPE_P(retval);
|
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 info about element type, including its receive conversion proc */
|
||||||
system_cache_lookup(element_type, IOFunc_receive,
|
get_type_metadata(element_type, IOFunc_receive,
|
||||||
&typlen, &typbyval, &typdelim,
|
&typlen, &typbyval, &typdelim,
|
||||||
&typelem, &typreceive, &typalign);
|
&typelem, &typreceive, &typalign);
|
||||||
if (!OidIsValid(typreceive))
|
if (!OidIsValid(typreceive))
|
||||||
|
@ -840,6 +921,26 @@ array_recv(PG_FUNCTION_ARGS)
|
||||||
format_type_be(element_type));
|
format_type_be(element_type));
|
||||||
fmgr_info(typreceive, &receiveproc);
|
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,
|
dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
|
||||||
typlen, typbyval, typalign,
|
typlen, typbyval, typalign,
|
||||||
&nbytes);
|
&nbytes);
|
||||||
|
@ -976,16 +1077,55 @@ array_send(PG_FUNCTION_ARGS)
|
||||||
int ndim,
|
int ndim,
|
||||||
*dim;
|
*dim;
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
|
ArrayMetaState *my_extra;
|
||||||
|
|
||||||
/* Get information about the element type and the array dimensions */
|
/* Get information about the element type and the array dimensions */
|
||||||
element_type = ARR_ELEMTYPE(v);
|
element_type = ARR_ELEMTYPE(v);
|
||||||
system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
|
|
||||||
|
/*
|
||||||
|
* 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);
|
&typdelim, &typelem, &typsend, &typalign);
|
||||||
if (!OidIsValid(typsend))
|
if (!OidIsValid(typsend))
|
||||||
elog(ERROR, "No binary output function available for type %s",
|
elog(ERROR, "No binary output function available for type %s",
|
||||||
format_type_be(element_type));
|
format_type_be(element_type));
|
||||||
fmgr_info(typsend, &sendproc);
|
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);
|
ndim = ARR_NDIM(v);
|
||||||
dim = ARR_DIMS(v);
|
dim = ARR_DIMS(v);
|
||||||
nitems = ArrayGetNItems(ndim, dim);
|
nitems = ArrayGetNItems(ndim, dim);
|
||||||
|
@ -1476,6 +1616,26 @@ array_set(ArrayType *array,
|
||||||
array = DatumGetArrayTypeP(PointerGetDatum(array));
|
array = DatumGetArrayTypeP(PointerGetDatum(array));
|
||||||
|
|
||||||
ndim = ARR_NDIM(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)
|
if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
|
||||||
elog(ERROR, "Invalid array subscripts");
|
elog(ERROR, "Invalid array subscripts");
|
||||||
|
|
||||||
|
@ -1632,6 +1792,31 @@ array_set_slice(ArrayType *array,
|
||||||
/* note: we assume srcArray contains no toasted elements */
|
/* note: we assume srcArray contains no toasted elements */
|
||||||
|
|
||||||
ndim = ARR_NDIM(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 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)
|
if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
|
||||||
elog(ERROR, "Invalid array subscripts");
|
elog(ERROR, "Invalid array subscripts");
|
||||||
|
|
||||||
|
@ -1811,6 +1996,13 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
|
||||||
Oid typelem;
|
Oid typelem;
|
||||||
Oid proc;
|
Oid proc;
|
||||||
char *s;
|
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 */
|
/* Get input array */
|
||||||
if (fcinfo->nargs < 1)
|
if (fcinfo->nargs < 1)
|
||||||
|
@ -1829,12 +2021,82 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
|
||||||
if (nitems <= 0)
|
if (nitems <= 0)
|
||||||
PG_RETURN_ARRAYTYPE_P(v);
|
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. */
|
/* Lookup source and result types. Unneeded variables are reused. */
|
||||||
system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
|
get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
|
||||||
&typdelim, &typelem, &proc, &inp_typalign);
|
&typdelim, &typelem, &proc, &inp_typalign);
|
||||||
system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
|
|
||||||
|
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);
|
&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 */
|
/* Allocate temporary array for new values */
|
||||||
values = (Datum *) palloc(nitems * sizeof(Datum));
|
values = (Datum *) palloc(nitems * sizeof(Datum));
|
||||||
|
|
||||||
|
@ -2049,8 +2311,6 @@ deconstruct_array(ArrayType *array,
|
||||||
* compares two arrays for equality
|
* compares two arrays for equality
|
||||||
* result :
|
* result :
|
||||||
* returns true if the arrays are equal, false otherwise.
|
* returns true if the arrays are equal, false otherwise.
|
||||||
*
|
|
||||||
* XXX bitwise equality is pretty bogus ...
|
|
||||||
*-----------------------------------------------------------------------------
|
*-----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
Datum
|
Datum
|
||||||
|
@ -2058,12 +2318,118 @@ array_eq(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
|
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
|
||||||
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
|
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;
|
bool result = true;
|
||||||
|
int typlen;
|
||||||
|
bool typbyval;
|
||||||
|
char typdelim;
|
||||||
|
Oid typelem;
|
||||||
|
char typalign;
|
||||||
|
Oid typiofunc;
|
||||||
|
int i;
|
||||||
|
ArrayMetaState *my_extra;
|
||||||
|
FunctionCallInfoData locfcinfo;
|
||||||
|
|
||||||
if (ARR_SIZE(array1) != ARR_SIZE(array2))
|
/* fast path if the arrays do not have the same number of elements */
|
||||||
|
if (nitems1 != nitems2)
|
||||||
result = false;
|
result = false;
|
||||||
else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0)
|
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;
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Avoid leaking memory when handed toasted input. */
|
/* Avoid leaking memory when handed toasted input. */
|
||||||
PG_FREE_IF_COPY(array1, 0);
|
PG_FREE_IF_COPY(array1, 0);
|
||||||
|
@ -2073,53 +2439,190 @@ array_eq(PG_FUNCTION_ARGS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------------------------------------------------------------------
|
||||||
|
* array-array bool operators:
|
||||||
|
* Given two arrays, iterate comparison operators
|
||||||
|
* over the array. Uses logic similar to text comparison
|
||||||
|
* functions, except element-by-element instead of
|
||||||
|
* character-by-character.
|
||||||
|
*----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
array_ne(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
array_lt(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
array_gt(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
array_le(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
array_ge(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Datum
|
||||||
|
btarraycmp(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
PG_RETURN_INT32(array_cmp(fcinfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* array_cmp()
|
||||||
|
* Internal comparison function for arrays.
|
||||||
|
*
|
||||||
|
* Returns -1, 0 or 1
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
array_cmp(FunctionCallInfo fcinfo)
|
||||||
|
{
|
||||||
|
ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0);
|
||||||
|
ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1);
|
||||||
|
FmgrInfo *ac_fmgr_info = fcinfo->flinfo;
|
||||||
|
Datum opresult;
|
||||||
|
int result = 0;
|
||||||
|
Oid element_type = InvalidOid;
|
||||||
|
int typlen;
|
||||||
|
bool typbyval;
|
||||||
|
char 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 |*****************/
|
/******************| 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
|
* Fetch array element at pointer, converted correctly to a Datum
|
||||||
*/
|
*/
|
||||||
|
@ -2423,6 +2926,18 @@ array_type_coerce(PG_FUNCTION_ARGS)
|
||||||
if (tgt_elem_type == InvalidOid)
|
if (tgt_elem_type == InvalidOid)
|
||||||
elog(ERROR, "Target type is not an array");
|
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,
|
if (!find_coercion_pathway(tgt_elem_type, src_elem_type,
|
||||||
COERCION_EXPLICIT, &funcId))
|
COERCION_EXPLICIT, &funcId))
|
||||||
{
|
{
|
||||||
|
@ -2439,10 +2954,16 @@ array_type_coerce(PG_FUNCTION_ARGS)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If it's binary-compatible, return the array unmodified.
|
* If it's binary-compatible, modify the element type in the array header,
|
||||||
|
* but otherwise leave the array as we received it.
|
||||||
*/
|
*/
|
||||||
if (my_extra->coerce_finfo.fn_oid == InvalidOid)
|
if (my_extra->coerce_finfo.fn_oid == InvalidOid)
|
||||||
PG_RETURN_ARRAYTYPE_P(src);
|
{
|
||||||
|
ArrayType *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
|
||||||
|
|
||||||
|
ARR_ELEMTYPE(result) = my_extra->desttype;
|
||||||
|
PG_RETURN_ARRAYTYPE_P(result);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Use array_map to apply the function to each array element.
|
* Use array_map to apply the function to each array element.
|
||||||
|
@ -2454,3 +2975,118 @@ array_type_coerce(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.98 2003/05/15 15:50:19 petere Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.99 2003/06/24 23:14:46 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -19,11 +19,14 @@
|
||||||
#include "mb/pg_wchar.h"
|
#include "mb/pg_wchar.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "access/tuptoaster.h"
|
#include "access/tuptoaster.h"
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
#include "lib/stringinfo.h"
|
#include "lib/stringinfo.h"
|
||||||
#include "libpq/crypt.h"
|
#include "libpq/crypt.h"
|
||||||
#include "libpq/pqformat.h"
|
#include "libpq/pqformat.h"
|
||||||
|
#include "utils/array.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/pg_locale.h"
|
#include "utils/pg_locale.h"
|
||||||
|
#include "utils/lsyscache.h"
|
||||||
|
|
||||||
|
|
||||||
typedef struct varlena unknown;
|
typedef struct varlena unknown;
|
||||||
|
@ -1983,8 +1986,7 @@ split_text(PG_FUNCTION_ARGS)
|
||||||
if (fldnum == 1) /* first field - just return the input
|
if (fldnum == 1) /* first field - just return the input
|
||||||
* string */
|
* string */
|
||||||
PG_RETURN_TEXT_P(inputstring);
|
PG_RETURN_TEXT_P(inputstring);
|
||||||
else
|
else /* otherwise return an empty string */
|
||||||
/* otherwise return an empty string */
|
|
||||||
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
|
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2004,8 +2006,7 @@ split_text(PG_FUNCTION_ARGS)
|
||||||
if (fldnum == 1) /* first field - just return the input
|
if (fldnum == 1) /* first field - just return the input
|
||||||
* string */
|
* string */
|
||||||
PG_RETURN_TEXT_P(inputstring);
|
PG_RETURN_TEXT_P(inputstring);
|
||||||
else
|
else /* otherwise return an empty string */
|
||||||
/* otherwise return an empty string */
|
|
||||||
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
|
PG_RETURN_TEXT_P(PG_STR_GET_TEXT(""));
|
||||||
}
|
}
|
||||||
else if ((start_posn != 0) && (end_posn == 0))
|
else if ((start_posn != 0) && (end_posn == 0))
|
||||||
|
@ -2028,6 +2029,191 @@ split_text(PG_FUNCTION_ARGS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* text_to_array
|
||||||
|
* parse input string
|
||||||
|
* return text array of elements
|
||||||
|
* based on provided field separator
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
text_to_array(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
text *inputstring = PG_GETARG_TEXT_P(0);
|
||||||
|
int inputstring_len = TEXTLEN(inputstring);
|
||||||
|
text *fldsep = PG_GETARG_TEXT_P(1);
|
||||||
|
int fldsep_len = TEXTLEN(fldsep);
|
||||||
|
int fldnum;
|
||||||
|
int start_posn = 0;
|
||||||
|
int end_posn = 0;
|
||||||
|
text *result_text = NULL;
|
||||||
|
ArrayBuildState *astate = NULL;
|
||||||
|
MemoryContext oldcontext = CurrentMemoryContext;
|
||||||
|
|
||||||
|
/* return NULL for empty input string */
|
||||||
|
if (inputstring_len < 1)
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
|
||||||
|
/* empty field separator
|
||||||
|
* return one element, 1D, array using the input string */
|
||||||
|
if (fldsep_len < 1)
|
||||||
|
PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
|
||||||
|
CStringGetDatum(inputstring), 1));
|
||||||
|
|
||||||
|
/* start with end position holding the initial start position */
|
||||||
|
end_posn = 0;
|
||||||
|
for (fldnum=1;;fldnum++) /* field number is 1 based */
|
||||||
|
{
|
||||||
|
Datum dvalue;
|
||||||
|
bool disnull = false;
|
||||||
|
|
||||||
|
start_posn = end_posn;
|
||||||
|
end_posn = text_position(PointerGetDatum(inputstring),
|
||||||
|
PointerGetDatum(fldsep),
|
||||||
|
fldnum);
|
||||||
|
|
||||||
|
if ((start_posn == 0) && (end_posn == 0)) /* fldsep not found */
|
||||||
|
{
|
||||||
|
if (fldnum == 1)
|
||||||
|
{
|
||||||
|
/* first element
|
||||||
|
* return one element, 1D, array using the input string */
|
||||||
|
PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID,
|
||||||
|
CStringGetDatum(inputstring), 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* otherwise create array and exit */
|
||||||
|
PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((start_posn != 0) && (end_posn == 0))
|
||||||
|
{
|
||||||
|
/* last field requested */
|
||||||
|
result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true);
|
||||||
|
}
|
||||||
|
else if ((start_posn == 0) && (end_posn != 0))
|
||||||
|
{
|
||||||
|
/* first field requested */
|
||||||
|
result_text = LEFT(inputstring, fldsep);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* prior to last field requested */
|
||||||
|
result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn - fldsep_len, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stash away current value */
|
||||||
|
dvalue = PointerGetDatum(result_text);
|
||||||
|
astate = accumArrayResult(astate, dvalue,
|
||||||
|
disnull, TEXTOID, oldcontext);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* never reached -- keep compiler quiet */
|
||||||
|
PG_RETURN_NULL();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* array_to_text
|
||||||
|
* concatenate Cstring representation of input array elements
|
||||||
|
* using provided field separator
|
||||||
|
*/
|
||||||
|
Datum
|
||||||
|
array_to_text(PG_FUNCTION_ARGS)
|
||||||
|
{
|
||||||
|
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
|
||||||
|
char *fldsep = PG_TEXTARG_GET_STR(1);
|
||||||
|
int nitems, *dims, ndims;
|
||||||
|
char *p;
|
||||||
|
Oid element_type;
|
||||||
|
int typlen;
|
||||||
|
bool typbyval;
|
||||||
|
char 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
|
#define HEXBASE 16
|
||||||
/*
|
/*
|
||||||
* Convert a int32 to a string containing a base 16 (hex) representation of
|
* Convert a int32 to a string containing a base 16 (hex) representation of
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.96 2003/06/22 22:04:54 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.97 2003/06/24 23:14:46 momjian Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Eventually, the index information should go through here, too.
|
* Eventually, the index information should go through here, too.
|
||||||
|
@ -718,6 +718,40 @@ get_func_rettype(Oid funcid)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
* get_func_retset
|
||||||
* Given procedure id, return the function's proretset flag.
|
* Given procedure id, return the function's proretset flag.
|
||||||
|
@ -1090,6 +1124,56 @@ get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
|
||||||
ReleaseSysCache(tp);
|
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
|
#ifdef NOT_USED
|
||||||
char
|
char
|
||||||
get_typalign(Oid typid)
|
get_typalign(Oid typid)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.68 2003/04/08 23:20:02 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.69 2003/06/24 23:14:46 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1673,3 +1673,29 @@ get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum)
|
||||||
|
|
||||||
return exprType((Node *) nth(argnum, args));
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: pg_amop.h,v 1.50 2003/06/22 22:04:55 tgl Exp $
|
* $Id: pg_amop.h,v 1.51 2003/06/24 23:14:46 momjian Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* the genbki.sh script reads this file and generates .bki
|
* the genbki.sh script reads this file and generates .bki
|
||||||
|
@ -418,6 +418,15 @@ DATA(insert ( 2098 3 f 2334 ));
|
||||||
DATA(insert ( 2098 4 f 2335 ));
|
DATA(insert ( 2098 4 f 2335 ));
|
||||||
DATA(insert ( 2098 5 f 2336 ));
|
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
|
* hash index _ops
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: pg_amproc.h,v 1.38 2003/06/22 22:04:55 tgl Exp $
|
* $Id: pg_amproc.h,v 1.39 2003/06/24 23:14:46 momjian Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* the genbki.sh script reads this file and generates .bki
|
* the genbki.sh script reads this file and generates .bki
|
||||||
|
@ -78,6 +78,7 @@ DATA(insert ( 1993 3 199 ));
|
||||||
|
|
||||||
|
|
||||||
/* btree */
|
/* btree */
|
||||||
|
DATA(insert ( 397 1 382 ));
|
||||||
DATA(insert ( 421 1 357 ));
|
DATA(insert ( 421 1 357 ));
|
||||||
DATA(insert ( 423 1 1596 ));
|
DATA(insert ( 423 1 1596 ));
|
||||||
DATA(insert ( 424 1 1693 ));
|
DATA(insert ( 424 1 1693 ));
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: pg_opclass.h,v 1.51 2003/06/22 22:04:55 tgl Exp $
|
* $Id: pg_opclass.h,v 1.52 2003/06/24 23:14:46 momjian Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* the genbki.sh script reads this file and generates .bki
|
* the genbki.sh script reads this file and generates .bki
|
||||||
|
@ -87,6 +87,8 @@ typedef FormData_pg_opclass *Form_pg_opclass;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
DATA(insert OID = 421 ( 403 abstime_ops PGNSP PGUID 702 t 0 ));
|
DATA(insert OID = 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 = 422 ( 402 bigbox_ops PGNSP PGUID 603 f 0 ));
|
||||||
DATA(insert OID = 423 ( 403 bit_ops PGNSP PGUID 1560 t 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 ));
|
DATA(insert OID = 424 ( 403 bool_ops PGNSP PGUID 16 t 0 ));
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: pg_operator.h,v 1.115 2003/06/22 22:04:55 tgl Exp $
|
* $Id: pg_operator.h,v 1.116 2003/06/24 23:14:46 momjian Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* the genbki.sh script reads this file and generates .bki
|
* the genbki.sh script reads this file and generates .bki
|
||||||
|
@ -116,7 +116,12 @@ DATA(insert OID = 96 ( "=" PGNSP PGUID b t 23 23 16 96 518 97 97 97 521 int
|
||||||
DATA(insert OID = 97 ( "<" PGNSP PGUID b f 23 23 16 521 525 0 0 0 0 int4lt scalarltsel scalarltjoinsel ));
|
DATA(insert OID = 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 = 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 = 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 = 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 = 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 = 375 ( "||" PGNSP PGUID b f 2277 2277 2277 0 0 0 0 0 0 array_cat - - ));
|
||||||
|
@ -425,6 +430,7 @@ DATA(insert OID = 965 ( "^" PGNSP PGUID b f 701 701 701 0 0 0 0 0 0 dpow -
|
||||||
DATA(insert OID = 966 ( "+" PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
|
DATA(insert OID = 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 = 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 = 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 */
|
/* 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 - - ));
|
DATA(insert OID = 969 ( "@@" PGNSP PGUID l f 0 601 600 0 0 0 0 0 0 lseg_center - - ));
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: pg_proc.h,v 1.305 2003/06/24 22:21:23 momjian Exp $
|
* $Id: pg_proc.h,v 1.306 2003/06/24 23:14:47 momjian Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* The script catalog/genbki.sh reads this file and generates .bki
|
* The script catalog/genbki.sh reads this file and generates .bki
|
||||||
|
@ -758,6 +758,8 @@ DATA(insert OID = 359 ( btnamecmp PGNSP PGUID 12 f f t f i 2 23 "19 19" btn
|
||||||
DESCR("btree less-equal-greater");
|
DESCR("btree less-equal-greater");
|
||||||
DATA(insert OID = 360 ( bttextcmp PGNSP PGUID 12 f f t f i 2 23 "25 25" bttextcmp - _null_ ));
|
DATA(insert OID = 360 ( bttextcmp PGNSP PGUID 12 f f t f i 2 23 "25 25" bttextcmp - _null_ ));
|
||||||
DESCR("btree less-equal-greater");
|
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_ ));
|
DATA(insert OID = 361 ( lseg_distance PGNSP PGUID 12 f f t f i 2 701 "601 601" lseg_distance - _null_ ));
|
||||||
DESCR("distance between");
|
DESCR("distance between");
|
||||||
|
@ -988,14 +990,23 @@ DESCR("greater-than");
|
||||||
DATA(insert OID = 743 ( text_ge PGNSP PGUID 12 f f t f i 2 16 "25 25" text_ge - _null_ ));
|
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");
|
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_ ));
|
DATA(insert OID = 745 ( current_user PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ ));
|
||||||
DESCR("current user name");
|
DESCR("current user name");
|
||||||
DATA(insert OID = 746 ( session_user PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
|
DATA(insert OID = 746 ( session_user PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ ));
|
||||||
DESCR("session user name");
|
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_ ));
|
DATA(insert OID = 747 ( array_dims PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ ));
|
||||||
DESCR("array dimensions");
|
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_ ));
|
DATA(insert OID = 750 ( array_in PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23" array_in - _null_ ));
|
||||||
|
@ -1006,22 +1017,18 @@ DATA(insert OID = 2091 ( array_lower PGNSP PGUID 12 f f t f i 2 23 "2277 23"
|
||||||
DESCR("array lower dimension");
|
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_ ));
|
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");
|
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_ ));
|
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");
|
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_ ));
|
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");
|
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_ ));
|
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");
|
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_ ));
|
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");
|
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_ ));
|
DATA(insert OID = 760 ( smgrin PGNSP PGUID 12 f f t f s 1 210 "2275" smgrin - _null_ ));
|
||||||
DESCR("I/O");
|
DESCR("I/O");
|
||||||
|
@ -1322,6 +1329,8 @@ DATA(insert OID = 1036 ( aclremove PGNSP PGUID 12 f f t f s 2 1034 "1034 10
|
||||||
DESCR("remove ACL item");
|
DESCR("remove ACL item");
|
||||||
DATA(insert OID = 1037 ( aclcontains PGNSP PGUID 12 f f t f s 2 16 "1034 1033" aclcontains - _null_ ));
|
DATA(insert OID = 1037 ( aclcontains PGNSP PGUID 12 f f t f s 2 16 "1034 1033" aclcontains - _null_ ));
|
||||||
DESCR("does ACL contain item?");
|
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_ ));
|
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");
|
DESCR("make ACL item");
|
||||||
DATA(insert OID = 1038 ( seteval PGNSP PGUID 12 f f t t v 1 23 "26" seteval - _null_ ));
|
DATA(insert OID = 1038 ( seteval PGNSP PGUID 12 f f t t v 1 23 "26" seteval - _null_ ));
|
||||||
|
|
|
@ -11,13 +11,14 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: fmgr.h,v 1.27 2003/04/08 23:20:04 tgl Exp $
|
* $Id: fmgr.h,v 1.28 2003/06/24 23:14:46 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#ifndef FMGR_H
|
#ifndef FMGR_H
|
||||||
#define FMGR_H
|
#define FMGR_H
|
||||||
|
|
||||||
|
#include "nodes/nodes.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All functions that can be called directly by fmgr must have this signature.
|
* All functions that can be called directly by fmgr must have this signature.
|
||||||
|
@ -372,7 +373,6 @@ extern Datum OidFunctionCall9(Oid functionId, Datum arg1, Datum arg2,
|
||||||
Datum arg6, Datum arg7, Datum arg8,
|
Datum arg6, Datum arg7, Datum arg8,
|
||||||
Datum arg9);
|
Datum arg9);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Routines in fmgr.c
|
* Routines in fmgr.c
|
||||||
*/
|
*/
|
||||||
|
@ -380,6 +380,7 @@ extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname);
|
||||||
extern Oid fmgr_internal_function(const char *proname);
|
extern Oid fmgr_internal_function(const char *proname);
|
||||||
extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
|
extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo);
|
||||||
extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
|
extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum);
|
||||||
|
extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Routines in dfmgr.c
|
* Routines in dfmgr.c
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: primnodes.h,v 1.83 2003/06/06 15:04:03 tgl Exp $
|
* $Id: primnodes.h,v 1.84 2003/06/24 23:14:48 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -226,6 +226,7 @@ typedef struct Aggref
|
||||||
Index agglevelsup; /* > 0 if agg belongs to outer query */
|
Index agglevelsup; /* > 0 if agg belongs to outer query */
|
||||||
bool aggstar; /* TRUE if argument was really '*' */
|
bool aggstar; /* TRUE if argument was really '*' */
|
||||||
bool aggdistinct; /* TRUE if it's agg(DISTINCT ...) */
|
bool aggdistinct; /* TRUE if it's agg(DISTINCT ...) */
|
||||||
|
List *args; /* arguments to the aggregate */
|
||||||
} Aggref;
|
} Aggref;
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
|
@ -358,15 +359,19 @@ typedef struct BoolExpr
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* SubLink
|
* SubLink
|
||||||
*
|
*
|
||||||
* A SubLink represents a subselect appearing in an expression, and in some
|
* A SubLink represents a subselect, or an expression, appearing in an
|
||||||
* cases also the combining operator(s) just above it. The subLinkType
|
* expression, and in some cases also the combining operator(s) just above
|
||||||
* indicates the form of the expression represented:
|
* it. The subLinkType indicates the form of the expression represented:
|
||||||
* EXISTS_SUBLINK EXISTS(SELECT ...)
|
* EXISTS_SUBLINK EXISTS(SELECT ...)
|
||||||
* ALL_SUBLINK (lefthand) op ALL (SELECT ...)
|
* ALL_SUBLINK (lefthand) op ALL (SELECT ...)
|
||||||
* ANY_SUBLINK (lefthand) op ANY (SELECT ...)
|
* ANY_SUBLINK (lefthand) op ANY (SELECT ...)
|
||||||
* MULTIEXPR_SUBLINK (lefthand) op (SELECT ...)
|
* MULTIEXPR_SUBLINK (lefthand) op (SELECT ...)
|
||||||
* EXPR_SUBLINK (SELECT with single targetlist item ...)
|
* EXPR_SUBLINK (SELECT with single targetlist item ...)
|
||||||
* ARRAY_SUBLINK ARRAY(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
|
* 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
|
* 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
|
* a list with more than one entry; if the subselect has just one target
|
||||||
|
@ -415,6 +420,8 @@ typedef struct SubLink
|
||||||
SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
|
SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
|
||||||
bool useOr; /* TRUE to combine column results with
|
bool useOr; /* TRUE to combine column results with
|
||||||
* "OR" not "AND" */
|
* "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
|
List *lefthand; /* list of outer-query expressions on the
|
||||||
* left */
|
* left */
|
||||||
List *operName; /* originally specified operator name */
|
List *operName; /* originally specified operator name */
|
||||||
|
@ -456,6 +463,15 @@ typedef struct SubPlan
|
||||||
SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
|
SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */
|
||||||
bool useOr; /* TRUE to combine column results with
|
bool useOr; /* TRUE to combine column results with
|
||||||
* "OR" not "AND" */
|
* "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: */
|
/* The combining operators, transformed to executable expressions: */
|
||||||
List *exprs; /* list of OpExpr expression trees */
|
List *exprs; /* list of OpExpr expression trees */
|
||||||
List *paramIds; /* IDs of Params embedded in the above */
|
List *paramIds; /* IDs of Params embedded in the above */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: clauses.h,v 1.63 2003/05/28 16:04:02 tgl Exp $
|
* $Id: clauses.h,v 1.64 2003/06/24 23:14:49 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -28,6 +28,9 @@ extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset,
|
||||||
extern Node *get_leftop(Expr *clause);
|
extern Node *get_leftop(Expr *clause);
|
||||||
extern Node *get_rightop(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 bool not_clause(Node *clause);
|
||||||
extern Expr *make_notclause(Expr *notclause);
|
extern Expr *make_notclause(Expr *notclause);
|
||||||
extern Expr *get_notclausearg(Expr *notclause);
|
extern Expr *get_notclausearg(Expr *notclause);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: parse_oper.h,v 1.25 2003/04/29 22:13:11 tgl Exp $
|
* $Id: parse_oper.h,v 1.26 2003/06/24 23:14:49 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -44,6 +44,7 @@ extern Operator ordering_oper(Oid argtype, bool noError);
|
||||||
/* Convenience routines for common calls on the above */
|
/* Convenience routines for common calls on the above */
|
||||||
extern Oid compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
|
extern Oid compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
|
||||||
extern Oid equality_oper_funcid(Oid argtype);
|
extern Oid equality_oper_funcid(Oid argtype);
|
||||||
|
extern Oid ordering_oper_funcid(Oid argtype);
|
||||||
extern Oid ordering_oper_opid(Oid argtype);
|
extern Oid ordering_oper_opid(Oid argtype);
|
||||||
|
|
||||||
/* Extract operator OID or underlying-function OID from an Operator tuple */
|
/* Extract operator OID or underlying-function OID from an Operator tuple */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: acl.h,v 1.52 2003/06/11 09:23:55 petere Exp $
|
* $Id: acl.h,v 1.53 2003/06/24 23:14:49 momjian Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* For backward-compatibility purposes we have to allow there
|
* For backward-compatibility purposes we have to allow there
|
||||||
|
@ -192,6 +192,7 @@ extern Datum aclinsert(PG_FUNCTION_ARGS);
|
||||||
extern Datum aclremove(PG_FUNCTION_ARGS);
|
extern Datum aclremove(PG_FUNCTION_ARGS);
|
||||||
extern Datum aclcontains(PG_FUNCTION_ARGS);
|
extern Datum aclcontains(PG_FUNCTION_ARGS);
|
||||||
extern Datum makeaclitem(PG_FUNCTION_ARGS);
|
extern Datum makeaclitem(PG_FUNCTION_ARGS);
|
||||||
|
extern Datum aclitem_eq(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* prototypes for functions in aclchk.c
|
* prototypes for functions in aclchk.c
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: array.h,v 1.38 2003/05/08 22:19:57 tgl Exp $
|
* $Id: array.h,v 1.39 2003/06/24 23:14:49 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -32,6 +32,37 @@ typedef struct
|
||||||
Oid elemtype; /* element type OID */
|
Oid elemtype; /* element type OID */
|
||||||
} ArrayType;
|
} 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
|
* fmgr macros for array objects
|
||||||
*/
|
*/
|
||||||
|
@ -86,11 +117,15 @@ extern Datum array_recv(PG_FUNCTION_ARGS);
|
||||||
extern Datum array_send(PG_FUNCTION_ARGS);
|
extern Datum array_send(PG_FUNCTION_ARGS);
|
||||||
extern Datum array_length_coerce(PG_FUNCTION_ARGS);
|
extern Datum array_length_coerce(PG_FUNCTION_ARGS);
|
||||||
extern Datum array_eq(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_dims(PG_FUNCTION_ARGS);
|
||||||
extern Datum array_lower(PG_FUNCTION_ARGS);
|
extern Datum array_lower(PG_FUNCTION_ARGS);
|
||||||
extern Datum array_upper(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_type_coerce(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
|
extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
|
||||||
|
@ -124,7 +159,14 @@ extern void deconstruct_array(ArrayType *array,
|
||||||
Oid elmtype,
|
Oid elmtype,
|
||||||
int elmlen, bool elmbyval, char elmalign,
|
int elmlen, bool elmbyval, char elmalign,
|
||||||
Datum **elemsp, int *nelemsp);
|
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
|
* prototypes for functions defined in arrayutils.c
|
||||||
|
@ -141,12 +183,11 @@ extern int mda_next_tuple(int n, int *curr, int *span);
|
||||||
/*
|
/*
|
||||||
* prototypes for functions defined in array_userfuncs.c
|
* 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_push(PG_FUNCTION_ARGS);
|
||||||
extern Datum array_accum(PG_FUNCTION_ARGS);
|
|
||||||
extern Datum array_cat(PG_FUNCTION_ARGS);
|
extern Datum array_cat(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
extern ArrayType *create_singleton_array(Oid element_type,
|
extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
|
||||||
|
Oid element_type,
|
||||||
Datum element,
|
Datum element,
|
||||||
int ndims);
|
int ndims);
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: builtins.h,v 1.220 2003/06/24 22:21:23 momjian Exp $
|
* $Id: builtins.h,v 1.221 2003/06/24 23:14:49 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -530,6 +530,8 @@ extern bool SplitIdentifierString(char *rawstring, char separator,
|
||||||
List **namelist);
|
List **namelist);
|
||||||
extern Datum replace_text(PG_FUNCTION_ARGS);
|
extern Datum replace_text(PG_FUNCTION_ARGS);
|
||||||
extern Datum split_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_hex32(PG_FUNCTION_ARGS);
|
||||||
extern Datum to_hex64(PG_FUNCTION_ARGS);
|
extern Datum to_hex64(PG_FUNCTION_ARGS);
|
||||||
extern Datum md5_text(PG_FUNCTION_ARGS);
|
extern Datum md5_text(PG_FUNCTION_ARGS);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: lsyscache.h,v 1.71 2003/06/22 22:04:55 tgl Exp $
|
* $Id: lsyscache.h,v 1.72 2003/06/24 23:14:49 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -15,6 +15,15 @@
|
||||||
|
|
||||||
#include "access/htup.h"
|
#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_in_opclass(Oid opno, Oid opclass);
|
||||||
extern bool op_requires_recheck(Oid opno, Oid opclass);
|
extern bool op_requires_recheck(Oid opno, Oid opclass);
|
||||||
extern Oid get_opclass_member(Oid opclass, int16 strategy);
|
extern Oid get_opclass_member(Oid opclass, int16 strategy);
|
||||||
|
@ -41,6 +50,7 @@ extern RegProcedure get_oprrest(Oid opno);
|
||||||
extern RegProcedure get_oprjoin(Oid opno);
|
extern RegProcedure get_oprjoin(Oid opno);
|
||||||
extern char *get_func_name(Oid funcid);
|
extern char *get_func_name(Oid funcid);
|
||||||
extern Oid get_func_rettype(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 get_func_retset(Oid funcid);
|
||||||
extern bool func_strict(Oid funcid);
|
extern bool func_strict(Oid funcid);
|
||||||
extern char func_volatile(Oid funcid);
|
extern char func_volatile(Oid funcid);
|
||||||
|
@ -56,6 +66,14 @@ extern bool get_typbyval(Oid typid);
|
||||||
extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
|
extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
|
||||||
extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
|
extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval,
|
||||||
char *typalign);
|
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 char get_typstorage(Oid typid);
|
||||||
extern int32 get_typtypmod(Oid typid);
|
extern int32 get_typtypmod(Oid typid);
|
||||||
extern Node *get_typdefault(Oid typid);
|
extern Node *get_typdefault(Oid typid);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.236 2003/06/20 13:36:34 meskes Exp $ */
|
/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.237 2003/06/24 23:14:49 momjian Exp $ */
|
||||||
|
|
||||||
/* Copyright comment */
|
/* Copyright comment */
|
||||||
%{
|
%{
|
||||||
|
@ -4595,7 +4595,7 @@ type_declaration: S_TYPEDEF
|
||||||
$3.type_enum != ECPGt_char &&
|
$3.type_enum != ECPGt_char &&
|
||||||
$3.type_enum != ECPGt_unsigned_char &&
|
$3.type_enum != ECPGt_unsigned_char &&
|
||||||
atoi(this->type->type_index) >= 0)
|
atoi(this->type->type_index) >= 0)
|
||||||
mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
|
mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
|
||||||
|
|
||||||
types = this;
|
types = this;
|
||||||
}
|
}
|
||||||
|
@ -5415,7 +5415,7 @@ ECPGTypedef: TYPE_P
|
||||||
$5.type_enum != ECPGt_char &&
|
$5.type_enum != ECPGt_char &&
|
||||||
$5.type_enum != ECPGt_unsigned_char &&
|
$5.type_enum != ECPGt_unsigned_char &&
|
||||||
atoi(this->type->type_index) >= 0)
|
atoi(this->type->type_index) >= 0)
|
||||||
mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
|
mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
|
||||||
|
|
||||||
types = this;
|
types = this;
|
||||||
}
|
}
|
||||||
|
@ -5482,7 +5482,7 @@ ECPGVar: SQL_VAR
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (atoi(length) >= 0)
|
if (atoi(length) >= 0)
|
||||||
mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types");
|
mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types");
|
||||||
|
|
||||||
if (atoi(dimension) < 0)
|
if (atoi(dimension) < 0)
|
||||||
type = ECPGmake_simple_type($5.type_enum, make_str("1"));
|
type = ECPGmake_simple_type($5.type_enum, make_str("1"));
|
||||||
|
|
|
@ -504,7 +504,7 @@ ECPGfree_type(struct ECPGtype * type)
|
||||||
switch (type->u.element->type)
|
switch (type->u.element->type)
|
||||||
{
|
{
|
||||||
case ECPGt_array:
|
case ECPGt_array:
|
||||||
yyerror("internal error, found multi-dimensional array\n");
|
yyerror("internal error, found multidimensional array\n");
|
||||||
break;
|
break;
|
||||||
case ECPGt_struct:
|
case ECPGt_struct:
|
||||||
case ECPGt_union:
|
case ECPGt_union:
|
||||||
|
|
|
@ -436,7 +436,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
|
||||||
if (atoi(type_index) >= 0)
|
if (atoi(type_index) >= 0)
|
||||||
{
|
{
|
||||||
if (atoi(*length) >= 0)
|
if (atoi(*length) >= 0)
|
||||||
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
|
mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
|
||||||
|
|
||||||
*length = type_index;
|
*length = type_index;
|
||||||
}
|
}
|
||||||
|
@ -444,7 +444,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
|
||||||
if (atoi(type_dimension) >= 0)
|
if (atoi(type_dimension) >= 0)
|
||||||
{
|
{
|
||||||
if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
|
if (atoi(*dimension) >= 0 && atoi(*length) >= 0)
|
||||||
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
|
mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
|
||||||
|
|
||||||
if (atoi(*dimension) >= 0)
|
if (atoi(*dimension) >= 0)
|
||||||
*length = *dimension;
|
*length = *dimension;
|
||||||
|
@ -463,10 +463,10 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
|
||||||
mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
|
mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type");
|
||||||
|
|
||||||
if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
|
if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0))
|
||||||
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
|
mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
|
||||||
|
|
||||||
if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
|
if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len)
|
||||||
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support");
|
mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support");
|
||||||
|
|
||||||
switch (type_enum)
|
switch (type_enum)
|
||||||
{
|
{
|
||||||
|
@ -480,7 +480,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
|
||||||
}
|
}
|
||||||
|
|
||||||
if (atoi(*length) >= 0)
|
if (atoi(*length) >= 0)
|
||||||
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures");
|
mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ECPGt_varchar:
|
case ECPGt_varchar:
|
||||||
|
@ -525,7 +525,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty
|
||||||
}
|
}
|
||||||
|
|
||||||
if (atoi(*length) >= 0)
|
if (atoi(*length) >= 0)
|
||||||
mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types");
|
mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,19 +178,13 @@ SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
-- functions
|
-- functions
|
||||||
SELECT singleton_array(42) AS "{42}";
|
SELECT array_append(array[42], 6) AS "{42,6}";
|
||||||
{42}
|
|
||||||
------
|
|
||||||
{42}
|
|
||||||
(1 row)
|
|
||||||
|
|
||||||
SELECT array_append(singleton_array(42), 6) AS "{42,6}";
|
|
||||||
{42,6}
|
{42,6}
|
||||||
--------
|
--------
|
||||||
{42,6}
|
{42,6}
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
SELECT array_prepend(6, singleton_array(42)) AS "{6,42}";
|
SELECT array_prepend(6, array[42]) AS "{6,42}";
|
||||||
{6,42}
|
{6,42}
|
||||||
--------
|
--------
|
||||||
{6,42}
|
{6,42}
|
||||||
|
@ -214,24 +208,6 @@ SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}";
|
||||||
{{3,4},{5,6},{1,2}}
|
{{3,4},{5,6},{1,2}}
|
||||||
(1 row)
|
(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
|
-- operators
|
||||||
SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
|
SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
|
||||||
a
|
a
|
||||||
|
|
|
@ -130,15 +130,11 @@ SELECT ARRAY[ARRAY['hello'],ARRAY['world']];
|
||||||
SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
|
SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY";
|
||||||
|
|
||||||
-- functions
|
-- functions
|
||||||
SELECT singleton_array(42) AS "{42}";
|
SELECT array_append(array[42], 6) AS "{42,6}";
|
||||||
SELECT array_append(singleton_array(42), 6) AS "{42,6}";
|
SELECT array_prepend(6, array[42]) AS "{6,42}";
|
||||||
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]) 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[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_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
|
-- operators
|
||||||
SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
|
SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];
|
||||||
|
|
Loading…
Reference in New Issue