Re: array support patch phase 1 patch - Mailing list pgsql-patches
From | Joe Conway |
---|---|
Subject | Re: array support patch phase 1 patch |
Date | |
Msg-id | 3EEE57BD.9020904@joeconway.com Whole thread Raw |
In response to | Re: array support patch phase 1 patch (Joe Conway <mail@joeconway.com>) |
Responses |
Re: array support patch phase 1 patch
|
List | pgsql-patches |
Joe Conway wrote: > The attached patch addresses Peter's concerns, subject to our agreements > above. I.e, the changes are: The previous patch was no longer applying cleanly, so here is an update. Applies and compiles clean on cvs tip, passes all regression tests. Please apply. Joe Index: doc/src/sgml/array.sgml =================================================================== RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v retrieving revision 1.25 diff -c -r1.25 array.sgml *** doc/src/sgml/array.sgml 13 Mar 2003 01:30:26 -0000 1.25 --- doc/src/sgml/array.sgml 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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.106 diff -c -r1.106 nodeAgg.c *** src/backend/executor/nodeAgg.c 6 Jun 2003 15:04:01 -0000 1.106 --- src/backend/executor/nodeAgg.c 16 Jun 2003 23:45:51 -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; /* Planner should have assigned aggregate to correct level */ *************** *** 1227,1232 **** --- 1240,1405 ---- &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. *************** *** 1239,1252 **** 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 --- 1412,1418 ---- 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 *************** *** 1457,1460 **** --- 1623,1659 ---- 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.46 diff -c -r1.46 nodeSubplan.c *** src/backend/executor/nodeSubplan.c 6 Jun 2003 15:04:01 -0000 1.46 --- src/backend/executor/nodeSubplan.c 16 Jun 2003 23:45:51 -0000 *************** *** 28,50 **** #include "utils/datum.h" #include "utils/lsyscache.h" - - typedef struct ArrayBuildState - { - MemoryContext mcontext; /* where all the temp stuff is kept */ - Datum *dvalues; /* array of accumulated Datums */ - /* - * The allocated size of dvalues[] is always a multiple of - * ARRAY_ELEMS_CHUNKSIZE - */ - #define ARRAY_ELEMS_CHUNKSIZE 64 - int nelems; /* number of valid Datums in dvalues[] */ - Oid element_type; /* data type of the Datums */ - int16 typlen; /* needed info about datatype */ - bool typbyval; - char typalign; - } ArrayBuildState; - static Datum ExecHashSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull); --- 28,33 ---- *************** *** 54,66 **** static void buildSubPlanHash(SubPlanState *node); static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot); static bool tupleAllNulls(HeapTuple tuple); - static ArrayBuildState *accumArrayResult(ArrayBuildState *astate, - Datum dvalue, bool disnull, - Oid element_type, - MemoryContext rcontext); - static Datum makeArrayResult(ArrayBuildState *astate, - MemoryContext rcontext); - /* ---------------------------------------------------------------- * ExecSubPlan --- 37,42 ---- *************** *** 224,229 **** --- 200,206 ---- PlanState *planstate = node->planstate; SubLinkType subLinkType = subplan->subLinkType; bool useOr = subplan->useOr; + bool isExpr = subplan->isExpr; MemoryContext oldcontext; TupleTableSlot *slot; Datum result; *************** *** 294,299 **** --- 271,281 ---- bool rownull = false; int col = 1; List *plst; + int numelems; + int elemnum; + Datum dvalue; + Datum *dvalues = NULL; + bool disnull; if (subLinkType == EXISTS_SUBLINK) { *************** *** 331,339 **** if (subLinkType == ARRAY_SUBLINK) { - Datum dvalue; - bool disnull; - found = true; /* stash away current value */ dvalue = heap_getattr(tup, 1, tdesc, &disnull); --- 313,318 ---- *************** *** 351,448 **** found = true; /* ! * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining ! * operators for columns of tuple. */ ! plst = subplan->paramIds; ! foreach(lst, node->exprs) { ! ExprState *exprstate = (ExprState *) lfirst(lst); ! int paramid = lfirsti(plst); ! ParamExecData *prmdata; ! Datum expresult; ! bool expnull; ! ! /* ! * Load up the Param representing this column of the sub-select. ! */ ! prmdata = &(econtext->ecxt_param_exec_vals[paramid]); ! Assert(prmdata->execPlan == NULL); ! prmdata->value = heap_getattr(tup, col, tdesc, ! &(prmdata->isnull)); ! /* ! * Now we can eval the combining operator for this column. ! */ ! expresult = ExecEvalExprSwitchContext(exprstate, econtext, ! &expnull, NULL); ! ! /* ! * Combine the result into the row result as appropriate. ! */ ! if (col == 1) { ! rowresult = expresult; ! rownull = expnull; } ! else if (useOr) { ! /* combine within row per OR semantics */ ! if (expnull) ! rownull = true; ! else if (DatumGetBool(expresult)) { ! rowresult = BoolGetDatum(true); ! rownull = false; ! break; /* needn't look at any more columns */ } } else { ! /* combine within row per AND semantics */ ! if (expnull) ! rownull = true; ! else if (!DatumGetBool(expresult)) ! { ! rowresult = BoolGetDatum(false); ! rownull = false; ! break; /* needn't look at any more columns */ ! } } - plst = lnext(plst); - col++; } ! if (subLinkType == ANY_SUBLINK) { ! /* combine across rows per OR semantics */ ! if (rownull) ! *isNull = true; ! else if (DatumGetBool(rowresult)) { ! result = BoolGetDatum(true); ! *isNull = false; ! break; /* needn't look at any more rows */ } ! } ! else if (subLinkType == ALL_SUBLINK) ! { ! /* combine across rows per AND semantics */ ! if (rownull) ! *isNull = true; ! else if (!DatumGetBool(rowresult)) { ! result = BoolGetDatum(false); ! *isNull = false; ! break; /* needn't look at any more rows */ } - } - else - { - /* must be MULTIEXPR_SUBLINK */ - result = rowresult; - *isNull = rownull; } } --- 330,492 ---- found = true; /* ! * When isExpr is true, we have either a scalar expression or an ! * array. In the former case, this is no different than the !isExpr ! * case. In the latter case, iterate over the elements as if they ! * were from multiple input tuples. */ ! if (!isExpr) ! numelems = 1; ! else { ! Oid expr_typeid = tdesc->attrs[0]->atttypid; ! if (expr_typeid != subplan->exprtype) { ! subplan->exprtype = expr_typeid; ! subplan->elemtype = get_element_type(expr_typeid); ! ! if (subplan->elemtype != InvalidOid) ! get_typlenbyvalalign(subplan->elemtype, ! &subplan->elmlen, ! &subplan->elmbyval, ! &subplan->elmalign); } ! ! /* get current value */ ! dvalue = heap_getattr(tup, 1, tdesc, &disnull); ! ! /* XXX this will need work if/when arrays support NULL elements */ ! if (!disnull) { ! if (subplan->elemtype != InvalidOid) ! { ! ArrayType *v = DatumGetArrayTypeP(dvalue); ! ! deconstruct_array(v, subplan->elemtype, subplan->elmlen, ! subplan->elmbyval, subplan->elmalign, ! &dvalues, &numelems); ! } ! else { ! numelems = 1; ! dvalues = (Datum *) palloc(numelems * sizeof(Datum)); ! dvalues[0] = dvalue; } } else { ! numelems = 1; ! dvalues = (Datum *) palloc(numelems * sizeof(Datum)); ! dvalues[0] = (Datum) 0; } } ! for (elemnum = 0; elemnum < numelems; elemnum++) { ! /* ! * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining ! * operators for columns of tuple. ! */ ! col = 1; ! plst = subplan->paramIds; ! foreach(lst, node->exprs) { ! ExprState *exprstate = (ExprState *) lfirst(lst); ! int paramid = lfirsti(plst); ! ParamExecData *prmdata; ! Datum expresult; ! bool expnull; ! ! /* ! * Load up the Param representing this column of the sub-select. ! */ ! prmdata = &(econtext->ecxt_param_exec_vals[paramid]); ! Assert(prmdata->execPlan == NULL); ! ! if (!isExpr) ! prmdata->value = heap_getattr(tup, col, tdesc, ! &(prmdata->isnull)); ! else ! { ! prmdata->value = dvalues[elemnum]; ! prmdata->isnull = disnull; ! } ! ! /* ! * Now we can eval the combining operator for this column. ! */ ! expresult = ExecEvalExprSwitchContext(exprstate, econtext, ! &expnull, NULL); ! ! /* ! * Combine the result into the row result as appropriate. ! */ ! if (col == 1) ! { ! rowresult = expresult; ! rownull = expnull; ! } ! else if (useOr) ! { ! /* combine within row per OR semantics */ ! if (expnull) ! rownull = true; ! else if (DatumGetBool(expresult)) ! { ! rowresult = BoolGetDatum(true); ! rownull = false; ! break; /* needn't look at any more columns */ ! } ! } ! else ! { ! /* combine within row per AND semantics */ ! if (expnull) ! rownull = true; ! else if (!DatumGetBool(expresult)) ! { ! rowresult = BoolGetDatum(false); ! rownull = false; ! break; /* needn't look at any more columns */ ! } ! } ! ! plst = lnext(plst); ! col++; } ! ! if (subLinkType == ANY_SUBLINK) { ! /* combine across rows per OR semantics */ ! if (rownull) ! *isNull = true; ! else if (DatumGetBool(rowresult)) ! { ! result = BoolGetDatum(true); ! *isNull = false; ! break; /* needn't look at any more rows */ ! } ! } ! else if (subLinkType == ALL_SUBLINK) ! { ! /* combine across rows per AND semantics */ ! if (rownull) ! *isNull = true; ! else if (!DatumGetBool(rowresult)) ! { ! result = BoolGetDatum(false); ! *isNull = false; ! break; /* needn't look at any more rows */ ! } ! } ! else ! { ! /* must be MULTIEXPR_SUBLINK */ ! result = rowresult; ! *isNull = rownull; } } } *************** *** 480,485 **** --- 524,530 ---- buildSubPlanHash(SubPlanState *node) { SubPlan *subplan = (SubPlan *) node->xprstate.expr; + bool isExpr = subplan->isExpr; PlanState *planstate = node->planstate; int ncols = length(node->exprs); ExprContext *innerecontext = node->innerecontext; *************** *** 487,492 **** --- 532,538 ---- MemoryContext oldcontext; int nbuckets; TupleTableSlot *slot; + TupleTableSlot *arrslot = NULL; Assert(subplan->subLinkType == ANY_SUBLINK); Assert(!subplan->useOr); *************** *** 564,606 **** { 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; } /* --- 610,748 ---- { 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; ! } } /* *************** *** 617,622 **** --- 759,766 ---- * have the potential for a double free attempt. */ ExecClearTuple(node->projRight->pi_slot); + if (arrslot) + ExecClearTuple(arrslot); MemoryContextSwitchTo(oldcontext); } *************** *** 1086,1187 **** 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); } --- 1230,1233 ---- Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/copyfuncs.c,v retrieving revision 1.252 diff -c -r1.252 copyfuncs.c *** src/backend/nodes/copyfuncs.c 6 Jun 2003 15:04:02 -0000 1.252 --- src/backend/nodes/copyfuncs.c 16 Jun 2003 23:45:51 -0000 *************** *** 728,733 **** --- 728,734 ---- COPY_SCALAR_FIELD(agglevelsup); COPY_SCALAR_FIELD(aggstar); COPY_SCALAR_FIELD(aggdistinct); + COPY_NODE_FIELD(args); return newnode; } *************** *** 826,831 **** --- 827,833 ---- COPY_SCALAR_FIELD(subLinkType); COPY_SCALAR_FIELD(useOr); + COPY_SCALAR_FIELD(isExpr); COPY_NODE_FIELD(lefthand); COPY_NODE_FIELD(operName); COPY_OIDLIST_FIELD(operOids); *************** *** 844,849 **** --- 846,857 ---- COPY_SCALAR_FIELD(subLinkType); COPY_SCALAR_FIELD(useOr); + COPY_SCALAR_FIELD(isExpr); + COPY_SCALAR_FIELD(exprtype); + COPY_SCALAR_FIELD(elemtype); + COPY_SCALAR_FIELD(elmlen); + COPY_SCALAR_FIELD(elmbyval); + COPY_SCALAR_FIELD(elmalign); COPY_NODE_FIELD(exprs); COPY_INTLIST_FIELD(paramIds); COPY_NODE_FIELD(plan); Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/equalfuncs.c,v retrieving revision 1.195 diff -c -r1.195 equalfuncs.c *** src/backend/nodes/equalfuncs.c 6 Jun 2003 15:04:02 -0000 1.195 --- src/backend/nodes/equalfuncs.c 16 Jun 2003 23:45:51 -0000 *************** *** 205,210 **** --- 205,211 ---- COMPARE_SCALAR_FIELD(agglevelsup); COMPARE_SCALAR_FIELD(aggstar); COMPARE_SCALAR_FIELD(aggdistinct); + COMPARE_NODE_FIELD(args); return true; } *************** *** 301,306 **** --- 302,308 ---- { COMPARE_SCALAR_FIELD(subLinkType); COMPARE_SCALAR_FIELD(useOr); + COMPARE_SCALAR_FIELD(isExpr); COMPARE_NODE_FIELD(lefthand); COMPARE_NODE_FIELD(operName); COMPARE_OIDLIST_FIELD(operOids); *************** *** 314,319 **** --- 316,327 ---- { COMPARE_SCALAR_FIELD(subLinkType); COMPARE_SCALAR_FIELD(useOr); + COMPARE_SCALAR_FIELD(isExpr); + COMPARE_SCALAR_FIELD(exprtype); + COMPARE_SCALAR_FIELD(elemtype); + COMPARE_SCALAR_FIELD(elmlen); + COMPARE_SCALAR_FIELD(elmbyval); + COMPARE_SCALAR_FIELD(elmalign); COMPARE_NODE_FIELD(exprs); COMPARE_INTLIST_FIELD(paramIds); /* should compare plans, but have to settle for comparing plan IDs */ Index: src/backend/nodes/outfuncs.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/outfuncs.c,v retrieving revision 1.208 diff -c -r1.208 outfuncs.c *** src/backend/nodes/outfuncs.c 15 Jun 2003 22:51:45 -0000 1.208 --- src/backend/nodes/outfuncs.c 16 Jun 2003 23:45:51 -0000 *************** *** 616,621 **** --- 616,622 ---- WRITE_UINT_FIELD(agglevelsup); WRITE_BOOL_FIELD(aggstar); WRITE_BOOL_FIELD(aggdistinct); + WRITE_NODE_FIELD(args); } static void *************** *** 701,706 **** --- 702,708 ---- WRITE_ENUM_FIELD(subLinkType, SubLinkType); WRITE_BOOL_FIELD(useOr); + WRITE_BOOL_FIELD(isExpr); WRITE_NODE_FIELD(lefthand); WRITE_NODE_FIELD(operName); WRITE_OIDLIST_FIELD(operOids); *************** *** 714,719 **** --- 716,727 ---- WRITE_ENUM_FIELD(subLinkType, SubLinkType); WRITE_BOOL_FIELD(useOr); + WRITE_BOOL_FIELD(isExpr); + WRITE_OID_FIELD(exprtype); + WRITE_OID_FIELD(elemtype); + WRITE_INT_FIELD(elmlen); + WRITE_BOOL_FIELD(elmbyval); + WRITE_CHAR_FIELD(elmalign); WRITE_NODE_FIELD(exprs); WRITE_INTLIST_FIELD(paramIds); WRITE_NODE_FIELD(plan); Index: src/backend/nodes/readfuncs.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/nodes/readfuncs.c,v retrieving revision 1.154 diff -c -r1.154 readfuncs.c *** src/backend/nodes/readfuncs.c 6 Jun 2003 15:04:02 -0000 1.154 --- src/backend/nodes/readfuncs.c 16 Jun 2003 23:45:51 -0000 *************** *** 416,421 **** --- 416,422 ---- READ_UINT_FIELD(agglevelsup); READ_BOOL_FIELD(aggstar); READ_BOOL_FIELD(aggdistinct); + READ_NODE_FIELD(args); READ_DONE(); } *************** *** 545,550 **** --- 546,552 ---- READ_ENUM_FIELD(subLinkType, SubLinkType); READ_BOOL_FIELD(useOr); + READ_BOOL_FIELD(isExpr); READ_NODE_FIELD(lefthand); READ_NODE_FIELD(operName); READ_OIDLIST_FIELD(operOids); Index: src/backend/optimizer/plan/subselect.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/plan/subselect.c,v retrieving revision 1.76 diff -c -r1.76 subselect.c *** src/backend/optimizer/plan/subselect.c 6 Jun 2003 15:04:02 -0000 1.76 --- src/backend/optimizer/plan/subselect.c 16 Jun 2003 23:45:51 -0000 *************** *** 83,89 **** static List *convert_sublink_opers(List *lefthand, List *operOids, List *targetlist, int rtindex, ! List **righthandIds); static bool subplan_is_hashable(SubLink *slink, SubPlan *node); static Node *replace_correlation_vars_mutator(Node *node, void *context); static Node *process_sublinks_mutator(Node *node, bool *isTopQual); --- 83,89 ---- static List *convert_sublink_opers(List *lefthand, List *operOids, List *targetlist, int rtindex, ! bool isExpr, List **righthandIds); static bool subplan_is_hashable(SubLink *slink, SubPlan *node); static Node *replace_correlation_vars_mutator(Node *node, void *context); static Node *process_sublinks_mutator(Node *node, bool *isTopQual); *************** *** 299,304 **** --- 299,310 ---- */ node->subLinkType = slink->subLinkType; node->useOr = slink->useOr; + node->isExpr = slink->isExpr; + node->exprtype = InvalidOid; + node->elemtype = InvalidOid; + node->elmlen = 0; + node->elmbyval = false; + node->elmalign = '\0'; node->exprs = NIL; node->paramIds = NIL; node->useHashTable = false; *************** *** 374,380 **** exprs = convert_sublink_opers(lefthand, slink->operOids, plan->targetlist, ! 0, &node->paramIds); node->setParam = listCopy(node->paramIds); PlannerInitPlan = lappend(PlannerInitPlan, node); --- 380,386 ---- exprs = convert_sublink_opers(lefthand, slink->operOids, plan->targetlist, ! 0, node->isExpr, &node->paramIds); node->setParam = listCopy(node->paramIds); PlannerInitPlan = lappend(PlannerInitPlan, node); *************** *** 457,463 **** node->exprs = convert_sublink_opers(lefthand, slink->operOids, plan->targetlist, ! 0, &node->paramIds); /* --- 463,469 ---- node->exprs = convert_sublink_opers(lefthand, slink->operOids, plan->targetlist, ! 0, node->isExpr, &node->paramIds); /* *************** *** 499,505 **** static List * convert_sublink_opers(List *lefthand, List *operOids, List *targetlist, int rtindex, ! List **righthandIds) { List *result = NIL; List *lst; --- 505,511 ---- static List * convert_sublink_opers(List *lefthand, List *operOids, List *targetlist, int rtindex, ! bool isExpr, List **righthandIds) { List *result = NIL; List *lst; *************** *** 554,566 **** * are not expecting to have to resolve unknown Params, so * it's okay to pass a null pstate.) */ ! result = lappend(result, ! make_op_expr(NULL, ! tup, ! leftop, ! rightop, ! exprType(leftop), ! te->resdom->restype)); ReleaseSysCache(tup); --- 560,597 ---- * are not expecting to have to resolve unknown Params, so * it's okay to pass a null pstate.) */ ! if (!isExpr) ! { ! result = lappend(result, ! make_op_expr(NULL, ! tup, ! leftop, ! rightop, ! exprType(leftop), ! te->resdom->restype)); ! } ! else ! { ! Oid exprtype = te->resdom->restype; ! Oid elemtype = get_element_type(exprtype); ! ! if (elemtype != InvalidOid) ! result = lappend(result, ! make_op_expr(NULL, ! tup, ! leftop, ! rightop, ! exprType(leftop), ! elemtype)); ! else ! result = lappend(result, ! make_op_expr(NULL, ! tup, ! leftop, ! rightop, ! exprType(leftop), ! exprtype)); ! } ReleaseSysCache(tup); *************** *** 671,683 **** /* * The sublink type must be "= ANY" --- that is, an IN operator. * (We require the operator name to be unqualified, which may be ! * overly paranoid, or may not be.) */ if (sublink->subLinkType != ANY_SUBLINK) return NULL; if (length(sublink->operName) != 1 || strcmp(strVal(lfirst(sublink->operName)), "=") != 0) return NULL; /* * The sub-select must not refer to any Vars of the parent query. * (Vars of higher levels should be okay, though.) --- 702,718 ---- /* * The sublink type must be "= ANY" --- that is, an IN operator. * (We require the operator name to be unqualified, which may be ! * overly paranoid, or may not be.) It must not be an Expression ! * sublink. */ if (sublink->subLinkType != ANY_SUBLINK) return NULL; if (length(sublink->operName) != 1 || strcmp(strVal(lfirst(sublink->operName)), "=") != 0) return NULL; + if (sublink->isExpr) + return NULL; + /* * The sub-select must not refer to any Vars of the parent query. * (Vars of higher levels should be okay, though.) *************** *** 730,736 **** exprs = convert_sublink_opers(sublink->lefthand, sublink->operOids, subselect->targetList, ! rtindex, &ininfo->sub_targetlist); return (Node *) make_ands_explicit(exprs); } --- 765,771 ---- exprs = convert_sublink_opers(sublink->lefthand, sublink->operOids, subselect->targetList, ! rtindex, sublink->isExpr, &ininfo->sub_targetlist); return (Node *) make_ands_explicit(exprs); } Index: src/backend/optimizer/util/clauses.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/optimizer/util/clauses.c,v retrieving revision 1.139 diff -c -r1.139 clauses.c *** src/backend/optimizer/util/clauses.c 6 Jun 2003 15:04:02 -0000 1.139 --- src/backend/optimizer/util/clauses.c 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -0000 *************** *** 436,441 **** --- 436,442 ---- sublink->operName = NIL; sublink->operOids = NIL; sublink->useOr = FALSE; + sublink->isExpr = FALSE; } else if (sublink->subLinkType == EXPR_SUBLINK || sublink->subLinkType == ARRAY_SUBLINK) *************** *** 463,468 **** --- 464,470 ---- sublink->operName = NIL; sublink->operOids = NIL; sublink->useOr = FALSE; + sublink->isExpr = FALSE; } else { *************** *** 538,547 **** * here, because make_subplan() will insert type * coercion calls if needed. */ ! optup = oper(op, ! exprType(lexpr), ! exprType((Node *) tent->expr), ! false); opform = (Form_pg_operator) GETSTRUCT(optup); if (opform->oprresult != BOOLOID) --- 540,569 ---- * here, because make_subplan() will insert type * coercion calls if needed. */ ! if (!sublink->isExpr) ! { ! optup = oper(op, ! exprType(lexpr), ! exprType((Node *) tent->expr), ! false); ! } ! else ! { ! Oid exprtype = exprType((Node *) tent->expr); ! Oid elemtype = get_element_type(exprtype); ! ! if (elemtype != InvalidOid) ! optup = oper(op, ! exprType(lexpr), ! elemtype, ! false); ! else ! optup = oper(op, ! exprType(lexpr), ! exprtype, ! false); ! } ! opform = (Form_pg_operator) GETSTRUCT(optup); if (opform->oprresult != BOOLOID) *************** *** 743,749 **** ArrayExpr *e = (ArrayExpr *) lfirst(element); if (!IsA(e, ArrayExpr)) ! elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions"); if (ndims == 0) ndims = e->ndims; else if (e->ndims != ndims) --- 765,771 ---- ArrayExpr *e = (ArrayExpr *) lfirst(element); if (!IsA(e, ArrayExpr)) ! elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions"); if (ndims == 0) ndims = e->ndims; else if (e->ndims != ndims) Index: src/backend/parser/parse_func.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v retrieving revision 1.149 diff -c -r1.149 parse_func.c *** src/backend/parser/parse_func.c 6 Jun 2003 15:04:02 -0000 1.149 --- src/backend/parser/parse_func.c 16 Jun 2003 23:45:51 -0000 *************** *** 336,341 **** --- 336,342 ---- aggref->target = lfirst(fargs); aggref->aggstar = agg_star; aggref->aggdistinct = agg_distinct; + aggref->args = fargs; /* parse_agg.c does additional aggregate-specific processing */ transformAggregateCall(pstate, aggref); Index: src/backend/parser/parse_oper.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_oper.c,v retrieving revision 1.64 diff -c -r1.64 parse_oper.c *** src/backend/parser/parse_oper.c 26 May 2003 00:11:27 -0000 1.64 --- src/backend/parser/parse_oper.c 16 Jun 2003 23:45:51 -0000 *************** *** 137,142 **** --- 137,169 ---- equality_oper(Oid argtype, bool noError) { Operator optup; + Oid elem_type = get_element_type(argtype); + + if (OidIsValid(elem_type)) + { + bool found = false; + /* + * If the datatype is an array, look for an "=" operator for the + * element datatype. We require it to be an exact or binary-compatible + * match, since most callers are not prepared to cope with adding any + * run-time type coercion steps. + */ + optup = equality_oper(elem_type, true); + if (optup != NULL) + { + found = true; + ReleaseSysCache(optup); + } + + if (!found) + { + if (!noError) + elog(ERROR, "Unable to identify an equality operator for " \ + "array type's element type %s", + format_type_be(elem_type)); + return NULL; + } + } /* * Look for an "=" operator for the datatype. We require it to be *************** *** 175,180 **** --- 202,234 ---- ordering_oper(Oid argtype, bool noError) { Operator optup; + Oid elem_type = get_element_type(argtype); + + if (OidIsValid(elem_type)) + { + bool found = false; + /* + * If the datatype is an array, find the array element type's equality + * operator, and use its lsortop (it *must* be mergejoinable). We use + * this definition because for sorting and grouping purposes, it's + * important that the equality and ordering operators are consistent. + */ + optup = ordering_oper(elem_type, true); + if (optup != NULL) + { + found = true; + ReleaseSysCache(optup); + } + + if (!found) + { + if (!noError) + elog(ERROR, "Unable to identify an ordering operator for " \ + "array type's element type %s", + format_type_be(elem_type)); + return NULL; + } + } /* * Find the type's equality operator, and use its lsortop (it *must* *************** *** 215,220 **** --- 269,289 ---- Oid result; optup = equality_oper(argtype, false); + result = oprfuncid(optup); + ReleaseSysCache(optup); + return result; + } + + /* + * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper()) + */ + Oid + ordering_oper_funcid(Oid argtype) + { + Operator optup; + Oid result; + + optup = ordering_oper(argtype, false); result = oprfuncid(optup); ReleaseSysCache(optup); return result; Index: src/backend/utils/adt/acl.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/acl.c,v retrieving revision 1.88 diff -c -r1.88 acl.c *** src/backend/utils/adt/acl.c 11 Jun 2003 09:23:55 -0000 1.88 --- src/backend/utils/adt/acl.c 16 Jun 2003 21:19:53 -0000 *************** *** 427,432 **** --- 427,441 ---- a1->ai_grantor == a2->ai_grantor; } + /* + * user-facing version of aclitemeq() for use as the + * aclitem equality operator + */ + Datum + aclitem_eq(PG_FUNCTION_ARGS) + { + PG_RETURN_BOOL(aclitemeq(PG_GETARG_ACLITEM_P(0), PG_GETARG_ACLITEM_P(1))); + } /* * acldefault() --- create an ACL describing default access permissions Index: src/backend/utils/adt/array_userfuncs.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/adt/array_userfuncs.c,v retrieving revision 1.1 diff -c -r1.1 array_userfuncs.c *** src/backend/utils/adt/array_userfuncs.c 8 Apr 2003 23:20:02 -0000 1.1 --- src/backend/utils/adt/array_userfuncs.c 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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.303 diff -c -r1.303 pg_proc.h *** src/include/catalog/pg_proc.h 11 Jun 2003 09:23:55 -0000 1.303 --- src/include/catalog/pg_proc.h 16 Jun 2003 23:45:51 -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 = 1365 ( makeaclitem PGNSP PGUID 12 f f t f s 5 1033 "23 23 23 25 16" makeaclitem - _null_ )); DESCR("make ACL item"); DATA(insert OID = 1038 ( seteval PGNSP PGUID 12 f f t t v 1 23 "26" seteval - _null_ )); Index: src/include/nodes/primnodes.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/nodes/primnodes.h,v retrieving revision 1.83 diff -c -r1.83 primnodes.h *** src/include/nodes/primnodes.h 6 Jun 2003 15:04:03 -0000 1.83 --- src/include/nodes/primnodes.h 16 Jun 2003 23:45:51 -0000 *************** *** 226,231 **** --- 226,232 ---- Index agglevelsup; /* > 0 if agg belongs to outer query */ bool aggstar; /* TRUE if argument was really '*' */ bool aggdistinct; /* TRUE if it's agg(DISTINCT ...) */ + List *args; /* arguments to the aggregate */ } Aggref; /* ---------------- *************** *** 358,372 **** /* ---------------- * SubLink * ! * A SubLink represents a subselect appearing in an expression, and in some ! * cases also the combining operator(s) just above it. The subLinkType ! * indicates the form of the expression represented: * EXISTS_SUBLINK EXISTS(SELECT ...) * ALL_SUBLINK (lefthand) op ALL (SELECT ...) * ANY_SUBLINK (lefthand) op ANY (SELECT ...) * MULTIEXPR_SUBLINK (lefthand) op (SELECT ...) * EXPR_SUBLINK (SELECT with single targetlist item ...) * ARRAY_SUBLINK ARRAY(SELECT with single targetlist item ...) * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the * same length as the subselect's targetlist. MULTIEXPR will *always* have * a list with more than one entry; if the subselect has just one target --- 359,377 ---- /* ---------------- * SubLink * ! * A SubLink represents a subselect, or an expression, appearing in an ! * expression, and in some cases also the combining operator(s) just above ! * it. The subLinkType indicates the form of the expression represented: * EXISTS_SUBLINK EXISTS(SELECT ...) * ALL_SUBLINK (lefthand) op ALL (SELECT ...) * ANY_SUBLINK (lefthand) op ANY (SELECT ...) * MULTIEXPR_SUBLINK (lefthand) op (SELECT ...) * EXPR_SUBLINK (SELECT with single targetlist item ...) * ARRAY_SUBLINK ARRAY(SELECT with single targetlist item ...) + * If an expression is used in place of the subselect, it is transformed + * into a simple "(SELECT expr)" in gram.y. This is to allow arrays to be + * used as if they were the result of a single column subselect. If the + * expression is scalar, it is treated as a one element array. * For ALL, ANY, and MULTIEXPR, the lefthand is a list of expressions of the * same length as the subselect's targetlist. MULTIEXPR will *always* have * a list with more than one entry; if the subselect has just one target *************** *** 415,420 **** --- 420,427 ---- SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */ bool useOr; /* TRUE to combine column results with * "OR" not "AND" */ + bool isExpr; /* TRUE if the subselect is really derived + * from a single expression */ List *lefthand; /* list of outer-query expressions on the * left */ List *operName; /* originally specified operator name */ *************** *** 456,461 **** --- 463,477 ---- SubLinkType subLinkType; /* EXISTS, ALL, ANY, MULTIEXPR, EXPR */ bool useOr; /* TRUE to combine column results with * "OR" not "AND" */ + bool isExpr; /* TRUE if the subselect is really derived + * from a single expression */ + /* runtime cache for single array expressions */ + Oid exprtype; /* array and element type, and other info + * needed deconstruct the array */ + Oid elemtype; + int16 elmlen; + bool elmbyval; + char elmalign; /* The combining operators, transformed to executable expressions: */ List *exprs; /* list of OpExpr expression trees */ List *paramIds; /* IDs of Params embedded in the above */ Index: src/include/optimizer/clauses.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/optimizer/clauses.h,v retrieving revision 1.63 diff -c -r1.63 clauses.h *** src/include/optimizer/clauses.h 28 May 2003 16:04:02 -0000 1.63 --- src/include/optimizer/clauses.h 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -0000 *************** *** 44,49 **** --- 44,50 ---- /* Convenience routines for common calls on the above */ extern Oid compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError); extern Oid equality_oper_funcid(Oid argtype); + extern Oid ordering_oper_funcid(Oid argtype); extern Oid ordering_oper_opid(Oid argtype); /* Extract operator OID or underlying-function OID from an Operator tuple */ Index: src/include/utils/acl.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/utils/acl.h,v retrieving revision 1.52 diff -c -r1.52 acl.h *** src/include/utils/acl.h 11 Jun 2003 09:23:55 -0000 1.52 --- src/include/utils/acl.h 16 Jun 2003 23:41:46 -0000 *************** *** 192,197 **** --- 192,198 ---- extern Datum aclremove(PG_FUNCTION_ARGS); extern Datum aclcontains(PG_FUNCTION_ARGS); extern Datum makeaclitem(PG_FUNCTION_ARGS); + extern Datum aclitem_eq(PG_FUNCTION_ARGS); /* * prototypes for functions in aclchk.c Index: src/include/utils/array.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/utils/array.h,v retrieving revision 1.38 diff -c -r1.38 array.h *** src/include/utils/array.h 8 May 2003 22:19:57 -0000 1.38 --- src/include/utils/array.h 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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.232 diff -c -r1.232 preproc.y *** src/interfaces/ecpg/preproc/preproc.y 16 Jun 2003 16:58:11 -0000 1.232 --- src/interfaces/ecpg/preproc/preproc.y 16 Jun 2003 23:45:51 -0000 *************** *** 4592,4598 **** $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; } --- 4592,4598 ---- $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; } *************** *** 5424,5430 **** $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; } --- 5424,5430 ---- $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; } *************** *** 5491,5497 **** 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")); --- 5491,5497 ---- 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 16 Jun 2003 23:45:51 -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.21 diff -c -r1.21 variable.c *** src/interfaces/ecpg/preproc/variable.c 11 Jun 2003 06:39:13 -0000 1.21 --- src/interfaces/ecpg/preproc/variable.c 16 Jun 2003 23:45:51 -0000 *************** *** 436,442 **** if (atoi(type_index) >= 0) { if (atoi(*length) >= 0) ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support"); *length = type_index; } --- 436,442 ---- if (atoi(type_index) >= 0) { if (atoi(*length) >= 0) ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support"); *length = type_index; } *************** *** 444,450 **** if (atoi(type_dimension) >= 0) { if (atoi(*dimension) >= 0 && atoi(*length) >= 0) ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support"); if (atoi(*dimension) >= 0) *length = *dimension; --- 444,450 ---- if (atoi(type_dimension) >= 0) { if (atoi(*dimension) >= 0 && atoi(*length) >= 0) ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support"); if (atoi(*dimension) >= 0) *length = *dimension; *************** *** 463,472 **** mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type"); if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0)) ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support"); if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len) ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support"); switch (type_enum) { --- 463,472 ---- mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type"); if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0)) ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support"); if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len) ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support"); switch (type_enum) { *************** *** 480,486 **** } if (atoi(*length) >= 0) ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures"); break; case ECPGt_varchar: --- 480,486 ---- } if (atoi(*length) >= 0) ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures"); break; case ECPGt_varchar: *************** *** 525,531 **** } if (atoi(*length) >= 0) ! mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types"); break; } --- 525,531 ---- } if (atoi(*length) >= 0) ! mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types"); break; } Index: src/test/regress/expected/arrays.out =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/arrays.out,v retrieving revision 1.11 diff -c -r1.11 arrays.out *** src/test/regress/expected/arrays.out 8 Apr 2003 23:20:04 -0000 1.11 --- src/test/regress/expected/arrays.out 16 Jun 2003 23:45:51 -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 16 Jun 2003 23:45:51 -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]]];
pgsql-patches by date: