array/polymorphic function doc cleanup - Mailing list pgsql-patches
From | Joe Conway |
---|---|
Subject | array/polymorphic function doc cleanup |
Date | |
Msg-id | 3F329A12.6040306@joeconway.com Whole thread Raw |
Responses |
Re: array/polymorphic function doc cleanup
|
List | pgsql-patches |
I think this rounds off the documentation updates that I owed for array and polymorphic function/aggregate changes. Let me know if you think I missed anything major. In any case, please apply. Thanks, Joe Index: doc/src/sgml/array.sgml =================================================================== RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/array.sgml,v retrieving revision 1.28 diff -c -r1.28 array.sgml *** doc/src/sgml/array.sgml 27 Jun 2003 00:33:25 -0000 1.28 --- doc/src/sgml/array.sgml 7 Aug 2003 15:41:38 -0000 *************** *** 116,122 **** 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> --- 116,122 ---- 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> *************** *** 206,224 **** </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> An array value can be replaced completely: <programlisting> --- 206,211 ---- *************** *** 233,247 **** 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> --- 220,225 ---- *************** *** 256,278 **** 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> An array can be enlarged by assigning to an element adjacent to those already present, or by assigning to a slice that is adjacent ! to or overlaps the data already present. For example, if an array ! value currently has 4 elements, it will have five elements after an ! update that assigns to <literal>array[5]</>. Currently, enlargement in ! this fashion is only allowed for one-dimensional arrays, not ! multidimensional arrays. </para> <para> --- 234,249 ---- WHERE name = 'Carol'; </programlisting> </para> <para> An array can be enlarged by assigning to an element adjacent to those already present, or by assigning to a slice that is adjacent ! to or overlaps the data already present. For example, if array ! <literal>myarray</> currently has 4 elements, it will have five ! elements after an update that assigns to <literal>myarray[5]</>. ! Currently, enlargement in this fashion is only allowed for one-dimensional ! arrays, not multidimensional arrays. </para> <para> *************** *** 434,472 **** </programlisting> However, this quickly becomes tedious for large arrays, and is not ! helpful if the size of the array is unknown. Although it is not built ! into <productname>PostgreSQL</productname>, ! there is an extension available that defines new functions and ! operators for iterating over array values. Using this, the above query could be: <programlisting> ! SELECT * FROM sal_emp WHERE pay_by_quarter[1:4] *= 10000; ! </programlisting> ! ! To search the entire array (not just specified slices), you could ! use: ! ! <programlisting> ! SELECT * FROM sal_emp WHERE pay_by_quarter *= 10000; </programlisting> In addition, you could find rows where the array had all values ! equal to 10 000 with: <programlisting> ! SELECT * FROM sal_emp WHERE pay_by_quarter **= 10000; </programlisting> - To install this optional module, look in the - <filename>contrib/array</filename> directory of the - <productname>PostgreSQL</productname> source distribution. </para> <tip> <para> Arrays are not sets; using arrays in the manner described in the ! previous paragraph is often a sign of database misdesign. The array field should generally be split off into a separate table. Tables can obviously be searched easily. </para> --- 405,431 ---- </programlisting> However, this quickly becomes tedious for large arrays, and is not ! helpful if the size of the array is unknown. An alternative method is ! described in <xref linkend="functions-comparisons">. Using this, the above query could be: <programlisting> ! SELECT * FROM sal_emp WHERE 10000 = ANY (pay_by_quarter); </programlisting> In addition, you could find rows where the array had all values ! equal to 10000 with: <programlisting> ! SELECT * FROM sal_emp WHERE 10000 = ALL (pay_by_quarter); </programlisting> </para> <tip> <para> Arrays are not sets; using arrays in the manner described in the ! previous paragraph may be a sign of database misdesign. The array field should generally be split off into a separate table. Tables can obviously be searched easily. </para> *************** *** 498,513 **** <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 --- 457,473 ---- <para> As illustrated earlier in this chapter, arrays may also be represented ! in many cases 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 Index: doc/src/sgml/func.sgml =================================================================== RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/func.sgml,v retrieving revision 1.164 diff -c -r1.164 func.sgml *** doc/src/sgml/func.sgml 4 Aug 2003 14:00:13 -0000 1.164 --- doc/src/sgml/func.sgml 7 Aug 2003 15:41:38 -0000 *************** *** 7044,7071 **** <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> --- 7044,7110 ---- <tbody> <row> <entry> <literal>=</literal> </entry> ! <entry>equal</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>not equal</entry> + <entry><literal>ARRAY[1,2,3] != ARRAY[1,2,4]</literal></entry> + <entry><literal>t</literal></entry> + </row> + + <row> + <entry> <literal><</literal> </entry> + <entry>less than</entry> + <entry><literal>ARRAY[1,2,3] < ARRAY[1,2,4]</literal></entry> + <entry><literal>t</literal></entry> + </row> + + <row> + <entry> <literal>></literal> </entry> + <entry>greater than</entry> + <entry><literal>ARRAY[1,4,3] > ARRAY[1,2,4]</literal></entry> + <entry><literal>t</literal></entry> + </row> + + <row> + <entry> <literal><=</literal> </entry> + <entry>less than or equal</entry> + <entry><literal>ARRAY[1,2,3] <= ARRAY[1,2,3]</literal></entry> + <entry><literal>t</literal></entry> + </row> + + <row> + <entry> <literal>>=</literal> </entry> + <entry>greater than or equal</entry> + <entry><literal>ARRAY[1,4,3] >= ARRAY[1,4,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> Index: doc/src/sgml/plpgsql.sgml =================================================================== RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/plpgsql.sgml,v retrieving revision 1.19 diff -c -r1.19 plpgsql.sgml *** doc/src/sgml/plpgsql.sgml 28 May 2003 16:03:55 -0000 1.19 --- doc/src/sgml/plpgsql.sgml 7 Aug 2003 18:12:23 -0000 *************** *** 464,482 **** </programlisting> </para> <sect2 id="plpgsql-declaration-aliases"> ! <title>Aliases for Function Parameters</title> <synopsis> <replaceable>name</replaceable> ALIAS FOR $<replaceable>n</replaceable>; </synopsis> <para> - Parameters passed to functions are named with the identifiers - <literal>$1</literal>, <literal>$2</literal>, - etc. Optionally, aliases can be declared for <literal>$<replaceable>n</replaceable></literal> - parameter names for increased readability. Either the alias or the - numeric identifier can then be used to refer to the parameter value. Some examples: <programlisting> CREATE FUNCTION sales_tax(real) RETURNS real AS ' --- 464,512 ---- </programlisting> </para> + <sect2 id="plpgsql-polymorphic-functions"> + <title>Polymorphic <acronym>PL/pgSQL</acronym> Functions</title> + + <para> + <acronym>PL/pgSQL</acronym> Functions may be specified to accept, and + optionally return, the types <type>anyelement</type> or + <type>anyarray</type>, otherwise known as polymorphic types. + See <xref linkend="xfunc"> for a more detailed explanation + of polymorphic functions. An example is shown in + <xref linkend="plpgsql-declaration-aliases"> + </para> + </sect2> + <sect2 id="plpgsql-declaration-aliases"> ! <title>Default Identifiers and Aliases</title> ! ! <para> ! Parameters passed to functions are named with the identifiers ! <literal>$1</literal>, <literal>$2</literal>, etc. ! </para> ! ! <para> ! When the return type of a <application>PL/pgSQL</application> ! function is declared a polymorphic type, <type>anyelement</type> ! or <type>anyarray</type>, the return value is also named with an ! identifier, <literal>$0</literal>. It is initialized to NULL, but ! may be used to hold the return value as calculated by the function. ! <literal>$0</literal> can be used by polymorphic functions to discover ! their own return type, as discussed in ! <xref linkend="plpgsql-declaration-type"> ! </para> ! ! <para> ! Aliases can optionally be declared for <literal>$<replaceable>n</replaceable> ! </literal> identifiers for increased readability. Either the alias or the ! numeric identifier can then be used to refer to the value. ! </para> <synopsis> <replaceable>name</replaceable> ALIAS FOR $<replaceable>n</replaceable>; </synopsis> <para> Some examples: <programlisting> CREATE FUNCTION sales_tax(real) RETURNS real AS ' *************** *** 505,510 **** --- 535,553 ---- RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7; END; ' LANGUAGE plpgsql; + + CREATE FUNCTION add_many_fields(anyelement, anyelement, anyelement) + RETURNS anyelement AS ' + DECLARE + result ALIAS FOR $0; + first ALIAS FOR $1; + second ALIAS FOR $2; + third ALIAS FOR $3; + BEGIN + result := first + second + third; + RETURN result; + END; + ' LANGUAGE plpgsql; </programlisting> </para> </sect2> *************** *** 536,541 **** --- 579,593 ---- from <type>integer</type> to <type>real</type>), you may not need to change your function definition. </para> + + <para> + Similarly, using <literal>%TYPE</literal> allows you to define variables + within your function, without needing to know the data type of the + structure you are referencing in advance. This is important in polymorphic + functions in which the data type of the referenced item may change + from one call to the next. + </para> + </sect2> <sect2 id="plpgsql-declaration-rowtypes"> Index: doc/src/sgml/xaggr.sgml =================================================================== RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/xaggr.sgml,v retrieving revision 1.20 diff -c -r1.20 xaggr.sgml *** doc/src/sgml/xaggr.sgml 10 Apr 2003 01:22:44 -0000 1.20 --- doc/src/sgml/xaggr.sgml 7 Aug 2003 18:27:53 -0000 *************** *** 34,39 **** --- 34,48 ---- </para> <para> + <acronym>Aggregate</acronym> Functions may use polymorphic + <firstterm>state transition functions</firstterm> or + <firstterm>final functions</firstterm>. See <xref linkend="xfunc"> + for a more detailed explanation of polymorphic functions. + <acronym>Aggregate</acronym> Functions may also be specified with a + polymorphic base type and state type. + </para> + + <para> If we define an aggregate that does not use a final function, we have an aggregate that computes a running function of the column values from each row. <function>sum</> is an *************** *** 107,112 **** --- 116,154 ---- finalfunc = float8_avg, initcond = '{0,0}' ); + </programlisting> + </para> + + <para> + <function>array_accum</> is an example of a polymorphic aggregate: + + <programlisting> + CREATE AGGREGATE array_accum ( + sfunc = array_append, + basetype = anyelement, + stype = anyarray, + initcond = '{}' + ); + </programlisting> + + Here's the output using two different runtime data types as arguments: + + <programlisting> + SELECT attrelid::regclass, array_accum(attname) + FROM pg_attribute WHERE attnum > 0 + AND attrelid = 'pg_user'::regclass GROUP BY attrelid; + attrelid | array_accum + ----------+----------------------------------------------------------------------------- + pg_user | {usename,usesysid,usecreatedb,usesuper,usecatupd,passwd,valuntil,useconfig} + (1 row) + + SELECT attrelid::regclass, array_accum(atttypid) + FROM pg_attribute WHERE attnum > 0 + AND attrelid = 'pg_user'::regclass GROUP BY attrelid; + attrelid | array_accum + ----------+------------------------------ + pg_user | {19,23,16,16,16,25,702,1009} + (1 row) </programlisting> </para> Index: doc/src/sgml/xfunc.sgml =================================================================== RCS file: /opt/src/cvs/pgsql-server/doc/src/sgml/xfunc.sgml,v retrieving revision 1.70 diff -c -r1.70 xfunc.sgml *** doc/src/sgml/xfunc.sgml 25 Jul 2003 20:17:49 -0000 1.70 --- doc/src/sgml/xfunc.sgml 7 Aug 2003 18:12:10 -0000 *************** *** 47,56 **** </para> <para> It's easiest to define <acronym>SQL</acronym> ! functions, so we'll start with those. Examples in this section ! can also be found in <filename>funcs.sql</filename> ! and <filename>funcs.c</filename> in the tutorial directory. </para> <para> --- 47,76 ---- </para> <para> + Many kinds of functions can be specified to accept, and + optionally return, the types <type>anyelement</type> or + <type>anyarray</type>, otherwise known as polymorphic types. + These datatypes are tied to each other and resolved to a deterministic + type at runtime. Each position (i.e. either argument or return type) + defined as <type>anyelement</type> can have any data type at runtime, + but they must all be the <emphasis>same</emphasis> runtime type. Each + position defined as <type>anyarray</type> can have any array data type + at runtime, but similarly they must all be the same. If there are + positions declared <type>anyarray</type> and others declared + <type>anyelement</type>, the runtime array type in the + <type>anyarray</type> positions must be an array of the runtime type + at the <type>anyelement</type> positions. + </para> + + <para> + See the individual sections for each type of function to determine + if polymorphic functions are supported for that language. + </para> + + <para> It's easiest to define <acronym>SQL</acronym> ! functions, so we'll start with those. Some examples in this section ! can also be found in <filename>funcs.c</filename> in the tutorial directory. </para> <para> *************** *** 383,388 **** --- 403,470 ---- </sect2> <sect2> + <title>Polymorphic <acronym>SQL</acronym> Functions</title> + + <para> + <acronym>SQL</acronym> Functions may be specified to accept, and + optionally return, the types <type>anyelement</type> or + <type>anyarray</type>, otherwise known as polymorphic types. + See <xref linkend="xfunc"> for a more detailed explanation + of polymorphic functions. Here is a polymorphic function + <function>make_array</function> that builds up an array from two + arbitrary data type elements: + <screen> + CREATE FUNCTION make_array(anyelement, anyelement) RETURNS anyarray AS ' + SELECT ARRAY[$1, $2]; + ' LANGUAGE SQL; + + SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray; + intarray | textarray + ----------+----------- + {1,2} | {a,b} + (1 row) + </screen> + </para> + + <para> + Notice the use of the typecast <literal>'a'::text</literal> + to specify a runtime <type>text</type> type. This is + required if the runtime type would otherwise be resolved as + <type>unknown</type>, because there is currently no way + to delay resolution of the element type to the time of array + creation, and array of <type>unknown</type> is not a valid type. + Without the typecast, you will get errors like this: + <screen> + <computeroutput> + ERROR: could not determine ANYARRAY/ANYELEMENT type because input is UNKNOWN + </computeroutput> + </screen> + </para> + + <para> + It is permitted to have polymorphic arguments with a deterministic + return type, but the converse is not. For example: + <screen> + CREATE FUNCTION is_greater(anyelement, anyelement) RETURNS bool AS ' + SELECT $1 > $2; + ' LANGUAGE SQL; + + SELECT is_greater(1, 2); + is_greater + ------------ + f + (1 row) + + CREATE FUNCTION invalid_func() RETURNS anyelement AS ' + SELECT 1; + ' LANGUAGE SQL; + ERROR: cannot determine result datatype + DETAIL: A function returning ANYARRAY or ANYELEMENT must have at least one argument of either type. + </screen> + </para> + </sect2> + + <sect2> <title><acronym>SQL</acronym> Functions as Table Sources</title> <para> *************** *** 1584,1589 **** --- 1666,1793 ---- AS '<replaceable>DIRECTORY</replaceable>/funcs', 'c_overpaid' LANGUAGE C; </programlisting> + </para> + </sect2> + + <sect2> + <title>Polymorphic Arguments and Return Types</title> + + <para> + C-Language functions may be specified to accept, and + optionally return, the types <type>anyelement</type> or + <type>anyarray</type>, otherwise known as polymorphic types. + See <xref linkend="xfunc"> for a more detailed explanation + of polymorphic functions. When function arguments or return types + are defined as polymorphic types, the function author cannot know + in advance what data type it will be called with, or + need to return. There are two exported routines in fmgr.c available + to allow a user defined function to discover the actual data types + of its arguments and the type it is expected to return. The routines are + declared in + <programlisting> + #include "fmgr.h" + </programlisting> + and are called get_fn_expr_rettype(FmgrInfo *flinfo) and + get_fn_expr_argtype(FmgrInfo *flinfo, int argnum). The structure + flinfo is normally accessed as fcinfo->flinfo. The parameter argnum + is zero based. + </para> + + <para> + For example, suppose we want to write a function to accept a single + element of any type, and return a one-dimensional array of that type: + + <programlisting> + PG_FUNCTION_INFO_V1(make_array); + Datum + make_array(PG_FUNCTION_ARGS) + { + ArrayType *result; + Oid element_type = get_fn_expr_argtype(fcinfo->flinfo, 0); + Datum element; + int16 typlen; + bool typbyval; + char typalign; + int ndims; + int dims[MAXDIM]; + int lbs[MAXDIM]; + + /* get the provided element */ + element = PG_GETARG_DATUM(0); + + /* we have one dimension */ + ndims = 1; + /* and one element */ + dims[0] = 1; + /* and lower bound is 1 */ + lbs[0] = 1; + + /* get required info about the element type */ + get_typlenbyvalalign(element_type, &typlen, &typbyval, &typalign); + + /* now build the array */ + result = construct_md_array(&element, ndims, dims, lbs, + element_type, typlen, typbyval, typalign); + + PG_RETURN_ARRAYTYPE_P(result); + } + </programlisting> + </para> + + <para> + The following command declares the function + <function>make_array</function> in SQL: + + <programlisting> + CREATE FUNCTION make_array(anyelement) + RETURNS anyarray + AS '<replaceable>DIRECTORY</replaceable>/funcs', 'make_array' + LANGUAGE 'C' STRICT; + </programlisting> + </para> + + <para> + The <function>make_array</function> function is then used as + in the following: + <programlisting> + select make_array('a'::text); + make_array + ------------ + {a} + (1 row) + + select make_array(1); + make_array + ------------ + {1} + (1 row) + + select make_array(1.1); + make_array + ------------ + {1.1} + (1 row) + </programlisting> + </para> + + <para> + Notice the use of the typecast <literal>'a'::text</literal> + to specify a runtime <type>text</type> type. This is + required if the runtime type would otherwise be resolved as + <type>unknown</type>, because there is currently no way + to delay resolution of the element type to the time of array + creation, and array of <type>unknown</type> is not a valid type. + Without the typecast, you will get errors like this: + <screen> + <computeroutput> + ERROR: could not determine ANYARRAY/ANYELEMENT type because input is UNKNOWN + </computeroutput> + </screen> + </para> + + <para> + It is permitted to have polymorphic arguments with a deterministic + return type, but the converse is not. </para> </sect2>
pgsql-patches by date: