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  (Bruce Momjian <pgman@candle.pha.pa.us>)
Re: [HACKERS] SQL99 ARRAY support proposal  (Bruce Momjian <pgman@candle.pha.pa.us>)
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:

Previous
From: Neil Conway
Date:
Subject: minor SELECT doc patch
Next
From: Peter Eisentraut
Date:
Subject: Re: restructure libpq docs (WIP)