Re: array support patch phase 1 patch - Mailing list pgsql-patches
From | Bruce Momjian |
---|---|
Subject | Re: array support patch phase 1 patch |
Date | |
Msg-id | 200306120746.h5C7krU02802@candle.pha.pa.us Whole thread Raw |
In response to | Re: array support patch phase 1 patch (Joe Conway <mail@joeconway.com>) |
List | pgsql-patches |
Your patch has been added to the PostgreSQL unapplied patches list at: http://momjian.postgresql.org/cgi-bin/pgpatches I will try to apply it within the next 48 hours. --------------------------------------------------------------------------- Joe Conway wrote: > Peter Eisentraut wrote: > [...much snipping follows...] > > No, the analogues would be "unidimensional", "bidimensional", and > > "many-dimensional". There difference is that you close up prefixes (which > > are not words by themselves), but you hyphenate compounds of independent > > words. > > > >>>The function array_subscript() should be removed from code and > >>>The function array_assign() should be removed from code and documentation. > >>>The function singleton_array() should be removed from code and > >> > >>>The function array_append() should be documented as being equivalent to > >>>'array || element' and specifically intended for user-defined aggregate > >>>functions. > >> > >>That's fine. I would add similar descriptions for array_prepend() and > >>array_cat(). > > > > Sounds good. My main concern is that people will have a clear view of > > what's standard and recommended for which situation, so that support will > > be easier and users won't be confronted with a long list of equivalent, > > undifferentiated options. > > > > The attached patch addresses Peter's concerns, subject to our agreements > above. I.e, the changes are: > > 1) All instances of "multi-dimensional array" and "multiple dimension > array" throughout the source tree have been changed to "multidimensional > array". Even if everyone doesn't agree this is correct, it is at least > consistent throughout, so if we later decide to change it back, it will > be easier ;-) > > 2) The functions singleton_array(), array_assign(), and > array_subscript() have been removed along with all references thereof. > > 3) The documentation emphasizes use of the concatenation operator '||' > as preferred over the functions array_append(), array_prepend(), and > array_cat(). A mention of direct use of the functions in user-defined > aggregates is also included. > > [...more snipage from a different part of the thread...] > Tom Lane wrote: > > BTW, it might be better for array_cmp to insist that the array element > > type have a default btree opclass, and use the comparison proc from > > that opclass in place of equality_oper() and ordering_oper(). This'd > > be faster (only one call per comparison) as well as more semantically > > pure. > > I stopped short of this change because there doesn't seem to be a way to > get the btree opclass tuple given just the opcintype without scanning > pg_opclass. Am I missing something? Or should I just not worry about it > and scan pg_opclass? > > > I've got some other problems with this patch (I still don't understand > > what the hacking on aggregates is supposed to accomplish, for example) > > but the array comparison stuff seems relatively straightforward. > > As I said in an earlier message, I believe that I have implemented this > correctly. Is there a specific corner case not covered? I'd really like > to see some form of polymorphic function support for aggregates -- > please let me know what I can do to make that happen. > > Joe > > p.s. I added Bruce to the thread because he asked me about the status of > the patch, and because the lists have been so slow the last few days. > Index: doc/src/sgml/array.sgml > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v > retrieving revision 1.25 > diff -c -r1.25 array.sgml > *** doc/src/sgml/array.sgml 13 Mar 2003 01:30:26 -0000 1.25 > --- doc/src/sgml/array.sgml 9 Jun 2003 02:13:32 -0000 > *************** > *** 60,73 **** > </programlisting> > </para> > > <note> > <para> > ! A limitation of the present array implementation is that individual > ! elements of an array cannot be SQL null values. The entire array can be set > ! to null, but you can't have an array with some elements null and some > ! not. Fixing this is on the to-do list. > </para> > </note> > </sect2> > > <sect2> > --- 60,133 ---- > </programlisting> > </para> > > + <para> > + A limitation of the present array implementation is that individual > + elements of an array cannot be SQL null values. The entire array can be set > + to null, but you can't have an array with some elements null and some > + not. > + </para> > + <para> > + This can lead to surprising results. For example, the result of the > + previous two inserts looks like this: > + <programlisting> > + SELECT * FROM sal_emp; > + name | pay_by_quarter | schedule > + -------+---------------------------+-------------------- > + Bill | {10000,10000,10000,10000} | {{meeting},{""}} > + Carol | {20000,25000,25000,25000} | {{talk},{meeting}} > + (2 rows) > + </programlisting> > + Because the <literal>[2][2]</literal> element of > + <structfield>schedule</structfield> is missing in each of the > + <command>INSERT</command> statements, the <literal>[1][2]</literal> > + element is discarded. > + </para> > + > + <note> > + <para> > + Fixing this is on the to-do list. > + </para> > + </note> > + > + <para> > + The <command>ARRAY</command> expression syntax may also be used: > + <programlisting> > + INSERT INTO sal_emp > + VALUES ('Bill', > + ARRAY[10000, 10000, 10000, 10000], > + ARRAY[['meeting', 'lunch'], ['','']]); > + > + INSERT INTO sal_emp > + VALUES ('Carol', > + ARRAY[20000, 25000, 25000, 25000], > + ARRAY[['talk', 'consult'], ['meeting', '']]); > + SELECT * FROM sal_emp; > + name | pay_by_quarter | schedule > + -------+---------------------------+------------------------------- > + Bill | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}} > + Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}} > + (2 rows) > + </programlisting> > + Note that with this syntax, multidimensional arrays must have matching > + extents for each dimension. This eliminates the missing-array-elements > + problem above. For example: > + <programlisting> > + INSERT INTO sal_emp > + VALUES ('Carol', > + ARRAY[20000, 25000, 25000, 25000], > + ARRAY[['talk', 'consult'], ['meeting']]); > + ERROR: Multidimensional arrays must have array expressions with matching dimensions > + </programlisting> > + Also notice that string literals are single quoted instead of double quoted. > + </para> > + > <note> > <para> > ! The examples in the rest of this section are based on the > ! <command>ARRAY</command> expression syntax <command>INSERT</command>s. > </para> > </note> > + > </sect2> > > <sect2> > *************** > *** 132,142 **** > </programlisting> > > with the same result. An array subscripting operation is always taken to > ! represent an array slice if any of the subscripts are written in the > ! form > <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>. > A lower bound of 1 is assumed for any subscript where only one value > ! is specified. > </para> > > <para> > --- 192,221 ---- > </programlisting> > > with the same result. An array subscripting operation is always taken to > ! represent an array slice if any of the subscripts are written in the form > <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>. > A lower bound of 1 is assumed for any subscript where only one value > ! is specified; another example follows: > ! <programlisting> > ! SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill'; > ! schedule > ! --------------------------- > ! {{meeting,lunch},{"",""}} > ! (1 row) > ! </programlisting> > ! </para> > ! > ! <para> > ! Additionally, we can also access a single arbitrary array element of > ! a one-dimensional array with the <function>array_subscript</function> > ! function: > ! <programlisting> > ! SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill'; > ! array_subscript > ! ----------------- > ! 10000 > ! (1 row) > ! </programlisting> > </para> > > <para> > *************** > *** 147,153 **** > WHERE name = 'Carol'; > </programlisting> > > ! or updated at a single element: > > <programlisting> > UPDATE sal_emp SET pay_by_quarter[4] = 15000 > --- 226,248 ---- > WHERE name = 'Carol'; > </programlisting> > > ! or using the <command>ARRAY</command> expression syntax: > ! > ! <programlisting> > ! UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000] > ! WHERE name = 'Carol'; > ! </programlisting> > ! > ! <note> > ! <para> > ! Anywhere you can use the <quote>curly braces</quote> array syntax, > ! you can also use the <command>ARRAY</command> expression syntax. The > ! remainder of this section will illustrate only one or the other, but > ! not both. > ! </para> > ! </note> > ! > ! An array may also be updated at a single element: > > <programlisting> > UPDATE sal_emp SET pay_by_quarter[4] = 15000 > *************** > *** 160,165 **** > --- 255,268 ---- > UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}' > WHERE name = 'Carol'; > </programlisting> > + > + A one-dimensional array may also be updated with the > + <function>array_assign</function> function: > + > + <programlisting> > + UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000) > + WHERE name = 'Bill'; > + </programListing> > </para> > > <para> > *************** > *** 179,184 **** > --- 282,369 ---- > </para> > > <para> > + An array can also be enlarged by using the concatenation operator, > + <command>||</command>. > + <programlisting> > + SELECT ARRAY[1,2] || ARRAY[3,4]; > + ?column? > + --------------- > + {{1,2},{3,4}} > + (1 row) > + > + SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]]; > + ?column? > + --------------------- > + {{5,6},{1,2},{3,4}} > + (1 row) > + </programlisting> > + > + The concatenation operator allows a single element to be pushed on to the > + beginning or end of a one-dimensional array. It also allows two > + <replaceable>N</>-dimensional arrays, or an <replaceable>N</>-dimensional > + and an <replaceable>N+1</>-dimensional array. In the former case, the two > + <replaceable>N</>-dimension arrays become outer elements of an > + <replaceable>N+1</>-dimensional array. In the latter, the > + <replaceable>N</>-dimensional array is added as either the first or last > + outer element of the <replaceable>N+1</>-dimensional array. > + > + The array is extended in the direction of the push. Hence, by pushing > + onto the beginning of an array with a one-based subscript, a zero-based > + subscript array is created: > + > + <programlisting> > + SELECT array_dims(t.f) FROM (SELECT 1 || ARRAY[2,3] AS f) AS t; > + array_dims > + ------------ > + [0:2] > + (1 row) > + </programlisting> > + </para> > + > + <para> > + An array can also be enlarged by using the functions > + <function>array_prepend</function>, <function>array_append</function>, > + or <function>array_cat</function>. The first two only support one-dimensional > + arrays, but <function>array_cat</function> supports multidimensional arrays. > + > + Note that the concatenation operator discussed above is preferred over > + direct use of these functions. In fact, the functions are primarily for use > + in implementing the concatenation operator. However, they may be directly > + useful in the creation of user-defined aggregates. Some examples: > + > + <programlisting> > + SELECT array_prepend(1, ARRAY[2,3]); > + array_prepend > + --------------- > + {1,2,3} > + (1 row) > + > + SELECT array_append(ARRAY[1,2], 3); > + array_append > + -------------- > + {1,2,3} > + (1 row) > + > + SELECT array_cat(ARRAY[1,2], ARRAY[3,4]); > + array_cat > + --------------- > + {{1,2},{3,4}} > + (1 row) > + > + SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]); > + array_cat > + --------------------- > + {{1,2},{3,4},{5,6}} > + (1 row) > + > + SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]); > + array_cat > + --------------------- > + {{5,6},{1,2},{3,4}} > + </programlisting> > + </para> > + > + <para> > The syntax for <command>CREATE TABLE</command> allows fixed-length > arrays to be defined: > > *************** > *** 194,199 **** > --- 379,394 ---- > </para> > > <para> > + An alternative syntax for one-dimensional arrays may be used. > + <structfield>pay_by_quarter</structfield> could have been defined as: > + <programlisting> > + pay_by_quarter integer ARRAY[4], > + </programlisting> > + This syntax may <emphasis>only</emphasis> be used with the integer > + constant to denote the array size. > + </para> > + > + <para> > Actually, the current implementation does not enforce the declared > number of dimensions either. Arrays of a particular element type are > all considered to be of the same type, regardless of size or number > *************** > *** 300,305 **** > --- 495,566 ---- > is not ignored, however: after skipping leading whitespace, everything > up to the next right brace or delimiter is taken as the item value. > </para> > + > + <para> > + As illustrated earlier in this chapter, arrays may also be represented > + using the <command>ARRAY</command> expression syntax. This representation > + of an array value consists of items that are interpreted according to the > + I/O conversion rules for the array's element type, plus decoration that > + indicates the array structure. The decoration consists of the keyword > + <command>ARRAY</command> and square brackets (<literal>[</> and > + <literal>]</>) around the array values, plus delimiter characters between > + adjacent items. The delimiter character is always a comma (<literal>,</>). > + When representing multidimensional arrays, the keyword > + <command>ARRAY</command> is only necessary for the outer level. For example, > + <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as: > + <programlisting> > + SELECT ARRAY[['hello world', 'happy birthday']]; > + array > + ------------------------------------ > + {{"hello world","happy birthday"}} > + (1 row) > + </programlisting> > + or it also could be written as: > + <programlisting> > + SELECT ARRAY[ARRAY['hello world', 'happy birthday']]; > + array > + ------------------------------------ > + {{"hello world","happy birthday"}} > + (1 row) > + </programlisting> > + </para> > + > + <para> > + A final method to represent an array, is through an > + <command>ARRAY</command> sub-select expression. For example: > + <programlisting> > + SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%'); > + ?column? > + ------------------------------------------------------------- > + {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31} > + (1 row) > + </programlisting> > + The sub-select may <emphasis>only</emphasis> return a single column. The > + resulting one-dimensional array will have an element for each row in the > + sub-select result, with an element type matching that of the sub-select's > + target column. > + </para> > + > + <para> > + Arrays may be cast from one type to another in similar fashion to other > + data types: > + > + <programlisting> > + SELECT ARRAY[1,2,3]::oid[]; > + array > + --------- > + {1,2,3} > + (1 row) > + > + SELECT CAST(ARRAY[1,2,3] AS float8[]); > + array > + --------- > + {1,2,3} > + (1 row) > + </programlisting> > + > + </para> > + > </sect2> > > <sect2> > *************** > *** 316,321 **** > --- 577,590 ---- > Alternatively, you can use backslash-escaping to protect all data characters > that would otherwise be taken as array syntax or ignorable white space. > </para> > + > + <note> > + <para> > + The discussion in the preceding paragraph with respect to double quoting does > + not pertain to the <command>ARRAY</command> expression syntax. In that case, > + each element is quoted exactly as any other literal value of the element type. > + </para> > + </note> > > <para> > The array output routine will put double quotes around element values > Index: doc/src/sgml/func.sgml > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v > retrieving revision 1.154 > diff -c -r1.154 func.sgml > *** doc/src/sgml/func.sgml 5 May 2003 15:08:49 -0000 1.154 > --- doc/src/sgml/func.sgml 9 Jun 2003 01:53:35 -0000 > *************** > *** 6962,6967 **** > --- 6962,7164 ---- > > </sect1> > > + <sect1 id="functions-array"> > + <title>Array Functions</title> > + > + <para> > + <xref linkend="array-operators-table"> shows the operators > + available for the <type>array</type> types. > + </para> > + > + <table id="array-operators-table"> > + <title><type>array</type> Operators</title> > + <tgroup cols="4"> > + <thead> > + <row> > + <entry>Operator</entry> > + <entry>Description</entry> > + <entry>Example</entry> > + <entry>Result</entry> > + </row> > + </thead> > + <tbody> > + <row> > + <entry> <literal>=</literal> </entry> > + <entry>equals</entry> > + <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry> > + <entry><literal>t</literal></entry> > + </row> > + <row> > + <entry> <literal>||</literal> </entry> > + <entry>array-to-array concatenation</entry> > + <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry> > + <entry><literal>{{1,2,3},{4,5,6}}</literal></entry> > + </row> > + <row> > + <entry> <literal>||</literal> </entry> > + <entry>array-to-array concatenation</entry> > + <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry> > + <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry> > + </row> > + <row> > + <entry> <literal>||</literal> </entry> > + <entry>element-to-array concatenation</entry> > + <entry><literal>3 || ARRAY[4,5,6]</literal></entry> > + <entry><literal>{3,4,5,6}</literal></entry> > + </row> > + <row> > + <entry> <literal>||</literal> </entry> > + <entry>array-to-element concatenation</entry> > + <entry><literal>ARRAY[4,5,6] || 7</literal></entry> > + <entry><literal>{4,5,6,7}</literal></entry> > + </row> > + </tbody> > + </tgroup> > + </table> > + > + <para> > + <xref linkend="array-functions-table"> shows the functions > + available for use with array types. See <xref linkend="arrays"> > + for more discussion and examples for the use of these functions. > + </para> > + > + <table id="array-functions-table"> > + <title><type>array</type> Functions</title> > + <tgroup cols="5"> > + <thead> > + <row> > + <entry>Function</entry> > + <entry>Return Type</entry> > + <entry>Description</entry> > + <entry>Example</entry> > + <entry>Result</entry> > + </row> > + </thead> > + <tbody> > + <row> > + <entry> > + <literal> > + <function>array_append</function> > + (<type>anyarray</type>, <type>anyelement</type>) > + </literal> > + </entry> > + <entry><type>anyarray</type></entry> > + <entry> > + append an element to the end of an array, returning > + <literal>NULL</literal> for <literal>NULL</literal> inputs > + </entry> > + <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry> > + <entry><literal>{1,2,3}</literal></entry> > + </row> > + <row> > + <entry> > + <literal> > + <function>array_cat</function> > + (<type>anyarray</type>, <type>anyarray</type>) > + </literal> > + </entry> > + <entry><type>anyarray</type></entry> > + <entry> > + concatenate two arrays, returning <literal>NULL</literal> > + for <literal>NULL</literal> inputs > + </entry> > + <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry> > + <entry><literal>{{1,2,3},{4,5,6}}</literal></entry> > + </row> > + <row> > + <entry> > + <literal> > + <function>array_dims</function> > + (<type>anyarray</type>) > + </literal> > + </entry> > + <entry><type>text</type></entry> > + <entry> > + returns a text representation of array dimension lower and upper bounds, > + generating an ERROR for <literal>NULL</literal> inputs > + </entry> > + <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry> > + <entry><literal>[1:2][1:3]</literal></entry> > + </row> > + <row> > + <entry> > + <literal> > + <function>array_lower</function> > + (<type>anyarray</type>, <type>integer</type>) > + </literal> > + </entry> > + <entry><type>integer</type></entry> > + <entry> > + returns lower bound of the requested array dimension, returning > + <literal>NULL</literal> for <literal>NULL</literal> inputs > + </entry> > + <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry> > + <entry><literal>0</literal></entry> > + </row> > + <row> > + <entry> > + <literal> > + <function>array_prepend</function> > + (<type>anyelement</type>, <type>anyarray</type>) > + </literal> > + </entry> > + <entry><type>anyarray</type></entry> > + <entry> > + append an element to the beginning of an array, returning > + <literal>NULL</literal> for <literal>NULL</literal> inputs > + </entry> > + <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry> > + <entry><literal>{1,2,3}</literal></entry> > + </row> > + <row> > + <entry> > + <literal> > + <function>array_to_string</function> > + (<type>anyarray</type>, <type>text</type>) > + </literal> > + </entry> > + <entry><type>text</type></entry> > + <entry> > + concatenates array elements using provided delimiter, returning > + <literal>NULL</literal> for <literal>NULL</literal> inputs > + </entry> > + <entry><literal>array_to_string(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry> > + <entry><literal>1.10~^~2.20~^~3.30</literal></entry> > + </row> > + <row> > + <entry> > + <literal> > + <function>array_upper</function> > + (<type>anyarray</type>, <type>integer</type>) > + </literal> > + </entry> > + <entry><type>integer</type></entry> > + <entry> > + returns upper bound of the requested array dimension, returning > + <literal>NULL</literal> for <literal>NULL</literal> inputs > + </entry> > + <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry> > + <entry><literal>4</literal></entry> > + </row> > + <row> > + <entry> > + <literal> > + <function>string_to_array</function> > + (<type>text</type>, <type>text</type>) > + </literal> > + </entry> > + <entry><type>text[]</type></entry> > + <entry> > + splits string into array elements using provided delimiter, returning > + <literal>NULL</literal> for <literal>NULL</literal> inputs > + </entry> > + <entry><literal>string_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry> > + <entry><literal>{1.1,2.2,3.3}</literal></entry> > + </row> > + </tbody> > + </tgroup> > + </table> > + </sect1> > > <sect1 id="functions-aggregate"> > <title>Aggregate Functions</title> > Index: src/backend/catalog/pg_aggregate.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_aggregate.c,v > retrieving revision 1.56 > diff -c -r1.56 pg_aggregate.c > *** src/backend/catalog/pg_aggregate.c 18 Sep 2002 21:35:20 -0000 1.56 > --- src/backend/catalog/pg_aggregate.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 50,59 **** > Oid finalfn = InvalidOid; /* can be omitted */ > Oid finaltype; > Oid fnArgs[FUNC_MAX_ARGS]; > ! int nargs; > Oid procOid; > TupleDesc tupDesc; > int i; > ObjectAddress myself, > referenced; > > --- 50,65 ---- > Oid finalfn = InvalidOid; /* can be omitted */ > Oid finaltype; > Oid fnArgs[FUNC_MAX_ARGS]; > ! int nargs_transfn; > ! int nargs_finalfn; > Oid procOid; > TupleDesc tupDesc; > int i; > + Oid rettype; > + Oid *true_oid_array_transfn; > + Oid *true_oid_array_finalfn; > + bool retset; > + FuncDetailCode fdresult; > ObjectAddress myself, > referenced; > > *************** > *** 68,91 **** > MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid)); > fnArgs[0] = aggTransType; > if (aggBaseType == ANYOID) > ! nargs = 1; > else > { > fnArgs[1] = aggBaseType; > ! nargs = 2; > } > ! transfn = LookupFuncName(aggtransfnName, nargs, fnArgs); > if (!OidIsValid(transfn)) > ! func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL); > tup = SearchSysCache(PROCOID, > ObjectIdGetDatum(transfn), > 0, 0, 0); > if (!HeapTupleIsValid(tup)) > ! func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL); > proc = (Form_pg_proc) GETSTRUCT(tup); > - if (proc->prorettype != aggTransType) > - elog(ERROR, "return type of transition function %s is not %s", > - NameListToString(aggtransfnName), format_type_be(aggTransType)); > > /* > * If the transfn is strict and the initval is NULL, make sure input > --- 74,122 ---- > MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid)); > fnArgs[0] = aggTransType; > if (aggBaseType == ANYOID) > ! nargs_transfn = 1; > else > { > fnArgs[1] = aggBaseType; > ! nargs_transfn = 2; > } > ! > ! /* > ! * func_get_detail looks up the function in the catalogs, does > ! * disambiguation for polymorphic functions, handles inheritance, and > ! * returns the funcid and type and set or singleton status of the > ! * function's return value. it also returns the true argument types > ! * to the function. > ! */ > ! fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs, > ! &transfn, &rettype, &retset, > ! &true_oid_array_transfn); > ! > ! /* only valid case is a normal function */ > ! if (fdresult != FUNCDETAIL_NORMAL) > ! func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL); > ! > if (!OidIsValid(transfn)) > ! func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL); > ! > ! /* > ! * enforce consistency with ANYARRAY and ANYELEMENT argument > ! * and return types, possibly modifying return type along the way > ! */ > ! rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn, > ! nargs_transfn, rettype); > ! > ! if (rettype != aggTransType) > ! elog(ERROR, "return type of transition function %s is not %s", > ! NameListToString(aggtransfnName), format_type_be(aggTransType)); > ! > tup = SearchSysCache(PROCOID, > ObjectIdGetDatum(transfn), > 0, 0, 0); > if (!HeapTupleIsValid(tup)) > ! func_error("AggregateCreate", aggtransfnName, > ! nargs_transfn, fnArgs, NULL); > proc = (Form_pg_proc) GETSTRUCT(tup); > > /* > * If the transfn is strict and the initval is NULL, make sure input > *************** > *** 105,121 **** > { > MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid)); > fnArgs[0] = aggTransType; > ! finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs); > ! if (!OidIsValid(finalfn)) > func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL); > ! tup = SearchSysCache(PROCOID, > ! ObjectIdGetDatum(finalfn), > ! 0, 0, 0); > ! if (!HeapTupleIsValid(tup)) > func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL); > ! proc = (Form_pg_proc) GETSTRUCT(tup); > ! finaltype = proc->prorettype; > ! ReleaseSysCache(tup); > } > else > { > --- 136,161 ---- > { > MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid)); > fnArgs[0] = aggTransType; > ! nargs_finalfn = 1; > ! > ! fdresult = func_get_detail(aggfinalfnName, NIL, 1, fnArgs, > ! &finalfn, &rettype, &retset, > ! &true_oid_array_finalfn); > ! > ! /* only valid case is a normal function */ > ! if (fdresult != FUNCDETAIL_NORMAL) > func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL); > ! > ! if (!OidIsValid(finalfn)) > func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL); > ! > ! /* > ! * enforce consistency with ANYARRAY and ANYELEMENT argument > ! * and return types, possibly modifying return type along the way > ! */ > ! finaltype = enforce_generic_type_consistency(fnArgs, > ! true_oid_array_finalfn, > ! nargs_finalfn, rettype); > } > else > { > *************** > *** 125,130 **** > --- 165,191 ---- > finaltype = aggTransType; > } > Assert(OidIsValid(finaltype)); > + > + /* > + * special disallowed cases: > + * 1) if finaltype is polymorphic, basetype cannot be ANY > + * 2) if finaltype is polymorphic, both args to transfn must be > + * polymorphic > + */ > + if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID) > + { > + if (aggBaseType == ANYOID) > + elog(ERROR, "aggregate with base type ANY must have a " \ > + "non-polymorphic return type"); > + > + if (nargs_transfn > 1 && ( > + (true_oid_array_transfn[0] != ANYARRAYOID && > + true_oid_array_transfn[0] != ANYELEMENTOID) || > + (true_oid_array_transfn[1] != ANYARRAYOID && > + true_oid_array_transfn[1] != ANYELEMENTOID))) > + elog(ERROR, "aggregate with polymorphic return type requires " \ > + "state function with both arguments polymorphic"); > + } > > /* > * Everything looks okay. Try to create the pg_proc entry for the > Index: src/backend/commands/aggregatecmds.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/commands/aggregatecmds.c,v > retrieving revision 1.5 > diff -c -r1.5 aggregatecmds.c > *** src/backend/commands/aggregatecmds.c 4 Sep 2002 20:31:14 -0000 1.5 > --- src/backend/commands/aggregatecmds.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 119,125 **** > baseTypeId = typenameTypeId(baseType); > > transTypeId = typenameTypeId(transType); > ! if (get_typtype(transTypeId) == 'p') > elog(ERROR, "Aggregate transition datatype cannot be %s", > format_type_be(transTypeId)); > > --- 119,127 ---- > baseTypeId = typenameTypeId(baseType); > > transTypeId = typenameTypeId(transType); > ! if (get_typtype(transTypeId) == 'p' && > ! transTypeId != ANYARRAYOID && > ! transTypeId != ANYELEMENTOID) > elog(ERROR, "Aggregate transition datatype cannot be %s", > format_type_be(transTypeId)); > > Index: src/backend/executor/execQual.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/execQual.c,v > retrieving revision 1.130 > diff -c -r1.130 execQual.c > *** src/backend/executor/execQual.c 28 May 2003 22:32:49 -0000 1.130 > --- src/backend/executor/execQual.c 9 Jun 2003 01:42:58 -0000 > *************** > *** 1528,1544 **** > { > /* Check other sub-arrays are compatible */ > if (elem_ndims != ARR_NDIM(array)) > ! elog(ERROR, "Multiple dimension arrays must have array " > "expressions with matching number of dimensions"); > > if (memcmp(elem_dims, ARR_DIMS(array), > elem_ndims * sizeof(int)) != 0) > ! elog(ERROR, "Multiple dimension arrays must have array " > "expressions with matching dimensions"); > > if (memcmp(elem_lbs, ARR_LBOUND(array), > elem_ndims * sizeof(int)) != 0) > ! elog(ERROR, "Multiple dimension arrays must have array " > "expressions with matching dimensions"); > } > > --- 1528,1544 ---- > { > /* Check other sub-arrays are compatible */ > if (elem_ndims != ARR_NDIM(array)) > ! elog(ERROR, "Multidimensional arrays must have array " > "expressions with matching number of dimensions"); > > if (memcmp(elem_dims, ARR_DIMS(array), > elem_ndims * sizeof(int)) != 0) > ! elog(ERROR, "Multidimensional arrays must have array " > "expressions with matching dimensions"); > > if (memcmp(elem_lbs, ARR_LBOUND(array), > elem_ndims * sizeof(int)) != 0) > ! elog(ERROR, "Multidimensional arrays must have array " > "expressions with matching dimensions"); > } > > Index: src/backend/executor/nodeAgg.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeAgg.c,v > retrieving revision 1.105 > diff -c -r1.105 nodeAgg.c > *** src/backend/executor/nodeAgg.c 30 May 2003 20:23:10 -0000 1.105 > --- src/backend/executor/nodeAgg.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 58,63 **** > --- 58,64 ---- > #include "executor/executor.h" > #include "executor/nodeAgg.h" > #include "miscadmin.h" > + #include "nodes/makefuncs.h" > #include "optimizer/clauses.h" > #include "parser/parse_coerce.h" > #include "parser/parse_expr.h" > *************** > *** 212,218 **** > static void agg_fill_hash_table(AggState *aggstate); > static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate); > static Datum GetAggInitVal(Datum textInitVal, Oid transtype); > ! > > /* > * Initialize all aggregates for a new group of input values. > --- 213,219 ---- > static void agg_fill_hash_table(AggState *aggstate); > static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate); > static Datum GetAggInitVal(Datum textInitVal, Oid transtype); > ! static Oid resolve_type(Oid type_to_resolve, Oid context_type); > > /* > * Initialize all aggregates for a new group of input values. > *************** > *** 351,364 **** > fcinfo.context = NULL; > fcinfo.resultinfo = NULL; > fcinfo.isnull = false; > - > fcinfo.flinfo = &peraggstate->transfn; > fcinfo.nargs = 2; > fcinfo.arg[0] = pergroupstate->transValue; > fcinfo.argnull[0] = pergroupstate->transValueIsNull; > fcinfo.arg[1] = newVal; > fcinfo.argnull[1] = isNull; > - > newVal = FunctionCallInvoke(&fcinfo); > > /* > --- 352,363 ---- > *************** > *** 1176,1182 **** > --- 1175,1195 ---- > AclResult aclresult; > Oid transfn_oid, > finalfn_oid; > + FuncExpr *transfnexpr, > + *finalfnexpr; > Datum textInitVal; > + List *fargs; > + Oid agg_rt_type; > + Oid *transfn_arg_types; > + List *transfn_args = NIL; > + int transfn_nargs; > + Oid transfn_ret_type; > + Oid *finalfn_arg_types = NULL; > + List *finalfn_args = NIL; > + Oid finalfn_ret_type = InvalidOid; > + int finalfn_nargs = 0; > + Node *arg0; > + Node *arg1; > int i; > > /* Look for a previous duplicate aggregate */ > *************** > *** 1224,1229 **** > --- 1237,1402 ---- > &peraggstate->transtypeLen, > &peraggstate->transtypeByVal); > > + peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; > + peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; > + > + /* get the runtime aggregate argument type */ > + fargs = aggref->args; > + agg_rt_type = exprType((Node *) nth(0, fargs)); > + > + /* get the transition function argument and return types */ > + transfn_ret_type = get_func_rettype(transfn_oid); > + transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs); > + > + /* resolve any polymorphic types */ > + if (transfn_nargs == 2) > + /* base type was not ANY */ > + { > + if (transfn_arg_types[1] == ANYARRAYOID || > + transfn_arg_types[1] == ANYELEMENTOID) > + transfn_arg_types[1] = agg_rt_type; > + > + transfn_arg_types[0] = resolve_type(transfn_arg_types[0], > + agg_rt_type); > + > + /* > + * Build arg list to use on the transfn FuncExpr node. We really > + * only care that the node type is correct so that the transfn > + * can discover the actual argument types at runtime using > + * get_fn_expr_argtype() > + */ > + arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0], > + -1, COERCE_DONTCARE); > + arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1], > + -1, COERCE_DONTCARE); > + transfn_args = makeList2(arg0, arg1); > + > + /* > + * the state transition function always returns the same type > + * as its first argument > + */ > + if (transfn_ret_type == ANYARRAYOID || > + transfn_ret_type == ANYELEMENTOID) > + transfn_ret_type = transfn_arg_types[0]; > + } > + else if (transfn_nargs == 1) > + /* > + * base type was ANY, therefore the aggregate return type should > + * be non-polymorphic > + */ > + { > + Oid finaltype = get_func_rettype(aggref->aggfnoid); > + > + /* > + * this should have been prevented in AggregateCreate, > + * but check anyway > + */ > + if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID) > + elog(ERROR, "aggregate with base type ANY must have a " \ > + "non-polymorphic return type"); > + > + /* see if we have a final function */ > + if (OidIsValid(finalfn_oid)) > + { > + finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs); > + if (finalfn_nargs != 1) > + elog(ERROR, "final function takes unexpected number " \ > + "of arguments: %d", finalfn_nargs); > + > + /* > + * final function argument is always the same as the state > + * function return type > + */ > + if (finalfn_arg_types[0] != ANYARRAYOID && > + finalfn_arg_types[0] != ANYELEMENTOID) > + { > + /* if it is not ambiguous, use it */ > + transfn_ret_type = finalfn_arg_types[0]; > + } > + else > + { > + /* if it is ambiguous, try to derive it */ > + finalfn_ret_type = finaltype; > + finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0], > + finalfn_ret_type); > + transfn_ret_type = finalfn_arg_types[0]; > + } > + } > + else > + transfn_ret_type = finaltype; > + > + transfn_arg_types[0] = resolve_type(transfn_arg_types[0], > + transfn_ret_type); > + > + /* > + * Build arg list to use on the transfn FuncExpr node. We really > + * only care that the node type is correct so that the transfn > + * can discover the actual argument types at runtime using > + * get_fn_expr_argtype() > + */ > + arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0], > + -1, COERCE_DONTCARE); > + transfn_args = makeList1(arg0); > + } > + else > + elog(ERROR, "state transition function takes unexpected number " \ > + "of arguments: %d", transfn_nargs); > + > + if (OidIsValid(finalfn_oid)) > + { > + /* get the final function argument and return types */ > + if (finalfn_ret_type == InvalidOid) > + finalfn_ret_type = get_func_rettype(finalfn_oid); > + > + if (!finalfn_arg_types) > + { > + finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs); > + if (finalfn_nargs != 1) > + elog(ERROR, "final function takes unexpected number " \ > + "of arguments: %d", finalfn_nargs); > + } > + > + /* > + * final function argument is always the same as the state > + * function return type, which by now should have been resolved > + */ > + if (finalfn_arg_types[0] == ANYARRAYOID || > + finalfn_arg_types[0] == ANYELEMENTOID) > + finalfn_arg_types[0] = transfn_ret_type; > + > + /* > + * Build arg list to use on the finalfn FuncExpr node. We really > + * only care that the node type is correct so that the finalfn > + * can discover the actual argument type at runtime using > + * get_fn_expr_argtype() > + */ > + arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0], > + -1, COERCE_DONTCARE); > + finalfn_args = makeList1(arg0); > + > + finalfn_ret_type = resolve_type(finalfn_ret_type, > + finalfn_arg_types[0]); > + } > + > + fmgr_info(transfn_oid, &peraggstate->transfn); > + transfnexpr = (FuncExpr *) make_funcclause(transfn_oid, > + transfn_ret_type, > + false, /* cannot be a set */ > + COERCE_DONTCARE, /* to match any user expr */ > + transfn_args); > + peraggstate->transfn.fn_expr = (Node *) transfnexpr; > + > + if (OidIsValid(finalfn_oid)) > + { > + fmgr_info(finalfn_oid, &peraggstate->finalfn); > + finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid, > + finalfn_ret_type, > + false, /* cannot be a set */ > + COERCE_DONTCARE, /* to match any user expr */ > + finalfn_args); > + peraggstate->finalfn.fn_expr = (Node *) finalfnexpr; > + } > + > /* > * initval is potentially null, so don't try to access it as a > * struct field. Must do it the hard way with SysCacheGetAttr. > *************** > *** 1236,1249 **** > peraggstate->initValue = (Datum) 0; > else > peraggstate->initValue = GetAggInitVal(textInitVal, > ! aggform->aggtranstype); > ! > ! peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; > ! peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; > ! > ! fmgr_info(transfn_oid, &peraggstate->transfn); > ! if (OidIsValid(finalfn_oid)) > ! fmgr_info(finalfn_oid, &peraggstate->finalfn); > > /* > * If the transfn is strict and the initval is NULL, make sure > --- 1409,1415 ---- > peraggstate->initValue = (Datum) 0; > else > peraggstate->initValue = GetAggInitVal(textInitVal, > ! transfn_arg_types[0]); > > /* > * If the transfn is strict and the initval is NULL, make sure > *************** > *** 1454,1457 **** > --- 1620,1656 ---- > elog(ERROR, "Aggregate function %u called as normal function", > fcinfo->flinfo->fn_oid); > return (Datum) 0; /* keep compiler quiet */ > + } > + > + static Oid > + resolve_type(Oid type_to_resolve, Oid context_type) > + { > + Oid resolved_type; > + > + if (context_type == ANYARRAYOID || context_type == ANYELEMENTOID) > + resolved_type = type_to_resolve; > + else if (type_to_resolve == ANYARRAYOID) > + /* any array */ > + { > + Oid context_type_arraytype = get_array_type(context_type); > + > + if (context_type_arraytype != InvalidOid) > + resolved_type = context_type_arraytype; > + else > + resolved_type = context_type; > + } > + else if (type_to_resolve == ANYELEMENTOID) > + /* any element */ > + { > + Oid context_type_elemtype = get_element_type(context_type); > + > + if (context_type_elemtype != InvalidOid) > + resolved_type = context_type_elemtype; > + else > + resolved_type = context_type; > + } > + else > + resolved_type = type_to_resolve; > + > + return resolved_type; > } > Index: src/backend/executor/nodeSubplan.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/executor/nodeSubplan.c,v > retrieving revision 1.45 > diff -c -r1.45 nodeSubplan.c > *** src/backend/executor/nodeSubplan.c 8 Apr 2003 23:20:01 -0000 1.45 > --- src/backend/executor/nodeSubplan.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 28,50 **** > #include "utils/datum.h" > #include "utils/lsyscache.h" > > - > - typedef struct ArrayBuildState > - { > - MemoryContext mcontext; /* where all the temp stuff is kept */ > - Datum *dvalues; /* array of accumulated Datums */ > - /* > - * The allocated size of dvalues[] is always a multiple of > - * ARRAY_ELEMS_CHUNKSIZE > - */ > - #define ARRAY_ELEMS_CHUNKSIZE 64 > - int nelems; /* number of valid Datums in dvalues[] */ > - Oid element_type; /* data type of the Datums */ > - int16 typlen; /* needed info about datatype */ > - bool typbyval; > - char typalign; > - } ArrayBuildState; > - > static Datum ExecHashSubPlan(SubPlanState *node, > ExprContext *econtext, > bool *isNull); > --- 28,33 ---- > *************** > *** 54,66 **** > static void buildSubPlanHash(SubPlanState *node); > static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot); > static bool tupleAllNulls(HeapTuple tuple); > - static ArrayBuildState *accumArrayResult(ArrayBuildState *astate, > - Datum dvalue, bool disnull, > - Oid element_type, > - MemoryContext rcontext); > - static Datum makeArrayResult(ArrayBuildState *astate, > - MemoryContext rcontext); > - > > /* ---------------------------------------------------------------- > * ExecSubPlan > --- 37,42 ---- > *************** > *** 224,229 **** > --- 200,206 ---- > PlanState *planstate = node->planstate; > SubLinkType subLinkType = subplan->subLinkType; > bool useOr = subplan->useOr; > + bool isExpr = subplan->isExpr; > MemoryContext oldcontext; > TupleTableSlot *slot; > Datum result; > *************** > *** 292,297 **** > --- 269,279 ---- > bool rownull = false; > int col = 1; > List *plst; > + int numelems; > + int elemnum; > + Datum dvalue; > + Datum *dvalues = NULL; > + bool disnull; > > if (subLinkType == EXISTS_SUBLINK) > { > *************** > *** 329,337 **** > > if (subLinkType == ARRAY_SUBLINK) > { > - Datum dvalue; > - bool disnull; > - > found = true; > /* stash away current value */ > dvalue = heap_getattr(tup, 1, tdesc, &disnull); > --- 311,316 ---- > *************** > *** 349,446 **** > found = true; > > /* > ! * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining > ! * operators for columns of tuple. > */ > ! plst = subplan->paramIds; > ! foreach(lst, node->exprs) > { > ! ExprState *exprstate = (ExprState *) lfirst(lst); > ! int paramid = lfirsti(plst); > ! ParamExecData *prmdata; > ! Datum expresult; > ! bool expnull; > ! > ! /* > ! * Load up the Param representing this column of the sub-select. > ! */ > ! prmdata = &(econtext->ecxt_param_exec_vals[paramid]); > ! Assert(prmdata->execPlan == NULL); > ! prmdata->value = heap_getattr(tup, col, tdesc, > ! &(prmdata->isnull)); > > ! /* > ! * Now we can eval the combining operator for this column. > ! */ > ! expresult = ExecEvalExprSwitchContext(exprstate, econtext, > ! &expnull, NULL); > ! > ! /* > ! * Combine the result into the row result as appropriate. > ! */ > ! if (col == 1) > { > ! rowresult = expresult; > ! rownull = expnull; > } > ! else if (useOr) > { > ! /* combine within row per OR semantics */ > ! if (expnull) > ! rownull = true; > ! else if (DatumGetBool(expresult)) > { > ! rowresult = BoolGetDatum(true); > ! rownull = false; > ! break; /* needn't look at any more columns */ > } > } > else > { > ! /* combine within row per AND semantics */ > ! if (expnull) > ! rownull = true; > ! else if (!DatumGetBool(expresult)) > ! { > ! rowresult = BoolGetDatum(false); > ! rownull = false; > ! break; /* needn't look at any more columns */ > ! } > } > > - plst = lnext(plst); > - col++; > } > > ! if (subLinkType == ANY_SUBLINK) > { > ! /* combine across rows per OR semantics */ > ! if (rownull) > ! *isNull = true; > ! else if (DatumGetBool(rowresult)) > { > ! result = BoolGetDatum(true); > ! *isNull = false; > ! break; /* needn't look at any more rows */ > } > ! } > ! else if (subLinkType == ALL_SUBLINK) > ! { > ! /* combine across rows per AND semantics */ > ! if (rownull) > ! *isNull = true; > ! else if (!DatumGetBool(rowresult)) > { > ! result = BoolGetDatum(false); > ! *isNull = false; > ! break; /* needn't look at any more rows */ > } > - } > - else > - { > - /* must be MULTIEXPR_SUBLINK */ > - result = rowresult; > - *isNull = rownull; > } > } > > --- 328,490 ---- > found = true; > > /* > ! * When isExpr is true, we have either a scalar expression or an > ! * array. In the former case, this is no different than the !isExpr > ! * case. In the latter case, iterate over the elements as if they > ! * were from multiple input tuples. > */ > ! if (!isExpr) > ! numelems = 1; > ! else > { > ! Oid expr_typeid = tdesc->attrs[0]->atttypid; > > ! if (expr_typeid != subplan->exprtype) > { > ! subplan->exprtype = expr_typeid; > ! subplan->elemtype = get_element_type(expr_typeid); > ! > ! if (subplan->elemtype != InvalidOid) > ! get_typlenbyvalalign(subplan->elemtype, > ! &subplan->elmlen, > ! &subplan->elmbyval, > ! &subplan->elmalign); > } > ! > ! /* get current value */ > ! dvalue = heap_getattr(tup, 1, tdesc, &disnull); > ! > ! /* XXX this will need work if/when arrays support NULL elements */ > ! if (!disnull) > { > ! if (subplan->elemtype != InvalidOid) > ! { > ! ArrayType *v = DatumGetArrayTypeP(dvalue); > ! > ! deconstruct_array(v, subplan->elemtype, subplan->elmlen, > ! subplan->elmbyval, subplan->elmalign, > ! &dvalues, &numelems); > ! } > ! else > { > ! numelems = 1; > ! dvalues = (Datum *) palloc(numelems * sizeof(Datum)); > ! dvalues[0] = dvalue; > } > } > else > { > ! numelems = 1; > ! dvalues = (Datum *) palloc(numelems * sizeof(Datum)); > ! dvalues[0] = (Datum) 0; > } > > } > > ! for (elemnum = 0; elemnum < numelems; elemnum++) > { > ! /* > ! * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining > ! * operators for columns of tuple. > ! */ > ! col = 1; > ! plst = subplan->paramIds; > ! foreach(lst, node->exprs) > { > ! ExprState *exprstate = (ExprState *) lfirst(lst); > ! int paramid = lfirsti(plst); > ! ParamExecData *prmdata; > ! Datum expresult; > ! bool expnull; > ! > ! /* > ! * Load up the Param representing this column of the sub-select. > ! */ > ! prmdata = &(econtext->ecxt_param_exec_vals[paramid]); > ! Assert(prmdata->execPlan == NULL); > ! > ! if (!isExpr) > ! prmdata->value = heap_getattr(tup, col, tdesc, > ! &(prmdata->isnull)); > ! else > ! { > ! prmdata->value = dvalues[elemnum]; > ! prmdata->isnull = disnull; > ! } > ! > ! /* > ! * Now we can eval the combining operator for this column. > ! */ > ! expresult = ExecEvalExprSwitchContext(exprstate, econtext, > ! &expnull, NULL); > ! > ! /* > ! * Combine the result into the row result as appropriate. > ! */ > ! if (col == 1) > ! { > ! rowresult = expresult; > ! rownull = expnull; > ! } > ! else if (useOr) > ! { > ! /* combine within row per OR semantics */ > ! if (expnull) > ! rownull = true; > ! else if (DatumGetBool(expresult)) > ! { > ! rowresult = BoolGetDatum(true); > ! rownull = false; > ! break; /* needn't look at any more columns */ > ! } > ! } > ! else > ! { > ! /* combine within row per AND semantics */ > ! if (expnull) > ! rownull = true; > ! else if (!DatumGetBool(expresult)) > ! { > ! rowresult = BoolGetDatum(false); > ! rownull = false; > ! break; /* needn't look at any more columns */ > ! } > ! } > ! > ! plst = lnext(plst); > ! col++; > } > ! > ! if (subLinkType == ANY_SUBLINK) > { > ! /* combine across rows per OR semantics */ > ! if (rownull) > ! *isNull = true; > ! else if (DatumGetBool(rowresult)) > ! { > ! result = BoolGetDatum(true); > ! *isNull = false; > ! break; /* needn't look at any more rows */ > ! } > ! } > ! else if (subLinkType == ALL_SUBLINK) > ! { > ! /* combine across rows per AND semantics */ > ! if (rownull) > ! *isNull = true; > ! else if (!DatumGetBool(rowresult)) > ! { > ! result = BoolGetDatum(false); > ! *isNull = false; > ! break; /* needn't look at any more rows */ > ! } > ! } > ! else > ! { > ! /* must be MULTIEXPR_SUBLINK */ > ! result = rowresult; > ! *isNull = rownull; > } > } > } > > *************** > *** 478,483 **** > --- 522,528 ---- > buildSubPlanHash(SubPlanState *node) > { > SubPlan *subplan = (SubPlan *) node->xprstate.expr; > + bool isExpr = subplan->isExpr; > PlanState *planstate = node->planstate; > int ncols = length(node->exprs); > ExprContext *innerecontext = node->innerecontext; > *************** > *** 485,490 **** > --- 530,536 ---- > MemoryContext oldcontext; > int nbuckets; > TupleTableSlot *slot; > + TupleTableSlot *arrslot = NULL; > > Assert(subplan->subLinkType == ANY_SUBLINK); > Assert(!subplan->useOr); > *************** > *** 562,604 **** > { > HeapTuple tup = slot->val; > TupleDesc tdesc = slot->ttc_tupleDescriptor; > ! int col = 1; > List *plst; > bool isnew; > > /* > ! * Load up the Params representing the raw sub-select outputs, > ! * then form the projection tuple to store in the hashtable. > */ > ! foreach(plst, subplan->paramIds) > { > ! int paramid = lfirsti(plst); > ! ParamExecData *prmdata; > > ! prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]); > ! Assert(prmdata->execPlan == NULL); > ! prmdata->value = heap_getattr(tup, col, tdesc, > ! &(prmdata->isnull)); > ! col++; > ! } > ! slot = ExecProject(node->projRight, NULL); > ! tup = slot->val; > > - /* > - * If result contains any nulls, store separately or not at all. > - * (Since we know the projection tuple has no junk columns, we > - * can just look at the overall hasnull info bit, instead of > - * groveling through the columns.) > - */ > - if (HeapTupleNoNulls(tup)) > - { > - (void) LookupTupleHashEntry(node->hashtable, slot, &isnew); > - node->havehashrows = true; > } > ! else if (node->hashnulls) > { > ! (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew); > ! node->havenullrows = true; > } > > /* > --- 608,746 ---- > { > HeapTuple tup = slot->val; > TupleDesc tdesc = slot->ttc_tupleDescriptor; > ! TupleDesc arrtdesc = NULL; > List *plst; > bool isnew; > + int numelems; > + int elemnum; > + Datum dvalue; > + Datum *dvalues = NULL; > + bool disnull; > > /* > ! * When isExpr is true, we have either a scalar expression or an > ! * array. In the former case, this is no different than the !isExpr > ! * case. In the latter case, iterate over the elements as if they > ! * were from multiple input tuples. > */ > ! if (!isExpr) > ! numelems = 1; > ! else > { > ! Oid expr_typeid = tdesc->attrs[0]->atttypid; > > ! if (expr_typeid != subplan->exprtype) > ! { > ! subplan->exprtype = expr_typeid; > ! subplan->elemtype = get_element_type(expr_typeid); > ! > ! if (subplan->elemtype != InvalidOid) > ! get_typlenbyvalalign(subplan->elemtype, > ! &subplan->elmlen, > ! &subplan->elmbyval, > ! &subplan->elmalign); > ! } > ! > ! /* get current value */ > ! dvalue = heap_getattr(tup, 1, tdesc, &disnull); > ! > ! if (subplan->elemtype != InvalidOid) > ! { > ! TupleTable tupleTable; > ! ArrayType *v = DatumGetArrayTypeP(dvalue); > ! > ! arrtdesc = CreateTemplateTupleDesc(1, false); > ! TupleDescInitEntry(arrtdesc, 1, "elem", subplan->elemtype, > ! -1, 0, false); > ! > ! tupleTable = ExecCreateTupleTable(1); > ! arrslot = ExecAllocTableSlot(tupleTable); > ! ExecSetSlotDescriptor(arrslot, arrtdesc, true); > ! > ! /* XXX this will need work if/when arrays support NULL elements */ > ! if (!disnull) > ! { > ! deconstruct_array(v, subplan->elemtype, subplan->elmlen, > ! subplan->elmbyval, subplan->elmalign, > ! &dvalues, &numelems); > ! } > ! else > ! { > ! numelems = 1; > ! dvalues = (Datum *) palloc(numelems * sizeof(Datum)); > ! dvalues[0] = (Datum) 0; > ! } > ! } > ! else > ! { > ! numelems = 1; > ! dvalues = (Datum *) palloc(numelems * sizeof(Datum)); > ! dvalues[0] = dvalue; > ! } > > } > ! > ! for (elemnum = 0; elemnum < numelems; elemnum++) > { > ! int col = 1; > ! > ! if (!isExpr || subplan->elemtype == InvalidOid) > ! { > ! /* > ! * Load up the Params representing the raw sub-select outputs, > ! * then form the projection tuple to store in the hashtable. > ! */ > ! foreach(plst, subplan->paramIds) > ! { > ! int paramid = lfirsti(plst); > ! ParamExecData *prmdata; > ! > ! prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]); > ! Assert(prmdata->execPlan == NULL); > ! > ! prmdata->value = heap_getattr(tup, col, tdesc, > ! &(prmdata->isnull)); > ! > ! col++; > ! } > ! slot = ExecProject(node->projRight, NULL); > ! tup = slot->val; > ! } > ! else > ! { > ! /* > ! * For array type expressions, we need to build up our own > ! * tuple and slot > ! */ > ! char nullflag; > ! > ! nullflag = disnull ? 'n' : ' '; > ! tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag); > ! arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true); > ! } > ! > ! /* > ! * If result contains any nulls, store separately or not at all. > ! * (Since we know the projection tuple has no junk columns, we > ! * can just look at the overall hasnull info bit, instead of > ! * groveling through the columns.) > ! */ > ! if (HeapTupleNoNulls(tup)) > ! { > ! if (!isExpr) > ! (void) LookupTupleHashEntry(node->hashtable, slot, &isnew); > ! else > ! (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew); > ! node->havehashrows = true; > ! } > ! else if (node->hashnulls) > ! { > ! if (!isExpr) > ! (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew); > ! else > ! (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew); > ! node->havenullrows = true; > ! } > } > > /* > *************** > *** 615,620 **** > --- 757,764 ---- > * have the potential for a double free attempt. > */ > ExecClearTuple(node->projRight->pi_slot); > + if (arrslot) > + ExecClearTuple(arrslot); > > MemoryContextSwitchTo(oldcontext); > } > *************** > *** 1084,1185 **** > prm->execPlan = node; > parent->chgParam = bms_add_member(parent->chgParam, paramid); > } > - } > - > - /* > - * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK > - * > - * astate is working state (NULL on first call) > - * rcontext is where to keep working state > - */ > - static ArrayBuildState * > - accumArrayResult(ArrayBuildState *astate, > - Datum dvalue, bool disnull, > - Oid element_type, > - MemoryContext rcontext) > - { > - MemoryContext arr_context, > - oldcontext; > - > - if (astate == NULL) > - { > - /* First time through --- initialize */ > - > - /* Make a temporary context to hold all the junk */ > - arr_context = AllocSetContextCreate(rcontext, > - "ARRAY_SUBLINK Result", > - ALLOCSET_DEFAULT_MINSIZE, > - ALLOCSET_DEFAULT_INITSIZE, > - ALLOCSET_DEFAULT_MAXSIZE); > - oldcontext = MemoryContextSwitchTo(arr_context); > - astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState)); > - astate->mcontext = arr_context; > - astate->dvalues = (Datum *) > - palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum)); > - astate->nelems = 0; > - astate->element_type = element_type; > - get_typlenbyvalalign(element_type, > - &astate->typlen, > - &astate->typbyval, > - &astate->typalign); > - } > - else > - { > - oldcontext = MemoryContextSwitchTo(astate->mcontext); > - Assert(astate->element_type == element_type); > - /* enlarge dvalues[] if needed */ > - if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0) > - astate->dvalues = (Datum *) > - repalloc(astate->dvalues, > - (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum)); > - } > - > - if (disnull) > - elog(ERROR, "NULL elements not allowed in Arrays"); > - > - /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */ > - astate->dvalues[astate->nelems++] = > - datumCopy(dvalue, astate->typbyval, astate->typlen); > - > - MemoryContextSwitchTo(oldcontext); > - > - return astate; > - } > - > - /* > - * makeArrayResult - produce final result of ARRAY_SUBLINK > - * > - * astate is working state (not NULL) > - * rcontext is where to construct result > - */ > - static Datum > - makeArrayResult(ArrayBuildState *astate, > - MemoryContext rcontext) > - { > - ArrayType *result; > - int dims[1]; > - int lbs[1]; > - MemoryContext oldcontext; > - > - /* Build the final array result in rcontext */ > - oldcontext = MemoryContextSwitchTo(rcontext); > - > - dims[0] = astate->nelems; > - lbs[0] = 1; > - > - result = construct_md_array(astate->dvalues, > - 1, > - dims, > - lbs, > - astate->element_type, > - astate->typlen, > - astate->typbyval, > - astate->typalign); > - > - MemoryContextSwitchTo(oldcontext); > - > - /* Clean up all the junk */ > - MemoryContextDelete(astate->mcontext); > - > - return PointerGetDatum(result); > } > --- 1228,1231 ---- > Index: src/backend/nodes/copyfuncs.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v > retrieving revision 1.251 > diff -c -r1.251 copyfuncs.c > *** src/backend/nodes/copyfuncs.c 28 May 2003 16:03:56 -0000 1.251 > --- src/backend/nodes/copyfuncs.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 727,732 **** > --- 727,733 ---- > COPY_NODE_FIELD(target); > COPY_SCALAR_FIELD(aggstar); > COPY_SCALAR_FIELD(aggdistinct); > + COPY_NODE_FIELD(args); > > return newnode; > } > *************** > *** 825,830 **** > --- 826,832 ---- > > COPY_SCALAR_FIELD(subLinkType); > COPY_SCALAR_FIELD(useOr); > + COPY_SCALAR_FIELD(isExpr); > COPY_NODE_FIELD(lefthand); > COPY_NODE_FIELD(operName); > COPY_OIDLIST_FIELD(operOids); > *************** > *** 843,848 **** > --- 845,856 ---- > > COPY_SCALAR_FIELD(subLinkType); > COPY_SCALAR_FIELD(useOr); > + COPY_SCALAR_FIELD(isExpr); > + COPY_SCALAR_FIELD(exprtype); > + COPY_SCALAR_FIELD(elemtype); > + COPY_SCALAR_FIELD(elmlen); > + COPY_SCALAR_FIELD(elmbyval); > + COPY_SCALAR_FIELD(elmalign); > COPY_NODE_FIELD(exprs); > COPY_INTLIST_FIELD(paramIds); > COPY_NODE_FIELD(plan); > Index: src/backend/nodes/equalfuncs.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v > retrieving revision 1.194 > diff -c -r1.194 equalfuncs.c > *** src/backend/nodes/equalfuncs.c 28 May 2003 16:03:56 -0000 1.194 > --- src/backend/nodes/equalfuncs.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 204,209 **** > --- 204,210 ---- > COMPARE_NODE_FIELD(target); > COMPARE_SCALAR_FIELD(aggstar); > COMPARE_SCALAR_FIELD(aggdistinct); > + COMPARE_NODE_FIELD(args); > > return true; > } > *************** > *** 300,305 **** > --- 301,307 ---- > { > COMPARE_SCALAR_FIELD(subLinkType); > COMPARE_SCALAR_FIELD(useOr); > + COMPARE_SCALAR_FIELD(isExpr); > COMPARE_NODE_FIELD(lefthand); > COMPARE_NODE_FIELD(operName); > COMPARE_OIDLIST_FIELD(operOids); > *************** > *** 313,318 **** > --- 315,326 ---- > { > COMPARE_SCALAR_FIELD(subLinkType); > COMPARE_SCALAR_FIELD(useOr); > + COMPARE_SCALAR_FIELD(isExpr); > + COMPARE_SCALAR_FIELD(exprtype); > + COMPARE_SCALAR_FIELD(elemtype); > + COMPARE_SCALAR_FIELD(elmlen); > + COMPARE_SCALAR_FIELD(elmbyval); > + COMPARE_SCALAR_FIELD(elmalign); > COMPARE_NODE_FIELD(exprs); > COMPARE_INTLIST_FIELD(paramIds); > /* should compare plans, but have to settle for comparing plan IDs */ > Index: src/backend/nodes/outfuncs.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v > retrieving revision 1.206 > diff -c -r1.206 outfuncs.c > *** src/backend/nodes/outfuncs.c 28 May 2003 16:03:56 -0000 1.206 > --- src/backend/nodes/outfuncs.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 615,620 **** > --- 615,621 ---- > WRITE_NODE_FIELD(target); > WRITE_BOOL_FIELD(aggstar); > WRITE_BOOL_FIELD(aggdistinct); > + WRITE_NODE_FIELD(args); > } > > static void > *************** > *** 700,705 **** > --- 701,707 ---- > > WRITE_ENUM_FIELD(subLinkType, SubLinkType); > WRITE_BOOL_FIELD(useOr); > + WRITE_BOOL_FIELD(isExpr); > WRITE_NODE_FIELD(lefthand); > WRITE_NODE_FIELD(operName); > WRITE_OIDLIST_FIELD(operOids); > *************** > *** 713,718 **** > --- 715,726 ---- > > WRITE_ENUM_FIELD(subLinkType, SubLinkType); > WRITE_BOOL_FIELD(useOr); > + WRITE_BOOL_FIELD(isExpr); > + WRITE_OID_FIELD(exprtype); > + WRITE_OID_FIELD(elemtype); > + WRITE_INT_FIELD(elmlen); > + WRITE_BOOL_FIELD(elmbyval); > + WRITE_CHAR_FIELD(elmalign); > WRITE_NODE_FIELD(exprs); > WRITE_INTLIST_FIELD(paramIds); > WRITE_NODE_FIELD(plan); > Index: src/backend/nodes/readfuncs.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v > retrieving revision 1.153 > diff -c -r1.153 readfuncs.c > *** src/backend/nodes/readfuncs.c 6 May 2003 00:20:32 -0000 1.153 > --- src/backend/nodes/readfuncs.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 415,420 **** > --- 415,421 ---- > READ_NODE_FIELD(target); > READ_BOOL_FIELD(aggstar); > READ_BOOL_FIELD(aggdistinct); > + READ_NODE_FIELD(args); > > READ_DONE(); > } > *************** > *** 544,549 **** > --- 545,551 ---- > > READ_ENUM_FIELD(subLinkType, SubLinkType); > READ_BOOL_FIELD(useOr); > + READ_BOOL_FIELD(isExpr); > READ_NODE_FIELD(lefthand); > READ_NODE_FIELD(operName); > READ_OIDLIST_FIELD(operOids); > Index: src/backend/optimizer/plan/subselect.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v > retrieving revision 1.75 > diff -c -r1.75 subselect.c > *** src/backend/optimizer/plan/subselect.c 29 Apr 2003 22:13:09 -0000 1.75 > --- src/backend/optimizer/plan/subselect.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 67,73 **** > > static List *convert_sublink_opers(List *lefthand, List *operOids, > List *targetlist, int rtindex, > ! List **righthandIds); > static bool subplan_is_hashable(SubLink *slink, SubPlan *node); > static Node *replace_correlation_vars_mutator(Node *node, void *context); > static Node *process_sublinks_mutator(Node *node, bool *isTopQual); > --- 67,73 ---- > > static List *convert_sublink_opers(List *lefthand, List *operOids, > List *targetlist, int rtindex, > ! bool isExpr, List **righthandIds); > static bool subplan_is_hashable(SubLink *slink, SubPlan *node); > static Node *replace_correlation_vars_mutator(Node *node, void *context); > static Node *process_sublinks_mutator(Node *node, bool *isTopQual); > *************** > *** 240,245 **** > --- 240,251 ---- > */ > node->subLinkType = slink->subLinkType; > node->useOr = slink->useOr; > + node->isExpr = slink->isExpr; > + node->exprtype = InvalidOid; > + node->elemtype = InvalidOid; > + node->elmlen = 0; > + node->elmbyval = false; > + node->elmalign = '\0'; > node->exprs = NIL; > node->paramIds = NIL; > node->useHashTable = false; > *************** > *** 316,322 **** > exprs = convert_sublink_opers(lefthand, > slink->operOids, > plan->targetlist, > ! 0, > &node->paramIds); > node->setParam = listCopy(node->paramIds); > PlannerInitPlan = lappend(PlannerInitPlan, node); > --- 322,328 ---- > exprs = convert_sublink_opers(lefthand, > slink->operOids, > plan->targetlist, > ! 0, node->isExpr, > &node->paramIds); > node->setParam = listCopy(node->paramIds); > PlannerInitPlan = lappend(PlannerInitPlan, node); > *************** > *** 399,405 **** > node->exprs = convert_sublink_opers(lefthand, > slink->operOids, > plan->targetlist, > ! 0, > &node->paramIds); > > /* > --- 405,411 ---- > node->exprs = convert_sublink_opers(lefthand, > slink->operOids, > plan->targetlist, > ! 0, node->isExpr, > &node->paramIds); > > /* > *************** > *** 444,450 **** > static List * > convert_sublink_opers(List *lefthand, List *operOids, > List *targetlist, int rtindex, > ! List **righthandIds) > { > List *result = NIL; > List *lst; > --- 450,456 ---- > static List * > convert_sublink_opers(List *lefthand, List *operOids, > List *targetlist, int rtindex, > ! bool isExpr, List **righthandIds) > { > List *result = NIL; > List *lst; > *************** > *** 499,511 **** > * are not expecting to have to resolve unknown Params, so > * it's okay to pass a null pstate.) > */ > ! result = lappend(result, > ! make_op_expr(NULL, > ! tup, > ! leftop, > ! rightop, > ! exprType(leftop), > ! te->resdom->restype)); > > ReleaseSysCache(tup); > > --- 505,542 ---- > * are not expecting to have to resolve unknown Params, so > * it's okay to pass a null pstate.) > */ > ! if (!isExpr) > ! { > ! result = lappend(result, > ! make_op_expr(NULL, > ! tup, > ! leftop, > ! rightop, > ! exprType(leftop), > ! te->resdom->restype)); > ! } > ! else > ! { > ! Oid exprtype = te->resdom->restype; > ! Oid elemtype = get_element_type(exprtype); > ! > ! if (elemtype != InvalidOid) > ! result = lappend(result, > ! make_op_expr(NULL, > ! tup, > ! leftop, > ! rightop, > ! exprType(leftop), > ! elemtype)); > ! else > ! result = lappend(result, > ! make_op_expr(NULL, > ! tup, > ! leftop, > ! rightop, > ! exprType(leftop), > ! exprtype)); > ! } > > ReleaseSysCache(tup); > > *************** > *** 616,628 **** > /* > * The sublink type must be "= ANY" --- that is, an IN operator. > * (We require the operator name to be unqualified, which may be > ! * overly paranoid, or may not be.) > */ > if (sublink->subLinkType != ANY_SUBLINK) > return NULL; > if (length(sublink->operName) != 1 || > strcmp(strVal(lfirst(sublink->operName)), "=") != 0) > return NULL; > /* > * The sub-select must not refer to any Vars of the parent query. > * (Vars of higher levels should be okay, though.) > --- 647,663 ---- > /* > * The sublink type must be "= ANY" --- that is, an IN operator. > * (We require the operator name to be unqualified, which may be > ! * overly paranoid, or may not be.) It must not be an Expression > ! * sublink. > */ > if (sublink->subLinkType != ANY_SUBLINK) > return NULL; > if (length(sublink->operName) != 1 || > strcmp(strVal(lfirst(sublink->operName)), "=") != 0) > return NULL; > + if (sublink->isExpr) > + return NULL; > + > /* > * The sub-select must not refer to any Vars of the parent query. > * (Vars of higher levels should be okay, though.) > *************** > *** 675,681 **** > exprs = convert_sublink_opers(sublink->lefthand, > sublink->operOids, > subselect->targetList, > ! rtindex, > &ininfo->sub_targetlist); > return (Node *) make_ands_explicit(exprs); > } > --- 710,716 ---- > exprs = convert_sublink_opers(sublink->lefthand, > sublink->operOids, > subselect->targetList, > ! rtindex, sublink->isExpr, > &ininfo->sub_targetlist); > return (Node *) make_ands_explicit(exprs); > } > Index: src/backend/optimizer/util/clauses.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v > retrieving revision 1.138 > diff -c -r1.138 clauses.c > *** src/backend/optimizer/util/clauses.c 28 May 2003 22:32:49 -0000 1.138 > --- src/backend/optimizer/util/clauses.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 133,138 **** > --- 133,160 ---- > } > > /***************************************************************************** > + * FUNCTION clause functions > + *****************************************************************************/ > + > + /* > + * make_funcclause > + * Creates a function clause given its function info and argument list. > + */ > + Expr * > + make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset, > + CoercionForm funcformat, List *funcargs) > + { > + FuncExpr *expr = makeNode(FuncExpr); > + > + expr->funcid = funcid; > + expr->funcresulttype = funcresulttype; > + expr->funcretset = funcretset; > + expr->funcformat = funcformat; > + expr->args = funcargs; > + return (Expr *) expr; > + } > + > + /***************************************************************************** > * NOT clause functions > *****************************************************************************/ > > Index: src/backend/parser/gram.y > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/gram.y,v > retrieving revision 2.416 > diff -c -r2.416 gram.y > *** src/backend/parser/gram.y 29 May 2003 20:40:36 -0000 2.416 > --- src/backend/parser/gram.y 9 Jun 2003 01:31:10 -0000 > *************** > *** 5490,5495 **** > --- 5490,5496 ---- > { > SubLink *n = makeNode(SubLink); > n->subLinkType = ANY_SUBLINK; > + n->isExpr = false; > n->lefthand = $1; > n->operName = makeList1(makeString("=")); > n->subselect = $3; > *************** > *** 5500,5505 **** > --- 5501,5507 ---- > /* Make an IN node */ > SubLink *n = makeNode(SubLink); > n->subLinkType = ANY_SUBLINK; > + n->isExpr = false; > n->lefthand = $1; > n->operName = makeList1(makeString("=")); > n->subselect = $4; > *************** > *** 5511,5516 **** > --- 5513,5519 ---- > { > SubLink *n = makeNode(SubLink); > n->subLinkType = $3; > + n->isExpr = false; > n->lefthand = $1; > n->operName = $2; > n->subselect = $4; > *************** > *** 5521,5526 **** > --- 5524,5530 ---- > { > SubLink *n = makeNode(SubLink); > n->subLinkType = MULTIEXPR_SUBLINK; > + n->isExpr = false; > n->lefthand = $1; > n->operName = $2; > n->subselect = $3; > *************** > *** 5904,5909 **** > --- 5908,5914 ---- > { > SubLink *n = (SubLink *)$3; > n->subLinkType = ANY_SUBLINK; > + n->isExpr = false; > n->lefthand = makeList1($1); > n->operName = makeList1(makeString("=")); > $$ = (Node *)n; > *************** > *** 5931,5936 **** > --- 5936,5942 ---- > { > /* Make an IN node */ > SubLink *n = (SubLink *)$4; > + n->isExpr = false; > n->subLinkType = ANY_SUBLINK; > n->lefthand = makeList1($1); > n->operName = makeList1(makeString("=")); > *************** > *** 5957,5967 **** > --- 5963,6000 ---- > { > SubLink *n = makeNode(SubLink); > n->subLinkType = $3; > + n->isExpr = false; > n->lefthand = makeList1($1); > n->operName = $2; > n->subselect = $4; > $$ = (Node *)n; > } > + | a_expr qual_all_Op sub_type '(' a_expr ')' %prec Op > + { > + SubLink *n = makeNode(SubLink); > + SelectStmt *s = makeNode(SelectStmt); > + ResTarget *r = makeNode(ResTarget); > + > + r->name = NULL; > + r->indirection = NIL; > + r->val = (Node *)$5; > + > + s->distinctClause = NIL; > + s->targetList = makeList1(r); > + s->into = NULL; > + s->intoColNames = NIL; > + s->fromClause = NIL; > + s->whereClause = NULL; > + s->groupClause = NIL; > + s->havingClause = NULL; > + > + n->subLinkType = $3; > + n->isExpr = true; > + n->lefthand = makeList1($1); > + n->operName = $2; > + n->subselect = (Node *) s; > + $$ = (Node *)n; > + } > | UNIQUE select_with_parens %prec Op > { > /* Not sure how to get rid of the parentheses > *************** > *** 6538,6543 **** > --- 6571,6577 ---- > { > SubLink *n = makeNode(SubLink); > n->subLinkType = EXPR_SUBLINK; > + n->isExpr = false; > n->lefthand = NIL; > n->operName = NIL; > n->subselect = $1; > *************** > *** 6547,6552 **** > --- 6581,6587 ---- > { > SubLink *n = makeNode(SubLink); > n->subLinkType = EXISTS_SUBLINK; > + n->isExpr = false; > n->lefthand = NIL; > n->operName = NIL; > n->subselect = $2; > *************** > *** 6556,6561 **** > --- 6591,6597 ---- > { > SubLink *n = makeNode(SubLink); > n->subLinkType = ARRAY_SUBLINK; > + n->isExpr = false; > n->lefthand = NIL; > n->operName = NIL; > n->subselect = $2; > *************** > *** 6730,6735 **** > --- 6766,6772 ---- > in_expr: select_with_parens > { > SubLink *n = makeNode(SubLink); > + n->isExpr = false; > n->subselect = $1; > /* other fields will be filled later */ > $$ = (Node *)n; > Index: src/backend/parser/parse_coerce.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v > retrieving revision 2.97 > diff -c -r2.97 parse_coerce.c > *** src/backend/parser/parse_coerce.c 26 May 2003 00:11:27 -0000 2.97 > --- src/backend/parser/parse_coerce.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 859,865 **** > /* Get the element type based on the array type, if we have one */ > if (OidIsValid(array_typeid)) > { > ! array_typelem = get_element_type(array_typeid); > if (!OidIsValid(array_typelem)) > elog(ERROR, "Argument declared ANYARRAY is not an array: %s", > format_type_be(array_typeid)); > --- 859,869 ---- > /* Get the element type based on the array type, if we have one */ > if (OidIsValid(array_typeid)) > { > ! if (array_typeid != ANYARRAYOID) > ! array_typelem = get_element_type(array_typeid); > ! else > ! array_typelem = ANYELEMENTOID; > ! > if (!OidIsValid(array_typelem)) > elog(ERROR, "Argument declared ANYARRAY is not an array: %s", > format_type_be(array_typeid)); > *************** > *** 919,925 **** > { > if (!OidIsValid(array_typeid)) > { > ! array_typeid = get_array_type(elem_typeid); > if (!OidIsValid(array_typeid)) > elog(ERROR, "Cannot find array type for datatype %s", > format_type_be(elem_typeid)); > --- 923,933 ---- > { > if (!OidIsValid(array_typeid)) > { > ! if (elem_typeid != ANYELEMENTOID) > ! array_typeid = get_array_type(elem_typeid); > ! else > ! array_typeid = ANYARRAYOID; > ! > if (!OidIsValid(array_typeid)) > elog(ERROR, "Cannot find array type for datatype %s", > format_type_be(elem_typeid)); > *************** > *** 1169,1174 **** > --- 1177,1187 ---- > /* Somewhat-fast path for domain -> base type case */ > if (srctype == targettype) > return true; > + > + /* Last of the fast-paths: check for matching polymorphic arrays */ > + if (targettype == ANYARRAYOID) > + if (get_element_type(srctype) != InvalidOid) > + return true; > > /* Else look in pg_cast */ > tuple = SearchSysCache(CASTSOURCETARGET, > Index: src/backend/parser/parse_expr.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_expr.c,v > retrieving revision 1.148 > diff -c -r1.148 parse_expr.c > *** src/backend/parser/parse_expr.c 29 Apr 2003 22:13:10 -0000 1.148 > --- src/backend/parser/parse_expr.c 9 Jun 2003 01:42:58 -0000 > *************** > *** 436,441 **** > --- 436,442 ---- > sublink->operName = NIL; > sublink->operOids = NIL; > sublink->useOr = FALSE; > + sublink->isExpr = FALSE; > } > else if (sublink->subLinkType == EXPR_SUBLINK || > sublink->subLinkType == ARRAY_SUBLINK) > *************** > *** 463,468 **** > --- 464,470 ---- > sublink->operName = NIL; > sublink->operOids = NIL; > sublink->useOr = FALSE; > + sublink->isExpr = FALSE; > } > else > { > *************** > *** 538,547 **** > * here, because make_subplan() will insert type > * coercion calls if needed. > */ > ! optup = oper(op, > ! exprType(lexpr), > ! exprType((Node *) tent->expr), > ! false); > opform = (Form_pg_operator) GETSTRUCT(optup); > > if (opform->oprresult != BOOLOID) > --- 540,569 ---- > * here, because make_subplan() will insert type > * coercion calls if needed. > */ > ! if (!sublink->isExpr) > ! { > ! optup = oper(op, > ! exprType(lexpr), > ! exprType((Node *) tent->expr), > ! false); > ! } > ! else > ! { > ! Oid exprtype = exprType((Node *) tent->expr); > ! Oid elemtype = get_element_type(exprtype); > ! > ! if (elemtype != InvalidOid) > ! optup = oper(op, > ! exprType(lexpr), > ! elemtype, > ! false); > ! else > ! optup = oper(op, > ! exprType(lexpr), > ! exprtype, > ! false); > ! } > ! > opform = (Form_pg_operator) GETSTRUCT(optup); > > if (opform->oprresult != BOOLOID) > *************** > *** 743,749 **** > ArrayExpr *e = (ArrayExpr *) lfirst(element); > > if (!IsA(e, ArrayExpr)) > ! elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions"); > if (ndims == 0) > ndims = e->ndims; > else if (e->ndims != ndims) > --- 765,771 ---- > ArrayExpr *e = (ArrayExpr *) lfirst(element); > > if (!IsA(e, ArrayExpr)) > ! elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions"); > if (ndims == 0) > ndims = e->ndims; > else if (e->ndims != ndims) > Index: src/backend/parser/parse_func.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v > retrieving revision 1.148 > diff -c -r1.148 parse_func.c > *** src/backend/parser/parse_func.c 26 May 2003 00:11:27 -0000 1.148 > --- src/backend/parser/parse_func.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 335,340 **** > --- 335,341 ---- > aggref->target = lfirst(fargs); > aggref->aggstar = agg_star; > aggref->aggdistinct = agg_distinct; > + aggref->args = fargs; > > retval = (Node *) aggref; > > Index: src/backend/parser/parse_oper.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v > retrieving revision 1.64 > diff -c -r1.64 parse_oper.c > *** src/backend/parser/parse_oper.c 26 May 2003 00:11:27 -0000 1.64 > --- src/backend/parser/parse_oper.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 137,142 **** > --- 137,169 ---- > equality_oper(Oid argtype, bool noError) > { > Operator optup; > + Oid elem_type = get_element_type(argtype); > + > + if (OidIsValid(elem_type)) > + { > + bool found = false; > + /* > + * If the datatype is an array, look for an "=" operator for the > + * element datatype. We require it to be an exact or binary-compatible > + * match, since most callers are not prepared to cope with adding any > + * run-time type coercion steps. > + */ > + optup = equality_oper(elem_type, true); > + if (optup != NULL) > + { > + found = true; > + ReleaseSysCache(optup); > + } > + > + if (!found) > + { > + if (!noError) > + elog(ERROR, "Unable to identify an equality operator for " \ > + "array type's element type %s", > + format_type_be(elem_type)); > + return NULL; > + } > + } > > /* > * Look for an "=" operator for the datatype. We require it to be > *************** > *** 175,180 **** > --- 202,234 ---- > ordering_oper(Oid argtype, bool noError) > { > Operator optup; > + Oid elem_type = get_element_type(argtype); > + > + if (OidIsValid(elem_type)) > + { > + bool found = false; > + /* > + * If the datatype is an array, find the array element type's equality > + * operator, and use its lsortop (it *must* be mergejoinable). We use > + * this definition because for sorting and grouping purposes, it's > + * important that the equality and ordering operators are consistent. > + */ > + optup = ordering_oper(elem_type, true); > + if (optup != NULL) > + { > + found = true; > + ReleaseSysCache(optup); > + } > + > + if (!found) > + { > + if (!noError) > + elog(ERROR, "Unable to identify an ordering operator for " \ > + "array type's element type %s", > + format_type_be(elem_type)); > + return NULL; > + } > + } > > /* > * Find the type's equality operator, and use its lsortop (it *must* > *************** > *** 215,220 **** > --- 269,289 ---- > Oid result; > > optup = equality_oper(argtype, false); > + result = oprfuncid(optup); > + ReleaseSysCache(optup); > + return result; > + } > + > + /* > + * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper()) > + */ > + Oid > + ordering_oper_funcid(Oid argtype) > + { > + Operator optup; > + Oid result; > + > + optup = ordering_oper(argtype, false); > result = oprfuncid(optup); > ReleaseSysCache(optup); > return result; > Index: src/backend/utils/adt/acl.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v > retrieving revision 1.86 > diff -c -r1.86 acl.c > *** src/backend/utils/adt/acl.c 24 Jan 2003 21:53:29 -0000 1.86 > --- src/backend/utils/adt/acl.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 720,732 **** > aidat = ACL_DAT(acl); > for (i = 0; i < num; ++i) > { > ! if (aip->ai_grantee == aidat[i].ai_grantee && > ! aip->ai_privs == aidat[i].ai_privs) > PG_RETURN_BOOL(true); > } > PG_RETURN_BOOL(false); > } > > > /* > * has_table_privilege variants > --- 720,740 ---- > aidat = ACL_DAT(acl); > for (i = 0; i < num; ++i) > { > ! if (aclitemeq(aip, &aidat[i])) > PG_RETURN_BOOL(true); > } > PG_RETURN_BOOL(false); > } > > + /* > + * 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))); > + } > > /* > * has_table_privilege variants > Index: src/backend/utils/adt/array_userfuncs.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v > retrieving revision 1.1 > diff -c -r1.1 array_userfuncs.c > *** src/backend/utils/adt/array_userfuncs.c 8 Apr 2003 23:20:02 -0000 1.1 > --- src/backend/utils/adt/array_userfuncs.c 9 Jun 2003 01:46:03 -0000 > *************** > *** 18,52 **** > #include "utils/lsyscache.h" > #include "utils/syscache.h" > > - > - /*----------------------------------------------------------------------------- > - * singleton_array : > - * Form a multi-dimensional array given one starting element. > - * > - * - first argument is the datum with which to build the array > - * - second argument is the number of dimensions the array should have; > - * defaults to 1 if no second argument is provided > - *---------------------------------------------------------------------------- > - */ > - Datum > - singleton_array(PG_FUNCTION_ARGS) > - { > - Oid elem_type = get_fn_expr_argtype(fcinfo, 0); > - int ndims; > - > - if (elem_type == InvalidOid) > - elog(ERROR, "Cannot determine input datatype"); > - > - if (PG_NARGS() == 2) > - ndims = PG_GETARG_INT32(1); > - else > - ndims = 1; > - > - PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type, > - PG_GETARG_DATUM(0), > - ndims)); > - } > - > /*----------------------------------------------------------------------------- > * array_push : > * push an element onto either end of a one-dimensional array > --- 18,23 ---- > *************** > *** 70,75 **** > --- 41,47 ---- > Oid arg1_typeid = get_fn_expr_argtype(fcinfo, 1); > Oid arg0_elemid; > Oid arg1_elemid; > + ArrayMetaState *my_extra; > > if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid) > elog(ERROR, "array_push: cannot determine input data types"); > *************** > *** 95,122 **** > PG_RETURN_NULL(); /* keep compiler quiet */ > } > > ! /* Sanity check: do we have a one-dimensional array */ > ! if (ARR_NDIM(v) != 1) > ! elog(ERROR, "Arrays greater than one-dimension are not supported"); > ! > ! lb = ARR_LBOUND(v); > ! dimv = ARR_DIMS(v); > ! if (arg0_elemid != InvalidOid) > { > ! /* append newelem */ > ! int ub = dimv[0] + lb[0] - 1; > ! indx = ub + 1; > } > else > { > ! /* prepend newelem */ > ! indx = lb[0] - 1; > } > > ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); > > ! result = array_set(v, 1, &indx, newelem, -1, > ! typlen, typbyval, typalign, &isNull); > > PG_RETURN_ARRAYTYPE_P(result); > } > --- 67,127 ---- > PG_RETURN_NULL(); /* keep compiler quiet */ > } > > ! if (ARR_NDIM(v) == 1) > { > ! lb = ARR_LBOUND(v); > ! dimv = ARR_DIMS(v); > ! > ! if (arg0_elemid != InvalidOid) > ! { > ! /* append newelem */ > ! int ub = dimv[0] + lb[0] - 1; > ! indx = ub + 1; > ! } > ! else > ! { > ! /* prepend newelem */ > ! indx = lb[0] - 1; > ! } > } > + else if (ARR_NDIM(v) == 0) > + indx = 1; > else > + elog(ERROR, "only empty and one-dimensional arrays are supported"); > + > + > + /* > + * We arrange to look up info about element type only once per series > + * of calls, assuming the element type doesn't change underneath us. > + */ > + my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > + if (my_extra == NULL) > { > ! fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, > ! sizeof(ArrayMetaState)); > ! my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > ! my_extra->element_type = InvalidOid; > } > > ! if (my_extra->element_type != element_type) > ! { > ! /* Get info about element type */ > ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); > > ! my_extra->element_type = element_type; > ! my_extra->typlen = typlen; > ! my_extra->typbyval = typbyval; > ! my_extra->typalign = typalign; > ! } > ! else > ! { > ! typlen = my_extra->typlen; > ! typbyval = my_extra->typbyval; > ! typalign = my_extra->typalign; > ! } > ! > ! result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval, > ! typalign, &isNull); > > PG_RETURN_ARRAYTYPE_P(result); > } > *************** > *** 145,157 **** > > /* > * We must have one of the following combinations of inputs: > ! * 1) two arrays with ndims1 == ndims2 > ! * 2) ndims1 == ndims2 - 1 > ! * 3) ndims1 == ndims2 + 1 > */ > ndims1 = ARR_NDIM(v1); > ndims2 = ARR_NDIM(v2); > > if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1) > elog(ERROR, "Cannot concatenate incompatible arrays of %d and " > "%d dimensions", ndims1, ndims2); > --- 150,177 ---- > > /* > * We must have one of the following combinations of inputs: > ! * 1) one empty array, and one non-empty array > ! * 2) both arrays empty > ! * 3) two arrays with ndims1 == ndims2 > ! * 4) ndims1 == ndims2 - 1 > ! * 5) ndims1 == ndims2 + 1 > */ > ndims1 = ARR_NDIM(v1); > ndims2 = ARR_NDIM(v2); > > + /* > + * short circuit - if one input array is empty, and the other is not, > + * we return the non-empty one as the result > + * > + * if both are empty, return the first one > + */ > + if (ndims1 == 0 && ndims2 > 0) > + PG_RETURN_ARRAYTYPE_P(v2); > + > + if (ndims2 == 0) > + PG_RETURN_ARRAYTYPE_P(v1); > + > + /* the rest fall into combo 2, 3, or 4 */ > if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1) > elog(ERROR, "Cannot concatenate incompatible arrays of %d and " > "%d dimensions", ndims1, ndims2); > *************** > *** 266,412 **** > PG_RETURN_ARRAYTYPE_P(result); > } > > - /*---------------------------------------------------------------------------- > - * array_accum : > - * accumulator to build a 1-D array from input values -- this can be used > - * to create custom aggregates. > - * > - * This function is not marked strict, so we have to be careful about nulls. > - *---------------------------------------------------------------------------- > - */ > - Datum > - array_accum(PG_FUNCTION_ARGS) > - { > - /* return NULL if both arguments are NULL */ > - if (PG_ARGISNULL(0) && PG_ARGISNULL(1)) > - PG_RETURN_NULL(); > - > - /* create a new 1-D array from the new element if the array is NULL */ > - if (PG_ARGISNULL(0)) > - { > - Oid tgt_type = get_fn_expr_rettype(fcinfo); > - Oid tgt_elem_type; > - > - if (tgt_type == InvalidOid) > - elog(ERROR, "Cannot determine target array type"); > - tgt_elem_type = get_element_type(tgt_type); > - if (tgt_elem_type == InvalidOid) > - elog(ERROR, "Target type is not an array"); > - > - PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type, > - PG_GETARG_DATUM(1), > - 1)); > - } > - > - /* return the array if the new element is NULL */ > - if (PG_ARGISNULL(1)) > - PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0)); > - > - /* > - * Otherwise this is equivalent to array_push. We hack the call a little > - * so that array_push can see the fn_expr information. > - */ > - return array_push(fcinfo); > - } > - > - /*----------------------------------------------------------------------------- > - * array_assign : > - * assign an element of an array to a new value and return the > - * redefined array > - *---------------------------------------------------------------------------- > - */ > - Datum > - array_assign(PG_FUNCTION_ARGS) > - { > - ArrayType *v; > - int idx_to_chg; > - Datum newelem; > - int *dimv, > - *lb, ub; > - ArrayType *result; > - bool isNull; > - Oid element_type; > - int16 typlen; > - bool typbyval; > - char typalign; > - > - v = PG_GETARG_ARRAYTYPE_P(0); > - idx_to_chg = PG_GETARG_INT32(1); > - newelem = PG_GETARG_DATUM(2); > - > - /* Sanity check: do we have a one-dimensional array */ > - if (ARR_NDIM(v) != 1) > - elog(ERROR, "Arrays greater than one-dimension are not supported"); > - > - lb = ARR_LBOUND(v); > - dimv = ARR_DIMS(v); > - ub = dimv[0] + lb[0] - 1; > - if (idx_to_chg < lb[0] || idx_to_chg > ub) > - elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg); > - > - element_type = ARR_ELEMTYPE(v); > - /* Sanity check: do we have a non-zero element type */ > - if (element_type == 0) > - elog(ERROR, "Invalid array element type: %u", element_type); > - > - get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); > - > - result = array_set(v, 1, &idx_to_chg, newelem, -1, > - typlen, typbyval, typalign, &isNull); > - > - PG_RETURN_ARRAYTYPE_P(result); > - } > - > - /*----------------------------------------------------------------------------- > - * array_subscript : > - * return specific element of an array > - *---------------------------------------------------------------------------- > - */ > - Datum > - array_subscript(PG_FUNCTION_ARGS) > - { > - ArrayType *v; > - int idx; > - int *dimv, > - *lb, ub; > - Datum result; > - bool isNull; > - Oid element_type; > - int16 typlen; > - bool typbyval; > - char typalign; > - > - v = PG_GETARG_ARRAYTYPE_P(0); > - idx = PG_GETARG_INT32(1); > - > - /* Sanity check: do we have a one-dimensional array */ > - if (ARR_NDIM(v) != 1) > - elog(ERROR, "Arrays greater than one-dimension are not supported"); > - > - lb = ARR_LBOUND(v); > - dimv = ARR_DIMS(v); > - ub = dimv[0] + lb[0] - 1; > - if (idx < lb[0] || idx > ub) > - elog(ERROR, "Cannot return nonexistent array element: %d", idx); > - > - element_type = ARR_ELEMTYPE(v); > - /* Sanity check: do we have a non-zero element type */ > - if (element_type == 0) > - elog(ERROR, "Invalid array element type: %u", element_type); > - > - get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); > - > - result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull); > - > - PG_RETURN_DATUM(result); > - } > > /* > ! * actually does the work for singleton_array(), and array_accum() if it is > ! * given a null input array. > */ > ArrayType * > ! create_singleton_array(Oid element_type, Datum element, int ndims) > { > Datum dvalues[1]; > int16 typlen; > --- 286,300 ---- > PG_RETURN_ARRAYTYPE_P(result); > } > > > /* > ! * used by text_to_array() in varlena.c > */ > ArrayType * > ! create_singleton_array(FunctionCallInfo fcinfo, > ! Oid element_type, > ! Datum element, > ! int ndims) > { > Datum dvalues[1]; > int16 typlen; > *************** > *** 415,420 **** > --- 303,309 ---- > int dims[MAXDIM]; > int lbs[MAXDIM]; > int i; > + ArrayMetaState *my_extra; > > if (element_type == 0) > elog(ERROR, "Invalid array element type: %u", element_type); > *************** > *** 429,435 **** > lbs[i] = 1; > } > > ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); > > return construct_md_array(dvalues, ndims, dims, lbs, element_type, > typlen, typbyval, typalign); > --- 318,352 ---- > lbs[i] = 1; > } > > ! /* > ! * We arrange to look up info about element type only once per series > ! * of calls, assuming the element type doesn't change underneath us. > ! */ > ! my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > ! if (my_extra == NULL) > ! { > ! fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, > ! sizeof(ArrayMetaState)); > ! my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > ! my_extra->element_type = InvalidOid; > ! } > ! > ! if (my_extra->element_type != element_type) > ! { > ! /* Get info about element type */ > ! get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); > ! > ! my_extra->element_type = element_type; > ! my_extra->typlen = typlen; > ! my_extra->typbyval = typbyval; > ! my_extra->typalign = typalign; > ! } > ! else > ! { > ! typlen = my_extra->typlen; > ! typbyval = my_extra->typbyval; > ! typalign = my_extra->typalign; > ! } > > return construct_md_array(dvalues, ndims, dims, lbs, element_type, > typlen, typbyval, typalign); > Index: src/backend/utils/adt/arrayfuncs.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/arrayfuncs.c,v > retrieving revision 1.89 > diff -c -r1.89 arrayfuncs.c > *** src/backend/utils/adt/arrayfuncs.c 9 May 2003 23:01:45 -0000 1.89 > --- src/backend/utils/adt/arrayfuncs.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 21,28 **** > --- 21,30 ---- > #include "catalog/pg_type.h" > #include "libpq/pqformat.h" > #include "parser/parse_coerce.h" > + #include "parser/parse_oper.h" > #include "utils/array.h" > #include "utils/builtins.h" > + #include "utils/datum.h" > #include "utils/memutils.h" > #include "utils/lsyscache.h" > #include "utils/syscache.h" > *************** > *** 70,85 **** > > #define RETURN_NULL(type) do { *isNull = true; return (type) 0; } while (0) > > - /* I/O function selector for system_cache_lookup */ > - typedef enum IOFuncSelector > - { > - IOFunc_input, > - IOFunc_output, > - IOFunc_receive, > - IOFunc_send > - } IOFuncSelector; > - > - > static int ArrayCount(char *str, int *dim, char typdelim); > static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim, > FmgrInfo *inputproc, Oid typelem, int32 typmod, > --- 72,77 ---- > *************** > *** 93,102 **** > static void CopyArrayEls(char *p, Datum *values, int nitems, > int typlen, bool typbyval, char typalign, > bool freedata); > - static void system_cache_lookup(Oid element_type, IOFuncSelector which_func, > - int *typlen, bool *typbyval, > - char *typdelim, Oid *typelem, > - Oid *proc, char *typalign); > static Datum ArrayCast(char *value, bool byval, int len); > static int ArrayCastAndSet(Datum src, > int typlen, bool typbyval, char typalign, > --- 85,90 ---- > *************** > *** 119,125 **** > char *destPtr, > int *st, int *endp, char *srcPtr, > int typlen, bool typbyval, char typalign); > ! > > /*--------------------------------------------------------------------- > * array_in : > --- 107,113 ---- > char *destPtr, > int *st, int *endp, char *srcPtr, > int typlen, bool typbyval, char typalign); > ! static int array_cmp(FunctionCallInfo fcinfo); > > /*--------------------------------------------------------------------- > * array_in : > *************** > *** 154,165 **** > dim[MAXDIM], > lBound[MAXDIM]; > char typalign; > > ! /* Get info about element type, including its input conversion proc */ > ! system_cache_lookup(element_type, IOFunc_input, > ! &typlen, &typbyval, &typdelim, > ! &typelem, &typinput, &typalign); > ! fmgr_info(typinput, &inputproc); > > /* Make a modifiable copy of the input */ > /* XXX why are we allocating an extra 2 bytes here? */ > --- 142,190 ---- > dim[MAXDIM], > lBound[MAXDIM]; > char typalign; > + ArrayMetaState *my_extra; > > ! /* > ! * We arrange to look up info about element type, including its input > ! * conversion proc only once per series of calls, assuming the element > ! * type doesn't change underneath us. > ! */ > ! my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > ! if (my_extra == NULL) > ! { > ! fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, > ! sizeof(ArrayMetaState)); > ! my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > ! my_extra->element_type = InvalidOid; > ! } > ! > ! if (my_extra->element_type != element_type) > ! { > ! /* Get info about element type, including its input conversion proc */ > ! get_type_metadata(element_type, IOFunc_input, > ! &typlen, &typbyval, &typdelim, > ! &typelem, &typinput, &typalign); > ! fmgr_info(typinput, &inputproc); > ! > ! my_extra->element_type = element_type; > ! my_extra->typlen = typlen; > ! my_extra->typbyval = typbyval; > ! my_extra->typdelim = typdelim; > ! my_extra->typelem = typelem; > ! my_extra->typiofunc = typinput; > ! my_extra->typalign = typalign; > ! my_extra->proc = inputproc; > ! } > ! else > ! { > ! typlen = my_extra->typlen; > ! typbyval = my_extra->typbyval; > ! typdelim = my_extra->typdelim; > ! typelem = my_extra->typelem; > ! typinput = my_extra->typiofunc; > ! typalign = my_extra->typalign; > ! inputproc = my_extra->proc; > ! } > > /* Make a modifiable copy of the input */ > /* XXX why are we allocating an extra 2 bytes here? */ > *************** > *** 636,647 **** > indx[MAXDIM]; > int ndim, > *dim; > > element_type = ARR_ELEMTYPE(v); > ! system_cache_lookup(element_type, IOFunc_output, > ! &typlen, &typbyval, &typdelim, > ! &typelem, &typoutput, &typalign); > ! fmgr_info(typoutput, &outputproc); > > ndim = ARR_NDIM(v); > dim = ARR_DIMS(v); > --- 661,711 ---- > indx[MAXDIM]; > int ndim, > *dim; > + ArrayMetaState *my_extra; > > element_type = ARR_ELEMTYPE(v); > ! > ! /* > ! * We arrange to look up info about element type, including its input > ! * conversion proc only once per series of calls, assuming the element > ! * type doesn't change underneath us. > ! */ > ! my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > ! if (my_extra == NULL) > ! { > ! fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, > ! sizeof(ArrayMetaState)); > ! my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > ! my_extra->element_type = InvalidOid; > ! } > ! > ! if (my_extra->element_type != element_type) > ! { > ! /* Get info about element type, including its output conversion proc */ > ! get_type_metadata(element_type, IOFunc_output, > ! &typlen, &typbyval, &typdelim, > ! &typelem, &typoutput, &typalign); > ! fmgr_info(typoutput, &outputproc); > ! > ! my_extra->element_type = element_type; > ! my_extra->typlen = typlen; > ! my_extra->typbyval = typbyval; > ! my_extra->typdelim = typdelim; > ! my_extra->typelem = typelem; > ! my_extra->typiofunc = typoutput; > ! my_extra->typalign = typalign; > ! my_extra->proc = outputproc; > ! } > ! else > ! { > ! typlen = my_extra->typlen; > ! typbyval = my_extra->typbyval; > ! typdelim = my_extra->typdelim; > ! typelem = my_extra->typelem; > ! typoutput = my_extra->typiofunc; > ! typalign = my_extra->typalign; > ! outputproc = my_extra->proc; > ! } > > ndim = ARR_NDIM(v); > dim = ARR_DIMS(v); > *************** > *** 800,805 **** > --- 864,870 ---- > dim[MAXDIM], > lBound[MAXDIM]; > char typalign; > + ArrayMetaState *my_extra; > > /* Get the array header information */ > ndim = pq_getmsgint(buf, 4); > *************** > *** 831,844 **** > PG_RETURN_ARRAYTYPE_P(retval); > } > > ! /* Get info about element type, including its receive conversion proc */ > ! system_cache_lookup(element_type, IOFunc_receive, > ! &typlen, &typbyval, &typdelim, > ! &typelem, &typreceive, &typalign); > ! if (!OidIsValid(typreceive)) > ! elog(ERROR, "No binary input function available for type %s", > ! format_type_be(element_type)); > ! fmgr_info(typreceive, &receiveproc); > > dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem, > typlen, typbyval, typalign, > --- 896,945 ---- > PG_RETURN_ARRAYTYPE_P(retval); > } > > ! /* > ! * We arrange to look up info about element type, including its receive > ! * conversion proc only once per series of calls, assuming the element > ! * type doesn't change underneath us. > ! */ > ! my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > ! if (my_extra == NULL) > ! { > ! fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, > ! sizeof(ArrayMetaState)); > ! my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > ! my_extra->element_type = InvalidOid; > ! } > ! > ! if (my_extra->element_type != element_type) > ! { > ! /* Get info about element type, including its receive conversion proc */ > ! get_type_metadata(element_type, IOFunc_receive, > ! &typlen, &typbyval, &typdelim, > ! &typelem, &typreceive, &typalign); > ! if (!OidIsValid(typreceive)) > ! elog(ERROR, "No binary input function available for type %s", > ! format_type_be(element_type)); > ! fmgr_info(typreceive, &receiveproc); > ! > ! my_extra->element_type = element_type; > ! my_extra->typlen = typlen; > ! my_extra->typbyval = typbyval; > ! my_extra->typdelim = typdelim; > ! my_extra->typelem = typelem; > ! my_extra->typiofunc = typreceive; > ! my_extra->typalign = typalign; > ! my_extra->proc = receiveproc; > ! } > ! else > ! { > ! typlen = my_extra->typlen; > ! typbyval = my_extra->typbyval; > ! typdelim = my_extra->typdelim; > ! typelem = my_extra->typelem; > ! typreceive = my_extra->typiofunc; > ! typalign = my_extra->typalign; > ! receiveproc = my_extra->proc; > ! } > > dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem, > typlen, typbyval, typalign, > *************** > *** 976,990 **** > int ndim, > *dim; > StringInfoData buf; > > /* Get information about the element type and the array dimensions */ > element_type = ARR_ELEMTYPE(v); > ! system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval, > ! &typdelim, &typelem, &typsend, &typalign); > ! if (!OidIsValid(typsend)) > ! elog(ERROR, "No binary output function available for type %s", > ! format_type_be(element_type)); > ! fmgr_info(typsend, &sendproc); > > ndim = ARR_NDIM(v); > dim = ARR_DIMS(v); > --- 1077,1130 ---- > int ndim, > *dim; > StringInfoData buf; > + ArrayMetaState *my_extra; > > /* Get information about the element type and the array dimensions */ > element_type = ARR_ELEMTYPE(v); > ! > ! /* > ! * We arrange to look up info about element type, including its send > ! * proc only once per series of calls, assuming the element > ! * type doesn't change underneath us. > ! */ > ! my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > ! if (my_extra == NULL) > ! { > ! fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, > ! sizeof(ArrayMetaState)); > ! my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > ! my_extra->element_type = InvalidOid; > ! } > ! > ! if (my_extra->element_type != element_type) > ! { > ! /* Get info about element type, including its send proc */ > ! get_type_metadata(element_type, IOFunc_send, &typlen, &typbyval, > ! &typdelim, &typelem, &typsend, &typalign); > ! if (!OidIsValid(typsend)) > ! elog(ERROR, "No binary output function available for type %s", > ! format_type_be(element_type)); > ! fmgr_info(typsend, &sendproc); > ! > ! my_extra->element_type = element_type; > ! my_extra->typlen = typlen; > ! my_extra->typbyval = typbyval; > ! my_extra->typdelim = typdelim; > ! my_extra->typelem = typelem; > ! my_extra->typiofunc = typsend; > ! my_extra->typalign = typalign; > ! my_extra->proc = sendproc; > ! } > ! else > ! { > ! typlen = my_extra->typlen; > ! typbyval = my_extra->typbyval; > ! typdelim = my_extra->typdelim; > ! typelem = my_extra->typelem; > ! typsend = my_extra->typiofunc; > ! typalign = my_extra->typalign; > ! sendproc = my_extra->proc; > ! } > > ndim = ARR_NDIM(v); > dim = ARR_DIMS(v); > *************** > *** 1476,1481 **** > --- 1616,1641 ---- > array = DatumGetArrayTypeP(PointerGetDatum(array)); > > ndim = ARR_NDIM(array); > + > + /* > + * if number of dims is zero, i.e. an empty array, create an array > + * with nSubscripts dimensions, and set the lower bounds to the supplied > + * subscripts > + */ > + if (ndim == 0) > + { > + Oid elmtype = ARR_ELEMTYPE(array); > + > + for (i = 0; i < nSubscripts; i++) > + { > + dim[i] = 1; > + lb[i] = indx[i]; > + } > + > + return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype, > + elmlen, elmbyval, elmalign); > + } > + > if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM) > elog(ERROR, "Invalid array subscripts"); > > *************** > *** 1632,1637 **** > --- 1792,1822 ---- > /* note: we assume srcArray contains no toasted elements */ > > ndim = ARR_NDIM(array); > + > + /* > + * if number of dims is zero, i.e. an empty array, create an array > + * with nSubscripts dimensions, and set the upper and lower bounds > + * to the supplied subscripts > + */ > + if (ndim == 0) > + { > + Datum *dvalues; > + int nelems; > + Oid elmtype = ARR_ELEMTYPE(array); > + > + deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign, > + &dvalues, &nelems); > + > + for (i = 0; i < nSubscripts; i++) > + { > + dim[i] = 1 + upperIndx[i] - lowerIndx[i]; > + lb[i] = lowerIndx[i]; > + } > + > + return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype, > + elmlen, elmbyval, elmalign); > + } > + > if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM) > elog(ERROR, "Invalid array subscripts"); > > *************** > *** 1811,1816 **** > --- 1996,2008 ---- > Oid typelem; > Oid proc; > char *s; > + typedef struct { > + ArrayMetaState *inp_extra; > + ArrayMetaState *ret_extra; > + } am_extra; > + am_extra *my_extra; > + ArrayMetaState *inp_extra; > + ArrayMetaState *ret_extra; > > /* Get input array */ > if (fcinfo->nargs < 1) > *************** > *** 1829,1839 **** > if (nitems <= 0) > PG_RETURN_ARRAYTYPE_P(v); > > ! /* Lookup source and result types. Unneeded variables are reused. */ > ! system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval, > ! &typdelim, &typelem, &proc, &inp_typalign); > ! system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval, > ! &typdelim, &typelem, &proc, &typalign); > > /* Allocate temporary array for new values */ > values = (Datum *) palloc(nitems * sizeof(Datum)); > --- 2021,2101 ---- > if (nitems <= 0) > PG_RETURN_ARRAYTYPE_P(v); > > ! /* > ! * We arrange to look up info about input and return element types only > ! * once per series of calls, assuming the element type doesn't change > ! * underneath us. > ! */ > ! my_extra = (am_extra *) fcinfo->flinfo->fn_extra; > ! if (my_extra == NULL) > ! { > ! fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, > ! sizeof(am_extra)); > ! my_extra = (am_extra *) fcinfo->flinfo->fn_extra; > ! > ! my_extra->inp_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, > ! sizeof(ArrayMetaState)); > ! inp_extra = my_extra->inp_extra; > ! inp_extra->element_type = InvalidOid; > ! > ! my_extra->ret_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, > ! sizeof(ArrayMetaState)); > ! ret_extra = my_extra->ret_extra; > ! ret_extra->element_type = InvalidOid; > ! } > ! else > ! { > ! inp_extra = my_extra->inp_extra; > ! ret_extra = my_extra->ret_extra; > ! } > ! > ! if (inp_extra->element_type != inpType) > ! { > ! /* Lookup source and result types. Unneeded variables are reused. */ > ! get_type_metadata(inpType, IOFunc_input, &inp_typlen, &inp_typbyval, > ! &typdelim, &typelem, &proc, &inp_typalign); > ! > ! inp_extra->element_type = inpType; > ! inp_extra->typlen = inp_typlen; > ! inp_extra->typbyval = inp_typbyval; > ! inp_extra->typdelim = typdelim; > ! inp_extra->typelem = typelem; > ! inp_extra->typiofunc = proc; > ! inp_extra->typalign = inp_typalign; > ! } > ! else > ! { > ! inp_typlen = inp_extra->typlen; > ! inp_typbyval = inp_extra->typbyval; > ! typdelim = inp_extra->typdelim; > ! typelem = inp_extra->typelem; > ! proc = inp_extra->typiofunc; > ! inp_typalign = inp_extra->typalign; > ! } > ! > ! if (ret_extra->element_type != retType) > ! { > ! /* Lookup source and result types. Unneeded variables are reused. */ > ! get_type_metadata(retType, IOFunc_input, &typlen, &typbyval, > ! &typdelim, &typelem, &proc, &typalign); > ! > ! ret_extra->element_type = retType; > ! ret_extra->typlen = typlen; > ! ret_extra->typbyval = typbyval; > ! ret_extra->typdelim = typdelim; > ! ret_extra->typelem = typelem; > ! ret_extra->typiofunc = proc; > ! ret_extra->typalign = typalign; > ! } > ! else > ! { > ! typlen = ret_extra->typlen; > ! typbyval = ret_extra->typbyval; > ! typdelim = ret_extra->typdelim; > ! typelem = ret_extra->typelem; > ! proc = ret_extra->typiofunc; > ! typalign = ret_extra->typalign; > ! } > > /* Allocate temporary array for new values */ > values = (Datum *) palloc(nitems * sizeof(Datum)); > *************** > *** 2049,2056 **** > * compares two arrays for equality > * result : > * returns true if the arrays are equal, false otherwise. > - * > - * XXX bitwise equality is pretty bogus ... > *----------------------------------------------------------------------------- > */ > Datum > --- 2311,2316 ---- > *************** > *** 2058,2069 **** > { > ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0); > ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1); > bool result = true; > > ! if (ARR_SIZE(array1) != ARR_SIZE(array2)) > ! result = false; > ! else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0) > result = false; > > /* Avoid leaking memory when handed toasted input. */ > PG_FREE_IF_COPY(array1, 0); > --- 2318,2435 ---- > { > ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0); > ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1); > + char *p1 = (char *) ARR_DATA_PTR(array1); > + char *p2 = (char *) ARR_DATA_PTR(array2); > + int ndims1 = ARR_NDIM(array1); > + int ndims2 = ARR_NDIM(array2); > + int *dims1 = ARR_DIMS(array1); > + int *dims2 = ARR_DIMS(array2); > + int nitems1 = ArrayGetNItems(ndims1, dims1); > + int nitems2 = ArrayGetNItems(ndims2, dims2); > + Oid element_type = ARR_ELEMTYPE(array1); > + FmgrInfo *ae_fmgr_info = fcinfo->flinfo; > bool result = true; > + int typlen; > + bool typbyval; > + char typdelim; > + Oid typelem; > + char typalign; > + Oid typiofunc; > + int i; > + ArrayMetaState *my_extra; > + FunctionCallInfoData locfcinfo; > > ! /* fast path if the arrays do not have the same number of elements */ > ! if (nitems1 != nitems2) > result = false; > + else > + { > + /* > + * We arrange to look up the equality function only once per series of > + * calls, assuming the element type doesn't change underneath us. > + */ > + my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra; > + if (my_extra == NULL) > + { > + ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt, > + sizeof(ArrayMetaState)); > + my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra; > + my_extra->element_type = InvalidOid; > + } > + > + if (my_extra->element_type != element_type) > + { > + Oid opfuncid = equality_oper_funcid(element_type); > + > + if (OidIsValid(opfuncid)) > + fmgr_info_cxt(opfuncid, &my_extra->proc, ae_fmgr_info->fn_mcxt); > + else > + elog(ERROR, > + "array_eq: cannot find equality operator for type: %u", > + element_type); > + > + get_type_metadata(element_type, IOFunc_output, > + &typlen, &typbyval, &typdelim, > + &typelem, &typiofunc, &typalign); > + > + my_extra->element_type = element_type; > + my_extra->typlen = typlen; > + my_extra->typbyval = typbyval; > + my_extra->typdelim = typdelim; > + my_extra->typelem = typelem; > + my_extra->typiofunc = typiofunc; > + my_extra->typalign = typalign; > + } > + else > + { > + typlen = my_extra->typlen; > + typbyval = my_extra->typbyval; > + typdelim = my_extra->typdelim; > + typelem = my_extra->typelem; > + typiofunc = my_extra->typiofunc; > + typalign = my_extra->typalign; > + } > + > + /* > + * apply the operator to each pair of array elements. > + */ > + MemSet(&locfcinfo, 0, sizeof(locfcinfo)); > + locfcinfo.flinfo = &my_extra->proc; > + locfcinfo.nargs = 2; > + > + /* Loop over source data */ > + for (i = 0; i < nitems1; i++) > + { > + Datum elt1; > + Datum elt2; > + bool oprresult; > + > + /* Get element pair */ > + elt1 = fetch_att(p1, typbyval, typlen); > + elt2 = fetch_att(p2, typbyval, typlen); > + > + p1 = att_addlength(p1, typlen, PointerGetDatum(p1)); > + p1 = (char *) att_align(p1, typalign); > + > + p2 = att_addlength(p2, typlen, PointerGetDatum(p2)); > + p2 = (char *) att_align(p2, typalign); > + > + /* > + * Apply the operator to the element pair > + */ > + locfcinfo.arg[0] = elt1; > + locfcinfo.arg[1] = elt2; > + locfcinfo.argnull[0] = false; > + locfcinfo.argnull[1] = false; > + locfcinfo.isnull = false; > + oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo)); > + if (!oprresult) > + { > + result = false; > + break; > + } > + } > + } > > /* Avoid leaking memory when handed toasted input. */ > PG_FREE_IF_COPY(array1, 0); > *************** > *** 2073,2125 **** > } > > > ! /***************************************************************************/ > ! /******************| Support Routines |*****************/ > ! /***************************************************************************/ > > ! static void > ! system_cache_lookup(Oid element_type, > ! IOFuncSelector which_func, > ! int *typlen, > ! bool *typbyval, > ! char *typdelim, > ! Oid *typelem, > ! Oid *proc, > ! char *typalign) > ! { > ! HeapTuple typeTuple; > ! Form_pg_type typeStruct; > ! > ! typeTuple = SearchSysCache(TYPEOID, > ! ObjectIdGetDatum(element_type), > ! 0, 0, 0); > ! if (!HeapTupleIsValid(typeTuple)) > ! elog(ERROR, "cache lookup failed for type %u", element_type); > ! typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); > ! > ! *typlen = typeStruct->typlen; > ! *typbyval = typeStruct->typbyval; > ! *typdelim = typeStruct->typdelim; > ! *typelem = typeStruct->typelem; > ! *typalign = typeStruct->typalign; > ! switch (which_func) > ! { > ! case IOFunc_input: > ! *proc = typeStruct->typinput; > ! break; > ! case IOFunc_output: > ! *proc = typeStruct->typoutput; > ! break; > ! case IOFunc_receive: > ! *proc = typeStruct->typreceive; > ! break; > ! case IOFunc_send: > ! *proc = typeStruct->typsend; > ! break; > } > ! ReleaseSysCache(typeTuple); > } > > /* > * Fetch array element at pointer, converted correctly to a Datum > */ > --- 2439,2628 ---- > } > > > ! /*----------------------------------------------------------------------------- > ! * array-array bool operators: > ! * Given two arrays, iterate comparison operators > ! * over the array. Uses logic similar to text comparison > ! * functions, except element-by-element instead of > ! * character-by-character. > ! *---------------------------------------------------------------------------- > ! */ > ! Datum > ! array_ne(PG_FUNCTION_ARGS) > ! { > ! PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo))); > ! } > > ! Datum > ! array_lt(PG_FUNCTION_ARGS) > ! { > ! PG_RETURN_BOOL(array_cmp(fcinfo) < 0); > ! } > ! > ! Datum > ! array_gt(PG_FUNCTION_ARGS) > ! { > ! PG_RETURN_BOOL(array_cmp(fcinfo) > 0); > ! } > ! > ! Datum > ! array_le(PG_FUNCTION_ARGS) > ! { > ! PG_RETURN_BOOL(array_cmp(fcinfo) <= 0); > ! } > ! > ! Datum > ! array_ge(PG_FUNCTION_ARGS) > ! { > ! PG_RETURN_BOOL(array_cmp(fcinfo) >= 0); > ! } > ! > ! Datum > ! btarraycmp(PG_FUNCTION_ARGS) > ! { > ! PG_RETURN_INT32(array_cmp(fcinfo)); > ! } > ! > ! /* > ! * array_cmp() > ! * Internal comparison function for arrays. > ! * > ! * Returns -1, 0 or 1 > ! */ > ! static int > ! array_cmp(FunctionCallInfo fcinfo) > ! { > ! ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0); > ! ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1); > ! FmgrInfo *ac_fmgr_info = fcinfo->flinfo; > ! Datum opresult; > ! int result = 0; > ! Oid element_type = InvalidOid; > ! int typlen; > ! bool typbyval; > ! char typdelim; > ! Oid typelem; > ! char typalign; > ! Oid typiofunc; > ! Datum *dvalues1; > ! int nelems1; > ! Datum *dvalues2; > ! int nelems2; > ! int min_nelems; > ! int i; > ! typedef struct > ! { > ! Oid element_type; > ! int typlen; > ! bool typbyval; > ! char typdelim; > ! Oid typelem; > ! Oid typiofunc; > ! char typalign; > ! FmgrInfo eqproc; > ! FmgrInfo ordproc; > ! } ac_extra; > ! ac_extra *my_extra; > ! > ! element_type = ARR_ELEMTYPE(array1); > ! > ! /* > ! * We arrange to look up the element type operator function only once > ! * per series of calls, assuming the element type and opname don't > ! * change underneath us. > ! */ > ! my_extra = (ac_extra *) ac_fmgr_info->fn_extra; > ! if (my_extra == NULL) > ! { > ! ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt, > ! sizeof(ac_extra)); > ! my_extra = (ac_extra *) ac_fmgr_info->fn_extra; > ! my_extra->element_type = InvalidOid; > ! } > ! > ! if (my_extra->element_type != element_type) > ! { > ! Oid eqfuncid = equality_oper_funcid(element_type); > ! Oid ordfuncid = ordering_oper_funcid(element_type); > ! > ! fmgr_info_cxt(eqfuncid, &my_extra->eqproc, ac_fmgr_info->fn_mcxt); > ! fmgr_info_cxt(ordfuncid, &my_extra->ordproc, ac_fmgr_info->fn_mcxt); > ! > ! if (my_extra->eqproc.fn_nargs != 2) > ! elog(ERROR, "Equality operator does not take 2 arguments: %u", > ! eqfuncid); > ! if (my_extra->ordproc.fn_nargs != 2) > ! elog(ERROR, "Ordering operator does not take 2 arguments: %u", > ! ordfuncid); > ! > ! get_type_metadata(element_type, IOFunc_output, > ! &typlen, &typbyval, &typdelim, > ! &typelem, &typiofunc, &typalign); > ! > ! my_extra->element_type = element_type; > ! my_extra->typlen = typlen; > ! my_extra->typbyval = typbyval; > ! my_extra->typdelim = typdelim; > ! my_extra->typelem = typelem; > ! my_extra->typiofunc = InvalidOid; > ! my_extra->typalign = typalign; > ! } > ! else > ! { > ! typlen = my_extra->typlen; > ! typbyval = my_extra->typbyval; > ! typalign = my_extra->typalign; > ! } > ! > ! /* extract a C array of arg array datums */ > ! deconstruct_array(array1, element_type, typlen, typbyval, typalign, > ! &dvalues1, &nelems1); > ! > ! deconstruct_array(array2, element_type, typlen, typbyval, typalign, > ! &dvalues2, &nelems2); > ! > ! min_nelems = Min(nelems1, nelems2); > ! for (i = 0; i < min_nelems; i++) > ! { > ! /* are they equal */ > ! opresult = FunctionCall2(&my_extra->eqproc, > ! dvalues1[i], dvalues2[i]); > ! > ! if (!DatumGetBool(opresult)) > ! { > ! /* nope, see if arg1 is less than arg2 */ > ! opresult = FunctionCall2(&my_extra->ordproc, > ! dvalues1[i], dvalues2[i]); > ! if (DatumGetBool(opresult)) > ! { > ! /* arg1 is less than arg2 */ > ! result = -1; > ! break; > ! } > ! else > ! { > ! /* arg1 is greater than arg2 */ > ! result = 1; > ! break; > ! } > ! } > } > ! > ! if ((result == 0) && (nelems1 != nelems2)) > ! result = (nelems1 < nelems2) ? -1 : 1; > ! > ! /* Avoid leaking memory when handed toasted input. */ > ! PG_FREE_IF_COPY(array1, 0); > ! PG_FREE_IF_COPY(array2, 1); > ! > ! return result; > } > > + > + /***************************************************************************/ > + /******************| Support Routines |*****************/ > + /***************************************************************************/ > + > /* > * Fetch array element at pointer, converted correctly to a Datum > */ > *************** > *** 2423,2428 **** > --- 2926,2943 ---- > if (tgt_elem_type == InvalidOid) > elog(ERROR, "Target type is not an array"); > > + /* > + * We don't deal with domain constraints yet, so bail out. > + * This isn't currently a problem, because we also don't > + * support arrays of domain type elements either. But in the > + * future we might. At that point consideration should be given > + * to removing the check below and adding a domain constraints > + * check to the coercion. > + */ > + if (getBaseType(tgt_elem_type) != tgt_elem_type) > + elog(ERROR, "array coercion to domain type elements not " \ > + "currently supported"); > + > if (!find_coercion_pathway(tgt_elem_type, src_elem_type, > COERCION_EXPLICIT, &funcId)) > { > *************** > *** 2439,2448 **** > } > > /* > ! * If it's binary-compatible, return the array unmodified. > */ > if (my_extra->coerce_finfo.fn_oid == InvalidOid) > ! PG_RETURN_ARRAYTYPE_P(src); > > /* > * Use array_map to apply the function to each array element. > --- 2954,2969 ---- > } > > /* > ! * If it's binary-compatible, modify the element type in the array header, > ! * but otherwise leave the array as we received it. > */ > if (my_extra->coerce_finfo.fn_oid == InvalidOid) > ! { > ! ArrayType *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0)); > ! > ! ARR_ELEMTYPE(result) = my_extra->desttype; > ! PG_RETURN_ARRAYTYPE_P(result); > ! } > > /* > * Use array_map to apply the function to each array element. > *************** > *** 2453,2456 **** > --- 2974,3092 ---- > locfcinfo.arg[0] = PointerGetDatum(src); > > return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype); > + } > + > + /* > + * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK > + * > + * astate is working state (NULL on first call) > + * rcontext is where to keep working state > + */ > + ArrayBuildState * > + accumArrayResult(ArrayBuildState *astate, > + Datum dvalue, bool disnull, > + Oid element_type, > + MemoryContext rcontext) > + { > + MemoryContext arr_context, > + oldcontext; > + > + if (astate == NULL) > + { > + /* First time through --- initialize */ > + > + /* Make a temporary context to hold all the junk */ > + arr_context = AllocSetContextCreate(rcontext, > + "accumArrayResult", > + ALLOCSET_DEFAULT_MINSIZE, > + ALLOCSET_DEFAULT_INITSIZE, > + ALLOCSET_DEFAULT_MAXSIZE); > + oldcontext = MemoryContextSwitchTo(arr_context); > + astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState)); > + astate->mcontext = arr_context; > + astate->dvalues = (Datum *) > + palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum)); > + astate->nelems = 0; > + astate->element_type = element_type; > + get_typlenbyvalalign(element_type, > + &astate->typlen, > + &astate->typbyval, > + &astate->typalign); > + } > + else > + { > + oldcontext = MemoryContextSwitchTo(astate->mcontext); > + Assert(astate->element_type == element_type); > + /* enlarge dvalues[] if needed */ > + if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0) > + astate->dvalues = (Datum *) > + repalloc(astate->dvalues, > + (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum)); > + } > + > + if (disnull) > + elog(ERROR, "NULL elements not allowed in Arrays"); > + > + /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */ > + astate->dvalues[astate->nelems++] = > + datumCopy(dvalue, astate->typbyval, astate->typlen); > + > + MemoryContextSwitchTo(oldcontext); > + > + return astate; > + } > + > + /* > + * makeArrayResult - produce final result of accumArrayResult > + * > + * astate is working state (not NULL) > + * rcontext is where to construct result > + */ > + Datum > + makeArrayResult(ArrayBuildState *astate, > + MemoryContext rcontext) > + { > + int dims[1]; > + int lbs[1]; > + > + dims[0] = astate->nelems; > + lbs[0] = 1; > + > + return makeMdArrayResult(astate, 1, dims, lbs, rcontext); > + } > + > + /* > + * makeMdArrayResult - produce md final result of accumArrayResult > + * > + * astate is working state (not NULL) > + * rcontext is where to construct result > + */ > + Datum > + makeMdArrayResult(ArrayBuildState *astate, > + int ndims, > + int *dims, > + int *lbs, > + MemoryContext rcontext) > + { > + ArrayType *result; > + MemoryContext oldcontext; > + > + /* Build the final array result in rcontext */ > + oldcontext = MemoryContextSwitchTo(rcontext); > + > + result = construct_md_array(astate->dvalues, > + ndims, > + dims, > + lbs, > + astate->element_type, > + astate->typlen, > + astate->typbyval, > + astate->typalign); > + > + MemoryContextSwitchTo(oldcontext); > + > + /* Clean up all the junk */ > + MemoryContextDelete(astate->mcontext); > + > + return PointerGetDatum(result); > } > Index: src/backend/utils/adt/varlena.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/varlena.c,v > retrieving revision 1.98 > diff -c -r1.98 varlena.c > *** src/backend/utils/adt/varlena.c 15 May 2003 15:50:19 -0000 1.98 > --- src/backend/utils/adt/varlena.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 19,29 **** > --- 19,32 ---- > #include "mb/pg_wchar.h" > #include "miscadmin.h" > #include "access/tuptoaster.h" > + #include "catalog/pg_type.h" > #include "lib/stringinfo.h" > #include "libpq/crypt.h" > #include "libpq/pqformat.h" > + #include "utils/array.h" > #include "utils/builtins.h" > #include "utils/pg_locale.h" > + #include "utils/lsyscache.h" > > > typedef struct varlena unknown; > *************** > *** 1983,1990 **** > if (fldnum == 1) /* first field - just return the input > * string */ > PG_RETURN_TEXT_P(inputstring); > ! else > ! /* otherwise return an empty string */ > PG_RETURN_TEXT_P(PG_STR_GET_TEXT("")); > } > > --- 1986,1992 ---- > if (fldnum == 1) /* first field - just return the input > * string */ > PG_RETURN_TEXT_P(inputstring); > ! else /* otherwise return an empty string */ > PG_RETURN_TEXT_P(PG_STR_GET_TEXT("")); > } > > *************** > *** 2004,2011 **** > if (fldnum == 1) /* first field - just return the input > * string */ > PG_RETURN_TEXT_P(inputstring); > ! else > ! /* otherwise return an empty string */ > PG_RETURN_TEXT_P(PG_STR_GET_TEXT("")); > } > else if ((start_posn != 0) && (end_posn == 0)) > --- 2006,2012 ---- > if (fldnum == 1) /* first field - just return the input > * string */ > PG_RETURN_TEXT_P(inputstring); > ! else /* otherwise return an empty string */ > PG_RETURN_TEXT_P(PG_STR_GET_TEXT("")); > } > else if ((start_posn != 0) && (end_posn == 0)) > *************** > *** 2026,2031 **** > --- 2027,2217 ---- > result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn - fldsep_len,false); > PG_RETURN_TEXT_P(result_text); > } > + } > + > + /* > + * text_to_array > + * parse input string > + * return text array of elements > + * based on provided field separator > + */ > + Datum > + text_to_array(PG_FUNCTION_ARGS) > + { > + text *inputstring = PG_GETARG_TEXT_P(0); > + int inputstring_len = TEXTLEN(inputstring); > + text *fldsep = PG_GETARG_TEXT_P(1); > + int fldsep_len = TEXTLEN(fldsep); > + int fldnum; > + int start_posn = 0; > + int end_posn = 0; > + text *result_text = NULL; > + ArrayBuildState *astate = NULL; > + MemoryContext oldcontext = CurrentMemoryContext; > + > + /* return NULL for empty input string */ > + if (inputstring_len < 1) > + PG_RETURN_NULL(); > + > + /* empty field separator > + * return one element, 1D, array using the input string */ > + if (fldsep_len < 1) > + PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID, > + CStringGetDatum(inputstring), 1)); > + > + /* start with end position holding the initial start position */ > + end_posn = 0; > + for (fldnum=1;;fldnum++) /* field number is 1 based */ > + { > + Datum dvalue; > + bool disnull = false; > + > + start_posn = end_posn; > + end_posn = text_position(PointerGetDatum(inputstring), > + PointerGetDatum(fldsep), > + fldnum); > + > + if ((start_posn == 0) && (end_posn == 0)) /* fldsep not found */ > + { > + if (fldnum == 1) > + { > + /* first element > + * return one element, 1D, array using the input string */ > + PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID, > + CStringGetDatum(inputstring), 1)); > + } > + else > + { > + /* otherwise create array and exit */ > + PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext)); > + } > + } > + else if ((start_posn != 0) && (end_posn == 0)) > + { > + /* last field requested */ > + result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true); > + } > + else if ((start_posn == 0) && (end_posn != 0)) > + { > + /* first field requested */ > + result_text = LEFT(inputstring, fldsep); > + } > + else > + { > + /* prior to last field requested */ > + result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn- fldsep_len, false); > + } > + > + /* stash away current value */ > + dvalue = PointerGetDatum(result_text); > + astate = accumArrayResult(astate, dvalue, > + disnull, TEXTOID, oldcontext); > + > + } > + > + /* never reached -- keep compiler quiet */ > + PG_RETURN_NULL(); > + } > + > + /* > + * array_to_text > + * concatenate Cstring representation of input array elements > + * using provided field separator > + */ > + Datum > + array_to_text(PG_FUNCTION_ARGS) > + { > + ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); > + char *fldsep = PG_TEXTARG_GET_STR(1); > + int nitems, *dims, ndims; > + char *p; > + Oid element_type; > + int typlen; > + bool typbyval; > + char typdelim; > + Oid typoutput, > + typelem; > + FmgrInfo outputproc; > + char typalign; > + StringInfo result_str = makeStringInfo(); > + int i; > + ArrayMetaState *my_extra; > + > + p = ARR_DATA_PTR(v); > + ndims = ARR_NDIM(v); > + dims = ARR_DIMS(v); > + nitems = ArrayGetNItems(ndims, dims); > + > + /* if there are no elements, return an empty string */ > + if (nitems == 0) > + PG_RETURN_TEXT_P(PG_STR_GET_TEXT("")); > + > + element_type = ARR_ELEMTYPE(v); > + > + /* > + * We arrange to look up info about element type, including its output > + * conversion proc only once per series of calls, assuming the element > + * type doesn't change underneath us. > + */ > + my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > + if (my_extra == NULL) > + { > + fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, > + sizeof(ArrayMetaState)); > + my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; > + my_extra->element_type = InvalidOid; > + } > + > + if (my_extra->element_type != element_type) > + { > + /* Get info about element type, including its output conversion proc */ > + get_type_metadata(element_type, IOFunc_output, > + &typlen, &typbyval, &typdelim, > + &typelem, &typoutput, &typalign); > + fmgr_info(typoutput, &outputproc); > + > + my_extra->element_type = element_type; > + my_extra->typlen = typlen; > + my_extra->typbyval = typbyval; > + my_extra->typdelim = typdelim; > + my_extra->typelem = typelem; > + my_extra->typiofunc = typoutput; > + my_extra->typalign = typalign; > + my_extra->proc = outputproc; > + } > + else > + { > + typlen = my_extra->typlen; > + typbyval = my_extra->typbyval; > + typdelim = my_extra->typdelim; > + typelem = my_extra->typelem; > + typoutput = my_extra->typiofunc; > + typalign = my_extra->typalign; > + outputproc = my_extra->proc; > + } > + > + for (i = 0; i < nitems; i++) > + { > + Datum itemvalue; > + char *value; > + > + itemvalue = fetch_att(p, typbyval, typlen); > + > + value = DatumGetCString(FunctionCall3(&outputproc, > + itemvalue, > + ObjectIdGetDatum(typelem), > + Int32GetDatum(-1))); > + > + if (i > 0) > + appendStringInfo(result_str, "%s%s", fldsep, value); > + else > + appendStringInfo(result_str, "%s", value); > + > + p = att_addlength(p, typlen, PointerGetDatum(p)); > + p = (char *) att_align(p, typalign); > + } > + > + PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data)); > } > > #define HEXBASE 16 > Index: src/backend/utils/cache/lsyscache.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v > retrieving revision 1.95 > diff -c -r1.95 lsyscache.c > *** src/backend/utils/cache/lsyscache.c 26 May 2003 00:11:27 -0000 1.95 > --- src/backend/utils/cache/lsyscache.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 625,630 **** > --- 625,664 ---- > } > > /* > + * get_func_argtypes > + * Given procedure id, return the function's argument types. > + * Also pass back the number of arguments. > + */ > + Oid * > + get_func_argtypes(Oid funcid, int *nargs) > + { > + HeapTuple tp; > + Form_pg_proc procstruct; > + Oid *result = NULL; > + int i; > + > + tp = SearchSysCache(PROCOID, > + ObjectIdGetDatum(funcid), > + 0, 0, 0); > + if (!HeapTupleIsValid(tp)) > + elog(ERROR, "Function OID %u does not exist", funcid); > + > + procstruct = (Form_pg_proc) GETSTRUCT(tp); > + *nargs = (int) procstruct->pronargs; > + > + if (*nargs > 0) > + { > + result = (Oid *) palloc(*nargs * sizeof(Oid)); > + > + for (i = 0; i < *nargs; i++) > + result[i] = procstruct->proargtypes[i]; > + } > + > + ReleaseSysCache(tp); > + return result; > + } > + > + /* > * get_func_retset > * Given procedure id, return the function's proretset flag. > */ > *************** > *** 994,999 **** > --- 1028,1083 ---- > *typbyval = typtup->typbyval; > *typalign = typtup->typalign; > ReleaseSysCache(tp); > + } > + > + /* > + * get_type_metadata > + * > + * A six-fer: given the type OID, return typlen, typbyval, typalign, > + * typdelim, typelem, IO function Oid. The IO function > + * returned is controlled by IOFuncSelector > + */ > + void > + get_type_metadata(Oid element_type, > + IOFuncSelector which_func, > + int *typlen, > + bool *typbyval, > + char *typdelim, > + Oid *typelem, > + Oid *proc, > + char *typalign) > + { > + HeapTuple typeTuple; > + Form_pg_type typeStruct; > + > + typeTuple = SearchSysCache(TYPEOID, > + ObjectIdGetDatum(element_type), > + 0, 0, 0); > + if (!HeapTupleIsValid(typeTuple)) > + elog(ERROR, "cache lookup failed for type %u", element_type); > + typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); > + > + *typlen = typeStruct->typlen; > + *typbyval = typeStruct->typbyval; > + *typdelim = typeStruct->typdelim; > + *typelem = typeStruct->typelem; > + *typalign = typeStruct->typalign; > + switch (which_func) > + { > + case IOFunc_input: > + *proc = typeStruct->typinput; > + break; > + case IOFunc_output: > + *proc = typeStruct->typoutput; > + break; > + case IOFunc_receive: > + *proc = typeStruct->typreceive; > + break; > + case IOFunc_send: > + *proc = typeStruct->typsend; > + break; > + } > + ReleaseSysCache(typeTuple); > } > > #ifdef NOT_USED > Index: src/backend/utils/fmgr/fmgr.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/fmgr/fmgr.c,v > retrieving revision 1.68 > diff -c -r1.68 fmgr.c > *** src/backend/utils/fmgr/fmgr.c 8 Apr 2003 23:20:02 -0000 1.68 > --- src/backend/utils/fmgr/fmgr.c 9 Jun 2003 01:31:10 -0000 > *************** > *** 1673,1675 **** > --- 1673,1701 ---- > > return exprType((Node *) nth(argnum, args)); > } > + > + /* > + * Get the OID of the function or operator > + * > + * Returns InvalidOid if information is not available > + */ > + Oid > + get_fn_expr_functype(FunctionCallInfo fcinfo) > + { > + Node *expr; > + > + /* > + * can't return anything useful if we have no FmgrInfo or if > + * its fn_expr node has not been initialized > + */ > + if (!fcinfo || !fcinfo->flinfo || !fcinfo->flinfo->fn_expr) > + return InvalidOid; > + > + expr = fcinfo->flinfo->fn_expr; > + if (IsA(expr, FuncExpr)) > + return ((FuncExpr *) expr)->funcid; > + else if (IsA(expr, OpExpr)) > + return ((OpExpr *) expr)->opno; > + else > + return InvalidOid; > + } > Index: src/include/fmgr.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/include/fmgr.h,v > retrieving revision 1.27 > diff -c -r1.27 fmgr.h > *** src/include/fmgr.h 8 Apr 2003 23:20:04 -0000 1.27 > --- src/include/fmgr.h 9 Jun 2003 01:31:10 -0000 > *************** > *** 18,23 **** > --- 18,24 ---- > #ifndef FMGR_H > #define FMGR_H > > + #include "nodes/nodes.h" > > /* > * All functions that can be called directly by fmgr must have this signature. > *************** > *** 372,385 **** > Datum arg6, Datum arg7, Datum arg8, > Datum arg9); > > - > /* > * Routines in fmgr.c > */ > extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname); > ! extern Oid fmgr_internal_function(const char *proname); > ! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo); > ! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum); > > /* > * Routines in dfmgr.c > --- 373,386 ---- > Datum arg6, Datum arg7, Datum arg8, > Datum arg9); > > /* > * Routines in fmgr.c > */ > extern Pg_finfo_record *fetch_finfo_record(void *filehandle, char *funcname); > ! extern Oid fmgr_internal_function(const char *proname); > ! extern Oid get_fn_expr_rettype(FunctionCallInfo fcinfo); > ! extern Oid get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum); > ! extern Oid get_fn_expr_functype(FunctionCallInfo fcinfo); > > /* > * Routines in dfmgr.c > Index: src/include/catalog/pg_amop.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amop.h,v > retrieving revision 1.49 > diff -c -r1.49 pg_amop.h > *** src/include/catalog/pg_amop.h 26 May 2003 00:11:27 -0000 1.49 > --- src/include/catalog/pg_amop.h 9 Jun 2003 01:31:10 -0000 > *************** > *** 418,423 **** > --- 418,432 ---- > DATA(insert ( 2098 4 f 2335 )); > DATA(insert ( 2098 5 f 2336 )); > > + /* > + * btree array_ops > + */ > + > + DATA(insert ( 397 1 f 1072 )); > + DATA(insert ( 397 2 f 1074 )); > + DATA(insert ( 397 3 f 1070 )); > + DATA(insert ( 397 4 f 1075 )); > + DATA(insert ( 397 5 f 1073 )); > > /* > * hash index _ops > Index: src/include/catalog/pg_amproc.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_amproc.h,v > retrieving revision 1.37 > diff -c -r1.37 pg_amproc.h > *** src/include/catalog/pg_amproc.h 26 May 2003 00:11:27 -0000 1.37 > --- src/include/catalog/pg_amproc.h 9 Jun 2003 01:31:10 -0000 > *************** > *** 78,83 **** > --- 78,84 ---- > > > /* btree */ > + DATA(insert ( 397 1 398 )); > DATA(insert ( 421 1 357 )); > DATA(insert ( 423 1 1596 )); > DATA(insert ( 424 1 1693 )); > Index: src/include/catalog/pg_opclass.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_opclass.h,v > retrieving revision 1.50 > diff -c -r1.50 pg_opclass.h > *** src/include/catalog/pg_opclass.h 28 May 2003 16:04:00 -0000 1.50 > --- src/include/catalog/pg_opclass.h 9 Jun 2003 02:48:16 -0000 > *************** > *** 87,92 **** > --- 87,94 ---- > */ > > DATA(insert OID = 421 ( 403 abstime_ops PGNSP PGUID 702 t 0 )); > + DATA(insert OID = 397 ( 403 array_ops PGNSP PGUID 2277 t 0 )); > + #define ARRAY_BTREE_OPS_OID 397 > DATA(insert OID = 422 ( 402 bigbox_ops PGNSP PGUID 603 f 0 )); > DATA(insert OID = 423 ( 403 bit_ops PGNSP PGUID 1560 t 0 )); > DATA(insert OID = 424 ( 403 bool_ops PGNSP PGUID 16 t 0 )); > Index: src/include/catalog/pg_operator.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_operator.h,v > retrieving revision 1.114 > diff -c -r1.114 pg_operator.h > *** src/include/catalog/pg_operator.h 26 May 2003 00:11:27 -0000 1.114 > --- src/include/catalog/pg_operator.h 9 Jun 2003 01:31:10 -0000 > *************** > *** 116,125 **** > DATA(insert OID = 97 ( "<" PGNSP PGUID b f 23 23 16 521 525 0 0 0 0 int4lt scalarltselscalarltjoinsel )); > DATA(insert OID = 98 ( "=" PGNSP PGUID b t 25 25 16 98 531 664 664 664 666 texteq eqsel eqjoinsel)); > > ! DATA(insert OID = 329 ( "=" PGNSP PGUID b f 2277 2277 16 329 0 0 0 0 0 array_eq eqsel eqjoinsel )); > ! DATA(insert OID = 349 ( "||" PGNSP PGUID b f 2277 2283 2277 0 0 0 0 0 0 array_append - - )); > ! DATA(insert OID = 374 ( "||" PGNSP PGUID b f 2283 2277 2277 0 0 0 0 0 0 array_prepend - - )); > ! DATA(insert OID = 375 ( "||" PGNSP PGUID b f 2277 2277 2277 0 0 0 0 0 0 array_cat - - )); > > DATA(insert OID = 352 ( "=" PGNSP PGUID b t 28 28 16 352 0 0 0 0 0 xideq eqsel eqjoinsel)); > DATA(insert OID = 353 ( "=" PGNSP PGUID b t 28 23 16 0 0 0 0 0 0 xideqint4 eqseleqjoinsel )); > --- 116,130 ---- > DATA(insert OID = 97 ( "<" PGNSP PGUID b f 23 23 16 521 525 0 0 0 0 int4lt scalarltselscalarltjoinsel )); > DATA(insert OID = 98 ( "=" PGNSP PGUID b t 25 25 16 98 531 664 664 664 666 texteq eqsel eqjoinsel)); > > ! DATA(insert OID = 1070 ( "=" PGNSP PGUID b f 2277 2277 16 1070 1071 1072 1072 1072 1073 array_eq eqsel eqjoinsel)); > ! DATA(insert OID = 1071 ( "<>" PGNSP PGUID b f 2277 2277 16 1071 1070 0 0 0 0 array_ne neqsel neqjoinsel )); > ! DATA(insert OID = 1072 ( "<" PGNSP PGUID b f 2277 2277 16 1073 1075 0 0 0 0 array_lt scalarltsel scalarltjoinsel)); > ! DATA(insert OID = 1073 ( ">" PGNSP PGUID b f 2277 2277 16 1072 1074 0 0 0 0 array_gt scalargtsel scalargtjoinsel)); > ! DATA(insert OID = 1074 ( "<=" PGNSP PGUID b f 2277 2277 16 1075 1073 0 0 0 0 array_le scalarltsel scalarltjoinsel)); > ! DATA(insert OID = 1075 ( ">=" PGNSP PGUID b f 2277 2277 16 1074 1072 0 0 0 0 array_ge scalargtsel scalargtjoinsel)); > ! DATA(insert OID = 349 ( "||" PGNSP PGUID b f 2277 2283 2277 0 0 0 0 0 0 array_append - - )); > ! DATA(insert OID = 374 ( "||" PGNSP PGUID b f 2283 2277 2277 0 0 0 0 0 0 array_prepend - - )); > ! DATA(insert OID = 375 ( "||" PGNSP PGUID b f 2277 2277 2277 0 0 0 0 0 0 array_cat - - )); > > DATA(insert OID = 352 ( "=" PGNSP PGUID b t 28 28 16 352 0 0 0 0 0 xideq eqsel eqjoinsel)); > DATA(insert OID = 353 ( "=" PGNSP PGUID b t 28 23 16 0 0 0 0 0 0 xideqint4 eqseleqjoinsel )); > *************** > *** 425,430 **** > --- 430,436 ---- > DATA(insert OID = 966 ( "+" PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - )); > DATA(insert OID = 967 ( "-" PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - )); > DATA(insert OID = 968 ( "~" PGNSP PGUID b f 1034 1033 16 0 0 0 0 0 0 aclcontains - - )); > + DATA(insert OID = 974 ( "=" PGNSP PGUID b f 1033 1033 16 0 0 0 0 0 0 aclitemeq eqsel eqjoinsel )); > > /* additional geometric operators - thomas 1997-07-09 */ > DATA(insert OID = 969 ( "@@" PGNSP PGUID l f 0 601 600 0 0 0 0 0 0 lseg_center - - )); > Index: src/include/catalog/pg_proc.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_proc.h,v > retrieving revision 1.302 > diff -c -r1.302 pg_proc.h > *** src/include/catalog/pg_proc.h 26 May 2003 00:11:27 -0000 1.302 > --- src/include/catalog/pg_proc.h 9 Jun 2003 01:52:28 -0000 > *************** > *** 758,763 **** > --- 758,765 ---- > DESCR("btree less-equal-greater"); > DATA(insert OID = 360 ( bttextcmp PGNSP PGUID 12 f f t f i 2 23 "25 25" bttextcmp - _null_ )); > DESCR("btree less-equal-greater"); > + DATA(insert OID = 398 ( btarraycmp PGNSP PGUID 12 f f t f i 2 23 "2277 2277" btarraycmp - _null_ )); > + DESCR("btree less-equal-greater"); > > DATA(insert OID = 361 ( lseg_distance PGNSP PGUID 12 f f t f i 2 701 "601 601" lseg_distance - _null_ )); > DESCR("distance between"); > *************** > *** 984,997 **** > DATA(insert OID = 743 ( text_ge PGNSP PGUID 12 f f t f i 2 16 "25 25" text_ge - _null_ )); > DESCR("greater-than-or-equal"); > > - DATA(insert OID = 744 ( array_eq PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ )); > - DESCR("array equal"); > - > DATA(insert OID = 745 ( current_user PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ )); > DESCR("current user name"); > DATA(insert OID = 746 ( session_user PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ )); > DESCR("session user name"); > > DATA(insert OID = 747 ( array_dims PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ )); > DESCR("array dimensions"); > DATA(insert OID = 750 ( array_in PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23" array_in - _null_ )); > --- 986,1008 ---- > DATA(insert OID = 743 ( text_ge PGNSP PGUID 12 f f t f i 2 16 "25 25" text_ge - _null_ )); > DESCR("greater-than-or-equal"); > > DATA(insert OID = 745 ( current_user PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ )); > DESCR("current user name"); > DATA(insert OID = 746 ( session_user PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ )); > DESCR("session user name"); > > + DATA(insert OID = 744 ( array_eq PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ )); > + DESCR("array equal"); > + DATA(insert OID = 390 ( array_ne PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ )); > + DESCR("array not equal"); > + DATA(insert OID = 391 ( array_lt PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ )); > + DESCR("array less than"); > + DATA(insert OID = 392 ( array_gt PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ )); > + DESCR("array greater than"); > + DATA(insert OID = 393 ( array_le PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ )); > + DESCR("array less than or equal"); > + DATA(insert OID = 396 ( array_ge PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ )); > + DESCR("array greater than or equal"); > DATA(insert OID = 747 ( array_dims PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ )); > DESCR("array dimensions"); > DATA(insert OID = 750 ( array_in PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23" array_in - _null_ )); > *************** > *** 1002,1023 **** > DESCR("array lower dimension"); > DATA(insert OID = 2092 ( array_upper PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ )); > DESCR("array upper dimension"); > - DATA(insert OID = 377 ( singleton_array PGNSP PGUID 12 f f t f i 1 2277 "2283" singleton_array - _null_ )); > - DESCR("create array from single element"); > DATA(insert OID = 378 ( array_append PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ )); > DESCR("append element onto end of array"); > DATA(insert OID = 379 ( array_prepend PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ )); > DESCR("prepend element onto front of array"); > - DATA(insert OID = 380 ( array_accum PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ )); > - DESCR("push element onto end of array, creating array if needed"); > - DATA(insert OID = 381 ( array_assign PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_ )); > - DESCR("assign specific array element"); > - DATA(insert OID = 382 ( array_subscript PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ )); > - DESCR("return specific array element"); > DATA(insert OID = 383 ( array_cat PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ )); > DESCR("concatenate two arrays"); > DATA(insert OID = 384 ( array_coerce PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ )); > DESCR("coerce array type to another array type"); > > DATA(insert OID = 760 ( smgrin PGNSP PGUID 12 f f t f s 1 210 "2275" smgrin - _null_ )); > DESCR("I/O"); > --- 1013,1030 ---- > DESCR("array lower dimension"); > DATA(insert OID = 2092 ( array_upper PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ )); > DESCR("array upper dimension"); > DATA(insert OID = 378 ( array_append PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ )); > DESCR("append element onto end of array"); > DATA(insert OID = 379 ( array_prepend PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ )); > DESCR("prepend element onto front of array"); > DATA(insert OID = 383 ( array_cat PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ )); > DESCR("concatenate two arrays"); > DATA(insert OID = 384 ( array_coerce PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ )); > DESCR("coerce array type to another array type"); > + DATA(insert OID = 394 ( string_to_array PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ )); > + DESCR("split delimited text into text[]"); > + DATA(insert OID = 395 ( array_to_string PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ )); > + DESCR("concatenate array elements, using delimiter, into text"); > > DATA(insert OID = 760 ( smgrin PGNSP PGUID 12 f f t f s 1 210 "2275" smgrin - _null_ )); > DESCR("I/O"); > *************** > *** 1318,1323 **** > --- 1325,1332 ---- > DESCR("remove ACL item"); > DATA(insert OID = 1037 ( aclcontains PGNSP PGUID 12 f f t f s 2 16 "1034 1033" aclcontains - _null_ )); > DESCR("does ACL contain item?"); > + DATA(insert OID = 1062 ( aclitemeq PGNSP PGUID 12 f f t f s 2 16 "1033 1033" aclitem_eq - _null_ )); > + DESCR("equality operator for ACL items"); > DATA(insert OID = 1038 ( seteval PGNSP PGUID 12 f f t t v 1 23 "26" seteval - _null_ )); > DESCR("internal function supporting PostQuel-style sets"); > DATA(insert OID = 1044 ( bpcharin PGNSP PGUID 12 f f t f i 3 1042 "2275 26 23" bpcharin - _null_ )); > Index: src/include/nodes/primnodes.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v > retrieving revision 1.82 > diff -c -r1.82 primnodes.h > *** src/include/nodes/primnodes.h 6 May 2003 00:20:33 -0000 1.82 > --- src/include/nodes/primnodes.h 9 Jun 2003 01:31:10 -0000 > *************** > *** 225,230 **** > --- 225,231 ---- > Expr *target; /* expression we are aggregating on */ > bool aggstar; /* TRUE if argument was really '*' */ > bool aggdistinct; /* TRUE if it's agg(DISTINCT ...) */ > + List *args; /* arguments to the aggregate */ > } Aggref; > > /* ---------------- > *************** > *** 357,371 **** > /* ---------------- > * SubLink > * > ! * A SubLink represents a subselect appearing in an expression, and in some > ! * cases also the combining operator(s) just above it. The subLinkType > ! * indicates the form of the expression represented: > * EXISTS_SUBLINK EXISTS(SELECT ...) > * ALL_SUBLINK (lefthand) op ALL (SELECT ...) > * ANY_SUBLINK (lefthand) op ANY (SELECT ...) > * MULTIEXPR_SUBLINK (lefthand) op (SELECT ...) > * EXPR_SUBLINK (SELECT with single targetlist item ...) > * ARRAY_SUBLINK ARRAY(SELECT with single targetlist item ...) > * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the > * same length as the subselect's targetlist. MULTIEXPR will *always* have > * a list with more than one entry; if the subselect has just one target > --- 358,376 ---- > /* ---------------- > * SubLink > * > ! * A SubLink represents a subselect, or an expression, appearing in an > ! * expression, and in some cases also the combining operator(s) just above > ! * it. The subLinkType indicates the form of the expression represented: > * EXISTS_SUBLINK EXISTS(SELECT ...) > * ALL_SUBLINK (lefthand) op ALL (SELECT ...) > * ANY_SUBLINK (lefthand) op ANY (SELECT ...) > * MULTIEXPR_SUBLINK (lefthand) op (SELECT ...) > * EXPR_SUBLINK (SELECT with single targetlist item ...) > * ARRAY_SUBLINK ARRAY(SELECT with single targetlist item ...) > + * If an expression is used in place of the subselect, it is transformed > + * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be > + * used as if they were the result of a single column subselect. If the > + * expression is scalar, it is treated as a one element array. > * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the > * same length as the subselect's targetlist. MULTIEXPR will *always* have > * a list with more than one entry; if the subselect has just one target > *************** > *** 414,419 **** > --- 419,426 ---- > SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */ > bool useOr; /* TRUE to combine column results with > * "OR" not "AND" */ > + bool isExpr; /* TRUE if the subselect is really derived > + * from a single expression */ > List *lefthand; /* list of outer-query expressions on the > * left */ > List *operName; /* originally specified operator name */ > *************** > *** 455,460 **** > --- 462,476 ---- > SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */ > bool useOr; /* TRUE to combine column results with > * "OR" not "AND" */ > + bool isExpr; /* TRUE if the subselect is really derived > + * from a single expression */ > + /* runtime cache for single array expressions */ > + Oid exprtype; /* array and element type, and other info > + * needed deconstruct the array */ > + Oid elemtype; > + int16 elmlen; > + bool elmbyval; > + char elmalign; > /* The combining operators, transformed to executable expressions: */ > List *exprs; /* list of OpExpr expression trees */ > List *paramIds; /* IDs of Params embedded in the above */ > Index: src/include/optimizer/clauses.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v > retrieving revision 1.63 > diff -c -r1.63 clauses.h > *** src/include/optimizer/clauses.h 28 May 2003 16:04:02 -0000 1.63 > --- src/include/optimizer/clauses.h 9 Jun 2003 01:31:10 -0000 > *************** > *** 28,33 **** > --- 28,36 ---- > extern Node *get_leftop(Expr *clause); > extern Node *get_rightop(Expr *clause); > > + extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset, > + CoercionForm funcformat, List *funcargs); > + > extern bool not_clause(Node *clause); > extern Expr *make_notclause(Expr *notclause); > extern Expr *get_notclausearg(Expr *notclause); > Index: src/include/parser/parse_oper.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/include/parser/parse_oper.h,v > retrieving revision 1.25 > diff -c -r1.25 parse_oper.h > *** src/include/parser/parse_oper.h 29 Apr 2003 22:13:11 -0000 1.25 > --- src/include/parser/parse_oper.h 9 Jun 2003 01:31:10 -0000 > *************** > *** 44,49 **** > --- 44,50 ---- > /* Convenience routines for common calls on the above */ > extern Oid compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError); > extern Oid equality_oper_funcid(Oid argtype); > + extern Oid ordering_oper_funcid(Oid argtype); > extern Oid ordering_oper_opid(Oid argtype); > > /* Extract operator OID or underlying-function OID from an Operator tuple */ > Index: src/include/utils/acl.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v > retrieving revision 1.51 > diff -c -r1.51 acl.h > *** src/include/utils/acl.h 23 Jan 2003 23:39:07 -0000 1.51 > --- src/include/utils/acl.h 9 Jun 2003 01:31:10 -0000 > *************** > *** 188,193 **** > --- 188,194 ---- > extern Datum aclinsert(PG_FUNCTION_ARGS); > extern Datum aclremove(PG_FUNCTION_ARGS); > extern Datum aclcontains(PG_FUNCTION_ARGS); > + extern Datum aclitem_eq(PG_FUNCTION_ARGS); > > /* > * prototypes for functions in aclchk.c > Index: src/include/utils/array.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v > retrieving revision 1.38 > diff -c -r1.38 array.h > *** src/include/utils/array.h 8 May 2003 22:19:57 -0000 1.38 > --- src/include/utils/array.h 9 Jun 2003 01:47:03 -0000 > *************** > *** 32,37 **** > --- 32,68 ---- > Oid elemtype; /* element type OID */ > } ArrayType; > > + typedef struct ArrayBuildState > + { > + MemoryContext mcontext; /* where all the temp stuff is kept */ > + Datum *dvalues; /* array of accumulated Datums */ > + /* > + * The allocated size of dvalues[] is always a multiple of > + * ARRAY_ELEMS_CHUNKSIZE > + */ > + #define ARRAY_ELEMS_CHUNKSIZE 64 > + int nelems; /* number of valid Datums in dvalues[] */ > + Oid element_type; /* data type of the Datums */ > + int16 typlen; /* needed info about datatype */ > + bool typbyval; > + char typalign; > + } ArrayBuildState; > + > + /* > + * structure to cache type metadata needed for array manipulation > + */ > + typedef struct ArrayMetaState > + { > + Oid element_type; > + int typlen; > + bool typbyval; > + char typdelim; > + Oid typelem; > + Oid typiofunc; > + char typalign; > + FmgrInfo proc; > + } ArrayMetaState; > + > /* > * fmgr macros for array objects > */ > *************** > *** 86,96 **** > extern Datum array_send(PG_FUNCTION_ARGS); > extern Datum array_length_coerce(PG_FUNCTION_ARGS); > extern Datum array_eq(PG_FUNCTION_ARGS); > extern Datum array_dims(PG_FUNCTION_ARGS); > extern Datum array_lower(PG_FUNCTION_ARGS); > extern Datum array_upper(PG_FUNCTION_ARGS); > - extern Datum array_assign(PG_FUNCTION_ARGS); > - extern Datum array_subscript(PG_FUNCTION_ARGS); > extern Datum array_type_coerce(PG_FUNCTION_ARGS); > > extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx, > --- 117,131 ---- > extern Datum array_send(PG_FUNCTION_ARGS); > extern Datum array_length_coerce(PG_FUNCTION_ARGS); > extern Datum array_eq(PG_FUNCTION_ARGS); > + extern Datum array_ne(PG_FUNCTION_ARGS); > + extern Datum array_lt(PG_FUNCTION_ARGS); > + extern Datum array_gt(PG_FUNCTION_ARGS); > + extern Datum array_le(PG_FUNCTION_ARGS); > + extern Datum array_ge(PG_FUNCTION_ARGS); > + extern Datum btarraycmp(PG_FUNCTION_ARGS); > extern Datum array_dims(PG_FUNCTION_ARGS); > extern Datum array_lower(PG_FUNCTION_ARGS); > extern Datum array_upper(PG_FUNCTION_ARGS); > extern Datum array_type_coerce(PG_FUNCTION_ARGS); > > extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx, > *************** > *** 124,130 **** > Oid elmtype, > int elmlen, bool elmbyval, char elmalign, > Datum **elemsp, int *nelemsp); > ! > > /* > * prototypes for functions defined in arrayutils.c > --- 159,172 ---- > Oid elmtype, > int elmlen, bool elmbyval, char elmalign, > Datum **elemsp, int *nelemsp); > ! extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate, > ! Datum dvalue, bool disnull, > ! Oid element_type, > ! MemoryContext rcontext); > ! extern Datum makeArrayResult(ArrayBuildState *astate, > ! MemoryContext rcontext); > ! extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims, > ! int *dims, int *lbs, MemoryContext rcontext); > > /* > * prototypes for functions defined in arrayutils.c > *************** > *** 141,152 **** > /* > * prototypes for functions defined in array_userfuncs.c > */ > - extern Datum singleton_array(PG_FUNCTION_ARGS); > extern Datum array_push(PG_FUNCTION_ARGS); > - extern Datum array_accum(PG_FUNCTION_ARGS); > extern Datum array_cat(PG_FUNCTION_ARGS); > > ! extern ArrayType *create_singleton_array(Oid element_type, > Datum element, > int ndims); > > --- 183,193 ---- > /* > * prototypes for functions defined in array_userfuncs.c > */ > extern Datum array_push(PG_FUNCTION_ARGS); > extern Datum array_cat(PG_FUNCTION_ARGS); > > ! extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo, > ! Oid element_type, > Datum element, > int ndims); > > Index: src/include/utils/builtins.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/include/utils/builtins.h,v > retrieving revision 1.219 > diff -c -r1.219 builtins.h > *** src/include/utils/builtins.h 26 May 2003 00:11:28 -0000 1.219 > --- src/include/utils/builtins.h 9 Jun 2003 01:31:10 -0000 > *************** > *** 530,535 **** > --- 530,537 ---- > List **namelist); > extern Datum replace_text(PG_FUNCTION_ARGS); > extern Datum split_text(PG_FUNCTION_ARGS); > + extern Datum text_to_array(PG_FUNCTION_ARGS); > + extern Datum array_to_text(PG_FUNCTION_ARGS); > extern Datum to_hex32(PG_FUNCTION_ARGS); > extern Datum to_hex64(PG_FUNCTION_ARGS); > extern Datum md5_text(PG_FUNCTION_ARGS); > Index: src/include/utils/lsyscache.h > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v > retrieving revision 1.70 > diff -c -r1.70 lsyscache.h > *** src/include/utils/lsyscache.h 26 May 2003 00:11:28 -0000 1.70 > --- src/include/utils/lsyscache.h 9 Jun 2003 01:31:10 -0000 > *************** > *** 15,20 **** > --- 15,29 ---- > > #include "access/htup.h" > > + /* I/O function selector for system_cache_lookup */ > + typedef enum IOFuncSelector > + { > + IOFunc_input, > + IOFunc_output, > + IOFunc_receive, > + IOFunc_send > + } IOFuncSelector; > + > extern bool op_in_opclass(Oid opno, Oid opclass); > extern bool op_requires_recheck(Oid opno, Oid opclass); > extern Oid get_opclass_member(Oid opclass, int16 strategy); > *************** > *** 39,44 **** > --- 48,54 ---- > extern RegProcedure get_oprjoin(Oid opno); > extern char *get_func_name(Oid funcid); > extern Oid get_func_rettype(Oid funcid); > + extern Oid *get_func_argtypes(Oid funcid, int *nargs); > extern bool get_func_retset(Oid funcid); > extern bool func_strict(Oid funcid); > extern char func_volatile(Oid funcid); > *************** > *** 54,59 **** > --- 64,77 ---- > extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval); > extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, > char *typalign); > + extern void get_type_metadata(Oid element_type, > + IOFuncSelector which_func, > + int *typlen, > + bool *typbyval, > + char *typdelim, > + Oid *typelem, > + Oid *proc, > + char *typalign); > extern char get_typstorage(Oid typid); > extern int32 get_typtypmod(Oid typid); > extern Node *get_typdefault(Oid typid); > Index: src/interfaces/ecpg/preproc/preproc.y > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/preproc.y,v > retrieving revision 1.227 > diff -c -r1.227 preproc.y > *** src/interfaces/ecpg/preproc/preproc.y 30 May 2003 13:22:02 -0000 1.227 > --- src/interfaces/ecpg/preproc/preproc.y 9 Jun 2003 01:42:58 -0000 > *************** > *** 4549,4555 **** > $3.type_enum != ECPGt_char && > $3.type_enum != ECPGt_unsigned_char && > atoi(this->type->type_index) >= 0) > ! mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types"); > > types = this; > } > --- 4549,4555 ---- > $3.type_enum != ECPGt_char && > $3.type_enum != ECPGt_unsigned_char && > atoi(this->type->type_index) >= 0) > ! mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types"); > > types = this; > } > *************** > *** 5372,5378 **** > $5.type_enum != ECPGt_char && > $5.type_enum != ECPGt_unsigned_char && > atoi(this->type->type_index) >= 0) > ! mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types"); > > types = this; > } > --- 5372,5378 ---- > $5.type_enum != ECPGt_char && > $5.type_enum != ECPGt_unsigned_char && > atoi(this->type->type_index) >= 0) > ! mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types"); > > types = this; > } > *************** > *** 5439,5445 **** > > default: > if (atoi(length) >= 0) > ! mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types"); > > if (atoi(dimension) < 0) > type = ECPGmake_simple_type($5.type_enum, make_str("1")); > --- 5439,5445 ---- > > default: > if (atoi(length) >= 0) > ! mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types"); > > if (atoi(dimension) < 0) > type = ECPGmake_simple_type($5.type_enum, make_str("1")); > Index: src/interfaces/ecpg/preproc/type.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/type.c,v > retrieving revision 1.51 > diff -c -r1.51 type.c > *** src/interfaces/ecpg/preproc/type.c 29 May 2003 13:59:26 -0000 1.51 > --- src/interfaces/ecpg/preproc/type.c 9 Jun 2003 01:42:58 -0000 > *************** > *** 493,499 **** > switch (type->u.element->type) > { > case ECPGt_array: > ! yyerror("internal error, found multi-dimensional array\n"); > break; > case ECPGt_struct: > case ECPGt_union: > --- 493,499 ---- > switch (type->u.element->type) > { > case ECPGt_array: > ! yyerror("internal error, found multidimensional array\n"); > break; > case ECPGt_struct: > case ECPGt_union: > Index: src/interfaces/ecpg/preproc/variable.c > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/interfaces/ecpg/preproc/variable.c,v > retrieving revision 1.20 > diff -c -r1.20 variable.c > *** src/interfaces/ecpg/preproc/variable.c 29 May 2003 13:59:26 -0000 1.20 > --- src/interfaces/ecpg/preproc/variable.c 9 Jun 2003 01:42:58 -0000 > *************** > *** 405,411 **** > if (atoi(type_index) >= 0) > { > if (atoi(*length) >= 0) > ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support"); > > *length = type_index; > } > --- 405,411 ---- > if (atoi(type_index) >= 0) > { > if (atoi(*length) >= 0) > ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support"); > > *length = type_index; > } > *************** > *** 413,419 **** > if (atoi(type_dimension) >= 0) > { > if (atoi(*dimension) >= 0 && atoi(*length) >= 0) > ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support"); > > if (atoi(*dimension) >= 0) > *length = *dimension; > --- 413,419 ---- > if (atoi(type_dimension) >= 0) > { > if (atoi(*dimension) >= 0 && atoi(*length) >= 0) > ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support"); > > if (atoi(*dimension) >= 0) > *length = *dimension; > *************** > *** 432,441 **** > mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type"); > > if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0)) > ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support"); > > if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len) > ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support"); > > switch (type_enum) > { > --- 432,441 ---- > mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type"); > > if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0)) > ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support"); > > if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len) > ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support"); > > switch (type_enum) > { > *************** > *** 449,455 **** > } > > if (atoi(*length) >= 0) > ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures"); > > break; > case ECPGt_varchar: > --- 449,455 ---- > } > > if (atoi(*length) >= 0) > ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures"); > > break; > case ECPGt_varchar: > *************** > *** 494,500 **** > } > > if (atoi(*length) >= 0) > ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types"); > > break; > } > --- 494,500 ---- > } > > if (atoi(*length) >= 0) > ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types"); > > break; > } > Index: src/test/regress/expected/arrays.out > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/arrays.out,v > retrieving revision 1.11 > diff -c -r1.11 arrays.out > *** src/test/regress/expected/arrays.out 8 Apr 2003 23:20:04 -0000 1.11 > --- src/test/regress/expected/arrays.out 9 Jun 2003 04:43:05 -0000 > *************** > *** 178,196 **** > (1 row) > > -- functions > ! SELECT singleton_array(42) AS "{42}"; > ! {42} > ! ------ > ! {42} > ! (1 row) > ! > ! SELECT array_append(singleton_array(42), 6) AS "{42,6}"; > {42,6} > -------- > {42,6} > (1 row) > > ! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}"; > {6,42} > -------- > {6,42} > --- 178,190 ---- > (1 row) > > -- functions > ! SELECT array_append(array[42], 6) AS "{42,6}"; > {42,6} > -------- > {42,6} > (1 row) > > ! SELECT array_prepend(6, array[42]) AS "{6,42}"; > {6,42} > -------- > {6,42} > *************** > *** 212,235 **** > {{3,4},{5,6},{1,2}} > --------------------- > {{3,4},{5,6},{1,2}} > - (1 row) > - > - SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2; > - 1.2 > - ----- > - 1.2 > - (1 row) > - > - SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2; > - {1.1,9.99,1.3} > - ---------------- > - {1.1,9.99,1.3} > - (1 row) > - > - SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2; > - 9.99 > - ------ > - 9.99 > (1 row) > > -- operators > --- 206,211 ---- > Index: src/test/regress/sql/arrays.sql > =================================================================== > RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/arrays.sql,v > retrieving revision 1.10 > diff -c -r1.10 arrays.sql > *** src/test/regress/sql/arrays.sql 8 Apr 2003 23:20:04 -0000 1.10 > --- src/test/regress/sql/arrays.sql 9 Jun 2003 04:42:20 -0000 > *************** > *** 130,144 **** > SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY"; > > -- functions > ! SELECT singleton_array(42) AS "{42}"; > ! SELECT array_append(singleton_array(42), 6) AS "{42,6}"; > ! SELECT array_prepend(6, singleton_array(42)) AS "{6,42}"; > SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}"; > SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}"; > SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}"; > - SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2; > - SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2; > - SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2; > > -- operators > SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]]; > --- 130,140 ---- > SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY"; > > -- functions > ! SELECT array_append(array[42], 6) AS "{42,6}"; > ! SELECT array_prepend(6, array[42]) AS "{6,42}"; > SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}"; > SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}"; > SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}"; > > -- operators > SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]]; -- Bruce Momjian | http://candle.pha.pa.us pgman@candle.pha.pa.us | (610) 359-1001 + If your life is a hard drive, | 13 Roberts Road + Christ can be your backup. | Newtown Square, Pennsylvania 19073
pgsql-patches by date: