Re: [HACKERS] SQL99 ARRAY support proposal - Mailing list pgsql-patches
| From | Joe Conway |
|---|---|
| Subject | Re: [HACKERS] SQL99 ARRAY support proposal |
| Date | |
| Msg-id | 3E701869.4020301@joeconway.com Whole thread Raw |
| Responses |
Re: [HACKERS] SQL99 ARRAY support proposal
Re: [HACKERS] SQL99 ARRAY support proposal |
| List | pgsql-patches |
Tom Lane wrote:
> Hmm. I don't see why we should drag ANY into this --- it should just be
> a no-constraints placeholder, same as before. What's the gain from
> constraining it that you don't get from ANYELEMENT?
[...snip...]
>> XXX should this case be rejected at the point of function creation?
>
> Probably. This case could be handled just as well by declaring the
> output to be ANY, I'd think.
[...snip...]
> Likewise. The point of (this reinterpretation of) ANYARRAY and
> ANYELEMENT is to let the parser deduce the actual output type.
> If it's not going to be able to deduce anything, use ANY instead.
Here's a new patch with the above corrections. I'm sending it to patches
in hopes it can be applied now rather than waiting. I think it stands
alone (shy some documentation, but I'm good for that ;-)) and makes
sense regardless of the other array support issues.
Thanks,
Joe
Index: src/backend/catalog/pg_proc.c
===================================================================
RCS file: /opt/src/cvs/pgsql-server/src/backend/catalog/pg_proc.c,v
retrieving revision 1.95
diff -c -r1.95 pg_proc.c
*** src/backend/catalog/pg_proc.c 12 Dec 2002 15:49:24 -0000 1.95
--- src/backend/catalog/pg_proc.c 13 Mar 2003 01:39:59 -0000
***************
*** 86,91 ****
--- 86,114 ----
elog(ERROR, "functions cannot have more than %d arguments",
FUNC_MAX_ARGS);
+ /*
+ * Do not allow return type ANYARRAY or ANYELEMENT unless at least one
+ * argument is also ANYARRAY or ANYELEMENT
+ */
+ if (returnType == ANYARRAYOID || returnType == ANYELEMENTOID)
+ {
+ bool genericParam = false;
+
+ for (i = 0; i < parameterCount; i++)
+ {
+ if (parameterTypes[i] == ANYARRAYOID ||
+ parameterTypes[i] == ANYELEMENTOID)
+ {
+ genericParam = true;
+ break;
+ }
+ }
+
+ if (!genericParam)
+ elog(ERROR, "functions returning ANYARRAY or ANYELEMENT must " \
+ "have at least one argument of either type");
+ }
+
/* Make sure we have a zero-padded param type array */
MemSet(typev, 0, FUNC_MAX_ARGS * sizeof(Oid));
if (parameterCount > 0)
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 13 Mar 2003 01:39:59 -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 13 Mar 2003 01:39:59 -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 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,466 ----
return retval;
}
+ /*
+ * If 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 ANYARRAY should have matching datatypes.
+ * 2) All arguments declared ANYELEMENT should have matching datatypes.
+ * 3) 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 ANYARRAY or ANYELEMENT:
+ *
+ * 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the
+ * arguments runtime type as the function's return type.
+ * 2) 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.
+ * 3) If return type is ANYARRAY, no argument is ANYARRAY or ANYELEMENT,
+ * generate an ERROR. This condition is prevented by CREATE FUNCTION
+ * and is therefore unexpected here.
+ * 4) If return type is ANYELEMENT, and any argument is ANYELEMENT, use the
+ * arguments runtime type as the function's return type.
+ * 5) 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.
+ * 6) If return type is ANYELEMENT, no argument is ANYARRAY or ANYELEMENT,
+ * generate an ERROR. This condition is prevented by CREATE FUNCTION
+ * and is therefore unexpected here.
+ */
+ static Oid
+ enforce_generic_type_consistency(Oid *oid_array, Oid *true_oid_array, int nargs, Oid rettype)
+ {
+ int j;
+ 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] == 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 ANYARRAY or ANYELEMENT,
+ * return the original rettype now
+ */
+ if (!OidIsValid(array_typeid) && !OidIsValid(elem_typeid))
+ 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 13 Mar 2003 01:39:59 -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 13 Mar 2003 01:39:59 -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 13 Mar 2003 01:39:59 -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 13 Mar 2003 01:39:59 -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 13 Mar 2003 01:39:59 -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
pgsql-patches by date: