But I think I like better the notion of extending my bound-together- ANYARRAY-and-ANYELEMENT proposal, http://archives.postgresql.org/pgsql-hackers/2003-03/msg00319.php
Suppose that we do that, and then further say that ANYARRAY or ANYELEMENT appearing as the return type implies that the return type is actually the common element or array type. Then we have such useful behaviors as:
array_push(anyarray, anyelement) returns anyarray array_pop(anyarray) returns anyelement array_subscript(anyarray, int) yields anyelement singleton_array(anyelement) yields anyarray
Before I get too far along, I'd like to get some feedback. The attached patch implements Tom's bound-together-ANYARRAY-and-ANYELEMENT proposal (and includes ANY as well, per earlier discussion). With it, the following works:
CREATE OR REPLACE FUNCTION array_push (anyarray, anyelement) RETURNS anyarray AS '$libdir/plr','array_push' LANGUAGE 'C';
regression=# select f1[2] from (select array_push('{1,2}'::integer[],3) as f1) as t;
f1
----
2
(1 row)
Does it make sense to commit this now, or should it wait for the other changes described below?
The following are my proposed next phases in array support changes. Please let me know now if you think any of these won't fly (conceptually):
1) Implement SQL99/200x ARRAY[] changes proposed here: http://archives.postgresql.org/pgsql-hackers/2003-03/msg00297.php as modified by http://archives.postgresql.org/pgsql-hackers/2003-03/msg00315.php
2) Implement the following new builtin functions array_push(anyarray, anyelement) returns anyarray array_pop(anyarray) returns anyelement array_subscript(anyarray, int) yields anyelement singleton_array(anyelement) returns anyarray - any reason not to call this one simply "array"? split(text, text) returns text[] - split string into array on delimiter implode(text[], text) returns text - join array elements into a string using given string delimiter
3) Modify contrib/array functions as needed and move to the backend. Or possibly write equivalent functions from scratch -- I just noticed this in contrib/array:
* This software is distributed under the GNU General Public License * either version 2, or (at your option) any later version.
Is anyone still in contact with Massimo Dal Zotto? Any chance he would change the license to BSD?
4) Update "User's Guide"->"Data Types"->"Arrays" documentation and create a new section: "User's Guide"-> "Functions and Operators"-> "Array Functions and Operators"
Thoughts, comments, objections all welcomed.
Thanks,
Joe
Index: src/backend/parser/parse_coerce.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_coerce.c,v retrieving revision 2.93 diff -c -r2.93 parse_coerce.c *** src/backend/parser/parse_coerce.c 9 Feb 2003 06:56:28 -0000 2.93 --- src/backend/parser/parse_coerce.c 12 Mar 2003 16:17:39 -0000 *************** *** 188,194 **** ReleaseSysCache(targetType); } ! else if (targetTypeId == ANYOID || targetTypeId == ANYARRAYOID) { /* assume can_coerce_type verified that implicit coercion is okay */ --- 188,194 ---- ReleaseSysCache(targetType); } ! else if (targetTypeId == ANYOID || targetTypeId == ANYELEMENTOID || targetTypeId == ANYARRAYOID) { /* assume can_coerce_type verified that implicit coercion is okay */ *************** *** 325,332 **** continue; } ! /* accept if target is ANY */ ! if (targetTypeId == ANYOID) continue; /* --- 325,332 ---- continue; } ! /* accept if target is ANY or ANYELEMENT */ ! if (targetTypeId == ANYOID || targetTypeId == ANYELEMENTOID) continue; /* Index: src/backend/parser/parse_func.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/parser/parse_func.c,v retrieving revision 1.144 diff -c -r1.144 parse_func.c *** src/backend/parser/parse_func.c 9 Feb 2003 06:56:28 -0000 1.144 --- src/backend/parser/parse_func.c 12 Mar 2003 17:46:44 -0000 *************** *** 41,46 **** --- 41,50 ---- List *fargs, Oid *input_typeids, Oid *function_typeids); + static Oid enforce_generic_type_consistency(Oid *oid_array, + Oid *true_oid_array, + int nargs, + Oid rettype); static int match_argtypes(int nargs, Oid *input_typeids, FuncCandidateList function_typeids, *************** *** 309,314 **** --- 313,324 ---- "\n\tYou may need to add explicit typecasts"); } + /* + * enforce consistency with ANY, ANYARRAY, and ANYELEMENT argument + * and return types, possibly modifying return type along the way + */ + rettype = enforce_generic_type_consistency(oid_array, true_oid_array, nargs, rettype); + /* perform the necessary typecasting of arguments */ make_arguments(nargs, fargs, oid_array, true_oid_array); *************** *** 347,352 **** --- 357,491 ---- return retval; } + /* + * If ANY, ANYARRAY, or ANYELEMENT is used for a function's arguments or + * return type, make sure the runtime types are consistent with + * each other. The argument consistency rules are like so: + * + * 1) All arguments declared ANY should have matching datatypes. + * 2) All arguments declared ANYARRAY should have matching datatypes. + * 3) All arguments declared ANYELEMENT should have matching datatypes. + * 4) If there are arguments of both ANYELEMENT and ANYARRAY, make sure + * the runtime scalar argument type is in fact the element type for + * the runtime array argument type. + * + * Rules are applied to the function's return type (possibly altering it) + * if it is declared ANY, ANYARRAY, or ANYELEMENT: + * + * 1) If return type is ANY, and any argument is ANY, use the + * arguments runtime type as the function's return type. + * 2) If return type is ANY but no argument is ANY, leave the return type as + * is. + * XXX should this case be rejected at the point of function creation? + * 3) If return type is ANYARRAY, and any argument is ANYARRAY, use the + * arguments runtime type as the function's return type. + * 4) If return type is ANYARRAY, no argument is ANYARRAY, but any argument + * is ANYELEMENT, use the runtime type of the argument to determine + * the function's return type, i.e. the element type's corresponding + * array type. + * 5) If return type is ANYARRAY, no argument is ANYARRAY or ANYELEMENT, + * leave the return type as is. + * XXX should this case be rejected at the point of function creation? + * 6) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the + * arguments runtime type as the function's return type. + * 7) If return type is ANYELEMENT, no argument is ANYELEMENT, but any argument + * is ANYARRAY, use the runtime type of the argument to determine + * the function's return type, i.e. the array type's corresponding + * element type. + * 8) If return type is ANYELEMENT, no argument is ANYARRAY or ANYELEMENT + * leave the return type as is. + * XXX should this case be rejected at the point of function creation? + */ + static Oid + enforce_generic_type_consistency(Oid *oid_array, Oid *true_oid_array, int nargs, Oid rettype) + { + int j; + Oid any_typeid = InvalidOid; + Oid elem_typeid = InvalidOid; + Oid array_typeid = InvalidOid; + Oid array_typelem = InvalidOid; + + /* + * Loop through the arguments to see if we have any that are + * ANYARRAY or ANYELEMENT. If so, require the runtime types to be + * self-consistent + */ + for (j = 0; j < nargs; j++) + { + if (true_oid_array[j] == ANYOID) + { + if (OidIsValid(any_typeid) && oid_array[j] != any_typeid) + elog(ERROR, "Inconsistent use of arguments declared ANY"); + any_typeid = oid_array[j]; + } + + if (true_oid_array[j] == ANYELEMENTOID) + { + if (OidIsValid(elem_typeid) && oid_array[j] != elem_typeid) + elog(ERROR, "Inconsistent use of arguments declared ANYELEMENT"); + elem_typeid = oid_array[j]; + } + + if (true_oid_array[j] == ANYARRAYOID) + { + if (OidIsValid(array_typeid) && oid_array[j] != array_typeid) + elog(ERROR, "Inconsistent use of arguments declared ANYARRAY"); + array_typeid = oid_array[j]; + } + } + + /* + * Fast Track: if none of the arguments are ANY, ANYARRAY, or ANYELEMENT, + * return the original rettype now + */ + if (!OidIsValid(any_typeid) && + !OidIsValid(array_typeid) && + !OidIsValid(elem_typeid)) + return rettype; + + /* if we return ANYOID check consistency with any like arguments */ + if (rettype == ANYOID) + { + if (OidIsValid(any_typeid)) + return any_typeid; + else + return rettype; + } + + /* get the element type based on the array type, if we have one */ + if (OidIsValid(array_typeid)) + { + array_typelem = get_typelem(array_typeid); + + if (!OidIsValid(elem_typeid)) + { + /* if we don't have an element type yet, use the one we just got */ + elem_typeid = array_typelem; + } + else if (array_typelem != elem_typeid) + { + /* otherwise, they better match */ + elog(ERROR, "Argument declared ANYARRAY not consistent with " \ + "argument declared ANYELEMENT"); + } + } + + /* if we return ANYARRAYOID enforce consistency with any like arguments */ + if (rettype == ANYARRAYOID) + { + if (OidIsValid(array_typeid)) + return array_typeid; + else + return get_arraytype(elem_typeid); + } + + /* if we return ANYELEMENTOID check consistency with any like arguments */ + if (rettype == ANYELEMENTOID) + return elem_typeid; + + /* we don't return a generic type; send back the original return type */ + return rettype; + } /* match_argtypes() * Index: src/backend/utils/cache/lsyscache.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/backend/utils/cache/lsyscache.c,v retrieving revision 1.90 diff -c -r1.90 lsyscache.c *** src/backend/utils/cache/lsyscache.c 3 Feb 2003 21:15:44 -0000 1.90 --- src/backend/utils/cache/lsyscache.c 12 Mar 2003 16:17:39 -0000 *************** *** 1040,1045 **** --- 1040,1108 ---- } /* + * get_typelem + * + * Given the type OID, return the typelem field (element type OID + * for array types) + */ + Oid + get_typelem(Oid typid) + { + HeapTuple tp; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + Oid result; + + result = typtup->typelem; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; + } + + /* + * get_arraytype + * + * Given an element type OID, return the OID the corresponding + * array type + */ + Oid + get_arraytype(Oid elem_typeid) + { + HeapTuple tp; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(elem_typeid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + char *elem_typename; + Oid elem_namespaceId; + char *array_typename; + + elem_typename = pstrdup(NameStr(typtup->typname)); + elem_namespaceId = typtup->typnamespace; + ReleaseSysCache(tp); + + array_typename = makeArrayTypeName(elem_typename); + + return GetSysCacheOid(TYPENAMENSP, + PointerGetDatum(array_typename), + ObjectIdGetDatum(elem_namespaceId), + 0, 0); + } + else + return InvalidOid; + } + + /* * get_typdefault * Given a type OID, return the type's default value, if any. * Index: src/include/catalog/pg_type.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/catalog/pg_type.h,v retrieving revision 1.139 diff -c -r1.139 pg_type.h *** src/include/catalog/pg_type.h 23 Jan 2003 23:39:06 -0000 1.139 --- src/include/catalog/pg_type.h 12 Mar 2003 16:17:39 -0000 *************** *** 523,528 **** --- 523,530 ---- #define ANYOID 2276 DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p t \054 0 0 anyarray_in anyarray_out i x f 0 -1 0 _null_ _null_ )); #define ANYARRAYOID 2277 + DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p t \054 0 0 any_in any_out i p f 0 -1 0 _null_ _null_ )); + #define ANYELEMENTOID 2283 DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p t \054 0 0 void_in void_out i p f 0 -1 0 _null_ _null_ )); #define VOIDOID 2278 DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p t \054 0 0 trigger_in trigger_out i p f 0 -1 0 _null_ _null_ )); Index: src/include/utils/lsyscache.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/include/utils/lsyscache.h,v retrieving revision 1.67 diff -c -r1.67 lsyscache.h *** src/include/utils/lsyscache.h 3 Feb 2003 21:15:45 -0000 1.67 --- src/include/utils/lsyscache.h 12 Mar 2003 16:17:39 -0000 *************** *** 55,60 **** --- 55,62 ---- char *typalign); extern char get_typstorage(Oid typid); extern int32 get_typtypmod(Oid typid); + extern Oid get_typelem(Oid typid); + extern Oid get_arraytype(Oid elem_typeid); extern Node *get_typdefault(Oid typid); extern char get_typtype(Oid typid); extern Oid get_typ_typrelid(Oid typid); Index: src/test/regress/expected/type_sanity.out =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/test/regress/expected/type_sanity.out,v retrieving revision 1.17 diff -c -r1.17 type_sanity.out *** src/test/regress/expected/type_sanity.out 15 Jan 2003 19:35:48 -0000 1.17 --- src/test/regress/expected/type_sanity.out 12 Mar 2003 18:59:47 -0000 *************** *** 101,119 **** -----+---------+-----+--------- (0 rows) ! -- As of 7.3, this check finds SET and refcursor, which are borrowing ! -- other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT (p1.typelem != 0 AND p1.typlen < 0) AND NOT (p2.prorettype = p1.oid AND NOT p2.proretset) ORDER BY 1; ! oid | typname | oid | proname ! ------+-----------+-----+----------- ! 32 | SET | 109 | unknownin ! 1790 | refcursor | 46 | textin ! (2 rows) -- Varlena array types will point to array_in SELECT p1.oid, p1.typname, p2.oid, p2.proname --- 101,120 ---- -----+---------+-----+--------- (0 rows) ! -- As of 7.4, this check finds SET, refcursor, and anyelement, which are ! -- borrowing other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT (p1.typelem != 0 AND p1.typlen < 0) AND NOT (p2.prorettype = p1.oid AND NOT p2.proretset) ORDER BY 1; ! oid | typname | oid | proname ! ------+------------+------+----------- ! 32 | SET | 109 | unknownin ! 1790 | refcursor | 46 | textin ! 2283 | anyelement | 2294 | any_in ! (3 rows) -- Varlena array types will point to array_in SELECT p1.oid, p1.typname, p2.oid, p2.proname *************** *** 126,133 **** (0 rows) -- Check for bogus typoutput routines ! -- As of 7.3, this check finds SET and refcursor, which are borrowing ! -- other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT --- 127,134 ---- (0 rows) -- Check for bogus typoutput routines ! -- As of 7.4, this check finds SET, refcursor, and anyelement, which are ! -- borrowing other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT *************** *** 135,145 **** (p2.oid = 'array_out'::regproc AND p1.typelem != 0 AND p1.typlen = -1)) ORDER BY 1; ! oid | typname | oid | proname ! ------+-----------+-----+------------ ! 32 | SET | 110 | unknownout ! 1790 | refcursor | 47 | textout ! (2 rows) SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 --- 136,147 ---- (p2.oid = 'array_out'::regproc AND p1.typelem != 0 AND p1.typlen = -1)) ORDER BY 1; ! oid | typname | oid | proname ! ------+------------+------+------------ ! 32 | SET | 110 | unknownout ! 1790 | refcursor | 47 | textout ! 2283 | anyelement | 2295 | any_out ! (3 rows) SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 Index: src/test/regress/sql/type_sanity.sql =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/test/regress/sql/type_sanity.sql,v retrieving revision 1.17 diff -c -r1.17 type_sanity.sql *** src/test/regress/sql/type_sanity.sql 15 Jan 2003 19:35:48 -0000 1.17 --- src/test/regress/sql/type_sanity.sql 12 Mar 2003 18:59:42 -0000 *************** *** 84,91 **** p2.proargtypes[1] = 'oid'::regtype AND p2.proargtypes[2] = 'int4'::regtype)); ! -- As of 7.3, this check finds SET and refcursor, which are borrowing ! -- other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT --- 84,91 ---- p2.proargtypes[1] = 'oid'::regtype AND p2.proargtypes[2] = 'int4'::regtype)); ! -- As of 7.4, this check finds SET, refcursor, and anyelement, which are ! -- borrowing other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT *************** *** 102,109 **** -- Check for bogus typoutput routines ! -- As of 7.3, this check finds SET and refcursor, which are borrowing ! -- other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT --- 102,109 ---- -- Check for bogus typoutput routines ! -- As of 7.4, this check finds SET, refcursor, and anyelement, which are ! -- borrowing other types' I/O routines SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
---------------------------(end of broadcast)--------------------------- TIP 3: if posting/reading through Usenet, please send an appropriate subscribe-nomail command to [EMAIL PROTECTED] so that your message can get through to the mailing list cleanly