diff --git a/doc/src/sgml/array.sgml b/doc/src/sgml/array.sgml index 3901ef4efc6ceb2c7541d6f7bbded2f5bc4fe82f..a7a05762de3cb50e25f615856f02a2c3c89281a4 100644 --- a/doc/src/sgml/array.sgml +++ b/doc/src/sgml/array.sgml @@ -1,4 +1,4 @@ -<!-- $Header: /cvsroot/pgsql/doc/src/sgml/array.sgml,v 1.25 2003/03/13 01:30:26 petere Exp $ --> +<!-- $Header: /cvsroot/pgsql/doc/src/sgml/array.sgml,v 1.26 2003/06/24 23:14:42 momjian Exp $ --> <sect1 id="arrays"> <title>Arrays</title> @@ -60,14 +60,74 @@ INSERT INTO sal_emp </programlisting> </para> + <para> + A limitation of the present array implementation is that individual + elements of an array cannot be SQL null values. The entire array can be set + to null, but you can't have an array with some elements null and some + not. + </para> + <para> + This can lead to surprising results. For example, the result of the + previous two inserts looks like this: +<programlisting> +SELECT * FROM sal_emp; + name | pay_by_quarter | schedule +-------+---------------------------+-------------------- + Bill | {10000,10000,10000,10000} | {{meeting},{""}} + Carol | {20000,25000,25000,25000} | {{talk},{meeting}} +(2 rows) +</programlisting> + Because the <literal>[2][2]</literal> element of + <structfield>schedule</structfield> is missing in each of the + <command>INSERT</command> statements, the <literal>[1][2]</literal> + element is discarded. + </para> + <note> <para> - A limitation of the present array implementation is that individual - elements of an array cannot be SQL null values. The entire array can be set - to null, but you can't have an array with some elements null and some - not. Fixing this is on the to-do list. + Fixing this is on the to-do list. </para> </note> + + <para> + The <command>ARRAY</command> expression syntax may also be used: +<programlisting> +INSERT INTO sal_emp + VALUES ('Bill', + ARRAY[10000, 10000, 10000, 10000], + ARRAY[['meeting', 'lunch'], ['','']]); + +INSERT INTO sal_emp + VALUES ('Carol', + ARRAY[20000, 25000, 25000, 25000], + ARRAY[['talk', 'consult'], ['meeting', '']]); +SELECT * FROM sal_emp; + name | pay_by_quarter | schedule +-------+---------------------------+------------------------------- + Bill | {10000,10000,10000,10000} | {{meeting,lunch},{"",""}} + Carol | {20000,25000,25000,25000} | {{talk,consult},{meeting,""}} +(2 rows) +</programlisting> + Note that with this syntax, multidimensional arrays must have matching + extents for each dimension. This eliminates the missing-array-elements + problem above. For example: +<programlisting> +INSERT INTO sal_emp + VALUES ('Carol', + ARRAY[20000, 25000, 25000, 25000], + ARRAY[['talk', 'consult'], ['meeting']]); +ERROR: Multidimensional arrays must have array expressions with matching dimensions +</programlisting> + Also notice that string literals are single quoted instead of double quoted. + </para> + + <note> + <para> + The examples in the rest of this section are based on the + <command>ARRAY</command> expression syntax <command>INSERT</command>s. + </para> + </note> + </sect2> <sect2> @@ -132,11 +192,30 @@ SELECT schedule[1:2][1] FROM sal_emp WHERE name = 'Bill'; </programlisting> with the same result. An array subscripting operation is always taken to - represent an array slice if any of the subscripts are written in the - form + represent an array slice if any of the subscripts are written in the form <literal><replaceable>lower</replaceable>:<replaceable>upper</replaceable></literal>. A lower bound of 1 is assumed for any subscript where only one value - is specified. + is specified; another example follows: +<programlisting> +SELECT schedule[1:2][2] FROM sal_emp WHERE name = 'Bill'; + schedule +--------------------------- + {{meeting,lunch},{"",""}} +(1 row) +</programlisting> + </para> + + <para> + Additionally, we can also access a single arbitrary array element of + a one-dimensional array with the <function>array_subscript</function> + function: +<programlisting> +SELECT array_subscript(pay_by_quarter, 2) FROM sal_emp WHERE name = 'Bill'; + array_subscript +----------------- + 10000 +(1 row) +</programlisting> </para> <para> @@ -147,7 +226,23 @@ UPDATE sal_emp SET pay_by_quarter = '{25000,25000,27000,27000}' WHERE name = 'Carol'; </programlisting> - or updated at a single element: + or using the <command>ARRAY</command> expression syntax: + +<programlisting> +UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000] + WHERE name = 'Carol'; +</programlisting> + + <note> + <para> + Anywhere you can use the <quote>curly braces</quote> array syntax, + you can also use the <command>ARRAY</command> expression syntax. The + remainder of this section will illustrate only one or the other, but + not both. + </para> + </note> + + An array may also be updated at a single element: <programlisting> UPDATE sal_emp SET pay_by_quarter[4] = 15000 @@ -160,6 +255,14 @@ UPDATE sal_emp SET pay_by_quarter[4] = 15000 UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}' WHERE name = 'Carol'; </programlisting> + + A one-dimensional array may also be updated with the + <function>array_assign</function> function: + +<programlisting> +UPDATE sal_emp SET pay_by_quarter = array_assign(pay_by_quarter, 4, 15000) + WHERE name = 'Bill'; +</programListing> </para> <para> @@ -178,6 +281,88 @@ UPDATE sal_emp SET pay_by_quarter[1:2] = '{27000,27000}' create an array with subscript values running from -2 to 7. </para> + <para> + An array can also be enlarged by using the concatenation operator, + <command>||</command>. +<programlisting> +SELECT ARRAY[1,2] || ARRAY[3,4]; + ?column? +--------------- + {{1,2},{3,4}} +(1 row) + +SELECT ARRAY[5,6] || ARRAY[[1,2],[3,4]]; + ?column? +--------------------- + {{5,6},{1,2},{3,4}} +(1 row) +</programlisting> + + The concatenation operator allows a single element to be pushed on to the + beginning or end of a one-dimensional array. It also allows two + <replaceable>N</>-dimensional arrays, or an <replaceable>N</>-dimensional + and an <replaceable>N+1</>-dimensional array. In the former case, the two + <replaceable>N</>-dimension arrays become outer elements of an + <replaceable>N+1</>-dimensional array. In the latter, the + <replaceable>N</>-dimensional array is added as either the first or last + outer element of the <replaceable>N+1</>-dimensional array. + + The array is extended in the direction of the push. Hence, by pushing + onto the beginning of an array with a one-based subscript, a zero-based + subscript array is created: + +<programlisting> +SELECT array_dims(t.f) FROM (SELECT 1 || ARRAY[2,3] AS f) AS t; + array_dims +------------ + [0:2] +(1 row) +</programlisting> + </para> + + <para> + An array can also be enlarged by using the functions + <function>array_prepend</function>, <function>array_append</function>, + or <function>array_cat</function>. The first two only support one-dimensional + arrays, but <function>array_cat</function> supports multidimensional arrays. + + Note that the concatenation operator discussed above is preferred over + direct use of these functions. In fact, the functions are primarily for use + in implementing the concatenation operator. However, they may be directly + useful in the creation of user-defined aggregates. Some examples: + +<programlisting> +SELECT array_prepend(1, ARRAY[2,3]); + array_prepend +--------------- + {1,2,3} +(1 row) + +SELECT array_append(ARRAY[1,2], 3); + array_append +-------------- + {1,2,3} +(1 row) + +SELECT array_cat(ARRAY[1,2], ARRAY[3,4]); + array_cat +--------------- + {{1,2},{3,4}} +(1 row) + +SELECT array_cat(ARRAY[[1,2],[3,4]], ARRAY[5,6]); + array_cat +--------------------- + {{1,2},{3,4},{5,6}} +(1 row) + +SELECT array_cat(ARRAY[5,6], ARRAY[[1,2],[3,4]]); + array_cat +--------------------- + {{5,6},{1,2},{3,4}} +</programlisting> + </para> + <para> The syntax for <command>CREATE TABLE</command> allows fixed-length arrays to be defined: @@ -193,6 +378,16 @@ CREATE TABLE tictactoe ( length. </para> + <para> + An alternative syntax for one-dimensional arrays may be used. + <structfield>pay_by_quarter</structfield> could have been defined as: +<programlisting> + pay_by_quarter integer ARRAY[4], +</programlisting> + This syntax may <emphasis>only</emphasis> be used with the integer + constant to denote the array size. + </para> + <para> Actually, the current implementation does not enforce the declared number of dimensions either. Arrays of a particular element type are @@ -300,6 +495,72 @@ SELECT * FROM sal_emp WHERE pay_by_quarter **= 10000; is not ignored, however: after skipping leading whitespace, everything up to the next right brace or delimiter is taken as the item value. </para> + + <para> + As illustrated earlier in this chapter, arrays may also be represented + using the <command>ARRAY</command> expression syntax. This representation + of an array value consists of items that are interpreted according to the + I/O conversion rules for the array's element type, plus decoration that + indicates the array structure. The decoration consists of the keyword + <command>ARRAY</command> and square brackets (<literal>[</> and + <literal>]</>) around the array values, plus delimiter characters between + adjacent items. The delimiter character is always a comma (<literal>,</>). + When representing multidimensional arrays, the keyword + <command>ARRAY</command> is only necessary for the outer level. For example, + <literal>'{{"hello world", "happy birthday"}}'</literal> could be written as: +<programlisting> +SELECT ARRAY[['hello world', 'happy birthday']]; + array +------------------------------------ + {{"hello world","happy birthday"}} +(1 row) +</programlisting> + or it also could be written as: +<programlisting> +SELECT ARRAY[ARRAY['hello world', 'happy birthday']]; + array +------------------------------------ + {{"hello world","happy birthday"}} +(1 row) +</programlisting> + </para> + + <para> + A final method to represent an array, is through an + <command>ARRAY</command> sub-select expression. For example: +<programlisting> +SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%'); + ?column? +------------------------------------------------------------- + {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31} +(1 row) +</programlisting> + The sub-select may <emphasis>only</emphasis> return a single column. The + resulting one-dimensional array will have an element for each row in the + sub-select result, with an element type matching that of the sub-select's + target column. + </para> + + <para> + Arrays may be cast from one type to another in similar fashion to other + data types: + +<programlisting> +SELECT ARRAY[1,2,3]::oid[]; + array +--------- + {1,2,3} +(1 row) + +SELECT CAST(ARRAY[1,2,3] AS float8[]); + array +--------- + {1,2,3} +(1 row) +</programlisting> + + </para> + </sect2> <sect2> @@ -317,6 +578,14 @@ SELECT * FROM sal_emp WHERE pay_by_quarter **= 10000; that would otherwise be taken as array syntax or ignorable white space. </para> + <note> + <para> + The discussion in the preceding paragraph with respect to double quoting does + not pertain to the <command>ARRAY</command> expression syntax. In that case, + each element is quoted exactly as any other literal value of the element type. + </para> + </note> + <para> The array output routine will put double quotes around element values if they are empty strings or contain curly braces, delimiter characters, diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 3519fad36e63904538d0d3b3b30519b83db3e5ed..d092cafa2da475ccc527cfb5e2bd9ff2f109faff 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.154 2003/05/05 15:08:49 tgl Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/func.sgml,v 1.155 2003/06/24 23:14:42 momjian Exp $ PostgreSQL documentation --> @@ -6962,6 +6962,203 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); </sect1> + <sect1 id="functions-array"> + <title>Array Functions</title> + + <para> + <xref linkend="array-operators-table"> shows the operators + available for the <type>array</type> types. + </para> + + <table id="array-operators-table"> + <title><type>array</type> Operators</title> + <tgroup cols="4"> + <thead> + <row> + <entry>Operator</entry> + <entry>Description</entry> + <entry>Example</entry> + <entry>Result</entry> + </row> + </thead> + <tbody> + <row> + <entry> <literal>=</literal> </entry> + <entry>equals</entry> + <entry><literal>ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]</literal></entry> + <entry><literal>t</literal></entry> + </row> + <row> + <entry> <literal>||</literal> </entry> + <entry>array-to-array concatenation</entry> + <entry><literal>ARRAY[1,2,3] || ARRAY[4,5,6]</literal></entry> + <entry><literal>{{1,2,3},{4,5,6}}</literal></entry> + </row> + <row> + <entry> <literal>||</literal> </entry> + <entry>array-to-array concatenation</entry> + <entry><literal>ARRAY[1,2,3] || ARRAY[[4,5,6],[7,8,9]]</literal></entry> + <entry><literal>{{1,2,3},{4,5,6},{7,8,9}}</literal></entry> + </row> + <row> + <entry> <literal>||</literal> </entry> + <entry>element-to-array concatenation</entry> + <entry><literal>3 || ARRAY[4,5,6]</literal></entry> + <entry><literal>{3,4,5,6}</literal></entry> + </row> + <row> + <entry> <literal>||</literal> </entry> + <entry>array-to-element concatenation</entry> + <entry><literal>ARRAY[4,5,6] || 7</literal></entry> + <entry><literal>{4,5,6,7}</literal></entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + <xref linkend="array-functions-table"> shows the functions + available for use with array types. See <xref linkend="arrays"> + for more discussion and examples for the use of these functions. + </para> + + <table id="array-functions-table"> + <title><type>array</type> Functions</title> + <tgroup cols="5"> + <thead> + <row> + <entry>Function</entry> + <entry>Return Type</entry> + <entry>Description</entry> + <entry>Example</entry> + <entry>Result</entry> + </row> + </thead> + <tbody> + <row> + <entry> + <literal> + <function>array_append</function> + (<type>anyarray</type>, <type>anyelement</type>) + </literal> + </entry> + <entry><type>anyarray</type></entry> + <entry> + append an element to the end of an array, returning + <literal>NULL</literal> for <literal>NULL</literal> inputs + </entry> + <entry><literal>array_append(ARRAY[1,2], 3)</literal></entry> + <entry><literal>{1,2,3}</literal></entry> + </row> + <row> + <entry> + <literal> + <function>array_cat</function> + (<type>anyarray</type>, <type>anyarray</type>) + </literal> + </entry> + <entry><type>anyarray</type></entry> + <entry> + concatenate two arrays, returning <literal>NULL</literal> + for <literal>NULL</literal> inputs + </entry> + <entry><literal>array_cat(ARRAY[1,2,3], ARRAY[4,5,6])</literal></entry> + <entry><literal>{{1,2,3},{4,5,6}}</literal></entry> + </row> + <row> + <entry> + <literal> + <function>array_dims</function> + (<type>anyarray</type>) + </literal> + </entry> + <entry><type>text</type></entry> + <entry> + returns a text representation of array dimension lower and upper bounds, + generating an ERROR for <literal>NULL</literal> inputs + </entry> + <entry><literal>array_dims(array[[1,2,3],[4,5,6]])</literal></entry> + <entry><literal>[1:2][1:3]</literal></entry> + </row> + <row> + <entry> + <literal> + <function>array_lower</function> + (<type>anyarray</type>, <type>integer</type>) + </literal> + </entry> + <entry><type>integer</type></entry> + <entry> + returns lower bound of the requested array dimension, returning + <literal>NULL</literal> for <literal>NULL</literal> inputs + </entry> + <entry><literal>array_lower(array_prepend(0, ARRAY[1,2,3]), 1)</literal></entry> + <entry><literal>0</literal></entry> + </row> + <row> + <entry> + <literal> + <function>array_prepend</function> + (<type>anyelement</type>, <type>anyarray</type>) + </literal> + </entry> + <entry><type>anyarray</type></entry> + <entry> + append an element to the beginning of an array, returning + <literal>NULL</literal> for <literal>NULL</literal> inputs + </entry> + <entry><literal>array_prepend(1, ARRAY[2,3])</literal></entry> + <entry><literal>{1,2,3}</literal></entry> + </row> + <row> + <entry> + <literal> + <function>array_to_string</function> + (<type>anyarray</type>, <type>text</type>) + </literal> + </entry> + <entry><type>text</type></entry> + <entry> + concatenates array elements using provided delimiter, returning + <literal>NULL</literal> for <literal>NULL</literal> inputs + </entry> + <entry><literal>array_to_string(array[1.1,2.2,3.3]::numeric(4,2)[],'~^~')</literal></entry> + <entry><literal>1.10~^~2.20~^~3.30</literal></entry> + </row> + <row> + <entry> + <literal> + <function>array_upper</function> + (<type>anyarray</type>, <type>integer</type>) + </literal> + </entry> + <entry><type>integer</type></entry> + <entry> + returns upper bound of the requested array dimension, returning + <literal>NULL</literal> for <literal>NULL</literal> inputs + </entry> + <entry><literal>array_upper(array_append(ARRAY[1,2,3], 4), 1)</literal></entry> + <entry><literal>4</literal></entry> + </row> + <row> + <entry> + <literal> + <function>string_to_array</function> + (<type>text</type>, <type>text</type>) + </literal> + </entry> + <entry><type>text[]</type></entry> + <entry> + splits string into array elements using provided delimiter, returning + <literal>NULL</literal> for <literal>NULL</literal> inputs + </entry> + <entry><literal>string_to_array('1.10~^~2.20~^~3.30','~^~')::float8[]</literal></entry> + <entry><literal>{1.1,2.2,3.3}</literal></entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> <sect1 id="functions-aggregate"> <title>Aggregate Functions</title> diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 37794645ee58d4f9d9a15c5831fd59f42cb34335..9025862be123f6734e65b537207ea2cfd1117ae9 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.56 2002/09/18 21:35:20 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_aggregate.c,v 1.57 2003/06/24 23:14:42 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -50,10 +50,16 @@ AggregateCreate(const char *aggName, Oid finalfn = InvalidOid; /* can be omitted */ Oid finaltype; Oid fnArgs[FUNC_MAX_ARGS]; - int nargs; + 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,24 +74,49 @@ AggregateCreate(const char *aggName, MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid)); fnArgs[0] = aggTransType; if (aggBaseType == ANYOID) - nargs = 1; + nargs_transfn = 1; else { fnArgs[1] = aggBaseType; - nargs = 2; + nargs_transfn = 2; } - transfn = LookupFuncName(aggtransfnName, nargs, fnArgs); + + /* + * func_get_detail looks up the function in the catalogs, does + * disambiguation for polymorphic functions, handles inheritance, and + * returns the funcid and type and set or singleton status of the + * function's return value. it also returns the true argument types + * to the function. + */ + fdresult = func_get_detail(aggtransfnName, NIL, nargs_transfn, fnArgs, + &transfn, &rettype, &retset, + &true_oid_array_transfn); + + /* only valid case is a normal function */ + if (fdresult != FUNCDETAIL_NORMAL) + func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL); + if (!OidIsValid(transfn)) - func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL); + func_error("AggregateCreate", aggtransfnName, nargs_transfn, fnArgs, NULL); + + /* + * enforce consistency with ANYARRAY and ANYELEMENT argument + * and return types, possibly modifying return type along the way + */ + rettype = enforce_generic_type_consistency(fnArgs, true_oid_array_transfn, + nargs_transfn, rettype); + + if (rettype != aggTransType) + elog(ERROR, "return type of transition function %s is not %s", + NameListToString(aggtransfnName), format_type_be(aggTransType)); + tup = SearchSysCache(PROCOID, ObjectIdGetDatum(transfn), 0, 0, 0); if (!HeapTupleIsValid(tup)) - func_error("AggregateCreate", aggtransfnName, nargs, fnArgs, NULL); + func_error("AggregateCreate", aggtransfnName, + nargs_transfn, 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 @@ -105,17 +136,26 @@ AggregateCreate(const char *aggName, { MemSet(fnArgs, 0, FUNC_MAX_ARGS * sizeof(Oid)); fnArgs[0] = aggTransType; - finalfn = LookupFuncName(aggfinalfnName, 1, fnArgs); - if (!OidIsValid(finalfn)) + 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); - tup = SearchSysCache(PROCOID, - ObjectIdGetDatum(finalfn), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) + + if (!OidIsValid(finalfn)) func_error("AggregateCreate", aggfinalfnName, 1, fnArgs, NULL); - proc = (Form_pg_proc) GETSTRUCT(tup); - finaltype = proc->prorettype; - ReleaseSysCache(tup); + + /* + * 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 { @@ -126,6 +166,27 @@ AggregateCreate(const char *aggName, } 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 * aggregate. (This could fail if there's already a conflicting diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index 552573dc1a2e61e71aa43fbbe965a3649841b1f7..07b9862e41a56d56deb1e24927c081003600dd24 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.5 2002/09/04 20:31:14 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.6 2003/06/24 23:14:43 momjian Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -119,7 +119,9 @@ DefineAggregate(List *names, List *parameters) baseTypeId = typenameTypeId(baseType); transTypeId = typenameTypeId(transType); - if (get_typtype(transTypeId) == 'p') + if (get_typtype(transTypeId) == 'p' && + transTypeId != ANYARRAYOID && + transTypeId != ANYELEMENTOID) elog(ERROR, "Aggregate transition datatype cannot be %s", format_type_be(transTypeId)); diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 8dac4b2e44049a74eeddd1d2184fc87503b5164b..e729344060e363c31254c8aaed9467b7208b86cf 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.130 2003/05/28 22:32:49 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.131 2003/06/24 23:14:43 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -1528,17 +1528,17 @@ ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, { /* Check other sub-arrays are compatible */ if (elem_ndims != ARR_NDIM(array)) - elog(ERROR, "Multiple dimension arrays must have array " + elog(ERROR, "Multidimensional arrays must have array " "expressions with matching number of dimensions"); if (memcmp(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int)) != 0) - elog(ERROR, "Multiple dimension arrays must have array " + elog(ERROR, "Multidimensional arrays must have array " "expressions with matching dimensions"); if (memcmp(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int)) != 0) - elog(ERROR, "Multiple dimension arrays must have array " + elog(ERROR, "Multidimensional arrays must have array " "expressions with matching dimensions"); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index d0dd6b31c997b9c8fb7f462a7e20894dd723d60b..dc8c1554bb0f5e3d725352ca015ce718e4ac680c 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -45,7 +45,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.107 2003/06/22 22:04:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.108 2003/06/24 23:14:43 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -58,6 +58,7 @@ #include "executor/executor.h" #include "executor/nodeAgg.h" #include "miscadmin.h" +#include "nodes/makefuncs.h" #include "optimizer/clauses.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" @@ -212,7 +213,7 @@ static TupleTableSlot *agg_retrieve_direct(AggState *aggstate); static void agg_fill_hash_table(AggState *aggstate); static 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,14 +352,12 @@ advance_transition_function(AggState *aggstate, 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); /* @@ -1187,7 +1186,21 @@ ExecInitAgg(Agg *node, EState *estate) AclResult aclresult; Oid transfn_oid, finalfn_oid; + FuncExpr *transfnexpr, + *finalfnexpr; Datum textInitVal; + List *fargs; + Oid agg_rt_type; + Oid *transfn_arg_types; + List *transfn_args = NIL; + int transfn_nargs; + Oid transfn_ret_type; + Oid *finalfn_arg_types = NULL; + List *finalfn_args = NIL; + Oid finalfn_ret_type = InvalidOid; + int finalfn_nargs = 0; + Node *arg0; + Node *arg1; int i; /* Planner should have assigned aggregate to correct level */ @@ -1238,6 +1251,166 @@ ExecInitAgg(Agg *node, EState *estate) &peraggstate->transtypeLen, &peraggstate->transtypeByVal); + peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; + peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; + + /* get the runtime aggregate argument type */ + fargs = aggref->args; + agg_rt_type = exprType((Node *) nth(0, fargs)); + + /* get the transition function argument and return types */ + transfn_ret_type = get_func_rettype(transfn_oid); + transfn_arg_types = get_func_argtypes(transfn_oid, &transfn_nargs); + + /* resolve any polymorphic types */ + if (transfn_nargs == 2) + /* base type was not ANY */ + { + if (transfn_arg_types[1] == ANYARRAYOID || + transfn_arg_types[1] == ANYELEMENTOID) + transfn_arg_types[1] = agg_rt_type; + + transfn_arg_types[0] = resolve_type(transfn_arg_types[0], + agg_rt_type); + + /* + * Build arg list to use on the transfn FuncExpr node. We really + * only care that the node type is correct so that the transfn + * can discover the actual argument types at runtime using + * get_fn_expr_argtype() + */ + arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0], + -1, COERCE_DONTCARE); + arg1 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[1], + -1, COERCE_DONTCARE); + transfn_args = makeList2(arg0, arg1); + + /* + * the state transition function always returns the same type + * as its first argument + */ + if (transfn_ret_type == ANYARRAYOID || + transfn_ret_type == ANYELEMENTOID) + transfn_ret_type = transfn_arg_types[0]; + } + else if (transfn_nargs == 1) + /* + * base type was ANY, therefore the aggregate return type should + * be non-polymorphic + */ + { + Oid finaltype = get_func_rettype(aggref->aggfnoid); + + /* + * this should have been prevented in AggregateCreate, + * but check anyway + */ + if (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID) + elog(ERROR, "aggregate with base type ANY must have a " \ + "non-polymorphic return type"); + + /* see if we have a final function */ + if (OidIsValid(finalfn_oid)) + { + finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs); + if (finalfn_nargs != 1) + elog(ERROR, "final function takes unexpected number " \ + "of arguments: %d", finalfn_nargs); + + /* + * final function argument is always the same as the state + * function return type + */ + if (finalfn_arg_types[0] != ANYARRAYOID && + finalfn_arg_types[0] != ANYELEMENTOID) + { + /* if it is not ambiguous, use it */ + transfn_ret_type = finalfn_arg_types[0]; + } + else + { + /* if it is ambiguous, try to derive it */ + finalfn_ret_type = finaltype; + finalfn_arg_types[0] = resolve_type(finalfn_arg_types[0], + finalfn_ret_type); + transfn_ret_type = finalfn_arg_types[0]; + } + } + else + transfn_ret_type = finaltype; + + transfn_arg_types[0] = resolve_type(transfn_arg_types[0], + transfn_ret_type); + + /* + * Build arg list to use on the transfn FuncExpr node. We really + * only care that the node type is correct so that the transfn + * can discover the actual argument types at runtime using + * get_fn_expr_argtype() + */ + arg0 = (Node *) makeRelabelType((Expr *) NULL, transfn_arg_types[0], + -1, COERCE_DONTCARE); + transfn_args = makeList1(arg0); + } + else + elog(ERROR, "state transition function takes unexpected number " \ + "of arguments: %d", transfn_nargs); + + if (OidIsValid(finalfn_oid)) + { + /* get the final function argument and return types */ + if (finalfn_ret_type == InvalidOid) + finalfn_ret_type = get_func_rettype(finalfn_oid); + + if (!finalfn_arg_types) + { + finalfn_arg_types = get_func_argtypes(finalfn_oid, &finalfn_nargs); + if (finalfn_nargs != 1) + elog(ERROR, "final function takes unexpected number " \ + "of arguments: %d", finalfn_nargs); + } + + /* + * final function argument is always the same as the state + * function return type, which by now should have been resolved + */ + if (finalfn_arg_types[0] == ANYARRAYOID || + finalfn_arg_types[0] == ANYELEMENTOID) + finalfn_arg_types[0] = transfn_ret_type; + + /* + * Build arg list to use on the finalfn FuncExpr node. We really + * only care that the node type is correct so that the finalfn + * can discover the actual argument type at runtime using + * get_fn_expr_argtype() + */ + arg0 = (Node *) makeRelabelType((Expr *) NULL, finalfn_arg_types[0], + -1, COERCE_DONTCARE); + finalfn_args = makeList1(arg0); + + finalfn_ret_type = resolve_type(finalfn_ret_type, + finalfn_arg_types[0]); + } + + fmgr_info(transfn_oid, &peraggstate->transfn); + transfnexpr = (FuncExpr *) make_funcclause(transfn_oid, + transfn_ret_type, + false, /* cannot be a set */ + COERCE_DONTCARE, /* to match any user expr */ + transfn_args); + peraggstate->transfn.fn_expr = (Node *) transfnexpr; + + if (OidIsValid(finalfn_oid)) + { + fmgr_info(finalfn_oid, &peraggstate->finalfn); + finalfnexpr = (FuncExpr *) make_funcclause(finalfn_oid, + finalfn_ret_type, + false, /* cannot be a set */ + COERCE_DONTCARE, /* to match any user expr */ + finalfn_args); + peraggstate->finalfn.fn_expr = (Node *) finalfnexpr; + } + /* * initval is potentially null, so don't try to access it as a * struct field. Must do it the hard way with SysCacheGetAttr. @@ -1250,14 +1423,7 @@ ExecInitAgg(Agg *node, EState *estate) 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); + transfn_arg_types[0]); /* * If the transfn is strict and the initval is NULL, make sure @@ -1469,3 +1635,36 @@ aggregate_dummy(PG_FUNCTION_ARGS) fcinfo->flinfo->fn_oid); 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; +} diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 82502c985e8e659c46ef01521f80647549958b7f..62aaa7e8d439cdd3eee4cd9f1ef010a00b5bbe37 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.47 2003/06/22 22:04:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.48 2003/06/24 23:14:43 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -28,23 +28,6 @@ #include "utils/datum.h" #include "utils/lsyscache.h" - -typedef struct ArrayBuildState -{ - MemoryContext mcontext; /* where all the temp stuff is kept */ - Datum *dvalues; /* array of accumulated Datums */ - /* - * The allocated size of dvalues[] is always a multiple of - * ARRAY_ELEMS_CHUNKSIZE - */ -#define ARRAY_ELEMS_CHUNKSIZE 64 - int nelems; /* number of valid Datums in dvalues[] */ - Oid element_type; /* data type of the Datums */ - int16 typlen; /* needed info about datatype */ - bool typbyval; - char typalign; -} ArrayBuildState; - static Datum ExecHashSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull); @@ -54,13 +37,6 @@ static Datum ExecScanSubPlan(SubPlanState *node, static void buildSubPlanHash(SubPlanState *node); static bool findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot); static bool tupleAllNulls(HeapTuple tuple); -static ArrayBuildState *accumArrayResult(ArrayBuildState *astate, - Datum dvalue, bool disnull, - Oid element_type, - MemoryContext rcontext); -static Datum makeArrayResult(ArrayBuildState *astate, - MemoryContext rcontext); - /* ---------------------------------------------------------------- * ExecSubPlan @@ -224,6 +200,7 @@ ExecScanSubPlan(SubPlanState *node, PlanState *planstate = node->planstate; SubLinkType subLinkType = subplan->subLinkType; bool useOr = subplan->useOr; + bool isExpr = subplan->isExpr; MemoryContext oldcontext; TupleTableSlot *slot; Datum result; @@ -294,6 +271,11 @@ ExecScanSubPlan(SubPlanState *node, bool rownull = false; int col = 1; List *plst; + int numelems; + int elemnum; + Datum dvalue; + Datum *dvalues = NULL; + bool disnull; if (subLinkType == EXISTS_SUBLINK) { @@ -331,9 +313,6 @@ ExecScanSubPlan(SubPlanState *node, if (subLinkType == ARRAY_SUBLINK) { - Datum dvalue; - bool disnull; - found = true; /* stash away current value */ dvalue = heap_getattr(tup, 1, tdesc, &disnull); @@ -351,98 +330,163 @@ ExecScanSubPlan(SubPlanState *node, found = true; /* - * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining - * operators for columns of tuple. + * 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. */ - plst = subplan->paramIds; - foreach(lst, node->exprs) + if (!isExpr) + numelems = 1; + else { - 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)); + Oid expr_typeid = tdesc->attrs[0]->atttypid; - /* - * 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) + if (expr_typeid != subplan->exprtype) { - rowresult = expresult; - rownull = expnull; + 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); } - else if (useOr) + + /* get current value */ + dvalue = heap_getattr(tup, 1, tdesc, &disnull); + + /* XXX this will need work if/when arrays support NULL elements */ + if (!disnull) { - /* combine within row per OR semantics */ - if (expnull) - rownull = true; - else if (DatumGetBool(expresult)) + if (subplan->elemtype != InvalidOid) { - rowresult = BoolGetDatum(true); - rownull = false; - break; /* needn't look at any more columns */ + 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 { - /* 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 */ - } + numelems = 1; + dvalues = (Datum *) palloc(numelems * sizeof(Datum)); + dvalues[0] = (Datum) 0; } - plst = lnext(plst); - col++; } - if (subLinkType == ANY_SUBLINK) + for (elemnum = 0; elemnum < numelems; elemnum++) { - /* combine across rows per OR semantics */ - if (rownull) - *isNull = true; - else if (DatumGetBool(rowresult)) + /* + * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining + * operators for columns of tuple. + */ + col = 1; + plst = subplan->paramIds; + foreach(lst, node->exprs) { - result = BoolGetDatum(true); - *isNull = false; - break; /* needn't look at any more rows */ + 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++; } - } - else if (subLinkType == ALL_SUBLINK) - { - /* combine across rows per AND semantics */ - if (rownull) - *isNull = true; - else if (!DatumGetBool(rowresult)) + + if (subLinkType == ANY_SUBLINK) { - result = BoolGetDatum(false); - *isNull = false; - break; /* needn't look at any more rows */ + /* 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; } - } - else - { - /* must be MULTIEXPR_SUBLINK */ - result = rowresult; - *isNull = rownull; } } @@ -480,6 +524,7 @@ static void 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,6 +532,7 @@ buildSubPlanHash(SubPlanState *node) MemoryContext oldcontext; int nbuckets; TupleTableSlot *slot; + TupleTableSlot *arrslot = NULL; Assert(subplan->subLinkType == ANY_SUBLINK); Assert(!subplan->useOr); @@ -566,43 +612,139 @@ buildSubPlanHash(SubPlanState *node) { HeapTuple tup = slot->val; TupleDesc tdesc = slot->ttc_tupleDescriptor; - int col = 1; + TupleDesc arrtdesc = NULL; List *plst; bool isnew; + int numelems; + int elemnum; + Datum dvalue; + Datum *dvalues = NULL; + bool disnull; /* - * Load up the Params representing the raw sub-select outputs, - * then form the projection tuple to store in the hashtable. + * 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. */ - foreach(plst, subplan->paramIds) + if (!isExpr) + numelems = 1; + else { - 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; + 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; + } - /* - * 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) + + for (elemnum = 0; elemnum < numelems; elemnum++) { - (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew); - node->havenullrows = true; + int col = 1; + + if (!isExpr || subplan->elemtype == InvalidOid) + { + /* + * Load up the Params representing the raw sub-select outputs, + * then form the projection tuple to store in the hashtable. + */ + foreach(plst, subplan->paramIds) + { + int paramid = lfirsti(plst); + ParamExecData *prmdata; + + prmdata = &(innerecontext->ecxt_param_exec_vals[paramid]); + Assert(prmdata->execPlan == NULL); + + prmdata->value = heap_getattr(tup, col, tdesc, + &(prmdata->isnull)); + + col++; + } + slot = ExecProject(node->projRight, NULL); + tup = slot->val; + } + else + { + /* + * For array type expressions, we need to build up our own + * tuple and slot + */ + char nullflag; + + nullflag = disnull ? 'n' : ' '; + tup = heap_formtuple(arrtdesc, &dvalues[elemnum], &nullflag); + arrslot = ExecStoreTuple(tup, arrslot, InvalidBuffer, true); + } + + /* + * If result contains any nulls, store separately or not at all. + * (Since we know the projection tuple has no junk columns, we + * can just look at the overall hasnull info bit, instead of + * groveling through the columns.) + */ + if (HeapTupleNoNulls(tup)) + { + if (!isExpr) + (void) LookupTupleHashEntry(node->hashtable, slot, &isnew); + else + (void) LookupTupleHashEntry(node->hashtable, arrslot, &isnew); + node->havehashrows = true; + } + else if (node->hashnulls) + { + if (!isExpr) + (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew); + else + (void) LookupTupleHashEntry(node->hashnulls, arrslot, &isnew); + node->havenullrows = true; + } } /* @@ -619,6 +761,8 @@ buildSubPlanHash(SubPlanState *node) * have the potential for a double free attempt. */ ExecClearTuple(node->projRight->pi_slot); + if (arrslot) + ExecClearTuple(arrslot); MemoryContextSwitchTo(oldcontext); } @@ -1099,101 +1243,3 @@ ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent) parent->chgParam = bms_add_member(parent->chgParam, paramid); } } - -/* - * accumArrayResult - accumulate one (more) Datum for an ARRAY_SUBLINK - * - * astate is working state (NULL on first call) - * rcontext is where to keep working state - */ -static ArrayBuildState * -accumArrayResult(ArrayBuildState *astate, - Datum dvalue, bool disnull, - Oid element_type, - MemoryContext rcontext) -{ - MemoryContext arr_context, - oldcontext; - - if (astate == NULL) - { - /* First time through --- initialize */ - - /* Make a temporary context to hold all the junk */ - arr_context = AllocSetContextCreate(rcontext, - "ARRAY_SUBLINK Result", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - oldcontext = MemoryContextSwitchTo(arr_context); - astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState)); - astate->mcontext = arr_context; - astate->dvalues = (Datum *) - palloc(ARRAY_ELEMS_CHUNKSIZE * sizeof(Datum)); - astate->nelems = 0; - astate->element_type = element_type; - get_typlenbyvalalign(element_type, - &astate->typlen, - &astate->typbyval, - &astate->typalign); - } - else - { - oldcontext = MemoryContextSwitchTo(astate->mcontext); - Assert(astate->element_type == element_type); - /* enlarge dvalues[] if needed */ - if ((astate->nelems % ARRAY_ELEMS_CHUNKSIZE) == 0) - astate->dvalues = (Datum *) - repalloc(astate->dvalues, - (astate->nelems + ARRAY_ELEMS_CHUNKSIZE) * sizeof(Datum)); - } - - if (disnull) - elog(ERROR, "NULL elements not allowed in Arrays"); - - /* Use datumCopy to ensure pass-by-ref stuff is copied into mcontext */ - astate->dvalues[astate->nelems++] = - datumCopy(dvalue, astate->typbyval, astate->typlen); - - MemoryContextSwitchTo(oldcontext); - - return astate; -} - -/* - * makeArrayResult - produce final result of ARRAY_SUBLINK - * - * astate is working state (not NULL) - * rcontext is where to construct result - */ -static Datum -makeArrayResult(ArrayBuildState *astate, - MemoryContext rcontext) -{ - ArrayType *result; - int dims[1]; - int lbs[1]; - MemoryContext oldcontext; - - /* Build the final array result in rcontext */ - oldcontext = MemoryContextSwitchTo(rcontext); - - dims[0] = astate->nelems; - lbs[0] = 1; - - result = construct_md_array(astate->dvalues, - 1, - dims, - lbs, - astate->element_type, - astate->typlen, - astate->typbyval, - astate->typalign); - - MemoryContextSwitchTo(oldcontext); - - /* Clean up all the junk */ - MemoryContextDelete(astate->mcontext); - - return PointerGetDatum(result); -} diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 12b82fc5bcb4c3a8fbb9044780ffae797d906d57..b5e5b112725c06ba9e69db48d8b00c3e48d40542 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.252 2003/06/06 15:04:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.253 2003/06/24 23:14:43 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -728,6 +728,7 @@ _copyAggref(Aggref *from) COPY_SCALAR_FIELD(agglevelsup); COPY_SCALAR_FIELD(aggstar); COPY_SCALAR_FIELD(aggdistinct); + COPY_NODE_FIELD(args); return newnode; } @@ -826,6 +827,7 @@ _copySubLink(SubLink *from) 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,6 +846,12 @@ _copySubPlan(SubPlan *from) 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); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 40211231c6f9b8c6a8b974551811f2accc797ec1..23c1018968d32634e272c156d662c5c8bb183015 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.195 2003/06/06 15:04:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.196 2003/06/24 23:14:43 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -205,6 +205,7 @@ _equalAggref(Aggref *a, Aggref *b) COMPARE_SCALAR_FIELD(agglevelsup); COMPARE_SCALAR_FIELD(aggstar); COMPARE_SCALAR_FIELD(aggdistinct); + COMPARE_NODE_FIELD(args); return true; } @@ -301,6 +302,7 @@ _equalSubLink(SubLink *a, SubLink *b) { 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,6 +316,12 @@ _equalSubPlan(SubPlan *a, SubPlan *b) { 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 */ diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index f042e8a8d210333c4fcc05b7acb7be385a7f2957..cbd5784f563e92ba8c16c0aea00fb8ccb0f1cd8a 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.208 2003/06/15 22:51:45 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.209 2003/06/24 23:14:43 momjian Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -616,6 +616,7 @@ _outAggref(StringInfo str, Aggref *node) WRITE_UINT_FIELD(agglevelsup); WRITE_BOOL_FIELD(aggstar); WRITE_BOOL_FIELD(aggdistinct); + WRITE_NODE_FIELD(args); } static void @@ -701,6 +702,7 @@ _outSubLink(StringInfo str, SubLink *node) 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,6 +716,12 @@ _outSubPlan(StringInfo str, SubPlan *node) 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); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 7d3a1506a81d17fb56ee2b3139e19c7b085388d0..a661f9e8ad1776bf9d99ad7a3d0b6f2cef533bd1 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.154 2003/06/06 15:04:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.155 2003/06/24 23:14:43 momjian Exp $ * * NOTES * Path and Plan nodes do not have any readfuncs support, because we @@ -416,6 +416,7 @@ _readAggref(void) READ_UINT_FIELD(agglevelsup); READ_BOOL_FIELD(aggstar); READ_BOOL_FIELD(aggdistinct); + READ_NODE_FIELD(args); READ_DONE(); } @@ -545,6 +546,7 @@ _readSubLink(void) 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); diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 930c3133030487d5b36314bf4833a5211db699ea..138e7d80638ce1d80c7bfaa2ac5fb5d96fc931d8 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.76 2003/06/06 15:04:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.77 2003/06/24 23:14:43 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -83,7 +83,7 @@ typedef struct finalize_primnode_context static List *convert_sublink_opers(List *lefthand, List *operOids, List *targetlist, int rtindex, - List **righthandIds); + 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,6 +299,12 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual) */ 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,7 +380,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual) exprs = convert_sublink_opers(lefthand, slink->operOids, plan->targetlist, - 0, + 0, node->isExpr, &node->paramIds); node->setParam = listCopy(node->paramIds); PlannerInitPlan = lappend(PlannerInitPlan, node); @@ -457,7 +463,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual) node->exprs = convert_sublink_opers(lefthand, slink->operOids, plan->targetlist, - 0, + 0, node->isExpr, &node->paramIds); /* @@ -499,7 +505,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual) static List * convert_sublink_opers(List *lefthand, List *operOids, List *targetlist, int rtindex, - List **righthandIds) + bool isExpr, List **righthandIds) { List *result = NIL; List *lst; @@ -554,13 +560,38 @@ convert_sublink_opers(List *lefthand, List *operOids, * 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)); + 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,13 +702,17 @@ convert_IN_to_join(Query *parse, SubLink *sublink) /* * 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.) + * 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,7 +765,7 @@ convert_IN_to_join(Query *parse, SubLink *sublink) exprs = convert_sublink_opers(sublink->lefthand, sublink->operOids, subselect->targetList, - rtindex, + rtindex, sublink->isExpr, &ininfo->sub_targetlist); return (Node *) make_ands_explicit(exprs); } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 3f4ced5a5532a1b959e46dff79e751a7f20d8b9d..6b5014f90e1b630d693000dabe1cb3bb29bdfa0d 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.139 2003/06/06 15:04:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.140 2003/06/24 23:14:43 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -132,6 +132,28 @@ get_rightop(Expr *clause) return NULL; } +/***************************************************************************** + * FUNCTION clause functions + *****************************************************************************/ + +/* + * make_funcclause + * Creates a function clause given its function info and argument list. + */ +Expr * +make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset, + CoercionForm funcformat, List *funcargs) +{ + FuncExpr *expr = makeNode(FuncExpr); + + expr->funcid = funcid; + expr->funcresulttype = funcresulttype; + expr->funcretset = funcretset; + expr->funcformat = funcformat; + expr->args = funcargs; + return (Expr *) expr; +} + /***************************************************************************** * NOT clause functions *****************************************************************************/ diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index f8958d5878329309b019d60135c744951f79f97d..e4cf67eca3679ec1a07dde76db94be496d84b5f0 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.417 2003/06/17 23:12:36 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.418 2003/06/24 23:14:43 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -5490,6 +5490,7 @@ r_expr: row IN_P select_with_parens { SubLink *n = makeNode(SubLink); n->subLinkType = ANY_SUBLINK; + n->isExpr = false; n->lefthand = $1; n->operName = makeList1(makeString("=")); n->subselect = $3; @@ -5500,6 +5501,7 @@ r_expr: row IN_P select_with_parens /* 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,6 +5513,7 @@ r_expr: row IN_P select_with_parens { SubLink *n = makeNode(SubLink); n->subLinkType = $3; + n->isExpr = false; n->lefthand = $1; n->operName = $2; n->subselect = $4; @@ -5521,6 +5524,7 @@ r_expr: row IN_P select_with_parens { SubLink *n = makeNode(SubLink); n->subLinkType = MULTIEXPR_SUBLINK; + n->isExpr = false; n->lefthand = $1; n->operName = $2; n->subselect = $3; @@ -5904,6 +5908,7 @@ a_expr: c_expr { $$ = $1; } { SubLink *n = (SubLink *)$3; n->subLinkType = ANY_SUBLINK; + n->isExpr = false; n->lefthand = makeList1($1); n->operName = makeList1(makeString("=")); $$ = (Node *)n; @@ -5931,6 +5936,7 @@ a_expr: c_expr { $$ = $1; } { /* Make an IN node */ SubLink *n = (SubLink *)$4; + n->isExpr = false; n->subLinkType = ANY_SUBLINK; n->lefthand = makeList1($1); n->operName = makeList1(makeString("=")); @@ -5957,11 +5963,38 @@ a_expr: c_expr { $$ = $1; } { 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,6 +6571,7 @@ c_expr: columnref { $$ = (Node *) $1; } { SubLink *n = makeNode(SubLink); n->subLinkType = EXPR_SUBLINK; + n->isExpr = false; n->lefthand = NIL; n->operName = NIL; n->subselect = $1; @@ -6547,6 +6581,7 @@ c_expr: columnref { $$ = (Node *) $1; } { SubLink *n = makeNode(SubLink); n->subLinkType = EXISTS_SUBLINK; + n->isExpr = false; n->lefthand = NIL; n->operName = NIL; n->subselect = $2; @@ -6556,6 +6591,7 @@ c_expr: columnref { $$ = (Node *) $1; } { SubLink *n = makeNode(SubLink); n->subLinkType = ARRAY_SUBLINK; + n->isExpr = false; n->lefthand = NIL; n->operName = NIL; n->subselect = $2; @@ -6730,6 +6766,7 @@ trim_list: a_expr FROM expr_list { $$ = lappend($3, $1); } in_expr: select_with_parens { SubLink *n = makeNode(SubLink); + n->isExpr = false; n->subselect = $1; /* other fields will be filled later */ $$ = (Node *)n; diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index fc35c2d6d41579171680dd0bbad6f62749154662..9861d18fdb05a8dff32c7f638f567b79a33c4419 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.97 2003/05/26 00:11:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.98 2003/06/24 23:14:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -859,7 +859,11 @@ enforce_generic_type_consistency(Oid *actual_arg_types, /* Get the element type based on the array type, if we have one */ if (OidIsValid(array_typeid)) { - array_typelem = get_element_type(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,7 +923,11 @@ enforce_generic_type_consistency(Oid *actual_arg_types, { if (!OidIsValid(array_typeid)) { - array_typeid = get_array_type(elem_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)); @@ -1170,6 +1178,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype) 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, ObjectIdGetDatum(srctype), diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 429a9ac8c8a3dffe1b5c8771163008b22848972c..5234c22e4fbf8e15dee6a4e399c50bf7c4b524ab 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.148 2003/04/29 22:13:10 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.149 2003/06/24 23:14:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -436,6 +436,7 @@ transformExpr(ParseState *pstate, Node *expr) sublink->operName = NIL; sublink->operOids = NIL; sublink->useOr = FALSE; + sublink->isExpr = FALSE; } else if (sublink->subLinkType == EXPR_SUBLINK || sublink->subLinkType == ARRAY_SUBLINK) @@ -463,6 +464,7 @@ transformExpr(ParseState *pstate, Node *expr) sublink->operName = NIL; sublink->operOids = NIL; sublink->useOr = FALSE; + sublink->isExpr = FALSE; } else { @@ -538,10 +540,30 @@ transformExpr(ParseState *pstate, Node *expr) * here, because make_subplan() will insert type * coercion calls if needed. */ - optup = oper(op, - exprType(lexpr), - exprType((Node *) tent->expr), - false); + 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,7 +765,7 @@ transformExpr(ParseState *pstate, Node *expr) ArrayExpr *e = (ArrayExpr *) lfirst(element); if (!IsA(e, ArrayExpr)) - elog(ERROR, "Multi-dimensional ARRAY[] must be built from nested array expressions"); + elog(ERROR, "Multidimensional ARRAY[] must be built from nested array expressions"); if (ndims == 0) ndims = e->ndims; else if (e->ndims != ndims) diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 6f858e17a8b065b1ba16a2d22a884bf4ef0df238..5ff2bc6d81973adfa4d5170d1d11eabe6534e4ae 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.149 2003/06/06 15:04:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.150 2003/06/24 23:14:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -336,6 +336,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, aggref->target = lfirst(fargs); aggref->aggstar = agg_star; aggref->aggdistinct = agg_distinct; + aggref->args = fargs; /* parse_agg.c does additional aggregate-specific processing */ transformAggregateCall(pstate, aggref); diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 21f73994c42b19965f3e959c70e989d65559a8fa..a6b66625be203e53ce2373e6b33131158664eed8 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.64 2003/05/26 00:11:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.65 2003/06/24 23:14:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -137,6 +137,33 @@ Operator equality_oper(Oid argtype, bool noError) { 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,6 +202,33 @@ Operator 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* @@ -220,6 +274,21 @@ equality_oper_funcid(Oid argtype) return result; } +/* + * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper()) + */ +Oid +ordering_oper_funcid(Oid argtype) +{ + Operator optup; + Oid result; + + optup = ordering_oper(argtype, false); + result = oprfuncid(optup); + ReleaseSysCache(optup); + return result; +} + /* * ordering_oper_opid - convenience routine for oprid(ordering_oper()) * diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 2870b5d0d6538680c51712f52b6a00ee87d1d75a..cfef2db349d21c9b76fdaa5264e5a583b3297194 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.88 2003/06/11 09:23:55 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.89 2003/06/24 23:14:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -427,6 +427,15 @@ aclitemeq(const AclItem *a1, const AclItem *a2) a1->ai_grantor == a2->ai_grantor; } +/* + * 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 diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c index 8c412675a38c007700c4055280d789eafbdfebc8..f5e50771b1337aae7dab378367da51fd2696a05d 100644 --- a/src/backend/utils/adt/array_userfuncs.c +++ b/src/backend/utils/adt/array_userfuncs.c @@ -6,7 +6,7 @@ * Copyright (c) 2003, PostgreSQL Global Development Group * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.1 2003/04/08 23:20:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/array_userfuncs.c,v 1.2 2003/06/24 23:14:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -18,35 +18,6 @@ #include "utils/lsyscache.h" #include "utils/syscache.h" - -/*----------------------------------------------------------------------------- - * singleton_array : - * Form a multi-dimensional array given one starting element. - * - * - first argument is the datum with which to build the array - * - second argument is the number of dimensions the array should have; - * defaults to 1 if no second argument is provided - *---------------------------------------------------------------------------- - */ -Datum -singleton_array(PG_FUNCTION_ARGS) -{ - Oid elem_type = get_fn_expr_argtype(fcinfo, 0); - int ndims; - - if (elem_type == InvalidOid) - elog(ERROR, "Cannot determine input datatype"); - - if (PG_NARGS() == 2) - ndims = PG_GETARG_INT32(1); - else - ndims = 1; - - PG_RETURN_ARRAYTYPE_P(create_singleton_array(elem_type, - PG_GETARG_DATUM(0), - ndims)); -} - /*----------------------------------------------------------------------------- * array_push : * push an element onto either end of a one-dimensional array @@ -70,6 +41,7 @@ array_push(PG_FUNCTION_ARGS) Oid arg1_typeid = get_fn_expr_argtype(fcinfo, 1); Oid arg0_elemid; Oid arg1_elemid; + ArrayMetaState *my_extra; if (arg0_typeid == InvalidOid || arg1_typeid == InvalidOid) elog(ERROR, "array_push: cannot determine input data types"); @@ -95,28 +67,61 @@ array_push(PG_FUNCTION_ARGS) PG_RETURN_NULL(); /* keep compiler quiet */ } - /* Sanity check: do we have a one-dimensional array */ - if (ARR_NDIM(v) != 1) - elog(ERROR, "Arrays greater than one-dimension are not supported"); - - lb = ARR_LBOUND(v); - dimv = ARR_DIMS(v); - if (arg0_elemid != InvalidOid) + if (ARR_NDIM(v) == 1) { - /* append newelem */ - int ub = dimv[0] + lb[0] - 1; - indx = ub + 1; + lb = ARR_LBOUND(v); + dimv = ARR_DIMS(v); + + if (arg0_elemid != InvalidOid) + { + /* append newelem */ + int ub = dimv[0] + lb[0] - 1; + indx = ub + 1; + } + else + { + /* prepend newelem */ + indx = lb[0] - 1; + } } + else if (ARR_NDIM(v) == 0) + indx = 1; else + elog(ERROR, "only empty and one-dimensional arrays are supported"); + + + /* + * We arrange to look up info about element type only once per series + * of calls, assuming the element type doesn't change underneath us. + */ + my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; + if (my_extra == NULL) { - /* prepend newelem */ - indx = lb[0] - 1; + fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + sizeof(ArrayMetaState)); + my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; + my_extra->element_type = InvalidOid; } - get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); + if (my_extra->element_type != element_type) + { + /* Get info about element type */ + get_typlenbyvalalign(element_type, &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); + result = array_set(v, 1, &indx, newelem, -1, typlen, typbyval, + typalign, &isNull); PG_RETURN_ARRAYTYPE_P(result); } @@ -145,13 +150,28 @@ array_cat(PG_FUNCTION_ARGS) /* * We must have one of the following combinations of inputs: - * 1) two arrays with ndims1 == ndims2 - * 2) ndims1 == ndims2 - 1 - * 3) ndims1 == ndims2 + 1 + * 1) one empty array, and one non-empty array + * 2) both arrays empty + * 3) two arrays with ndims1 == ndims2 + * 4) ndims1 == ndims2 - 1 + * 5) ndims1 == ndims2 + 1 */ ndims1 = ARR_NDIM(v1); ndims2 = ARR_NDIM(v2); + /* + * short circuit - if one input array is empty, and the other is not, + * we return the non-empty one as the result + * + * if both are empty, return the first one + */ + if (ndims1 == 0 && ndims2 > 0) + PG_RETURN_ARRAYTYPE_P(v2); + + if (ndims2 == 0) + PG_RETURN_ARRAYTYPE_P(v1); + + /* the rest fall into combo 2, 3, or 4 */ if (ndims1 != ndims2 && ndims1 != ndims2 - 1 && ndims1 != ndims2 + 1) elog(ERROR, "Cannot concatenate incompatible arrays of %d and " "%d dimensions", ndims1, ndims2); @@ -266,147 +286,15 @@ array_cat(PG_FUNCTION_ARGS) PG_RETURN_ARRAYTYPE_P(result); } -/*---------------------------------------------------------------------------- - * array_accum : - * accumulator to build a 1-D array from input values -- this can be used - * to create custom aggregates. - * - * This function is not marked strict, so we have to be careful about nulls. - *---------------------------------------------------------------------------- - */ -Datum -array_accum(PG_FUNCTION_ARGS) -{ - /* return NULL if both arguments are NULL */ - if (PG_ARGISNULL(0) && PG_ARGISNULL(1)) - PG_RETURN_NULL(); - - /* create a new 1-D array from the new element if the array is NULL */ - if (PG_ARGISNULL(0)) - { - Oid tgt_type = get_fn_expr_rettype(fcinfo); - Oid tgt_elem_type; - - if (tgt_type == InvalidOid) - elog(ERROR, "Cannot determine target array type"); - tgt_elem_type = get_element_type(tgt_type); - if (tgt_elem_type == InvalidOid) - elog(ERROR, "Target type is not an array"); - - PG_RETURN_ARRAYTYPE_P(create_singleton_array(tgt_elem_type, - PG_GETARG_DATUM(1), - 1)); - } - - /* return the array if the new element is NULL */ - if (PG_ARGISNULL(1)) - PG_RETURN_ARRAYTYPE_P(PG_GETARG_ARRAYTYPE_P_COPY(0)); - - /* - * Otherwise this is equivalent to array_push. We hack the call a little - * so that array_push can see the fn_expr information. - */ - return array_push(fcinfo); -} - -/*----------------------------------------------------------------------------- - * array_assign : - * assign an element of an array to a new value and return the - * redefined array - *---------------------------------------------------------------------------- - */ -Datum -array_assign(PG_FUNCTION_ARGS) -{ - ArrayType *v; - int idx_to_chg; - Datum newelem; - int *dimv, - *lb, ub; - ArrayType *result; - bool isNull; - Oid element_type; - int16 typlen; - bool typbyval; - char typalign; - - v = PG_GETARG_ARRAYTYPE_P(0); - idx_to_chg = PG_GETARG_INT32(1); - newelem = PG_GETARG_DATUM(2); - - /* Sanity check: do we have a one-dimensional array */ - if (ARR_NDIM(v) != 1) - elog(ERROR, "Arrays greater than one-dimension are not supported"); - - lb = ARR_LBOUND(v); - dimv = ARR_DIMS(v); - ub = dimv[0] + lb[0] - 1; - if (idx_to_chg < lb[0] || idx_to_chg > ub) - elog(ERROR, "Cannot alter nonexistent array element: %d", idx_to_chg); - - element_type = ARR_ELEMTYPE(v); - /* Sanity check: do we have a non-zero element type */ - if (element_type == 0) - elog(ERROR, "Invalid array element type: %u", element_type); - - get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); - - result = array_set(v, 1, &idx_to_chg, newelem, -1, - typlen, typbyval, typalign, &isNull); - - PG_RETURN_ARRAYTYPE_P(result); -} - -/*----------------------------------------------------------------------------- - * array_subscript : - * return specific element of an array - *---------------------------------------------------------------------------- - */ -Datum -array_subscript(PG_FUNCTION_ARGS) -{ - ArrayType *v; - int idx; - int *dimv, - *lb, ub; - Datum result; - bool isNull; - Oid element_type; - int16 typlen; - bool typbyval; - char typalign; - - v = PG_GETARG_ARRAYTYPE_P(0); - idx = PG_GETARG_INT32(1); - - /* Sanity check: do we have a one-dimensional array */ - if (ARR_NDIM(v) != 1) - elog(ERROR, "Arrays greater than one-dimension are not supported"); - - lb = ARR_LBOUND(v); - dimv = ARR_DIMS(v); - ub = dimv[0] + lb[0] - 1; - if (idx < lb[0] || idx > ub) - elog(ERROR, "Cannot return nonexistent array element: %d", idx); - - element_type = ARR_ELEMTYPE(v); - /* Sanity check: do we have a non-zero element type */ - if (element_type == 0) - elog(ERROR, "Invalid array element type: %u", element_type); - - get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); - - result = array_ref(v, 1, &idx, -1, typlen, typbyval, typalign, &isNull); - - PG_RETURN_DATUM(result); -} /* - * actually does the work for singleton_array(), and array_accum() if it is - * given a null input array. + * used by text_to_array() in varlena.c */ ArrayType * -create_singleton_array(Oid element_type, Datum element, int ndims) +create_singleton_array(FunctionCallInfo fcinfo, + Oid element_type, + Datum element, + int ndims) { Datum dvalues[1]; int16 typlen; @@ -415,6 +303,7 @@ create_singleton_array(Oid element_type, Datum element, int ndims) int dims[MAXDIM]; int lbs[MAXDIM]; int i; + ArrayMetaState *my_extra; if (element_type == 0) elog(ERROR, "Invalid array element type: %u", element_type); @@ -429,7 +318,35 @@ create_singleton_array(Oid element_type, Datum element, int ndims) lbs[i] = 1; } - get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); + /* + * We arrange to look up info about element type only once per series + * of calls, assuming the element type doesn't change underneath us. + */ + my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; + if (my_extra == NULL) + { + fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + sizeof(ArrayMetaState)); + my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; + my_extra->element_type = InvalidOid; + } + + if (my_extra->element_type != element_type) + { + /* Get info about element type */ + get_typlenbyvalalign(element_type, &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); diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index b53c896e4314b0fb9fd6f5378239e581e677ed54..27a805d9b28dd6e3af4456a4589113a3ddb75821 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.89 2003/05/09 23:01:45 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.90 2003/06/24 23:14:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -21,8 +21,10 @@ #include "catalog/pg_type.h" #include "libpq/pqformat.h" #include "parser/parse_coerce.h" +#include "parser/parse_oper.h" #include "utils/array.h" #include "utils/builtins.h" +#include "utils/datum.h" #include "utils/memutils.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -70,16 +72,6 @@ #define RETURN_NULL(type) do { *isNull = true; return (type) 0; } while (0) -/* I/O function selector for system_cache_lookup */ -typedef enum IOFuncSelector -{ - IOFunc_input, - IOFunc_output, - IOFunc_receive, - IOFunc_send -} IOFuncSelector; - - static int ArrayCount(char *str, int *dim, char typdelim); static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim, FmgrInfo *inputproc, Oid typelem, int32 typmod, @@ -93,10 +85,6 @@ static Datum *ReadArrayBinary(StringInfo buf, int nitems, static void CopyArrayEls(char *p, Datum *values, int nitems, int typlen, bool typbyval, char typalign, bool freedata); -static void system_cache_lookup(Oid element_type, IOFuncSelector which_func, - int *typlen, bool *typbyval, - char *typdelim, Oid *typelem, - Oid *proc, char *typalign); static Datum ArrayCast(char *value, bool byval, int len); static int ArrayCastAndSet(Datum src, int typlen, bool typbyval, char typalign, @@ -119,7 +107,7 @@ static void array_insert_slice(int ndim, int *dim, int *lb, char *destPtr, int *st, int *endp, char *srcPtr, int typlen, bool typbyval, char typalign); - +static int array_cmp(FunctionCallInfo fcinfo); /*--------------------------------------------------------------------- * array_in : @@ -154,12 +142,49 @@ array_in(PG_FUNCTION_ARGS) 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; + } - /* 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); + 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,12 +661,51 @@ array_out(PG_FUNCTION_ARGS) indx[MAXDIM]; int ndim, *dim; + ArrayMetaState *my_extra; element_type = ARR_ELEMTYPE(v); - system_cache_lookup(element_type, IOFunc_output, - &typlen, &typbyval, &typdelim, - &typelem, &typoutput, &typalign); - fmgr_info(typoutput, &outputproc); + + /* + * We arrange to look up info about element type, including its 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,6 +864,7 @@ array_recv(PG_FUNCTION_ARGS) dim[MAXDIM], lBound[MAXDIM]; char typalign; + ArrayMetaState *my_extra; /* Get the array header information */ ndim = pq_getmsgint(buf, 4); @@ -831,14 +896,50 @@ array_recv(PG_FUNCTION_ARGS) PG_RETURN_ARRAYTYPE_P(retval); } - /* Get info about element type, including its receive conversion proc */ - system_cache_lookup(element_type, IOFunc_receive, - &typlen, &typbyval, &typdelim, - &typelem, &typreceive, &typalign); - if (!OidIsValid(typreceive)) - elog(ERROR, "No binary input function available for type %s", - format_type_be(element_type)); - fmgr_info(typreceive, &receiveproc); + /* + * We arrange to look up info about element type, including its receive + * conversion proc only once per series of calls, assuming the element + * type doesn't change underneath us. + */ + my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; + if (my_extra == NULL) + { + fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + sizeof(ArrayMetaState)); + my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; + my_extra->element_type = InvalidOid; + } + + 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,15 +1077,54 @@ array_send(PG_FUNCTION_ARGS) int ndim, *dim; StringInfoData buf; + ArrayMetaState *my_extra; /* Get information about the element type and the array dimensions */ element_type = ARR_ELEMTYPE(v); - system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval, - &typdelim, &typelem, &typsend, &typalign); - if (!OidIsValid(typsend)) - elog(ERROR, "No binary output function available for type %s", - format_type_be(element_type)); - fmgr_info(typsend, &sendproc); + + /* + * We arrange to look up info about element type, including its send + * 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,6 +1616,26 @@ array_set(ArrayType *array, array = DatumGetArrayTypeP(PointerGetDatum(array)); ndim = ARR_NDIM(array); + + /* + * if number of dims is zero, i.e. an empty array, create an array + * with nSubscripts dimensions, and set the lower bounds to the supplied + * subscripts + */ + if (ndim == 0) + { + Oid elmtype = ARR_ELEMTYPE(array); + + for (i = 0; i < nSubscripts; i++) + { + dim[i] = 1; + lb[i] = indx[i]; + } + + return construct_md_array(&dataValue, nSubscripts, dim, lb, elmtype, + elmlen, elmbyval, elmalign); + } + if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM) elog(ERROR, "Invalid array subscripts"); @@ -1632,6 +1792,31 @@ array_set_slice(ArrayType *array, /* note: we assume srcArray contains no toasted elements */ ndim = ARR_NDIM(array); + + /* + * if number of dims is zero, i.e. an empty array, create an array + * with nSubscripts dimensions, and set the upper and lower bounds + * to the supplied subscripts + */ + if (ndim == 0) + { + Datum *dvalues; + int nelems; + Oid elmtype = ARR_ELEMTYPE(array); + + deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign, + &dvalues, &nelems); + + for (i = 0; i < nSubscripts; i++) + { + dim[i] = 1 + upperIndx[i] - lowerIndx[i]; + lb[i] = lowerIndx[i]; + } + + return construct_md_array(dvalues, nSubscripts, dim, lb, elmtype, + elmlen, elmbyval, elmalign); + } + if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM) elog(ERROR, "Invalid array subscripts"); @@ -1811,6 +1996,13 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) Oid typelem; Oid proc; char *s; + typedef struct { + ArrayMetaState *inp_extra; + ArrayMetaState *ret_extra; + } am_extra; + am_extra *my_extra; + ArrayMetaState *inp_extra; + ArrayMetaState *ret_extra; /* Get input array */ if (fcinfo->nargs < 1) @@ -1829,11 +2021,81 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType) if (nitems <= 0) PG_RETURN_ARRAYTYPE_P(v); - /* Lookup source and result types. Unneeded variables are reused. */ - system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval, - &typdelim, &typelem, &proc, &inp_typalign); - system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval, - &typdelim, &typelem, &proc, &typalign); + /* + * We arrange to look up info about input and return element types only + * once per series of calls, assuming the element type doesn't change + * underneath us. + */ + my_extra = (am_extra *) fcinfo->flinfo->fn_extra; + if (my_extra == NULL) + { + fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + sizeof(am_extra)); + my_extra = (am_extra *) fcinfo->flinfo->fn_extra; + + 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,8 +2311,6 @@ deconstruct_array(ArrayType *array, * compares two arrays for equality * result : * returns true if the arrays are equal, false otherwise. - * - * XXX bitwise equality is pretty bogus ... *----------------------------------------------------------------------------- */ Datum @@ -2058,12 +2318,118 @@ array_eq(PG_FUNCTION_ARGS) { ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0); ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1); + char *p1 = (char *) ARR_DATA_PTR(array1); + char *p2 = (char *) ARR_DATA_PTR(array2); + int ndims1 = ARR_NDIM(array1); + int ndims2 = ARR_NDIM(array2); + int *dims1 = ARR_DIMS(array1); + int *dims2 = ARR_DIMS(array2); + int nitems1 = ArrayGetNItems(ndims1, dims1); + int nitems2 = ArrayGetNItems(ndims2, dims2); + Oid element_type = ARR_ELEMTYPE(array1); + FmgrInfo *ae_fmgr_info = fcinfo->flinfo; bool result = true; + int typlen; + bool typbyval; + char typdelim; + Oid typelem; + char typalign; + Oid typiofunc; + int i; + ArrayMetaState *my_extra; + FunctionCallInfoData locfcinfo; - if (ARR_SIZE(array1) != ARR_SIZE(array2)) - result = false; - else if (memcmp(array1, array2, ARR_SIZE(array1)) != 0) + /* 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,53 +2439,190 @@ array_eq(PG_FUNCTION_ARGS) } -/***************************************************************************/ -/******************| Support Routines |*****************/ -/***************************************************************************/ +/*----------------------------------------------------------------------------- + * 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))); +} -static void -system_cache_lookup(Oid element_type, - IOFuncSelector which_func, - int *typlen, - bool *typbyval, - char *typdelim, - Oid *typelem, - Oid *proc, - char *typalign) +Datum +array_lt(PG_FUNCTION_ARGS) +{ + PG_RETURN_BOOL(array_cmp(fcinfo) < 0); +} + +Datum +array_gt(PG_FUNCTION_ARGS) { - 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); + 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,6 +2926,18 @@ array_type_coerce(PG_FUNCTION_ARGS) if (tgt_elem_type == InvalidOid) elog(ERROR, "Target type is not an array"); + /* + * We don't deal with domain constraints yet, so bail out. + * This isn't currently a problem, because we also don't + * support arrays of domain type elements either. But in the + * future we might. At that point consideration should be given + * to removing the check below and adding a domain constraints + * check to the coercion. + */ + if (getBaseType(tgt_elem_type) != tgt_elem_type) + elog(ERROR, "array coercion to domain type elements not " \ + "currently supported"); + if (!find_coercion_pathway(tgt_elem_type, src_elem_type, COERCION_EXPLICIT, &funcId)) { @@ -2439,10 +2954,16 @@ array_type_coerce(PG_FUNCTION_ARGS) } /* - * If it's binary-compatible, return the array unmodified. + * If it's binary-compatible, modify the element type in the array header, + * but otherwise leave the array as we received it. */ if (my_extra->coerce_finfo.fn_oid == InvalidOid) - PG_RETURN_ARRAYTYPE_P(src); + { + ArrayType *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0)); + + ARR_ELEMTYPE(result) = my_extra->desttype; + PG_RETURN_ARRAYTYPE_P(result); + } /* * Use array_map to apply the function to each array element. @@ -2454,3 +2975,118 @@ array_type_coerce(PG_FUNCTION_ARGS) 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); +} diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index 6be21d241f1af84d810b6251a7000fbe144077e0..aca19d17e89feea74746848e413f2755b85187e4 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.98 2003/05/15 15:50:19 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.99 2003/06/24 23:14:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -19,11 +19,14 @@ #include "mb/pg_wchar.h" #include "miscadmin.h" #include "access/tuptoaster.h" +#include "catalog/pg_type.h" #include "lib/stringinfo.h" #include "libpq/crypt.h" #include "libpq/pqformat.h" +#include "utils/array.h" #include "utils/builtins.h" #include "utils/pg_locale.h" +#include "utils/lsyscache.h" typedef struct varlena unknown; @@ -1983,8 +1986,7 @@ split_text(PG_FUNCTION_ARGS) if (fldnum == 1) /* first field - just return the input * string */ PG_RETURN_TEXT_P(inputstring); - else -/* otherwise return an empty string */ + else /* otherwise return an empty string */ PG_RETURN_TEXT_P(PG_STR_GET_TEXT("")); } @@ -2004,8 +2006,7 @@ split_text(PG_FUNCTION_ARGS) if (fldnum == 1) /* first field - just return the input * string */ PG_RETURN_TEXT_P(inputstring); - else -/* otherwise return an empty string */ + else /* otherwise return an empty string */ PG_RETURN_TEXT_P(PG_STR_GET_TEXT("")); } else if ((start_posn != 0) && (end_posn == 0)) @@ -2028,6 +2029,191 @@ split_text(PG_FUNCTION_ARGS) } } +/* + * text_to_array + * parse input string + * return text array of elements + * based on provided field separator + */ +Datum +text_to_array(PG_FUNCTION_ARGS) +{ + text *inputstring = PG_GETARG_TEXT_P(0); + int inputstring_len = TEXTLEN(inputstring); + text *fldsep = PG_GETARG_TEXT_P(1); + int fldsep_len = TEXTLEN(fldsep); + int fldnum; + int start_posn = 0; + int end_posn = 0; + text *result_text = NULL; + ArrayBuildState *astate = NULL; + MemoryContext oldcontext = CurrentMemoryContext; + + /* return NULL for empty input string */ + if (inputstring_len < 1) + PG_RETURN_NULL(); + + /* empty field separator + * return one element, 1D, array using the input string */ + if (fldsep_len < 1) + PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID, + CStringGetDatum(inputstring), 1)); + + /* start with end position holding the initial start position */ + end_posn = 0; + for (fldnum=1;;fldnum++) /* field number is 1 based */ + { + Datum dvalue; + bool disnull = false; + + start_posn = end_posn; + end_posn = text_position(PointerGetDatum(inputstring), + PointerGetDatum(fldsep), + fldnum); + + if ((start_posn == 0) && (end_posn == 0)) /* fldsep not found */ + { + if (fldnum == 1) + { + /* first element + * return one element, 1D, array using the input string */ + PG_RETURN_ARRAYTYPE_P(create_singleton_array(fcinfo, TEXTOID, + CStringGetDatum(inputstring), 1)); + } + else + { + /* otherwise create array and exit */ + PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, oldcontext)); + } + } + else if ((start_posn != 0) && (end_posn == 0)) + { + /* last field requested */ + result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, -1, true); + } + else if ((start_posn == 0) && (end_posn != 0)) + { + /* first field requested */ + result_text = LEFT(inputstring, fldsep); + } + else + { + /* prior to last field requested */ + result_text = text_substring(PointerGetDatum(inputstring), start_posn + fldsep_len, end_posn - start_posn - fldsep_len, false); + } + + /* stash away current value */ + dvalue = PointerGetDatum(result_text); + astate = accumArrayResult(astate, dvalue, + disnull, TEXTOID, oldcontext); + + } + + /* never reached -- keep compiler quiet */ + PG_RETURN_NULL(); +} + +/* + * array_to_text + * concatenate Cstring representation of input array elements + * using provided field separator + */ +Datum +array_to_text(PG_FUNCTION_ARGS) +{ + ArrayType *v = PG_GETARG_ARRAYTYPE_P(0); + char *fldsep = PG_TEXTARG_GET_STR(1); + int nitems, *dims, ndims; + char *p; + Oid element_type; + int typlen; + bool typbyval; + char typdelim; + Oid typoutput, + typelem; + FmgrInfo outputproc; + char typalign; + StringInfo result_str = makeStringInfo(); + int i; + ArrayMetaState *my_extra; + + p = ARR_DATA_PTR(v); + ndims = ARR_NDIM(v); + dims = ARR_DIMS(v); + nitems = ArrayGetNItems(ndims, dims); + + /* if there are no elements, return an empty string */ + if (nitems == 0) + PG_RETURN_TEXT_P(PG_STR_GET_TEXT("")); + + element_type = ARR_ELEMTYPE(v); + + /* + * We arrange to look up info about element type, including its output + * conversion proc only once per series of calls, assuming the element + * type doesn't change underneath us. + */ + my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; + if (my_extra == NULL) + { + fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, + sizeof(ArrayMetaState)); + my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; + my_extra->element_type = InvalidOid; + } + + if (my_extra->element_type != element_type) + { + /* Get info about element type, including its output conversion proc */ + get_type_metadata(element_type, IOFunc_output, + &typlen, &typbyval, &typdelim, + &typelem, &typoutput, &typalign); + fmgr_info(typoutput, &outputproc); + + my_extra->element_type = element_type; + my_extra->typlen = typlen; + my_extra->typbyval = typbyval; + my_extra->typdelim = typdelim; + my_extra->typelem = typelem; + my_extra->typiofunc = typoutput; + my_extra->typalign = typalign; + my_extra->proc = outputproc; + } + else + { + typlen = my_extra->typlen; + typbyval = my_extra->typbyval; + typdelim = my_extra->typdelim; + typelem = my_extra->typelem; + typoutput = my_extra->typiofunc; + typalign = my_extra->typalign; + outputproc = my_extra->proc; + } + + for (i = 0; i < nitems; i++) + { + Datum itemvalue; + char *value; + + itemvalue = fetch_att(p, typbyval, typlen); + + value = DatumGetCString(FunctionCall3(&outputproc, + itemvalue, + ObjectIdGetDatum(typelem), + Int32GetDatum(-1))); + + if (i > 0) + appendStringInfo(result_str, "%s%s", fldsep, value); + else + appendStringInfo(result_str, "%s", value); + + p = att_addlength(p, typlen, PointerGetDatum(p)); + p = (char *) att_align(p, typalign); + } + + PG_RETURN_TEXT_P(PG_STR_GET_TEXT(result_str->data)); +} + #define HEXBASE 16 /* * Convert a int32 to a string containing a base 16 (hex) representation of diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 095c5e6a8aad5732476f335ed858a4d4df50408f..a4fcd3fc286df6d9e9c7cb9fb2e2c42644e41341 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.96 2003/06/22 22:04:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.97 2003/06/24 23:14:46 momjian Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -718,6 +718,40 @@ get_func_rettype(Oid funcid) return result; } +/* + * get_func_argtypes + * Given procedure id, return the function's argument types. + * Also pass back the number of arguments. + */ +Oid * +get_func_argtypes(Oid funcid, int *nargs) +{ + HeapTuple tp; + Form_pg_proc procstruct; + Oid *result = NULL; + int i; + + tp = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcid), + 0, 0, 0); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "Function OID %u does not exist", funcid); + + procstruct = (Form_pg_proc) GETSTRUCT(tp); + *nargs = (int) procstruct->pronargs; + + if (*nargs > 0) + { + result = (Oid *) palloc(*nargs * sizeof(Oid)); + + for (i = 0; i < *nargs; i++) + result[i] = procstruct->proargtypes[i]; + } + + ReleaseSysCache(tp); + return result; +} + /* * get_func_retset * Given procedure id, return the function's proretset flag. @@ -1090,6 +1124,56 @@ get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, 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 char get_typalign(Oid typid) diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 2cd2e19946f156579c35a14bbba0dd510993dc26..d772a0a06cedf6aa2efa60c0c06c456061e53a6e 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.68 2003/04/08 23:20:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.69 2003/06/24 23:14:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -1673,3 +1673,29 @@ get_fn_expr_argtype(FunctionCallInfo fcinfo, int argnum) return exprType((Node *) nth(argnum, args)); } + +/* + * 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; +} diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index e2cb7560e175e678610421420d70cc12a95e57c5..43672f716f2d41ed4c78e9b9a310dfc69a9b8b46 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -16,7 +16,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_amop.h,v 1.50 2003/06/22 22:04:55 tgl Exp $ + * $Id: pg_amop.h,v 1.51 2003/06/24 23:14:46 momjian Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -418,6 +418,15 @@ DATA(insert ( 2098 3 f 2334 )); DATA(insert ( 2098 4 f 2335 )); DATA(insert ( 2098 5 f 2336 )); +/* + * btree array_ops + */ + +DATA(insert ( 397 1 f 1072 )); +DATA(insert ( 397 2 f 1074 )); +DATA(insert ( 397 3 f 1070 )); +DATA(insert ( 397 4 f 1075 )); +DATA(insert ( 397 5 f 1073 )); /* * hash index _ops diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index c2c37c136132ddc4d0f3b0d8556070a9cc3bd7df..87553a49807cd3a197c084149b692d491182f3b4 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -14,7 +14,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_amproc.h,v 1.38 2003/06/22 22:04:55 tgl Exp $ + * $Id: pg_amproc.h,v 1.39 2003/06/24 23:14:46 momjian Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -78,6 +78,7 @@ DATA(insert ( 1993 3 199 )); /* btree */ +DATA(insert ( 397 1 382 )); DATA(insert ( 421 1 357 )); DATA(insert ( 423 1 1596 )); DATA(insert ( 424 1 1693 )); diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index 1c8844b2fece39c1b08f5ae3a945c3d01a5844a3..828afe5aa8c4000a1efb3ff423ab76361174b1a8 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -26,7 +26,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_opclass.h,v 1.51 2003/06/22 22:04:55 tgl Exp $ + * $Id: pg_opclass.h,v 1.52 2003/06/24 23:14:46 momjian Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -87,6 +87,8 @@ typedef FormData_pg_opclass *Form_pg_opclass; */ DATA(insert OID = 421 ( 403 abstime_ops PGNSP PGUID 702 t 0 )); +DATA(insert OID = 397 ( 403 array_ops PGNSP PGUID 2277 t 0 )); +#define ARRAY_BTREE_OPS_OID 397 DATA(insert OID = 422 ( 402 bigbox_ops PGNSP PGUID 603 f 0 )); DATA(insert OID = 423 ( 403 bit_ops PGNSP PGUID 1560 t 0 )); DATA(insert OID = 424 ( 403 bool_ops PGNSP PGUID 16 t 0 )); diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index 88d3d998a05529cb14111f7afc8fb585b9328430..3a7b143c9f59eb8b174ea1918b4f77215f40a4d2 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_operator.h,v 1.115 2003/06/22 22:04:55 tgl Exp $ + * $Id: pg_operator.h,v 1.116 2003/06/24 23:14:46 momjian Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -116,10 +116,15 @@ DATA(insert OID = 96 ( "=" PGNSP PGUID b t 23 23 16 96 518 97 97 97 521 int DATA(insert OID = 97 ( "<" PGNSP PGUID b f 23 23 16 521 525 0 0 0 0 int4lt scalarltsel scalarltjoinsel )); DATA(insert OID = 98 ( "=" PGNSP PGUID b t 25 25 16 98 531 664 664 664 666 texteq eqsel eqjoinsel )); -DATA(insert OID = 329 ( "=" PGNSP PGUID b f 2277 2277 16 329 0 0 0 0 0 array_eq eqsel eqjoinsel )); -DATA(insert OID = 349 ( "||" PGNSP PGUID b f 2277 2283 2277 0 0 0 0 0 0 array_append - - )); -DATA(insert OID = 374 ( "||" PGNSP PGUID b f 2283 2277 2277 0 0 0 0 0 0 array_prepend - - )); -DATA(insert OID = 375 ( "||" PGNSP PGUID b f 2277 2277 2277 0 0 0 0 0 0 array_cat - - )); +DATA(insert OID = 1070 ( "=" PGNSP PGUID b f 2277 2277 16 1070 1071 1072 1072 1072 1073 array_eq eqsel eqjoinsel )); +DATA(insert OID = 1071 ( "<>" PGNSP PGUID b f 2277 2277 16 1071 1070 0 0 0 0 array_ne neqsel neqjoinsel )); +DATA(insert OID = 1072 ( "<" PGNSP PGUID b f 2277 2277 16 1073 1075 0 0 0 0 array_lt scalarltsel scalarltjoinsel )); +DATA(insert OID = 1073 ( ">" PGNSP PGUID b f 2277 2277 16 1072 1074 0 0 0 0 array_gt scalargtsel scalargtjoinsel )); +DATA(insert OID = 1074 ( "<=" PGNSP PGUID b f 2277 2277 16 1075 1073 0 0 0 0 array_le scalarltsel scalarltjoinsel )); +DATA(insert OID = 1075 ( ">=" PGNSP PGUID b f 2277 2277 16 1074 1072 0 0 0 0 array_ge scalargtsel scalargtjoinsel )); +DATA(insert OID = 349 ( "||" PGNSP PGUID b f 2277 2283 2277 0 0 0 0 0 0 array_append - - )); +DATA(insert OID = 374 ( "||" PGNSP PGUID b f 2283 2277 2277 0 0 0 0 0 0 array_prepend - - )); +DATA(insert OID = 375 ( "||" PGNSP PGUID b f 2277 2277 2277 0 0 0 0 0 0 array_cat - - )); DATA(insert OID = 352 ( "=" PGNSP PGUID b t 28 28 16 352 0 0 0 0 0 xideq eqsel eqjoinsel )); DATA(insert OID = 353 ( "=" PGNSP PGUID b f 28 23 16 0 0 0 0 0 0 xideqint4 eqsel eqjoinsel )); @@ -425,6 +430,7 @@ DATA(insert OID = 965 ( "^" PGNSP PGUID b f 701 701 701 0 0 0 0 0 0 dpow - DATA(insert OID = 966 ( "+" PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - )); DATA(insert OID = 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 - - )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 80d80b59397e664eb6713c754ae29daf83a59c0c..d4c2f64cb7e9cf01c4288cf5ac3ff66b5f289344 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.305 2003/06/24 22:21:23 momjian Exp $ + * $Id: pg_proc.h,v 1.306 2003/06/24 23:14:47 momjian Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -758,6 +758,8 @@ DATA(insert OID = 359 ( btnamecmp PGNSP PGUID 12 f f t f i 2 23 "19 19" btn DESCR("btree less-equal-greater"); DATA(insert OID = 360 ( bttextcmp PGNSP PGUID 12 f f t f i 2 23 "25 25" bttextcmp - _null_ )); DESCR("btree less-equal-greater"); +DATA(insert OID = 382 ( btarraycmp PGNSP PGUID 12 f f t f i 2 23 "2277 2277" btarraycmp - _null_ )); +DESCR("btree less-equal-greater"); DATA(insert OID = 361 ( lseg_distance PGNSP PGUID 12 f f t f i 2 701 "601 601" lseg_distance - _null_ )); DESCR("distance between"); @@ -988,14 +990,23 @@ DESCR("greater-than"); DATA(insert OID = 743 ( text_ge PGNSP PGUID 12 f f t f i 2 16 "25 25" text_ge - _null_ )); DESCR("greater-than-or-equal"); -DATA(insert OID = 744 ( array_eq PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ )); -DESCR("array equal"); - DATA(insert OID = 745 ( current_user PGNSP PGUID 12 f f t f s 0 19 "" current_user - _null_ )); DESCR("current user name"); DATA(insert OID = 746 ( session_user PGNSP PGUID 12 f f t f s 0 19 "" session_user - _null_ )); DESCR("session user name"); +DATA(insert OID = 744 ( array_eq PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_eq - _null_ )); +DESCR("array equal"); +DATA(insert OID = 390 ( array_ne PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ne - _null_ )); +DESCR("array not equal"); +DATA(insert OID = 391 ( array_lt PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_lt - _null_ )); +DESCR("array less than"); +DATA(insert OID = 392 ( array_gt PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_gt - _null_ )); +DESCR("array greater than"); +DATA(insert OID = 393 ( array_le PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_le - _null_ )); +DESCR("array less than or equal"); +DATA(insert OID = 396 ( array_ge PGNSP PGUID 12 f f t f i 2 16 "2277 2277" array_ge - _null_ )); +DESCR("array greater than or equal"); DATA(insert OID = 747 ( array_dims PGNSP PGUID 12 f f t f i 1 25 "2277" array_dims - _null_ )); DESCR("array dimensions"); DATA(insert OID = 750 ( array_in PGNSP PGUID 12 f f t f s 3 2277 "2275 26 23" array_in - _null_ )); @@ -1006,22 +1017,18 @@ DATA(insert OID = 2091 ( array_lower PGNSP PGUID 12 f f t f i 2 23 "2277 23" DESCR("array lower dimension"); DATA(insert OID = 2092 ( array_upper PGNSP PGUID 12 f f t f i 2 23 "2277 23" array_upper - _null_ )); DESCR("array upper dimension"); -DATA(insert OID = 377 ( singleton_array PGNSP PGUID 12 f f t f i 1 2277 "2283" singleton_array - _null_ )); -DESCR("create array from single element"); DATA(insert OID = 378 ( array_append PGNSP PGUID 12 f f t f i 2 2277 "2277 2283" array_push - _null_ )); DESCR("append element onto end of array"); DATA(insert OID = 379 ( array_prepend PGNSP PGUID 12 f f t f i 2 2277 "2283 2277" array_push - _null_ )); DESCR("prepend element onto front of array"); -DATA(insert OID = 380 ( array_accum PGNSP PGUID 12 f f f f i 2 2277 "2277 2283" array_accum - _null_ )); -DESCR("push element onto end of array, creating array if needed"); -DATA(insert OID = 381 ( array_assign PGNSP PGUID 12 f f t f i 3 2277 "2277 23 2283" array_assign - _null_ )); -DESCR("assign specific array element"); -DATA(insert OID = 382 ( array_subscript PGNSP PGUID 12 f f t f i 2 2283 "2277 23" array_subscript - _null_ )); -DESCR("return specific array element"); DATA(insert OID = 383 ( array_cat PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" array_cat - _null_ )); DESCR("concatenate two arrays"); DATA(insert OID = 384 ( array_coerce PGNSP PGUID 12 f f t f i 1 2277 "2277" array_type_coerce - _null_ )); DESCR("coerce array type to another array type"); +DATA(insert OID = 394 ( string_to_array PGNSP PGUID 12 f f t f i 2 1009 "25 25" text_to_array - _null_ )); +DESCR("split delimited text into text[]"); +DATA(insert OID = 395 ( array_to_string PGNSP PGUID 12 f f t f i 2 25 "2277 25" array_to_text - _null_ )); +DESCR("concatenate array elements, using delimiter, into text"); DATA(insert OID = 760 ( smgrin PGNSP PGUID 12 f f t f s 1 210 "2275" smgrin - _null_ )); DESCR("I/O"); @@ -1322,6 +1329,8 @@ DATA(insert OID = 1036 ( aclremove PGNSP PGUID 12 f f t f s 2 1034 "1034 10 DESCR("remove ACL item"); DATA(insert OID = 1037 ( aclcontains PGNSP PGUID 12 f f t f s 2 16 "1034 1033" aclcontains - _null_ )); DESCR("does ACL contain item?"); +DATA(insert OID = 1062 ( aclitemeq PGNSP PGUID 12 f f t f s 2 16 "1033 1033" aclitem_eq - _null_ )); +DESCR("equality operator for ACL items"); DATA(insert OID = 1365 ( makeaclitem PGNSP PGUID 12 f f t f s 5 1033 "23 23 23 25 16" makeaclitem - _null_ )); DESCR("make ACL item"); DATA(insert OID = 1038 ( seteval PGNSP PGUID 12 f f t t v 1 23 "26" seteval - _null_ )); diff --git a/src/include/fmgr.h b/src/include/fmgr.h index 620f06bd18c4bd3d134edb027b89a649c297aeca..784b50885ea257cfece5f6d5db74e5a6a4f862bd 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -11,13 +11,14 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: fmgr.h,v 1.27 2003/04/08 23:20:04 tgl Exp $ + * $Id: fmgr.h,v 1.28 2003/06/24 23:14:46 momjian Exp $ * *------------------------------------------------------------------------- */ #ifndef FMGR_H #define FMGR_H +#include "nodes/nodes.h" /* * All functions that can be called directly by fmgr must have this signature. @@ -372,14 +373,14 @@ extern Datum OidFunctionCall9(Oid functionId, Datum arg1, Datum arg2, 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 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 diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 12af0fd0925049fa432da2e151019d08ab4b9ff8..3756e443ff7e3e836f956178be372c961e31881a 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: primnodes.h,v 1.83 2003/06/06 15:04:03 tgl Exp $ + * $Id: primnodes.h,v 1.84 2003/06/24 23:14:48 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -226,6 +226,7 @@ typedef struct Aggref Index agglevelsup; /* > 0 if agg belongs to outer query */ bool aggstar; /* TRUE if argument was really '*' */ bool aggdistinct; /* TRUE if it's agg(DISTINCT ...) */ + List *args; /* arguments to the aggregate */ } Aggref; /* ---------------- @@ -358,15 +359,19 @@ typedef struct BoolExpr /* ---------------- * 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: + * 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,6 +420,8 @@ typedef struct SubLink 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,6 +463,15 @@ typedef struct SubPlan 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 */ diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h index 77dd37659515fbd4ec75e5465aabd1df15df233e..946fc68c463e0fafa0e6f31c82bb677db474e86e 100644 --- a/src/include/optimizer/clauses.h +++ b/src/include/optimizer/clauses.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: clauses.h,v 1.63 2003/05/28 16:04:02 tgl Exp $ + * $Id: clauses.h,v 1.64 2003/06/24 23:14:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -28,6 +28,9 @@ extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset, extern Node *get_leftop(Expr *clause); extern Node *get_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); diff --git a/src/include/parser/parse_oper.h b/src/include/parser/parse_oper.h index 6d2268bf5573529840c8a13d77f5009841039343..e7c8bdb56cbeec47a9818dbeb727dfda4ec1d27c 100644 --- a/src/include/parser/parse_oper.h +++ b/src/include/parser/parse_oper.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: parse_oper.h,v 1.25 2003/04/29 22:13:11 tgl Exp $ + * $Id: parse_oper.h,v 1.26 2003/06/24 23:14:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -44,6 +44,7 @@ extern Operator ordering_oper(Oid argtype, bool noError); /* Convenience routines for common calls on the above */ 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 */ diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 1a54042e5e9f6913fbbfe0b1c3022c0d84ffd2b3..0d7761190196cf15d728a1349af40b55c2182ea2 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: acl.h,v 1.52 2003/06/11 09:23:55 petere Exp $ + * $Id: acl.h,v 1.53 2003/06/24 23:14:49 momjian Exp $ * * NOTES * For backward-compatibility purposes we have to allow there @@ -192,6 +192,7 @@ extern Datum aclinsert(PG_FUNCTION_ARGS); extern Datum aclremove(PG_FUNCTION_ARGS); extern Datum aclcontains(PG_FUNCTION_ARGS); extern Datum makeaclitem(PG_FUNCTION_ARGS); +extern Datum aclitem_eq(PG_FUNCTION_ARGS); /* * prototypes for functions in aclchk.c diff --git a/src/include/utils/array.h b/src/include/utils/array.h index 23a1d2b30e01e32f134bc0d77090db6de2a0e9eb..5df86b1827f3bdc4eafbb509eddb78e08a9dc206 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -10,7 +10,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: array.h,v 1.38 2003/05/08 22:19:57 tgl Exp $ + * $Id: array.h,v 1.39 2003/06/24 23:14:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -32,6 +32,37 @@ typedef struct Oid elemtype; /* element type OID */ } 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,11 +117,15 @@ extern Datum array_recv(PG_FUNCTION_ARGS); extern Datum array_send(PG_FUNCTION_ARGS); extern Datum array_length_coerce(PG_FUNCTION_ARGS); extern Datum array_eq(PG_FUNCTION_ARGS); +extern Datum array_ne(PG_FUNCTION_ARGS); +extern Datum array_lt(PG_FUNCTION_ARGS); +extern Datum array_gt(PG_FUNCTION_ARGS); +extern Datum array_le(PG_FUNCTION_ARGS); +extern Datum array_ge(PG_FUNCTION_ARGS); +extern Datum btarraycmp(PG_FUNCTION_ARGS); extern Datum array_dims(PG_FUNCTION_ARGS); extern Datum array_lower(PG_FUNCTION_ARGS); extern Datum array_upper(PG_FUNCTION_ARGS); -extern Datum array_assign(PG_FUNCTION_ARGS); -extern Datum array_subscript(PG_FUNCTION_ARGS); extern Datum array_type_coerce(PG_FUNCTION_ARGS); extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx, @@ -124,7 +159,14 @@ extern void deconstruct_array(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, int *nelemsp); - +extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate, + Datum dvalue, bool disnull, + Oid element_type, + MemoryContext rcontext); +extern Datum makeArrayResult(ArrayBuildState *astate, + MemoryContext rcontext); +extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims, + int *dims, int *lbs, MemoryContext rcontext); /* * prototypes for functions defined in arrayutils.c @@ -141,12 +183,11 @@ extern int mda_next_tuple(int n, int *curr, int *span); /* * prototypes for functions defined in array_userfuncs.c */ -extern Datum singleton_array(PG_FUNCTION_ARGS); extern Datum array_push(PG_FUNCTION_ARGS); -extern Datum array_accum(PG_FUNCTION_ARGS); extern Datum array_cat(PG_FUNCTION_ARGS); -extern ArrayType *create_singleton_array(Oid element_type, +extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo, + Oid element_type, Datum element, int ndims); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 099b1ffa3786aca4cfcd8537ec894289f6ae1754..43e174722b3e6ccca4fc2bc44aae3ffeb95fc367 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: builtins.h,v 1.220 2003/06/24 22:21:23 momjian Exp $ + * $Id: builtins.h,v 1.221 2003/06/24 23:14:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -530,6 +530,8 @@ extern bool SplitIdentifierString(char *rawstring, char separator, List **namelist); 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); diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 878f5445c2a768506afccdc4ac53c8a2c41970a3..0b1badd6efe964b606e2b2b336b3b7e0a76f210f 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: lsyscache.h,v 1.71 2003/06/22 22:04:55 tgl Exp $ + * $Id: lsyscache.h,v 1.72 2003/06/24 23:14:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -15,6 +15,15 @@ #include "access/htup.h" +/* I/O function selector for system_cache_lookup */ +typedef enum IOFuncSelector +{ + IOFunc_input, + IOFunc_output, + IOFunc_receive, + IOFunc_send +} IOFuncSelector; + extern bool op_in_opclass(Oid opno, Oid opclass); extern bool op_requires_recheck(Oid opno, Oid opclass); extern Oid get_opclass_member(Oid opclass, int16 strategy); @@ -41,6 +50,7 @@ extern RegProcedure get_oprrest(Oid opno); extern RegProcedure get_oprjoin(Oid opno); extern char *get_func_name(Oid funcid); extern Oid get_func_rettype(Oid funcid); +extern Oid *get_func_argtypes(Oid funcid, int *nargs); extern bool get_func_retset(Oid funcid); extern bool func_strict(Oid funcid); extern char func_volatile(Oid funcid); @@ -56,6 +66,14 @@ extern bool get_typbyval(Oid typid); extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval); extern void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign); +extern void get_type_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); diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index 7a8c2284bd26bc4e6612684e1389d753a2b9415f..92776168822ec05eecb510229f97365418bc648f 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.236 2003/06/20 13:36:34 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.237 2003/06/24 23:14:49 momjian Exp $ */ /* Copyright comment */ %{ @@ -4595,7 +4595,7 @@ type_declaration: S_TYPEDEF $3.type_enum != ECPGt_char && $3.type_enum != ECPGt_unsigned_char && atoi(this->type->type_index) >= 0) - mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types"); + mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types"); types = this; } @@ -5415,7 +5415,7 @@ ECPGTypedef: TYPE_P $5.type_enum != ECPGt_char && $5.type_enum != ECPGt_unsigned_char && atoi(this->type->type_index) >= 0) - mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types"); + mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types"); types = this; } @@ -5482,7 +5482,7 @@ ECPGVar: SQL_VAR default: if (atoi(length) >= 0) - mmerror(PARSE_ERROR, ET_ERROR, "No multi-dimensional array support for simple data types"); + mmerror(PARSE_ERROR, ET_ERROR, "No multidimensional array support for simple data types"); if (atoi(dimension) < 0) type = ECPGmake_simple_type($5.type_enum, make_str("1")); diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c index 5f2dd86bb55f15fedccc6e005aa36cd255b2118a..80406bbccc5124115b194c0d5c71098e104dcb05 100644 --- a/src/interfaces/ecpg/preproc/type.c +++ b/src/interfaces/ecpg/preproc/type.c @@ -504,7 +504,7 @@ ECPGfree_type(struct ECPGtype * type) switch (type->u.element->type) { case ECPGt_array: - yyerror("internal error, found multi-dimensional array\n"); + yyerror("internal error, found multidimensional array\n"); break; case ECPGt_struct: case ECPGt_union: diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c index be96e18c0c5488d38bc3fe5ba0eafb644d39436c..9fa2ec8a6c9a3756975e9f8d93fb1c6cdcc295a6 100644 --- a/src/interfaces/ecpg/preproc/variable.c +++ b/src/interfaces/ecpg/preproc/variable.c @@ -436,7 +436,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty if (atoi(type_index) >= 0) { if (atoi(*length) >= 0) - mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support"); + mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support"); *length = type_index; } @@ -444,7 +444,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty if (atoi(type_dimension) >= 0) { if (atoi(*dimension) >= 0 && atoi(*length) >= 0) - mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support"); + mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support"); if (atoi(*dimension) >= 0) *length = *dimension; @@ -463,10 +463,10 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty mmerror(PARSE_ERROR, ET_FATAL, "No pointer to pointer supported for this type"); if (pointer_len > 1 && (atoi(*length) >= 0 || atoi(*dimension) >= 0)) - mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support"); + mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support"); if (atoi(*length) >= 0 && atoi(*dimension) >= 0 && pointer_len) - mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support"); + mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support"); switch (type_enum) { @@ -480,7 +480,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty } if (atoi(*length) >= 0) - mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for structures"); + mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for structures"); break; case ECPGt_varchar: @@ -525,7 +525,7 @@ adjust_array(enum ECPGttype type_enum, char **dimension, char **length, char *ty } if (atoi(*length) >= 0) - mmerror(PARSE_ERROR, ET_FATAL, "No multi-dimensional array support for simple data types"); + mmerror(PARSE_ERROR, ET_FATAL, "No multidimensional array support for simple data types"); break; } diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out index 617cf09a9a00a1f6b36339e019a60a7c3c317443..c5f638bd4653e23c9e98ac7cbb0d4614e7c3cfa6 100644 --- a/src/test/regress/expected/arrays.out +++ b/src/test/regress/expected/arrays.out @@ -178,19 +178,13 @@ SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY"; (1 row) -- functions -SELECT singleton_array(42) AS "{42}"; - {42} ------- - {42} -(1 row) - -SELECT array_append(singleton_array(42), 6) AS "{42,6}"; +SELECT array_append(array[42], 6) AS "{42,6}"; {42,6} -------- {42,6} (1 row) -SELECT array_prepend(6, singleton_array(42)) AS "{6,42}"; +SELECT array_prepend(6, array[42]) AS "{6,42}"; {6,42} -------- {6,42} @@ -214,24 +208,6 @@ SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}"; {{3,4},{5,6},{1,2}} (1 row) -SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2; - 1.2 ------ - 1.2 -(1 row) - -SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2; - {1.1,9.99,1.3} ----------------- - {1.1,9.99,1.3} -(1 row) - -SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2; - 9.99 ------- - 9.99 -(1 row) - -- operators SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]]; a diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql index 82eff24125df262f27466880cf96b500991134d1..e39044e40acabe1ba64f02c0be6b0ffd4219b5fc 100644 --- a/src/test/regress/sql/arrays.sql +++ b/src/test/regress/sql/arrays.sql @@ -130,15 +130,11 @@ SELECT ARRAY[ARRAY['hello'],ARRAY['world']]; SELECT ARRAY(select f2 from arrtest_f order by f2) AS "ARRAY"; -- functions -SELECT singleton_array(42) AS "{42}"; -SELECT array_append(singleton_array(42), 6) AS "{42,6}"; -SELECT array_prepend(6, singleton_array(42)) AS "{6,42}"; +SELECT array_append(array[42], 6) AS "{42,6}"; +SELECT array_prepend(6, array[42]) AS "{6,42}"; SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS "{{1,2},{3,4}}"; SELECT array_cat(ARRAY[1,2], ARRAY[[3,4],[5,6]]) AS "{{1,2},{3,4},{5,6}}"; SELECT array_cat(ARRAY[[3,4],[5,6]], ARRAY[1,2]) AS "{{3,4},{5,6},{1,2}}"; -SELECT array_subscript(n, 2) AS "1.2" FROM arrtest2; -SELECT array_assign(n, 2, 9.99) AS "{1.1,9.99,1.3}" FROM arrtest2; -SELECT array_subscript(array_assign(n, 2, 9.99), 2) AS "9.99" FROM arrtest2; -- operators SELECT a FROM arrtest WHERE b = ARRAY[[[113,142],[1,147]]];