OSDN Git Service

Implement types regprocedure, regoper, regoperator, regclass, regtype
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 25 Apr 2002 02:56:56 +0000 (02:56 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 25 Apr 2002 02:56:56 +0000 (02:56 +0000)
per pghackers discussion.  Add some more typsanity tests, and clean
up some problems exposed thereby (broken or missing array types for
some built-in types).  Also, clean up loose ends from unknownin/out
patch.

28 files changed:
contrib/findoidjoins/README.findoidjoins
contrib/findoidjoins/findoidjoins.c
doc/src/sgml/bki.sgml
doc/src/sgml/datatype.sgml
src/backend/bootstrap/bootstrap.c
src/backend/catalog/namespace.c
src/backend/catalog/pg_operator.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_node.c
src/backend/utils/adt/regproc.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/adt/selfuncs.c
src/backend/utils/adt/varlena.c
src/backend/utils/cache/catcache.c
src/bin/psql/describe.c
src/include/c.h
src/include/catalog/catversion.h
src/include/catalog/namespace.h
src/include/catalog/pg_namespace.h
src/include/catalog/pg_operator.h
src/include/catalog/pg_proc.h
src/include/catalog/pg_type.h
src/include/fmgr.h
src/include/utils/builtins.h
src/test/regress/expected/opr_sanity.out
src/test/regress/expected/type_sanity.out
src/test/regress/sql/opr_sanity.sql
src/test/regress/sql/type_sanity.sql

index 557fd04..14c17e1 100644 (file)
@@ -1,10 +1,10 @@
 
                              findoidjoins
 
-This program scans a database, and prints oid fields (also regproc fields)
-and the tables they join to.  CAUTION: it is ver-r-r-y slow on a large
-database, or even a not-so-large one.  We don't really recommend running
-it on anything but an empty database, such as template1.
+This program scans a database, and prints oid fields (also regproc, regclass
+and regtype fields) and the tables they join to.  CAUTION: it is ver-r-r-y
+slow on a large database, or even a not-so-large one.  We don't really
+recommend running it on anything but an empty database, such as template1.
 
 Uses pgeasy library.
 
index c559b7a..c426b55 100644 (file)
@@ -39,7 +39,9 @@ main(int argc, char **argv)
                WHERE a.attnum > 0 AND \
                          relkind = 'r' AND \
                          (typname = 'oid' OR \
-                          typname = 'regproc') AND \
+                          typname = 'regproc' OR \
+                          typname = 'regclass' OR \
+                          typname = 'regtype') AND \
                          a.attrelid = c.oid AND \
                          a.atttypid = t.oid \
                ORDER BY 2, a.attnum ; \
@@ -77,7 +79,7 @@ main(int argc, char **argv)
                                        DECLARE c_matches BINARY CURSOR FOR \
                                        SELECT  count(*)::int4 \
                                                FROM \"%s\" t1, \"%s\" t2 \
-                                       WHERE RegprocToOid(t1.\"%s\") = t2.oid ",
+                                       WHERE t1.\"%s\"::oid = t2.oid ",
                                                relname, relname2, attname);
 
                        doquery(query);
index 4ea0f5f..36503de 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/bki.sgml,v 1.10 2002/03/22 19:20:02 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/bki.sgml,v 1.11 2002/04/25 02:56:55 tgl Exp $
  -->
 
 <chapter id="bki">
@@ -122,7 +122,8 @@ $Header: /cvsroot/pgsql/doc/src/sgml/bki.sgml,v 1.10 2002/03/22 19:20:02 petere
       storage.  The following types are allowed: <type>bool</type>,
       <type>bytea</type>, <type>char</type> (1 byte),
       <type>name</type>, <type>int2</type>, <type>int2vector</type>,
-      <type>int4</type>, <type>regproc</type>, <type>text</type>,
+      <type>int4</type>, <type>regproc</type>, <type>regclass</type>,
+      <type>regtype</type>, <type>text</type>,
       <type>oid</type>, <type>tid</type>, <type>xid</type>,
       <type>cid</type>, <type>oidvector</type>, <type>smgr</type>,
       <type>_int4</type> (array), <type>_aclitem</type> (array).
index aef751c..4f06cee 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.89 2002/04/21 18:58:00 thomas Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.90 2002/04/25 02:56:55 tgl Exp $
 -->
 
  <chapter id="datatype">
@@ -173,12 +173,6 @@ $Header: /cvsroot/pgsql/doc/src/sgml/datatype.sgml,v 1.89 2002/04/21 18:58:00 th
       </row>
 
       <row>
-       <entry><type>oid</type></entry>
-       <entry></entry>
-       <entry>object identifier</entry>
-      </row>
-
-      <row>
        <entry><type>path</type></entry>
        <entry></entry>
        <entry>open and closed geometric path in 2D plane</entry>
@@ -2894,6 +2888,165 @@ SELECT SUBSTRING(b FROM 1 FOR 2) FROM test;
 
   </sect1>
 
+  <sect1 id="datatype-oid">
+   <title>Object Identifier Types</title>
+
+   <indexterm zone="datatype-oid">
+    <primary>object identifier</primary>
+    <secondary>data type</secondary>
+   </indexterm>
+
+   <indexterm zone="datatype-oid">
+    <primary>oid</primary>
+   </indexterm>
+
+   <indexterm zone="datatype-oid">
+    <primary>regproc</primary>
+   </indexterm>
+
+   <indexterm zone="datatype-oid">
+    <primary>regprocedure</primary>
+   </indexterm>
+
+   <indexterm zone="datatype-oid">
+    <primary>regoper</primary>
+   </indexterm>
+
+   <indexterm zone="datatype-oid">
+    <primary>regoperator</primary>
+   </indexterm>
+
+   <indexterm zone="datatype-oid">
+    <primary>regclass</primary>
+   </indexterm>
+
+   <indexterm zone="datatype-oid">
+    <primary>regtype</primary>
+   </indexterm>
+
+   <para>
+    Object identifiers (OIDs) are used internally by
+    <productname>PostgreSQL</productname> as primary keys for various system
+    tables.  Also, an OID system column is added to user-created tables
+    (unless <literal>WITHOUT OIDS</> is specified at table creation time).
+    Type <type>oid</> represents an object identifier.  There are also
+    several aliases for <type>oid</>: <type>regproc</>, <type>regprocedure</>,
+    <type>regoper</>, <type>regoperator</>, <type>regclass</>,
+    and <type>regtype</>.
+   </para>
+
+   <para>
+    The <type>oid</> type is currently implemented as an unsigned four-byte
+    integer.
+    Therefore, it is not large enough to provide database-wide uniqueness
+    in large databases, or even in large individual tables.  So, using a
+    user-created table's OID column as a primary key is discouraged.
+    OIDs are best used only for references to system tables.
+   </para>
+
+   <para>
+    The <type>oid</> type itself has few operations beyond comparison
+    (which is implemented as unsigned comparison).  It can be cast to
+    integer, however, and then manipulated using the standard integer
+    operators.  (Beware of possible signed-versus-unsigned confusion
+    if you do this.)
+   </para>
+
+   <para>
+    The  <type>oid</> alias types have no operations of their own except
+    for specialized input and output routines.  These routines are able
+    to accept and display symbolic names for system objects, rather than
+    the raw numeric value that type <type>oid</> would use.  The alias
+    types allow simplified lookup of OID values for objects: for example,
+    one may write <literal>'mytable'::regclass</> to get the OID of table
+    <literal>mytable</>, rather than <literal>SELECT oid FROM pg_class WHERE
+    relname = 'mytable'</>.  (In reality, a much more complicated SELECT would
+    be needed to deal with selecting the right OID when there are multiple
+    tables named <literal>mytable</> in different schemas.)
+   </para>
+
+   <para>
+    <table tocentry="1">
+     <title>Object Identifier Types</title>
+     <tgroup cols="4">
+      <thead>
+       <row>
+       <entry>Type name</entry>
+       <entry>References</entry>
+       <entry>Description</entry>
+       <entry>Examples</entry>
+       </row>
+      </thead>
+
+      <tbody>
+
+       <row>
+       <entry><type>oid</></entry>
+       <entry>any</entry>
+       <entry>Numeric object identifier</entry>
+       <entry>564182</entry>
+       </row>
+
+       <row>
+       <entry><type>regproc</></entry>
+       <entry>pg_proc</entry>
+       <entry>Function name</entry>
+       <entry>sum</entry>
+       </row>
+
+       <row>
+       <entry><type>regprocedure</></entry>
+       <entry>pg_proc</entry>
+       <entry>Function with argument types</entry>
+       <entry>sum(int4)</entry>
+       </row>
+
+       <row>
+       <entry><type>regoper</></entry>
+       <entry>pg_operator</entry>
+       <entry>Operator name</entry>
+       <entry>+</entry>
+       </row>
+
+       <row>
+       <entry><type>regoperator</></entry>
+       <entry>pg_operator</entry>
+       <entry>Operator with argument types</entry>
+       <entry>*(integer,integer)  -(NONE,integer)</entry>
+       </row>
+
+       <row>
+       <entry><type>regclass</></entry>
+       <entry>pg_class</entry>
+       <entry>Relation name</entry>
+       <entry>pg_type</entry>
+       </row>
+
+       <row>
+       <entry><type>regtype</></entry>
+       <entry>pg_type</entry>
+       <entry>Type name</entry>
+       <entry>integer</entry>
+       </row>
+      </tbody>
+     </tgroup>
+    </table>
+   </para>
+
+   <para>
+    All of the alias types accept schema-qualified names, and will
+    display schema-qualified names on output if the object would not
+    be found in the current search path without being qualified.
+    The <type>regproc</> and <type>regoper</> alias types will only
+    accept input names that are unique (not overloaded), so they are
+    of limited use; for most uses <type>regprocedure</> or
+    <type>regoperator</> is more appropriate.  For <type>regoperator</>,
+    unary operators are identified by writing NONE for the unused
+    operand.
+   </para>
+
+  </sect1>
+
  </chapter>
 
 <!-- Keep this comment at the end of the file
index a4ab6fc..07d0132 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.125 2002/03/26 19:15:16 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.126 2002/04/25 02:56:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -109,12 +109,14 @@ static struct typinfo Procid[] = {
        {"int2vector", INT2VECTOROID, 0, INDEX_MAX_KEYS * 2, F_INT2VECTORIN, F_INT2VECTOROUT},
        {"int4", INT4OID, 0, 4, F_INT4IN, F_INT4OUT},
        {"regproc", REGPROCOID, 0, 4, F_REGPROCIN, F_REGPROCOUT},
+       {"regclass", REGCLASSOID, 0, 4, F_REGCLASSIN, F_REGCLASSOUT},
+       {"regtype", REGTYPEOID, 0, 4, F_REGTYPEIN, F_REGTYPEOUT},
        {"text", TEXTOID, 0, -1, F_TEXTIN, F_TEXTOUT},
        {"oid", OIDOID, 0, 4, F_OIDIN, F_OIDOUT},
        {"tid", TIDOID, 0, 6, F_TIDIN, F_TIDOUT},
        {"xid", XIDOID, 0, 4, F_XIDIN, F_XIDOUT},
        {"cid", CIDOID, 0, 4, F_CIDIN, F_CIDOUT},
-       {"oidvector", 30, 0, INDEX_MAX_KEYS * 4, F_OIDVECTORIN, F_OIDVECTOROUT},
+       {"oidvector", OIDVECTOROID, 0, INDEX_MAX_KEYS * 4, F_OIDVECTORIN, F_OIDVECTOROUT},
        {"smgr", 210, 0, 2, F_SMGRIN, F_SMGROUT},
        {"_int4", 1007, INT4OID, -1, F_ARRAY_IN, F_ARRAY_OUT},
        {"_aclitem", 1034, 1033, -1, F_ARRAY_IN, F_ARRAY_OUT}
@@ -600,7 +602,7 @@ DefineAttr(char *name, char *type, int attnum)
                attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */
                attlen = attrtypes[attnum]->attlen = Ap->am_typ.typlen;
                attrtypes[attnum]->attbyval = Ap->am_typ.typbyval;
-               attrtypes[attnum]->attstorage = Ap->am_typ.typstorage;;
+               attrtypes[attnum]->attstorage = Ap->am_typ.typstorage;
                attrtypes[attnum]->attalign = Ap->am_typ.typalign;
        }
        else
@@ -610,28 +612,37 @@ DefineAttr(char *name, char *type, int attnum)
                elog(DEBUG3, "column %s %s", NameStr(attrtypes[attnum]->attname), type);
                attrtypes[attnum]->attnum = 1 + attnum; /* fillatt */
                attlen = attrtypes[attnum]->attlen = Procid[typeoid].len;
-               attrtypes[attnum]->attstorage = 'p';
 
                /*
                 * Cheat like mad to fill in these items from the length only.
-                * This only has to work for types used in the system catalogs...
+                * This only has to work for types that appear in Procid[].
                 */
                switch (attlen)
                {
                        case 1:
                                attrtypes[attnum]->attbyval = true;
+                               attrtypes[attnum]->attstorage = 'p';
                                attrtypes[attnum]->attalign = 'c';
                                break;
                        case 2:
                                attrtypes[attnum]->attbyval = true;
+                               attrtypes[attnum]->attstorage = 'p';
                                attrtypes[attnum]->attalign = 's';
                                break;
                        case 4:
                                attrtypes[attnum]->attbyval = true;
+                               attrtypes[attnum]->attstorage = 'p';
+                               attrtypes[attnum]->attalign = 'i';
+                               break;
+                       case -1:
+                               attrtypes[attnum]->attbyval = false;
+                               attrtypes[attnum]->attstorage = 'x';
                                attrtypes[attnum]->attalign = 'i';
                                break;
                        default:
+                               /* TID and fixed-length arrays, such as oidvector */
                                attrtypes[attnum]->attbyval = false;
+                               attrtypes[attnum]->attstorage = 'p';
                                attrtypes[attnum]->attalign = 'i';
                                break;
                }
@@ -803,6 +814,13 @@ cleanup()
 
 /* ----------------
  *             gettype
+ *
+ * NB: this is really ugly; it will return an integer index into Procid[],
+ * and not an OID at all, until the first reference to a type not known in
+ * Procid[].  At that point it will read and cache pg_type in the Typ array,
+ * and subsequently return a real OID (and set the global pointer Ap to
+ * point at the found row in Typ).  So caller must check whether Typ is
+ * still NULL to determine what the return value is!
  * ----------------
  */
 static Oid
@@ -827,7 +845,7 @@ gettype(char *type)
        }
        else
        {
-               for (i = 0; i <= n_types; i++)
+               for (i = 0; i < n_types; i++)
                {
                        if (strncmp(type, Procid[i].name, NAMEDATALEN) == 0)
                                return i;
index 0a3aefe..c81b990 100644 (file)
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.11 2002/04/17 20:57:56 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.12 2002/04/25 02:56:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -365,9 +365,12 @@ OpclassnameGetOpcid(Oid amid, const char *opcname)
  *             Given a possibly-qualified function name and argument count,
  *             retrieve a list of the possible matches.
  *
+ * If nargs is -1, we return all functions matching the given name,
+ * regardless of argument count.
+ *
  * We search a single namespace if the function name is qualified, else
  * all namespaces in the search path.  The return list will never contain
- * multiple entries with identical argument types --- in the multiple-
+ * multiple entries with identical argument lists --- in the multiple-
  * namespace case, we arrange for entries in earlier namespaces to mask
  * identical entries in later namespaces.
  */
@@ -423,11 +426,16 @@ FuncnameGetCandidates(List *names, int nargs)
                namespaceId = InvalidOid;
        }
 
-       /* Search syscache by name and nargs only */
-       catlist = SearchSysCacheList(PROCNAMENSP, 2,
-                                                                CStringGetDatum(funcname),
-                                                                Int16GetDatum(nargs),
-                                                                0, 0);
+       /* Search syscache by name and (optionally) nargs only */
+       if (nargs >= 0)
+               catlist = SearchSysCacheList(PROCNAMENSP, 2,
+                                                                        CStringGetDatum(funcname),
+                                                                        Int16GetDatum(nargs),
+                                                                        0, 0);
+       else
+               catlist = SearchSysCacheList(PROCNAMENSP, 1,
+                                                                        CStringGetDatum(funcname),
+                                                                        0, 0, 0);
 
        for (i = 0; i < catlist->n_members; i++)
        {
@@ -436,6 +444,8 @@ FuncnameGetCandidates(List *names, int nargs)
                int                     pathpos = 0;
                FuncCandidateList newResult;
 
+               nargs = procform->pronargs;
+
                if (OidIsValid(namespaceId))
                {
                        /* Consider only procs in specified namespace */
@@ -478,7 +488,8 @@ FuncnameGetCandidates(List *names, int nargs)
 
                                if (catlist->ordered)
                                {
-                                       if (memcmp(procform->proargtypes, resultList->args,
+                                       if (nargs == resultList->nargs &&
+                                               memcmp(procform->proargtypes, resultList->args,
                                                           nargs * sizeof(Oid)) == 0)
                                                prevResult = resultList;
                                        else
@@ -490,7 +501,8 @@ FuncnameGetCandidates(List *names, int nargs)
                                                 prevResult;
                                                 prevResult = prevResult->next)
                                        {
-                                               if (memcmp(procform->proargtypes, prevResult->args,
+                                               if (nargs == prevResult->nargs &&
+                                                       memcmp(procform->proargtypes, prevResult->args,
                                                                   nargs * sizeof(Oid)) == 0)
                                                        break;
                                        }
@@ -517,6 +529,7 @@ FuncnameGetCandidates(List *names, int nargs)
                                   + nargs * sizeof(Oid));
                newResult->pathpos = pathpos;
                newResult->oid = proctup->t_data->t_oid;
+               newResult->nargs = nargs;
                memcpy(newResult->args, procform->proargtypes, nargs * sizeof(Oid));
 
                newResult->next = resultList;
@@ -533,14 +546,17 @@ FuncnameGetCandidates(List *names, int nargs)
  *             Given a possibly-qualified operator name and operator kind,
  *             retrieve a list of the possible matches.
  *
+ * If oprkind is '\0', we return all operators matching the given name,
+ * regardless of arguments.
+ *
  * We search a single namespace if the operator name is qualified, else
  * all namespaces in the search path.  The return list will never contain
- * multiple entries with identical argument types --- in the multiple-
+ * multiple entries with identical argument lists --- in the multiple-
  * namespace case, we arrange for entries in earlier namespaces to mask
  * identical entries in later namespaces.
  *
  * The returned items always have two args[] entries --- one or the other
- * will be InvalidOid for a prefix or postfix oprkind.
+ * will be InvalidOid for a prefix or postfix oprkind.  nargs is 2, too.
  */
 FuncCandidateList
 OpernameGetCandidates(List *names, char oprkind)
@@ -606,8 +622,8 @@ OpernameGetCandidates(List *names, char oprkind)
                int                     pathpos = 0;
                FuncCandidateList newResult;
 
-               /* Ignore operators of wrong kind */
-               if (operform->oprkind != oprkind)
+               /* Ignore operators of wrong kind, if specific kind requested */
+               if (oprkind && operform->oprkind != oprkind)
                        continue;
 
                if (OidIsValid(namespaceId))
@@ -690,6 +706,7 @@ OpernameGetCandidates(List *names, char oprkind)
                        palloc(sizeof(struct _FuncCandidateList) + sizeof(Oid));
                newResult->pathpos = pathpos;
                newResult->oid = opertup->t_data->t_oid;
+               newResult->nargs = 2;
                newResult->args[0] = operform->oprleft;
                newResult->args[1] = operform->oprright;
                newResult->next = resultList;
index 3a4bb1b..52cb26e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.66 2002/04/16 23:08:10 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.67 2002/04/25 02:56:55 tgl Exp $
  *
  * NOTES
  *       these routines moved here from commands/define.c and somewhat cleaned up.
@@ -138,7 +138,7 @@ OperatorGet(const char *operatorName,
                                                 ObjectIdGetDatum(operatorNamespace));
        if (HeapTupleIsValid(tup))
        {
-               regproc         oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode;
+               RegProcedure oprcode = ((Form_pg_operator) GETSTRUCT(tup))->oprcode;
 
                operatorObjectId = tup->t_data->t_oid;
                *defined = RegProcedureIsValid(oprcode);
@@ -168,7 +168,7 @@ OperatorLookup(List *operatorName,
                           bool *defined)
 {
        Oid                     operatorObjectId;
-       regproc         oprcode;
+       RegProcedure oprcode;
 
        operatorObjectId = LookupOperName(operatorName, leftObjectId,
                                                                          rightObjectId);
index aca5afc..57aad4d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.70 2002/04/11 20:00:00 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.71 2002/04/25 02:56:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -81,8 +81,7 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 
                if (!con->constisnull)
                {
-                       /* We know the source constant is really of type 'text' */
-                       char       *val = DatumGetCString(DirectFunctionCall1(textout,
+                       char       *val = DatumGetCString(DirectFunctionCall1(unknownout,
                                                                                                           con->constvalue));
 
                        newcon->constvalue = stringTypeDatum(targetType, val, atttypmod);
@@ -477,6 +476,11 @@ TypeCategory(Oid inType)
 
                case (OIDOID):
                case (REGPROCOID):
+               case (REGPROCEDUREOID):
+               case (REGOPEROID):
+               case (REGOPERATOROID):
+               case (REGCLASSOID):
+               case (REGTYPEOID):
                case (INT2OID):
                case (INT4OID):
                case (INT8OID):
@@ -540,38 +544,72 @@ TypeCategory(Oid inType)
  * to allow for better type extensibility.
  */
 
+#define TypeIsTextGroup(t) \
+               ((t) == TEXTOID || \
+                (t) == BPCHAROID || \
+                (t) == VARCHAROID)
+
+/* Notice OidGroup is a subset of Int4GroupA */
+#define TypeIsOidGroup(t) \
+               ((t) == OIDOID || \
+                (t) == REGPROCOID || \
+                (t) == REGPROCEDUREOID || \
+                (t) == REGOPEROID || \
+                (t) == REGOPERATOROID || \
+                (t) == REGCLASSOID || \
+                (t) == REGTYPEOID)
+
 /*
- * This macro describes hard-coded knowledge of binary compatibility
- * for built-in types.
+ * INT4 is binary-compatible with many types, but we don't want to allow
+ * implicit coercion directly between, say, OID and AbsTime.  So we subdivide
+ * the categories.
  */
-#define IS_BINARY_COMPATIBLE(a,b) \
-                 (((a) == BPCHAROID && (b) == TEXTOID) \
-               || ((a) == BPCHAROID && (b) == VARCHAROID) \
-               || ((a) == VARCHAROID && (b) == TEXTOID) \
-               || ((a) == VARCHAROID && (b) == BPCHAROID) \
-               || ((a) == TEXTOID && (b) == BPCHAROID) \
-               || ((a) == TEXTOID && (b) == VARCHAROID) \
-               || ((a) == OIDOID && (b) == INT4OID) \
-               || ((a) == OIDOID && (b) == REGPROCOID) \
-               || ((a) == INT4OID && (b) == OIDOID) \
-               || ((a) == INT4OID && (b) == REGPROCOID) \
-               || ((a) == REGPROCOID && (b) == OIDOID) \
-               || ((a) == REGPROCOID && (b) == INT4OID) \
-               || ((a) == ABSTIMEOID && (b) == INT4OID) \
-               || ((a) == INT4OID && (b) == ABSTIMEOID) \
-               || ((a) == RELTIMEOID && (b) == INT4OID) \
-               || ((a) == INT4OID && (b) == RELTIMEOID) \
-               || ((a) == INETOID && (b) == CIDROID) \
-               || ((a) == CIDROID && (b) == INETOID) \
-               || ((a) == BITOID && (b) == VARBITOID) \
-               || ((a) == VARBITOID && (b) == BITOID))
+#define TypeIsInt4GroupA(t) \
+               ((t) == INT4OID || \
+                TypeIsOidGroup(t))
 
-bool
-IsBinaryCompatible(Oid type1, Oid type2)
+#define TypeIsInt4GroupB(t) \
+               ((t) == INT4OID || \
+                (t) == ABSTIMEOID)
+
+#define TypeIsInt4GroupC(t) \
+               ((t) == INT4OID || \
+                (t) == RELTIMEOID)
+
+#define TypeIsInetGroup(t) \
+               ((t) == INETOID || \
+                (t) == CIDROID)
+
+#define TypeIsBitGroup(t) \
+               ((t) == BITOID || \
+                (t) == VARBITOID)
+
+
+static bool
+DirectlyBinaryCompatible(Oid type1, Oid type2)
 {
        if (type1 == type2)
                return true;
-       if (IS_BINARY_COMPATIBLE(type1, type2))
+       if (TypeIsTextGroup(type1) && TypeIsTextGroup(type2))
+               return true;
+       if (TypeIsInt4GroupA(type1) && TypeIsInt4GroupA(type2))
+               return true;
+       if (TypeIsInt4GroupB(type1) && TypeIsInt4GroupB(type2))
+               return true;
+       if (TypeIsInt4GroupC(type1) && TypeIsInt4GroupC(type2))
+               return true;
+       if (TypeIsInetGroup(type1) && TypeIsInetGroup(type2))
+               return true;
+       if (TypeIsBitGroup(type1) && TypeIsBitGroup(type2))
+               return true;
+       return false;
+}
+
+
+bool
+IsBinaryCompatible(Oid type1, Oid type2)
+{
+       if (DirectlyBinaryCompatible(type1, type2))
                return true;
        /*
         * Perhaps the types are domains; if so, look at their base types
@@ -580,9 +618,7 @@ IsBinaryCompatible(Oid type1, Oid type2)
                type1 = getBaseType(type1);
        if (OidIsValid(type2))
                type2 = getBaseType(type2);
-       if (type1 == type2)
-               return true;
-       if (IS_BINARY_COMPATIBLE(type1, type2))
+       if (DirectlyBinaryCompatible(type1, type2))
                return true;
        return false;
 }
@@ -627,7 +663,7 @@ PreferredType(CATEGORY category, Oid type)
                        break;
 
                case (NUMERIC_TYPE):
-                       if (type == OIDOID)
+                       if (TypeIsOidGroup(type))
                                result = OIDOID;
                        else if (type == NUMERICOID)
                                result = NUMERICOID;
index 8a3dc4d..5c02e9b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.62 2002/04/16 23:08:11 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.63 2002/04/25 02:56:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -424,7 +424,8 @@ make_const(Value *value)
                        break;
 
                case T_String:
-                       val = DirectFunctionCall1(textin, CStringGetDatum(strVal(value)));
+                       val = DirectFunctionCall1(unknownin,
+                                                                         CStringGetDatum(strVal(value)));
 
                        typeid = UNKNOWNOID;    /* will be coerced later */
                        typelen = -1;           /* variable len */
index fd28c8f..89be1ba 100644 (file)
@@ -1,51 +1,74 @@
- /*-------------------------------------------------------------------------
+/*-------------------------------------------------------------------------
  *
  * regproc.c
- *       Functions for the built-in type "RegProcedure".
+ *       Functions for the built-in types regproc, regclass, regtype, etc.
+ *
+ * These types are all binary-compatible with type Oid, and rely on Oid
+ * for comparison and so forth.  Their only interesting behavior is in
+ * special I/O conversion routines.
+ *
  *
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.65 2002/04/05 00:31:29 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.66 2002/04/25 02:56:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include <ctype.h>
+
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "catalog/catname.h"
 #include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_operator.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "lib/stringinfo.h"
 #include "miscadmin.h"
+#include "parser/parse_type.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
+static List *stringToQualifiedNameList(const char *string, const char *caller);
+static void parseNameAndArgTypes(const char *string, const char *caller,
+                                                                bool allow_none,
+                                                                List **names, int *nargs, Oid *argtypes);
+
+
 /*****************************************************************************
  *      USER I/O ROUTINES                                                                                                               *
  *****************************************************************************/
 
 /*
- *             regprocin               - converts "proname" or "proid" to proid
+ * regprocin           - converts "proname" to proc OID
  *
- *             We need to accept an OID for cases where the name is ambiguous.
+ * We also accept a numeric OID, mostly for historical reasons.
  *
- *             proid of '-' signifies unknown, for consistency with regprocout
+ * '-' signifies unknown (OID 0).  In all other cases, the input must
+ * match an existing pg_proc entry.
  */
 Datum
 regprocin(PG_FUNCTION_ARGS)
 {
        char       *pro_name_or_oid = PG_GETARG_CSTRING(0);
        RegProcedure result = InvalidOid;
-       int                     matches = 0;
+       List       *names;
+       FuncCandidateList clist;
 
-       if (pro_name_or_oid[0] == '-' && pro_name_or_oid[1] == '\0')
+       /* '-' ? */
+       if (strcmp(pro_name_or_oid, "-") == 0)
                PG_RETURN_OID(InvalidOid);
 
+       /* Numeric OID? */
        if (pro_name_or_oid[0] >= '0' &&
                pro_name_or_oid[0] <= '9' &&
                strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid))
@@ -59,13 +82,23 @@ regprocin(PG_FUNCTION_ARGS)
                                                                                           0, 0, 0);
                if (!RegProcedureIsValid(result))
                        elog(ERROR, "No procedure with oid %s", pro_name_or_oid);
-               matches = 1;
+               PG_RETURN_OID(result);
        }
-       else
+
+       /* Else it's a name, possibly schema-qualified */
+
+       /*
+        * In bootstrap mode we assume the given name is not schema-qualified,
+        * and just search pg_proc for a unique match.  This is needed for
+        * initializing other system catalogs (pg_namespace may not exist yet,
+        * and certainly there are no schemas other than pg_catalog).
+        */
+       if (IsBootstrapProcessingMode())
        {
+               int                     matches = 0;
                Relation        hdesc;
                ScanKeyData skey[1];
-               SysScanDesc     funcscan;
+               SysScanDesc     sysscan;
                HeapTuple       tuple;
 
                ScanKeyEntryInitialize(&skey[0], 0x0,
@@ -74,46 +107,192 @@ regprocin(PG_FUNCTION_ARGS)
                                                           CStringGetDatum(pro_name_or_oid));
 
                hdesc = heap_openr(ProcedureRelationName, AccessShareLock);
+               sysscan = systable_beginscan(hdesc, ProcedureNameNspIndex, true,
+                                                                        SnapshotNow, 1, skey);
 
-               funcscan = systable_beginscan(hdesc, ProcedureNameNspIndex, true,
-                                                                         SnapshotNow, 1, skey);
-
-               while (HeapTupleIsValid(tuple = systable_getnext(funcscan)))
+               while (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
                {
                        result = (RegProcedure) tuple->t_data->t_oid;
                        if (++matches > 1)
                                break;
                }
 
-               systable_endscan(funcscan);
-
+               systable_endscan(sysscan);
                heap_close(hdesc, AccessShareLock);
+
+               if (matches == 0)
+                       elog(ERROR, "No procedure with name %s", pro_name_or_oid);
+               else if (matches > 1)
+                       elog(ERROR, "There is more than one procedure named %s",
+                                pro_name_or_oid);
+               PG_RETURN_OID(result);
        }
 
-       if (matches > 1)
-               elog(ERROR, "There is more than one procedure named %s.\n\tSupply the pg_proc oid inside single quotes.", pro_name_or_oid);
-       else if (matches == 0)
+       /*
+        * Normal case: parse the name into components and see if it
+        * matches any pg_proc entries in the current search path.
+        */
+       names = stringToQualifiedNameList(pro_name_or_oid, "regprocin");
+       clist = FuncnameGetCandidates(names, -1);
+
+       if (clist == NULL)
                elog(ERROR, "No procedure with name %s", pro_name_or_oid);
+       else if (clist->next != NULL)
+               elog(ERROR, "There is more than one procedure named %s",
+                        pro_name_or_oid);
+
+       result = clist->oid;
 
        PG_RETURN_OID(result);
 }
 
 /*
- *             regprocout              - converts proid to "pro_name"
+ * regprocout          - converts proc OID to "pro_name"
  */
 Datum
 regprocout(PG_FUNCTION_ARGS)
 {
        RegProcedure proid = PG_GETARG_OID(0);
-       HeapTuple       proctup;
        char       *result;
+       HeapTuple       proctup;
+
+       if (proid == InvalidOid)
+       {
+               result = pstrdup("-");
+               PG_RETURN_CSTRING(result);
+       }
+
+       proctup = SearchSysCache(PROCOID,
+                                                        ObjectIdGetDatum(proid),
+                                                        0, 0, 0);
+
+       if (HeapTupleIsValid(proctup))
+       {
+               Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
+               char       *proname = NameStr(procform->proname);
+
+               /*
+                * In bootstrap mode, skip the fancy namespace stuff and just
+                * return the proc name.  (This path is only needed for debugging
+                * output anyway.)
+                */
+               if (IsBootstrapProcessingMode())
+               {
+                       result = pstrdup(proname);
+               }
+               else
+               {
+                       char       *nspname;
+                       FuncCandidateList clist;
+
+                       /*
+                        * Would this proc be found (uniquely!) by regprocin?
+                        * If not, qualify it.
+                        */
+                       clist = FuncnameGetCandidates(makeList1(makeString(proname)), -1);
+                       if (clist != NULL && clist->next == NULL &&
+                               clist->oid == proid)
+                               nspname = NULL;
+                       else
+                               nspname = get_namespace_name(procform->pronamespace);
+
+                       result = quote_qualified_identifier(nspname, proname);
+               }
+
+               ReleaseSysCache(proctup);
+       }
+       else
+       {
+               /* If OID doesn't match any pg_proc entry, return it numerically */
+               result = (char *) palloc(NAMEDATALEN);
+               snprintf(result, NAMEDATALEN, "%u", proid);
+       }
+
+       PG_RETURN_CSTRING(result);
+}
 
-       result = (char *) palloc(NAMEDATALEN);
+
+/*
+ * regprocedurein              - converts "proname(args)" to proc OID
+ *
+ * We also accept a numeric OID, mostly for historical reasons.
+ *
+ * '-' signifies unknown (OID 0).  In all other cases, the input must
+ * match an existing pg_proc entry.
+ */
+Datum
+regprocedurein(PG_FUNCTION_ARGS)
+{
+       char       *pro_name_or_oid = PG_GETARG_CSTRING(0);
+       RegProcedure result = InvalidOid;
+       List       *names;
+       int                     nargs;
+       Oid                     argtypes[FUNC_MAX_ARGS];
+       FuncCandidateList clist;
+
+       /* '-' ? */
+       if (strcmp(pro_name_or_oid, "-") == 0)
+               PG_RETURN_OID(InvalidOid);
+
+       /* Numeric OID? */
+       if (pro_name_or_oid[0] >= '0' &&
+               pro_name_or_oid[0] <= '9' &&
+               strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid))
+       {
+               Oid                     searchOid;
+
+               searchOid = DatumGetObjectId(DirectFunctionCall1(oidin,
+                                                                         CStringGetDatum(pro_name_or_oid)));
+               result = (RegProcedure) GetSysCacheOid(PROCOID,
+                                                                                        ObjectIdGetDatum(searchOid),
+                                                                                          0, 0, 0);
+               if (!RegProcedureIsValid(result))
+                       elog(ERROR, "No procedure with oid %s", pro_name_or_oid);
+               PG_RETURN_OID(result);
+       }
+
+       /*
+        * Else it's a name and arguments.  Parse the name and arguments,
+        * look up potential matches in the current namespace search list,
+        * and scan to see which one exactly matches the given argument
+        * types.  (There will not be more than one match.)
+        *
+        * XXX at present, this code will not work in bootstrap mode, hence this
+        * datatype cannot be used for any system column that needs to receive
+        * data during bootstrap.
+        */
+       parseNameAndArgTypes(pro_name_or_oid, "regprocedurein", false,
+                                                &names, &nargs, argtypes);
+
+       clist = FuncnameGetCandidates(names, nargs);
+
+       for (; clist; clist = clist->next)
+       {
+               if (memcmp(clist->args, argtypes, nargs * sizeof(Oid)) == 0)
+                       break;
+       }
+
+       if (clist == NULL)
+               elog(ERROR, "No procedure with name %s", pro_name_or_oid);
+
+       result = clist->oid;
+
+       PG_RETURN_OID(result);
+}
+
+/*
+ * regprocedureout             - converts proc OID to "pro_name(args)"
+ */
+Datum
+regprocedureout(PG_FUNCTION_ARGS)
+{
+       RegProcedure proid = PG_GETARG_OID(0);
+       char       *result;
+       HeapTuple       proctup;
 
        if (proid == InvalidOid)
        {
-               result[0] = '-';
-               result[1] = '\0';
+               result = pstrdup("-");
                PG_RETURN_CSTRING(result);
        }
 
@@ -123,38 +302,862 @@ regprocout(PG_FUNCTION_ARGS)
 
        if (HeapTupleIsValid(proctup))
        {
-               char       *s;
+               Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
+               char       *proname = NameStr(procform->proname);
+               int                     nargs = procform->pronargs;
+               int                     i;
+               char       *nspname;
+               FuncCandidateList clist;
+               StringInfoData buf;
+
+               /* XXX no support here for bootstrap mode */
+
+               /*
+                * Would this proc be found (given the right args) by regprocedurein?
+                * If not, we need to qualify it.
+                */
+               clist = FuncnameGetCandidates(makeList1(makeString(proname)), nargs);
+
+               for (; clist; clist = clist->next)
+               {
+                       if (memcmp(clist->args, procform->proargtypes,
+                                          nargs * sizeof(Oid)) == 0)
+                               break;
+               }
+
+               if (clist != NULL && clist->oid == proid)
+                       nspname = NULL;
+               else
+                       nspname = get_namespace_name(procform->pronamespace);
+
+               initStringInfo(&buf);
+
+               appendStringInfo(&buf, "%s(",
+                                                quote_qualified_identifier(nspname, proname));
+               for (i = 0; i < nargs; i++)
+               {
+                       appendStringInfo(&buf, "%s%s",
+                                                        (i > 0) ? "," : "",
+                                                        format_type_be(procform->proargtypes[i]));
+               }
+
+               appendStringInfo(&buf, ")");
+
+               result = buf.data;
 
-               s = NameStr(((Form_pg_proc) GETSTRUCT(proctup))->proname);
-               StrNCpy(result, s, NAMEDATALEN);
                ReleaseSysCache(proctup);
        }
        else
        {
-               result[0] = '-';
-               result[1] = '\0';
+               /* If OID doesn't match any pg_proc entry, return it numerically */
+               result = (char *) palloc(NAMEDATALEN);
+               snprintf(result, NAMEDATALEN, "%u", proid);
+       }
+
+       PG_RETURN_CSTRING(result);
+}
+
+
+/*
+ * regoperin           - converts "oprname" to operator OID
+ *
+ * We also accept a numeric OID, mostly for historical reasons.
+ *
+ * '0' signifies unknown (OID 0).  In all other cases, the input must
+ * match an existing pg_operator entry.
+ */
+Datum
+regoperin(PG_FUNCTION_ARGS)
+{
+       char       *opr_name_or_oid = PG_GETARG_CSTRING(0);
+       Oid                     result = InvalidOid;
+       List       *names;
+       FuncCandidateList clist;
+
+       /* '0' ? */
+       if (strcmp(opr_name_or_oid, "0") == 0)
+               PG_RETURN_OID(InvalidOid);
+
+       /* Numeric OID? */
+       if (opr_name_or_oid[0] >= '0' &&
+               opr_name_or_oid[0] <= '9' &&
+               strspn(opr_name_or_oid, "0123456789") == strlen(opr_name_or_oid))
+       {
+               Oid                     searchOid;
+
+               searchOid = DatumGetObjectId(DirectFunctionCall1(oidin,
+                                                                         CStringGetDatum(opr_name_or_oid)));
+               result = GetSysCacheOid(OPEROID,
+                                                               ObjectIdGetDatum(searchOid),
+                                                               0, 0, 0);
+               if (!OidIsValid(result))
+                       elog(ERROR, "No operator with oid %s", opr_name_or_oid);
+               PG_RETURN_OID(result);
+       }
+
+       /* Else it's a name, possibly schema-qualified */
+
+       /*
+        * In bootstrap mode we assume the given name is not schema-qualified,
+        * and just search pg_operator for a unique match.  This is needed for
+        * initializing other system catalogs (pg_namespace may not exist yet,
+        * and certainly there are no schemas other than pg_catalog).
+        */
+       if (IsBootstrapProcessingMode())
+       {
+               int                     matches = 0;
+               Relation        hdesc;
+               ScanKeyData skey[1];
+               SysScanDesc     sysscan;
+               HeapTuple       tuple;
+
+               ScanKeyEntryInitialize(&skey[0], 0x0,
+                                                          (AttrNumber) Anum_pg_operator_oprname,
+                                                          (RegProcedure) F_NAMEEQ,
+                                                          CStringGetDatum(opr_name_or_oid));
+
+               hdesc = heap_openr(OperatorRelationName, AccessShareLock);
+               sysscan = systable_beginscan(hdesc, OperatorNameNspIndex, true,
+                                                                        SnapshotNow, 1, skey);
+
+               while (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
+               {
+                       result = tuple->t_data->t_oid;
+                       if (++matches > 1)
+                               break;
+               }
+
+               systable_endscan(sysscan);
+               heap_close(hdesc, AccessShareLock);
+
+               if (matches == 0)
+                       elog(ERROR, "No operator with name %s", opr_name_or_oid);
+               else if (matches > 1)
+                       elog(ERROR, "There is more than one operator named %s",
+                                opr_name_or_oid);
+               PG_RETURN_OID(result);
+       }
+
+       /*
+        * Normal case: parse the name into components and see if it
+        * matches any pg_operator entries in the current search path.
+        */
+       names = stringToQualifiedNameList(opr_name_or_oid, "regoperin");
+       clist = OpernameGetCandidates(names, '\0');
+
+       if (clist == NULL)
+               elog(ERROR, "No operator with name %s", opr_name_or_oid);
+       else if (clist->next != NULL)
+               elog(ERROR, "There is more than one operator named %s",
+                        opr_name_or_oid);
+
+       result = clist->oid;
+
+       PG_RETURN_OID(result);
+}
+
+/*
+ * regoperout          - converts operator OID to "opr_name"
+ */
+Datum
+regoperout(PG_FUNCTION_ARGS)
+{
+       Oid                     oprid = PG_GETARG_OID(0);
+       char       *result;
+       HeapTuple       opertup;
+
+       if (oprid == InvalidOid)
+       {
+               result = pstrdup("0");
+               PG_RETURN_CSTRING(result);
+       }
+
+       opertup = SearchSysCache(OPEROID,
+                                                        ObjectIdGetDatum(oprid),
+                                                        0, 0, 0);
+
+       if (HeapTupleIsValid(opertup))
+       {
+               Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
+               char       *oprname = NameStr(operform->oprname);
+
+               /*
+                * In bootstrap mode, skip the fancy namespace stuff and just
+                * return the oper name.  (This path is only needed for debugging
+                * output anyway.)
+                */
+               if (IsBootstrapProcessingMode())
+               {
+                       result = pstrdup(oprname);
+               }
+               else
+               {
+                       FuncCandidateList clist;
+
+                       /*
+                        * Would this oper be found (uniquely!) by regoperin?
+                        * If not, qualify it.
+                        */
+                       clist = OpernameGetCandidates(makeList1(makeString(oprname)),
+                                                                                 '\0');
+                       if (clist != NULL && clist->next == NULL &&
+                               clist->oid == oprid)
+                               result = pstrdup(oprname);
+                       else
+                       {
+                               const char *nspname;
+
+                               nspname = get_namespace_name(operform->oprnamespace);
+                               nspname = quote_identifier(nspname);
+                               result = (char *) palloc(strlen(nspname)+strlen(oprname)+2);
+                               sprintf(result, "%s.%s", nspname, oprname);
+                       }
+               }
+
+               ReleaseSysCache(opertup);
+       }
+       else
+       {
+               /* If OID doesn't match any pg_operator entry, return it numerically */
+               result = (char *) palloc(NAMEDATALEN);
+               snprintf(result, NAMEDATALEN, "%u", oprid);
+       }
+
+       PG_RETURN_CSTRING(result);
+}
+
+
+/*
+ * regoperatorin               - converts "oprname(args)" to operator OID
+ *
+ * We also accept a numeric OID, mostly for historical reasons.
+ *
+ * '0' signifies unknown (OID 0).  In all other cases, the input must
+ * match an existing pg_operator entry.
+ */
+Datum
+regoperatorin(PG_FUNCTION_ARGS)
+{
+       char       *opr_name_or_oid = PG_GETARG_CSTRING(0);
+       Oid                     result = InvalidOid;
+       List       *names;
+       int                     nargs;
+       Oid                     argtypes[FUNC_MAX_ARGS];
+       char            oprkind;
+       FuncCandidateList clist;
+
+       /* '0' ? */
+       if (strcmp(opr_name_or_oid, "0") == 0)
+               PG_RETURN_OID(InvalidOid);
+
+       /* Numeric OID? */
+       if (opr_name_or_oid[0] >= '0' &&
+               opr_name_or_oid[0] <= '9' &&
+               strspn(opr_name_or_oid, "0123456789") == strlen(opr_name_or_oid))
+       {
+               Oid                     searchOid;
+
+               searchOid = DatumGetObjectId(DirectFunctionCall1(oidin,
+                                                                         CStringGetDatum(opr_name_or_oid)));
+               result = GetSysCacheOid(OPEROID,
+                                                               ObjectIdGetDatum(searchOid),
+                                                               0, 0, 0);
+               if (!OidIsValid(result))
+                       elog(ERROR, "No operator with oid %s", opr_name_or_oid);
+               PG_RETURN_OID(result);
+       }
+
+       /*
+        * Else it's a name and arguments.  Parse the name and arguments,
+        * look up potential matches in the current namespace search list,
+        * and scan to see which one exactly matches the given argument
+        * types.  (There will not be more than one match.)
+        *
+        * XXX at present, this code will not work in bootstrap mode, hence this
+        * datatype cannot be used for any system column that needs to receive
+        * data during bootstrap.
+        */
+       parseNameAndArgTypes(opr_name_or_oid, "regoperatorin", true,
+                                                &names, &nargs, argtypes);
+       if (nargs == 1)
+               elog(ERROR, "regoperatorin: use NONE to denote the missing argument of a unary operator");
+       if (nargs != 2)
+               elog(ERROR, "regoperatorin: provide two argument types for operator");
+
+       if (argtypes[0] == InvalidOid)
+               oprkind = 'l';
+       else if (argtypes[1] == InvalidOid)
+               oprkind = 'r';
+       else
+               oprkind = 'b';
+
+       clist = OpernameGetCandidates(names, oprkind);
+
+       for (; clist; clist = clist->next)
+       {
+               if (memcmp(clist->args, argtypes, 2 * sizeof(Oid)) == 0)
+                       break;
+       }
+
+       if (clist == NULL)
+               elog(ERROR, "No operator with name %s", opr_name_or_oid);
+
+       result = clist->oid;
+
+       PG_RETURN_OID(result);
+}
+
+/*
+ * regoperatorout              - converts operator OID to "opr_name(args)"
+ */
+Datum
+regoperatorout(PG_FUNCTION_ARGS)
+{
+       Oid                     oprid = PG_GETARG_OID(0);
+       char       *result;
+       HeapTuple       opertup;
+
+       if (oprid == InvalidOid)
+       {
+               result = pstrdup("0");
+               PG_RETURN_CSTRING(result);
+       }
+
+       opertup = SearchSysCache(OPEROID,
+                                                        ObjectIdGetDatum(oprid),
+                                                        0, 0, 0);
+
+       if (HeapTupleIsValid(opertup))
+       {
+               Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
+               char       *oprname = NameStr(operform->oprname);
+               char       *nspname;
+               FuncCandidateList clist;
+               StringInfoData buf;
+
+               /* XXX no support here for bootstrap mode */
+
+               /*
+                * Would this oper be found (given the right args) by regoperatorin?
+                * If not, we need to qualify it.
+                */
+               clist = OpernameGetCandidates(makeList1(makeString(oprname)),
+                                                                         operform->oprkind);
+
+               for (; clist; clist = clist->next)
+               {
+                       if (clist->args[0] == operform->oprleft &&
+                               clist->args[1] == operform->oprright)
+                               break;
+               }
+
+               initStringInfo(&buf);
+
+               if (clist == NULL || clist->oid != oprid)
+               {
+                       nspname = get_namespace_name(operform->oprnamespace);
+                       appendStringInfo(&buf, "%s.",
+                                                        quote_identifier(nspname));
+               }
+
+               appendStringInfo(&buf, "%s(", oprname);
+
+               if (operform->oprleft)
+                       appendStringInfo(&buf, "%s,",
+                                                        format_type_be(operform->oprleft));
+               else
+                       appendStringInfo(&buf, "NONE,");
+
+               if (operform->oprright)
+                       appendStringInfo(&buf, "%s)",
+                                                        format_type_be(operform->oprright));
+               else
+                       appendStringInfo(&buf, "NONE)");
+
+               result = buf.data;
+
+               ReleaseSysCache(opertup);
+       }
+       else
+       {
+               /* If OID doesn't match any pg_operator entry, return it numerically */
+               result = (char *) palloc(NAMEDATALEN);
+               snprintf(result, NAMEDATALEN, "%u", oprid);
        }
 
        PG_RETURN_CSTRING(result);
 }
 
 
+/*
+ * regclassin          - converts "classname" to class OID
+ *
+ * We also accept a numeric OID, mostly for historical reasons.
+ *
+ * '-' signifies unknown (OID 0).  In all other cases, the input must
+ * match an existing pg_class entry.
+ */
+Datum
+regclassin(PG_FUNCTION_ARGS)
+{
+       char       *class_name_or_oid = PG_GETARG_CSTRING(0);
+       Oid                     result = InvalidOid;
+       List       *names;
+
+       /* '-' ? */
+       if (strcmp(class_name_or_oid, "-") == 0)
+               PG_RETURN_OID(InvalidOid);
+
+       /* Numeric OID? */
+       if (class_name_or_oid[0] >= '0' &&
+               class_name_or_oid[0] <= '9' &&
+               strspn(class_name_or_oid, "0123456789") == strlen(class_name_or_oid))
+       {
+               Oid                     searchOid;
+
+               searchOid = DatumGetObjectId(DirectFunctionCall1(oidin,
+                                                                         CStringGetDatum(class_name_or_oid)));
+               result = GetSysCacheOid(RELOID,
+                                                               ObjectIdGetDatum(searchOid),
+                                                               0, 0, 0);
+               if (!OidIsValid(result))
+                       elog(ERROR, "No class with oid %s", class_name_or_oid);
+               PG_RETURN_OID(result);
+       }
+
+       /* Else it's a name, possibly schema-qualified */
+
+       /*
+        * In bootstrap mode we assume the given name is not schema-qualified,
+        * and just search pg_class for a match.  This is needed for
+        * initializing other system catalogs (pg_namespace may not exist yet,
+        * and certainly there are no schemas other than pg_catalog).
+        */
+       if (IsBootstrapProcessingMode())
+       {
+               Relation        hdesc;
+               ScanKeyData skey[1];
+               SysScanDesc     sysscan;
+               HeapTuple       tuple;
+
+               ScanKeyEntryInitialize(&skey[0], 0x0,
+                                                          (AttrNumber) Anum_pg_class_relname,
+                                                          (RegProcedure) F_NAMEEQ,
+                                                          CStringGetDatum(class_name_or_oid));
+
+               hdesc = heap_openr(RelationRelationName, AccessShareLock);
+               sysscan = systable_beginscan(hdesc, ClassNameNspIndex, true,
+                                                                        SnapshotNow, 1, skey);
+
+               if (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
+                       result = tuple->t_data->t_oid;
+               else
+                       elog(ERROR, "No class with name %s", class_name_or_oid);
+
+               /* We assume there can be only one match */
+
+               systable_endscan(sysscan);
+               heap_close(hdesc, AccessShareLock);
+
+               PG_RETURN_OID(result);
+       }
+
+       /*
+        * Normal case: parse the name into components and see if it
+        * matches any pg_class entries in the current search path.
+        */
+       names = stringToQualifiedNameList(class_name_or_oid, "regclassin");
+
+       result = RangeVarGetRelid(makeRangeVarFromNameList(names), false);
+
+       PG_RETURN_OID(result);
+}
+
+/*
+ * regclassout         - converts class OID to "class_name"
+ */
+Datum
+regclassout(PG_FUNCTION_ARGS)
+{
+       Oid                     classid = PG_GETARG_OID(0);
+       char       *result;
+       HeapTuple       classtup;
+
+       if (classid == InvalidOid)
+       {
+               result = pstrdup("-");
+               PG_RETURN_CSTRING(result);
+       }
+
+       classtup = SearchSysCache(RELOID,
+                                                         ObjectIdGetDatum(classid),
+                                                         0, 0, 0);
+
+       if (HeapTupleIsValid(classtup))
+       {
+               Form_pg_class classform = (Form_pg_class) GETSTRUCT(classtup);
+               char       *classname = NameStr(classform->relname);
+
+               /*
+                * In bootstrap mode, skip the fancy namespace stuff and just
+                * return the class name.  (This path is only needed for debugging
+                * output anyway.)
+                */
+               if (IsBootstrapProcessingMode())
+               {
+                       result = pstrdup(classname);
+               }
+               else
+               {
+                       char       *nspname;
+
+                       /*
+                        * Would this class be found by regclassin?
+                        * If not, qualify it.
+                        */
+                       if (RelnameGetRelid(classname) == classid)
+                               nspname = NULL;
+                       else
+                               nspname = get_namespace_name(classform->relnamespace);
+
+                       result = quote_qualified_identifier(nspname, classname);
+               }
+
+               ReleaseSysCache(classtup);
+       }
+       else
+       {
+               /* If OID doesn't match any pg_class entry, return it numerically */
+               result = (char *) palloc(NAMEDATALEN);
+               snprintf(result, NAMEDATALEN, "%u", classid);
+       }
+
+       PG_RETURN_CSTRING(result);
+}
+
+
+/*
+ * regtypein           - converts "typename" to type OID
+ *
+ * We also accept a numeric OID, mostly for historical reasons.
+ *
+ * '-' signifies unknown (OID 0).  In all other cases, the input must
+ * match an existing pg_type entry.
+ *
+ * In bootstrap mode the name must just equal some existing name in pg_type.
+ * In normal mode the type name can be specified using the full type syntax
+ * recognized by the parser; for example, DOUBLE PRECISION and INTEGER[] will
+ * work and be translated to the correct type names.  (We ignore any typmod
+ * info generated by the parser, however.)
+ */
+Datum
+regtypein(PG_FUNCTION_ARGS)
+{
+       char       *typ_name_or_oid = PG_GETARG_CSTRING(0);
+       Oid                     result = InvalidOid;
+       int32           typmod;
+
+       /* '-' ? */
+       if (strcmp(typ_name_or_oid, "-") == 0)
+               PG_RETURN_OID(InvalidOid);
+
+       /* Numeric OID? */
+       if (typ_name_or_oid[0] >= '0' &&
+               typ_name_or_oid[0] <= '9' &&
+               strspn(typ_name_or_oid, "0123456789") == strlen(typ_name_or_oid))
+       {
+               Oid                     searchOid;
+
+               searchOid = DatumGetObjectId(DirectFunctionCall1(oidin,
+                                                                         CStringGetDatum(typ_name_or_oid)));
+               result = GetSysCacheOid(TYPEOID,
+                                                               ObjectIdGetDatum(searchOid),
+                                                               0, 0, 0);
+               if (!OidIsValid(result))
+                       elog(ERROR, "No type with oid %s", typ_name_or_oid);
+               PG_RETURN_OID(result);
+       }
+
+       /* Else it's a type name, possibly schema-qualified or decorated */
+
+       /*
+        * In bootstrap mode we assume the given name is not schema-qualified,
+        * and just search pg_type for a match.  This is needed for
+        * initializing other system catalogs (pg_namespace may not exist yet,
+        * and certainly there are no schemas other than pg_catalog).
+        */
+       if (IsBootstrapProcessingMode())
+       {
+               Relation        hdesc;
+               ScanKeyData skey[1];
+               SysScanDesc     sysscan;
+               HeapTuple       tuple;
+
+               ScanKeyEntryInitialize(&skey[0], 0x0,
+                                                          (AttrNumber) Anum_pg_type_typname,
+                                                          (RegProcedure) F_NAMEEQ,
+                                                          CStringGetDatum(typ_name_or_oid));
+
+               hdesc = heap_openr(TypeRelationName, AccessShareLock);
+               sysscan = systable_beginscan(hdesc, TypeNameNspIndex, true,
+                                                                        SnapshotNow, 1, skey);
+
+               if (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
+                       result = tuple->t_data->t_oid;
+               else
+                       elog(ERROR, "No type with name %s", typ_name_or_oid);
+
+               /* We assume there can be only one match */
+
+               systable_endscan(sysscan);
+               heap_close(hdesc, AccessShareLock);
+
+               PG_RETURN_OID(result);
+       }
+
+       /*
+        * Normal case: invoke the full parser to deal with special cases
+        * such as array syntax.
+        */
+       parseTypeString(typ_name_or_oid, &result, &typmod);
+
+       PG_RETURN_OID(result);
+}
+
+/*
+ * regtypeout          - converts type OID to "typ_name"
+ */
+Datum
+regtypeout(PG_FUNCTION_ARGS)
+{
+       Oid                     typid = PG_GETARG_OID(0);
+       char       *result;
+       HeapTuple       typetup;
+
+       if (typid == InvalidOid)
+       {
+               result = pstrdup("-");
+               PG_RETURN_CSTRING(result);
+       }
+
+       typetup = SearchSysCache(TYPEOID,
+                                                        ObjectIdGetDatum(typid),
+                                                        0, 0, 0);
+
+       if (HeapTupleIsValid(typetup))
+       {
+               Form_pg_type typeform = (Form_pg_type) GETSTRUCT(typetup);
+               char       *typname = NameStr(typeform->typname);
+
+               /*
+                * In bootstrap mode, skip the fancy namespace stuff and just
+                * return the type name.  (This path is only needed for debugging
+                * output anyway.)
+                */
+               if (IsBootstrapProcessingMode())
+               {
+                       result = pstrdup(typname);
+               }
+               else
+               {
+                       char       *nspname;
+
+                       /*
+                        * Would this type be found by regtypein?
+                        * If not, qualify it.
+                        *
+                        * XXX shouldn't we use format_type instead?
+                        */
+                       if (TypenameGetTypid(typname) == typid)
+                               nspname = NULL;
+                       else
+                               nspname = get_namespace_name(typeform->typnamespace);
+
+                       result = quote_qualified_identifier(nspname, typname);
+               }
+
+               ReleaseSysCache(typetup);
+       }
+       else
+       {
+               /* If OID doesn't match any pg_type entry, return it numerically */
+               result = (char *) palloc(NAMEDATALEN);
+               snprintf(result, NAMEDATALEN, "%u", typid);
+       }
+
+       PG_RETURN_CSTRING(result);
+}
+
 
 /*****************************************************************************
- *      PUBLIC ROUTINES                                                                                                                 *
+ *      SUPPORT ROUTINES                                                                                                                *
  *****************************************************************************/
 
-/* regproctooid()
- * Lowercase version of RegprocToOid() to allow case-insensitive SQL.
- * Define RegprocToOid() as a macro in builtins.h.
- * Referenced in pg_proc.h. - tgl 97/04/26
+/*
+ * Given a C string, parse it into a qualified-name list.
  */
-Datum
-regproctooid(PG_FUNCTION_ARGS)
+static List *
+stringToQualifiedNameList(const char *string, const char *caller)
 {
-       RegProcedure rp = PG_GETARG_OID(0);
+       char       *rawname;
+       List       *result = NIL;
+       List       *namelist;
+       List       *l;
+
+       /* We need a modifiable copy of the input string. */
+       rawname = pstrdup(string);
+
+       if (!SplitIdentifierString(rawname, '.', &namelist))
+               elog(ERROR, "%s: invalid name syntax", caller);
+
+       if (namelist == NIL)
+               elog(ERROR, "%s: invalid name syntax", caller);
+
+       foreach(l, namelist)
+       {
+               char   *curname = (char *) lfirst(l);
 
-       PG_RETURN_OID((Oid) rp);
+               result = lappend(result, makeString(pstrdup(curname)));
+       }
+
+       pfree(rawname);
+       freeList(namelist);
+
+       return result;
 }
 
-/* (see int.c for comparison/operation routines) */
+/*
+ * Given a C string, parse it into a qualified function or operator name
+ * followed by a parenthesized list of type names.  Reduce the
+ * type names to an array of OIDs (returned into *nargs and *argtypes;
+ * the argtypes array should be of size FUNC_MAX_ARGS).  The function or
+ * operator name is returned to *names as a List of Strings.
+ *
+ * NONE is accepted as a placeholder for OID 0 if allow_none is true.
+ */
+static void
+parseNameAndArgTypes(const char *string, const char *caller, bool allow_none,
+                                        List **names, int *nargs, Oid *argtypes)
+{
+       char       *rawname;
+       char       *ptr;
+       char       *ptr2;
+       char       *typename;
+       bool            in_quote;
+       bool            had_comma;
+       int                     paren_count;
+       Oid                     typeid;
+       int32           typmod;
+
+       /* We need a modifiable copy of the input string. */
+       rawname = pstrdup(string);
+
+       /* Scan to find the expected left paren; mustn't be quoted */
+       in_quote = false;
+       for (ptr = rawname; *ptr; ptr++)
+       {
+               if (*ptr == '"')
+                       in_quote = !in_quote;
+               else if (*ptr == '(' && !in_quote)
+                       break;
+       }
+       if (*ptr == '\0')
+               elog(ERROR, "%s: expected a left parenthesis", caller);
+
+       /* Separate the name and parse it into a list */
+       *ptr++ = '\0';
+       *names = stringToQualifiedNameList(rawname, caller);
+
+       /* Check for the trailing right parenthesis and remove it */
+       ptr2 = ptr + strlen(ptr);
+       while (--ptr2 > ptr)
+       {
+               if (!isspace((unsigned char) *ptr2))
+                       break;
+       }
+       if (*ptr2 != ')')
+               elog(ERROR, "%s: expected a right parenthesis", caller);
+       *ptr2 = '\0';
+
+       /* Separate the remaining string into comma-separated type names */
+       *nargs = 0;
+       had_comma = false;
+
+       for (;;)
+       {
+               /* allow leading whitespace */
+               while (isspace((unsigned char) *ptr))
+                       ptr++;
+               if (*ptr == '\0')
+               {
+                       /* End of string.  Okay unless we had a comma before. */
+                       if (had_comma)
+                               elog(ERROR, "%s: expected a type name", caller);
+                       break;
+               }
+               typename = ptr;
+               /* Find end of type name --- end of string or comma */
+               /* ... but not a quoted or parenthesized comma */
+               in_quote = false;
+               paren_count = 0;
+               for (; *ptr; ptr++)
+               {
+                       if (*ptr == '"')
+                               in_quote = !in_quote;
+                       else if (*ptr == ',' && !in_quote && paren_count == 0)
+                               break;
+                       else if (!in_quote)
+                       {
+                               switch (*ptr)
+                               {
+                                       case '(':
+                                       case '[':
+                                               paren_count++;
+                                               break;
+                                       case ')':
+                                       case ']':
+                                               paren_count--;
+                                               break;
+                               }
+                       }
+               }
+               if (in_quote || paren_count != 0)
+                       elog(ERROR, "%s: improper type name", caller);
+               ptr2 = ptr;
+               if (*ptr == ',')
+               {
+                       had_comma = true;
+                       *ptr++ = '\0';
+               }
+               else
+               {
+                       had_comma = false;
+                       Assert(*ptr == '\0');
+               }
+               /* Lop off trailing whitespace */
+               while (--ptr2 >= typename)
+               {
+                       if (!isspace((unsigned char) *ptr2))
+                               break;
+                       *ptr2 = '\0';
+               }
+
+               if (allow_none && strcasecmp(typename, "none") == 0)
+               {
+                       /* Report NONE as OID 0 */
+                       typeid = InvalidOid;
+                       typmod = -1;
+               }
+               else
+               {
+                       /* Use full parser to resolve the type name */
+                       parseTypeString(typename, &typeid, &typmod);
+               }
+               if (*nargs >= FUNC_MAX_ARGS)
+                       elog(ERROR, "%s: too many argument datatypes", caller);
+               argtypes[*nargs] = typeid;
+               (*nargs)++;
+       }
+
+       pfree(rawname);
+}
index b99c886..580abea 100644 (file)
@@ -3,7 +3,7 @@
  *                             back to source text
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.98 2002/04/19 23:13:54 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.99 2002/04/25 02:56:55 tgl Exp $
  *
  *       This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -123,7 +123,7 @@ static void get_delete_query_def(Query *query, deparse_context *context);
 static void get_utility_query_def(Query *query, deparse_context *context);
 static void get_basic_select_query(Query *query, deparse_context *context);
 static void get_setop_query(Node *setOp, Query *query,
-                               deparse_context *context, bool toplevel);
+                                                       deparse_context *context);
 static void get_rule_sortgroupclause(SortClause *srt, List *tlist,
                                                 bool force_colno,
                                                 deparse_context *context);
@@ -142,7 +142,6 @@ static void get_from_clause_item(Node *jtnode, Query *query,
 static void get_opclass_name(Oid opclass, Oid actual_datatype,
                                 StringInfo buf);
 static bool tleIsArrayAssign(TargetEntry *tle);
-static char *quote_identifier(char *ident);
 static char *get_relid_attribute_name(Oid relid, AttrNumber attnum);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
@@ -1109,7 +1108,7 @@ get_select_query_def(Query *query, deparse_context *context)
         */
        if (query->setOperations)
        {
-               get_setop_query(query->setOperations, query, context, true);
+               get_setop_query(query->setOperations, query, context);
                /* ORDER BY clauses must be simple in this case */
                force_colno = true;
        }
@@ -1266,8 +1265,7 @@ get_basic_select_query(Query *query, deparse_context *context)
 }
 
 static void
-get_setop_query(Node *setOp, Query *query, deparse_context *context,
-                               bool toplevel)
+get_setop_query(Node *setOp, Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
 
@@ -1284,33 +1282,29 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context,
        {
                SetOperationStmt *op = (SetOperationStmt *) setOp;
 
-               /*
-                * Must suppress parens at top level of a setop tree because of
-                * grammar limitations...
-                */
-               if (!toplevel)
-                       appendStringInfo(buf, "(");
-               get_setop_query(op->larg, query, context, false);
+               appendStringInfo(buf, "((");
+               get_setop_query(op->larg, query, context);
                switch (op->op)
                {
                        case SETOP_UNION:
-                               appendStringInfo(buf, " UNION ");
+                               appendStringInfo(buf, ") UNION ");
                                break;
                        case SETOP_INTERSECT:
-                               appendStringInfo(buf, " INTERSECT ");
+                               appendStringInfo(buf, ") INTERSECT ");
                                break;
                        case SETOP_EXCEPT:
-                               appendStringInfo(buf, " EXCEPT ");
+                               appendStringInfo(buf, ") EXCEPT ");
                                break;
                        default:
                                elog(ERROR, "get_setop_query: unexpected set op %d",
                                         (int) op->op);
                }
                if (op->all)
-                       appendStringInfo(buf, "ALL ");
-               get_setop_query(op->rarg, query, context, false);
-               if (!toplevel)
-                       appendStringInfo(buf, ")");
+                       appendStringInfo(buf, "ALL (");
+               else
+                       appendStringInfo(buf, "(");
+               get_setop_query(op->rarg, query, context);
+               appendStringInfo(buf, "))");
        }
        else
        {
@@ -2585,8 +2579,8 @@ tleIsArrayAssign(TargetEntry *tle)
  * space-wasteful but well worth it for notational simplicity.
  * ----------
  */
-static char *
-quote_identifier(char *ident)
+const char *
+quote_identifier(const char *ident)
 {
        /*
         * Can avoid quoting if ident starts with a lowercase letter and
@@ -2603,7 +2597,7 @@ quote_identifier(char *ident)
        safe = (ident[0] >= 'a' && ident[0] <= 'z');
        if (safe)
        {
-               char       *ptr;
+               const char *ptr;
 
                for (ptr = ident + 1; *ptr; ptr++)
                {
@@ -2628,7 +2622,7 @@ quote_identifier(char *ident)
                 * Note: ScanKeywordLookup() does case-insensitive comparison, but
                 * that's fine, since we already know we have all-lower-case.
                 */
-               if (ScanKeywordLookup(ident) != NULL)
+               if (ScanKeywordLookup((char *) ident) != NULL)
                        safe = false;
        }
 
@@ -2641,6 +2635,26 @@ quote_identifier(char *ident)
 }
 
 /* ----------
+ * quote_qualified_identifier  - Quote a possibly-qualified identifier
+ *
+ * Return a name of the form namespace.ident, or just ident if namespace
+ * is NULL, quoting each component if necessary.  The result is palloc'd.
+ * ----------
+ */
+char *
+quote_qualified_identifier(const char *namespace,
+                                                  const char *ident)
+{
+       StringInfoData buf;
+
+       initStringInfo(&buf);
+       if (namespace)
+               appendStringInfo(&buf, "%s.", quote_identifier(namespace));
+       appendStringInfo(&buf, "%s", quote_identifier(ident));
+       return buf.data;
+}
+
+/* ----------
  * get_relid_attribute_name
  *             Get an attribute name by its relations Oid and its attnum
  *
index d045705..8294612 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.109 2002/04/21 19:48:13 thomas Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.110 2002/04/25 02:56:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1991,6 +1991,11 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
                case NUMERICOID:
                case OIDOID:
                case REGPROCOID:
+               case REGPROCEDUREOID:
+               case REGOPEROID:
+               case REGOPERATOROID:
+               case REGCLASSOID:
+               case REGTYPEOID:
                        *scaledvalue = convert_numeric_to_scalar(value, valuetypid);
                        *scaledlobound = convert_numeric_to_scalar(lobound, boundstypid);
                        *scaledhibound = convert_numeric_to_scalar(hibound, boundstypid);
@@ -2088,6 +2093,11 @@ convert_numeric_to_scalar(Datum value, Oid typid)
                                                                                                   value));
                case OIDOID:
                case REGPROCOID:
+               case REGPROCEDUREOID:
+               case REGOPEROID:
+               case REGOPERATOROID:
+               case REGCLASSOID:
+               case REGTYPEOID:
                        /* we can treat OIDs as integers... */
                        return (double) DatumGetObjectId(value);
        }
index e7ac4b3..5c7aaf6 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.84 2002/04/24 02:12:53 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.85 2002/04/25 02:56:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/builtins.h"
 #include "utils/pg_locale.h"
 
+
+typedef struct varlena unknown;
+
+#define DatumGetUnknownP(X)                    ((unknown *) PG_DETOAST_DATUM(X))
+#define PG_GETARG_UNKNOWN_P(n)         DatumGetUnknownP(PG_GETARG_DATUM(n))
+#define PG_RETURN_UNKNOWN_P(x)         PG_RETURN_POINTER(x)
+
 static int     text_cmp(text *arg1, text *arg2);
 
 
index efcb65d..a028d09 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.94 2002/04/06 06:59:22 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.95 2002/04/25 02:56:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -136,8 +136,13 @@ GetCCHashFunc(Oid keytype)
                        return hashint4;
                case TEXTOID:
                        return hashvarlena;
-               case REGPROCOID:
                case OIDOID:
+               case REGPROCOID:
+               case REGPROCEDUREOID:
+               case REGOPEROID:
+               case REGOPERATOROID:
+               case REGCLASSOID:
+               case REGTYPEOID:
                        return hashoid;
                case OIDVECTOROID:
                        return hashoidvector;
index 67e602f..2f163f6 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright 2000 by PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.52 2002/04/24 06:17:04 petere Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.53 2002/04/25 02:56:56 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "describe.h"
@@ -361,7 +361,7 @@ objectDescription(const char *object)
 
        /* Operator descriptions (must get comment via associated function) */
                         "UNION ALL\n"
-                        "  SELECT RegprocToOid(o.oprcode) as oid,\n"
+                        "  SELECT CAST(o.oprcode AS oid) as oid,\n"
                         "  (SELECT oid FROM pg_class WHERE relname = 'pg_proc') as tableoid,\n"
          "  CAST(o.oprname AS text) as name, CAST('%s' AS text) as object\n"
                         "  FROM pg_operator o\n"
index 40abf99..0a8a129 100644 (file)
@@ -12,7 +12,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: c.h,v 1.118 2002/04/24 02:12:53 momjian Exp $
+ * $Id: c.h,v 1.119 2002/04/25 02:56:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -361,9 +361,12 @@ typedef double float8;
 
 /* typedef Oid is in postgres_ext.h */
 
-/* unfortunately, both regproc and RegProcedure are used */
+/*
+ * regproc is the type name used in the include/catalog headers, but
+ * RegProcedure is the preferred name in C code.
+ */
 typedef Oid regproc;
-typedef Oid RegProcedure;
+typedef regproc RegProcedure;
 
 typedef uint32 TransactionId;
 
@@ -404,7 +407,6 @@ struct varlena
  */
 typedef struct varlena bytea;
 typedef struct varlena text;
-typedef struct varlena unknown;
 typedef struct varlena BpChar; /* blank-padded char, ie SQL char(n) */
 typedef struct varlena VarChar; /* var-length char, ie SQL varchar(n) */
 
index e240913..28893a9 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.125 2002/04/24 05:23:14 momjian Exp $
+ * $Id: catversion.h,v 1.126 2002/04/25 02:56:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200204241
+#define CATALOG_VERSION_NO     200204242
 
 #endif
index 148fc21..6509e61 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: namespace.h,v 1.9 2002/04/17 20:57:56 tgl Exp $
+ * $Id: namespace.h,v 1.10 2002/04/25 02:56:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  *     found by namespace lookup.  Each function/operator is identified
  *     by OID and by argument types; the list must be pruned by type
  *     resolution rules that are embodied in the parser, not here.
- *     The number of arguments is assumed to be known a priori.
  */
 typedef struct _FuncCandidateList
 {
        struct _FuncCandidateList *next;
        int                     pathpos;                /* for internal use of namespace lookup */
        Oid                     oid;                    /* the function or operator's OID */
+       int                     nargs;                  /* number of arg types returned */
        Oid                     args[1];                /* arg types --- VARIABLE LENGTH ARRAY */
 } *FuncCandidateList;                  /* VARIABLE LENGTH STRUCT */
 
index 3b7f7d5..9b1e2e7 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_namespace.h,v 1.5 2002/04/21 00:26:43 tgl Exp $
+ * $Id: pg_namespace.h,v 1.6 2002/04/25 02:56:56 tgl Exp $
  *
  * NOTES
  *       the genbki.sh script reads this file and generates .bki
@@ -72,9 +72,9 @@ DESCR("System catalog namespace");
 DATA(insert OID = 99 ( "pg_toast" PGUID "{=}" ));
 DESCR("Reserved namespace for TOAST tables");
 #define PG_TOAST_NAMESPACE 99
-DATA(insert OID = 2071 ( "public" PGUID "{=UC}" ));
+DATA(insert OID = 2200 ( "public" PGUID "{=UC}" ));
 DESCR("Standard public namespace");
-#define PG_PUBLIC_NAMESPACE 2071
+#define PG_PUBLIC_NAMESPACE 2200
 
 
 /*
index 90484c9..d64daa3 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_operator.h,v 1.103 2002/04/21 19:48:23 thomas Exp $
+ * $Id: pg_operator.h,v 1.104 2002/04/25 02:56:56 tgl Exp $
  *
  * NOTES
  *       the genbki.sh script reads this file and generates .bki
@@ -847,6 +847,33 @@ DATA(insert OID = 2066 (  "+"         PGNSP PGUID 0 b t f 1114 1186 1114    0      0 0 0 0
 DATA(insert OID = 2067 (  "-"     PGNSP PGUID 0 b t f 1114 1114 1186    0      0 0 0 0 0 timestamp_mi - - ));
 DATA(insert OID = 2068 (  "-"     PGNSP PGUID 0 b t f 1114 1186 1114    0      0 0 0 0 0 timestamp_mi_span - - ));
 
+/* array equality operators */
+DATA(insert OID = 2222 (  "="     PGNSP PGUID 0 b t f  2207    2207  16  2222 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2223 (  "="     PGNSP PGUID 0 b t f  2208    2208  16  2223 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2224 (  "="     PGNSP PGUID 0 b t f  2209    2209  16  2224 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2225 (  "="     PGNSP PGUID 0 b t f  2210    2210  16  2225 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2226 (  "="     PGNSP PGUID 0 b t f  2211    2211  16  2226 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+
+DATA(insert OID = 2227 (  "="     PGNSP PGUID 0 b t f   629     629  16  2227 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2228 (  "="     PGNSP PGUID 0 b t f   651     651  16  2228 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2229 (  "="     PGNSP PGUID 0 b t f   719     719  16  2229 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2230 (  "="     PGNSP PGUID 0 b t f   791     791  16  2230 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2231 (  "="     PGNSP PGUID 0 b t f  1014    1014  16  2231 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2232 (  "="     PGNSP PGUID 0 b t f  1015    1015  16  2232 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2233 (  "="     PGNSP PGUID 0 b t f  1016    1016  16  2233 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2234 (  "="     PGNSP PGUID 0 b t f  1040    1040  16  2234 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2235 (  "="     PGNSP PGUID 0 b t f  1041    1041  16  2235 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2236 (  "="     PGNSP PGUID 0 b t f  1115    1115  16  2236 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2237 (  "="     PGNSP PGUID 0 b t f  1182    1182  16  2237 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2238 (  "="     PGNSP PGUID 0 b t f  1183    1183  16  2238 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2239 (  "="     PGNSP PGUID 0 b t f  1185    1185  16  2239 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2240 (  "="     PGNSP PGUID 0 b t f  1187    1187  16  2240 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2241 (  "="     PGNSP PGUID 0 b t f  1231    1231  16  2241 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2242 (  "="     PGNSP PGUID 0 b t f  1270    1270  16  2242 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2243 (  "="     PGNSP PGUID 0 b t f  1561    1561  16  2243 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2244 (  "="     PGNSP PGUID 0 b t f  1563    1563  16  2244 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+DATA(insert OID = 2245 (  "="     PGNSP PGUID 0 b t f  2201    2201  16  2245 0  0     0   0   0 array_eq eqsel eqjoinsel ));
+
 
 /*
  * function prototypes
index 3e8f0e2..8fffef5 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.232 2002/04/24 05:22:20 momjian Exp $
+ * $Id: pg_proc.h,v 1.233 2002/04/25 02:56:56 tgl Exp $
  *
  * NOTES
  *       The script catalog/genbki.sh reads this file and generates .bki
@@ -1216,8 +1216,6 @@ DESCR("closest point to line on box");
 
 DATA(insert OID = 964 (  lo_unlink                PGNSP PGUID 12 f t f t f v 1  23 "26" 100 0 0 100  lo_unlink - _null_ ));
 DESCR("large object unlink(delete)");
-DATA(insert OID = 972 (  regproctooid     PGNSP PGUID 12 f t f t f i 1  26 "24" 100 0 0 100  regproctooid - _null_ ));
-DESCR("get oid for regproc");
 
 DATA(insert OID = 973 (  path_inter               PGNSP PGUID 12 f t f t f i 2  16 "602 602" 100 0 0 100  path_inter - _null_ ));
 DESCR("paths intersect?");
@@ -2930,6 +2928,28 @@ DATA(insert OID = 2158 (  stddev                 PGNSP PGUID 12 t t f f f i 1 701 "701" 100 0
 DATA(insert OID = 2159 (  stddev                       PGNSP PGUID 12 t t f f f i 1 1700 "1700" 100 0 0 100  aggregate_dummy - _null_ ));
 
 
+DATA(insert OID = 2212 (  regprocedurein       PGNSP PGUID 12 f t f t f s 1 2202 "0" 100 0 0 100       regprocedurein - _null_ ));
+DESCR("(internal)");
+DATA(insert OID = 2213 (  regprocedureout      PGNSP PGUID 12 f t f t f s 1   23 "0" 100 0 0 100       regprocedureout - _null_ ));
+DESCR("(internal)");
+DATA(insert OID = 2214 (  regoperin                    PGNSP PGUID 12 f t f t f s 1 2203 "0" 100 0 0 100       regoperin - _null_ ));
+DESCR("(internal)");
+DATA(insert OID = 2215 (  regoperout           PGNSP PGUID 12 f t f t f s 1   23 "0" 100 0 0 100       regoperout - _null_ ));
+DESCR("(internal)");
+DATA(insert OID = 2216 (  regoperatorin                PGNSP PGUID 12 f t f t f s 1 2204 "0" 100 0 0 100       regoperatorin - _null_ ));
+DESCR("(internal)");
+DATA(insert OID = 2217 (  regoperatorout       PGNSP PGUID 12 f t f t f s 1   23 "0" 100 0 0 100       regoperatorout - _null_ ));
+DESCR("(internal)");
+DATA(insert OID = 2218 (  regclassin           PGNSP PGUID 12 f t f t f s 1 2205 "0" 100 0 0 100       regclassin - _null_ ));
+DESCR("(internal)");
+DATA(insert OID = 2219 (  regclassout          PGNSP PGUID 12 f t f t f s 1   23 "0" 100 0 0 100       regclassout - _null_ ));
+DESCR("(internal)");
+DATA(insert OID = 2220 (  regtypein                    PGNSP PGUID 12 f t f t f s 1 2206 "0" 100 0 0 100       regtypein - _null_ ));
+DESCR("(internal)");
+DATA(insert OID = 2221 (  regtypeout           PGNSP PGUID 12 f t f t f s 1   23 "0" 100 0 0 100       regtypeout - _null_ ));
+DESCR("(internal)");
+
+
 /*
  * Symbolic values for provolatile column: these indicate whether the result
  * of a function is dependent *only* on the values of its explicit arguments,
index f6d0de6..d67e343 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_type.h,v 1.121 2002/04/24 02:12:53 momjian Exp $
+ * $Id: pg_type.h,v 1.122 2002/04/25 02:56:56 tgl Exp $
  *
  * NOTES
  *       the genbki.sh script reads this file and generates .bki
@@ -274,7 +274,7 @@ DATA(insert OID = 23 (      int4       PGNSP PGUID  4  10 t b t \054 0   0 int4in int4ou
 DESCR("-2 billion to 2 billion integer, 4-byte storage");
 #define INT4OID                        23
 
-DATA(insert OID = 24 ( regproc    PGNSP PGUID  4  16 t b t \054 0   0 regprocin regprocout regprocin regprocout i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 24 ( regproc    PGNSP PGUID  4  -1 t b t \054 0   0 regprocin regprocout regprocin regprocout i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered procedure");
 #define REGPROCOID             24
 
@@ -444,7 +444,7 @@ DESCR("hh:mm:ss, ANSI SQL time");
 DATA(insert OID = 1114 ( timestamp      PGNSP PGUID    8  47 f b t \054 0      0 timestamp_in timestamp_out timestamp_in timestamp_out d p f 0 -1 0 _null_ _null_ ));
 DESCR("date and time");
 #define TIMESTAMPOID   1114
-DATA(insert OID = 1115 ( _timestamp  PGNSP PGUID       -1 -1 f b t \054 0      1184 array_in array_out array_in array_out d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp  PGNSP PGUID       -1 -1 f b t \054 0      1114 array_in array_out array_in array_out d x f 0 -1 0 _null_ _null_ ));
 DATA(insert OID = 1182 ( _date          PGNSP PGUID    -1 -1 f b t \054 0      1082 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ ));
 DATA(insert OID = 1183 ( _time          PGNSP PGUID    -1 -1 f b t \054 0      1083 array_in array_out array_in array_out d x f 0 -1 0 _null_ _null_ ));
 DATA(insert OID = 1184 ( timestamptz PGNSP PGUID       8  47 f b t \054 0      0 timestamptz_in timestamptz_out timestamptz_in timestamptz_out d p f 0 -1 0 _null_ _null_ ));
@@ -484,8 +484,34 @@ DATA(insert OID = 1790 ( refcursor    PGNSP PGUID -1  -1 f b t \054 0  0 textin
 DESCR("reference cursor (portal name)");
 #define REFCURSOROID   1790
 
-/* OIDS 2000 - 2099 */
-DATA(insert OID = 2019 ( _refcursor       PGNSP PGUID -1  -1 f b t \054 0 1790 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ ));
+/* OIDS 2200 - 2299 */
+DATA(insert OID = 2201 ( _refcursor       PGNSP PGUID -1  -1 f b t \054 0 1790 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ ));
+
+DATA(insert OID = 2202 ( regprocedure  PGNSP PGUID  4  -1 t b t \054 0   0 regprocedurein regprocedureout regprocedurein regprocedureout i p f 0 -1 0 _null_ _null_ ));
+DESCR("registered procedure (with args)");
+#define REGPROCEDUREOID        2202
+
+DATA(insert OID = 2203 ( regoper       PGNSP PGUID  4  -1 t b t \054 0   0 regoperin regoperout regoperin regoperout i p f 0 -1 0 _null_ _null_ ));
+DESCR("registered operator");
+#define REGOPEROID             2203
+
+DATA(insert OID = 2204 ( regoperator   PGNSP PGUID  4  -1 t b t \054 0   0 regoperatorin regoperatorout regoperatorin regoperatorout i p f 0 -1 0 _null_ _null_ ));
+DESCR("registered operator (with args)");
+#define REGOPERATOROID 2204
+
+DATA(insert OID = 2205 ( regclass      PGNSP PGUID  4  -1 t b t \054 0   0 regclassin regclassout regclassin regclassout i p f 0 -1 0 _null_ _null_ ));
+DESCR("registered class");
+#define REGCLASSOID            2205
+
+DATA(insert OID = 2206 ( regtype       PGNSP PGUID  4  -1 t b t \054 0   0 regtypein regtypeout regtypein regtypeout i p f 0 -1 0 _null_ _null_ ));
+DESCR("registered type");
+#define REGTYPEOID             2206
+
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1  -1 f b t \054 0 2202 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper      PGNSP PGUID -1  -1 f b t \054 0 2203 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator  PGNSP PGUID -1  -1 f b t \054 0 2204 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass     PGNSP PGUID -1  -1 f b t \054 0 2205 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype      PGNSP PGUID -1  -1 f b t \054 0 2206 array_in array_out array_in array_out i x f 0 -1 0 _null_ _null_ ));
 
 
 /*
index 6280ff2..f5d0b0c 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: fmgr.h,v 1.20 2002/04/24 02:12:53 momjian Exp $
+ * $Id: fmgr.h,v 1.21 2002/04/25 02:56:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -185,7 +185,6 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum,
 /* DatumGetFoo macros for varlena types will typically look like this: */
 #define DatumGetByteaP(X)                      ((bytea *) PG_DETOAST_DATUM(X))
 #define DatumGetTextP(X)                       ((text *) PG_DETOAST_DATUM(X))
-#define DatumGetUnknownP(X)                    ((unknown *) PG_DETOAST_DATUM(X))
 #define DatumGetBpCharP(X)                     ((BpChar *) PG_DETOAST_DATUM(X))
 #define DatumGetVarCharP(X)                    ((VarChar *) PG_DETOAST_DATUM(X))
 /* And we also offer variants that return an OK-to-write copy */
@@ -201,7 +200,6 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum,
 /* GETARG macros for varlena types will typically look like this: */
 #define PG_GETARG_BYTEA_P(n)           DatumGetByteaP(PG_GETARG_DATUM(n))
 #define PG_GETARG_TEXT_P(n)                    DatumGetTextP(PG_GETARG_DATUM(n))
-#define PG_GETARG_UNKNOWN_P(n)         DatumGetUnknownP(PG_GETARG_DATUM(n))
 #define PG_GETARG_BPCHAR_P(n)          DatumGetBpCharP(PG_GETARG_DATUM(n))
 #define PG_GETARG_VARCHAR_P(n)         DatumGetVarCharP(PG_GETARG_DATUM(n))
 /* And we also offer variants that return an OK-to-write copy */
@@ -241,7 +239,6 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum,
 /* RETURN macros for other pass-by-ref types will typically look like this: */
 #define PG_RETURN_BYTEA_P(x)   PG_RETURN_POINTER(x)
 #define PG_RETURN_TEXT_P(x)    PG_RETURN_POINTER(x)
-#define PG_RETURN_UNKNOWN_P(x) PG_RETURN_POINTER(x)
 #define PG_RETURN_BPCHAR_P(x)  PG_RETURN_POINTER(x)
 #define PG_RETURN_VARCHAR_P(x) PG_RETURN_POINTER(x)
 
index 59b1c9d..f386665 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.178 2002/04/24 02:12:53 momjian Exp $
+ * $Id: builtins.h,v 1.179 2002/04/25 02:56:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -329,10 +329,16 @@ extern Datum texticregexne(PG_FUNCTION_ARGS);
 /* regproc.c */
 extern Datum regprocin(PG_FUNCTION_ARGS);
 extern Datum regprocout(PG_FUNCTION_ARGS);
-extern Datum regproctooid(PG_FUNCTION_ARGS);
-
-/* define macro to replace mixed-case function call - tgl 97/04/27 */
-#define RegprocToOid(rp) ((Oid) (rp))
+extern Datum regprocedurein(PG_FUNCTION_ARGS);
+extern Datum regprocedureout(PG_FUNCTION_ARGS);
+extern Datum regoperin(PG_FUNCTION_ARGS);
+extern Datum regoperout(PG_FUNCTION_ARGS);
+extern Datum regoperatorin(PG_FUNCTION_ARGS);
+extern Datum regoperatorout(PG_FUNCTION_ARGS);
+extern Datum regclassin(PG_FUNCTION_ARGS);
+extern Datum regclassout(PG_FUNCTION_ARGS);
+extern Datum regtypein(PG_FUNCTION_ARGS);
+extern Datum regtypeout(PG_FUNCTION_ARGS);
 
 /* ruleutils.c */
 extern Datum pg_get_ruledef(PG_FUNCTION_ARGS);
@@ -349,6 +355,9 @@ extern List *deparse_context_for_plan(int outer_varno, Node *outercontext,
 extern Node *deparse_context_for_relation(const char *aliasname, Oid relid);
 extern Node *deparse_context_for_subplan(const char *name, List *tlist,
                                                                                 List *rtable);
+extern const char *quote_identifier(const char *ident);
+extern char *quote_qualified_identifier(const char *namespace,
+                                                                               const char *ident);
 
 /* tid.c */
 extern void setLastTid(const ItemPointer tid);
index e7712b1..87f8a7a 100644 (file)
 --
 -- NB: run this test earlier than the create_operator test, because
 -- that test creates some bogus operators...
---
--- NOTE hardwired assumptions about standard types:
---                type bool has OID 16
---                type float8 has OID 701
---
 -- **************** pg_proc ****************
 -- Look for illegal values in pg_proc fields.
 -- NOTE: currently there are a few pg_proc entries that have prorettype = 0.
@@ -26,7 +21,7 @@
 SELECT p1.oid, p1.proname
 FROM pg_proc as p1
 WHERE (p1.prolang = 0 OR p1.prorettype = 0 OR
-    p1.pronargs < 0 OR p1.pronargs > 9)
+       p1.pronargs < 0 OR p1.pronargs > 16)
        AND p1.proname !~ '^pl[^_]+_call_handler$'
        AND p1.proname !~ '^RI_FKey_'
        AND p1.proname !~ 'costestimate$'
@@ -196,7 +191,7 @@ WHERE p1.proimplicit AND
                  t.typname = p1.proname) OR
      NOT ((p1.pronargs = 1 AND p1.proargtypes[0] != prorettype) OR
           (p1.pronargs = 2 AND p1.proargtypes[0] = prorettype AND
-           p1.proargtypes[1] = 23)));
+           p1.proargtypes[1] = 'int4'::regtype)));
  oid | proname 
 -----+---------
 (0 rows)
@@ -263,8 +258,8 @@ WHERE p1.oprnegate = p2.oid AND
     (p1.oprkind != p2.oprkind OR
      p1.oprleft != p2.oprleft OR
      p1.oprright != p2.oprright OR
-     p1.oprresult != 16 OR
-     p2.oprresult != 16 OR
+     p1.oprresult != 'bool'::regtype OR
+     p2.oprresult != 'bool'::regtype OR
      p1.oid != p2.oprnegate OR
      p1.oid = p2.oid);
  oid | oprcode | oid | oprcode 
@@ -282,8 +277,8 @@ WHERE p1.oprlsortop = p2.oid AND
      p1.oprkind != 'b' OR p2.oprkind != 'b' OR
      p1.oprleft != p2.oprleft OR
      p1.oprleft != p2.oprright OR
-     p1.oprresult != 16 OR
-     p2.oprresult != 16 OR
+     p1.oprresult != 'bool'::regtype OR
+     p2.oprresult != 'bool'::regtype OR
      p1.oprrsortop = 0);
  oid | oprcode | oid | oprcode 
 -----+---------+-----+---------
@@ -296,8 +291,8 @@ WHERE p1.oprrsortop = p2.oid AND
      p1.oprkind != 'b' OR p2.oprkind != 'b' OR
      p1.oprright != p2.oprleft OR
      p1.oprright != p2.oprright OR
-     p1.oprresult != 16 OR
-     p2.oprresult != 16 OR
+     p1.oprresult != 'bool'::regtype OR
+     p2.oprresult != 'bool'::regtype OR
      p1.oprlsortop = 0);
  oid | oprcode | oid | oprcode 
 -----+---------+-----+---------
@@ -363,8 +358,8 @@ WHERE p1.oprlsortop != p1.oprrsortop AND
 SELECT p1.oid, p1.oprname
 FROM pg_operator AS p1
 WHERE p1.oprcanhash AND NOT
-    (p1.oprkind = 'b' AND p1.oprresult = 16 AND p1.oprleft = p1.oprright AND
-     p1.oprname = '=' AND p1.oprcom = p1.oid);
+    (p1.oprkind = 'b' AND p1.oprresult = 'bool'::regtype AND
+     p1.oprleft = p1.oprright AND p1.oprname = '=' AND p1.oprcom = p1.oid);
  oid  | oprname 
 ------+---------
   353 | =
@@ -448,11 +443,11 @@ WHERE p1.oprcode = p2.oid AND
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprrest = p2.oid AND
-    (p1.oprresult != 16 OR
-     p2.prorettype != 701 OR p2.proretset OR
+    (p1.oprresult != 'bool'::regtype OR
+     p2.prorettype != 'float8'::regtype OR p2.proretset OR
      p2.pronargs != 4 OR
-     p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR
-     p2.proargtypes[2] != 0 OR p2.proargtypes[3] != 23);
+     p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 'oid'::regtype OR
+     p2.proargtypes[2] != 0 OR p2.proargtypes[3] != 'int4'::regtype);
  oid | oprname | oid | proname 
 -----+---------+-----+---------
 (0 rows)
@@ -464,10 +459,10 @@ WHERE p1.oprrest = p2.oid AND
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprjoin = p2.oid AND
-    (p1.oprkind != 'b' OR p1.oprresult != 16 OR
-     p2.prorettype != 701 OR p2.proretset OR
+    (p1.oprkind != 'b' OR p1.oprresult != 'bool'::regtype OR
+     p2.prorettype != 'float8'::regtype OR p2.proretset OR
      p2.pronargs != 3 OR
-     p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR
+     p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 'oid'::regtype OR
      p2.proargtypes[2] != 0);
  oid | oprname | oid | proname 
 -----+---------+-----+---------
@@ -611,7 +606,8 @@ WHERE p2.opcamid = p1.oid AND
 SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname
 FROM pg_amop AS p1, pg_operator AS p2
 WHERE p1.amopopr = p2.oid AND
-    (p2.oprkind != 'b' OR p2.oprresult != 16 OR p2.oprleft != p2.oprright);
+    (p2.oprkind != 'b' OR p2.oprresult != 'bool'::regtype OR
+     p2.oprleft != p2.oprright);
  amopclaid | amopopr | oid | oprname 
 -----------+---------+-----+---------
 (0 rows)
index 4713768..a625054 100644 (file)
@@ -19,7 +19,9 @@ WHERE (p1.typlen <= 0 AND p1.typlen != -1) OR
     (p1.typtype != 'b' AND p1.typtype != 'c') OR
     NOT p1.typisdefined OR
     (p1.typalign != 'c' AND p1.typalign != 's' AND
-     p1.typalign != 'i' AND p1.typalign != 'd');
+     p1.typalign != 'i' AND p1.typalign != 'd') OR
+    (p1.typstorage != 'p' AND p1.typstorage != 'x' AND
+     p1.typstorage != 'e' AND p1.typstorage != 'm');
  oid | typname 
 -----+---------
 (0 rows)
@@ -35,6 +37,15 @@ WHERE p1.typbyval AND
 -----+---------
 (0 rows)
 
+-- Look for "toastable" types that aren't varlena.
+SELECT p1.oid, p1.typname
+FROM pg_type as p1
+WHERE p1.typstorage != 'p' AND
+    (p1.typbyval OR p1.typlen != -1);
+ oid | typname 
+-----+---------
+(0 rows)
+
 -- Look for complex types that do not have a typrelid entry,
 -- or basic types that do.
 SELECT p1.oid, p1.typname
@@ -45,6 +56,31 @@ WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR
 -----+---------
 (0 rows)
 
+-- Look for basic types that don't have an array type.
+-- NOTE: as of 7.3, this check finds SET, smgr, and unknown.
+SELECT p1.oid, p1.typname
+FROM pg_type as p1
+WHERE p1.typtype != 'c' AND p1.typname NOT LIKE '\\_%' AND NOT EXISTS
+    (SELECT 1 FROM pg_type as p2
+     WHERE p2.typname = ('_' || p1.typname)::name AND
+           p2.typelem = p1.oid);
+ oid | typname 
+-----+---------
+  32 | SET
+ 210 | smgr
+ 705 | unknown
+(3 rows)
+
+-- Look for array types that don't have an equality operator.
+SELECT p1.oid, p1.typname
+FROM pg_type as p1
+WHERE p1.typtype != 'c' AND p1.typname LIKE '\\_%' AND NOT EXISTS
+    (SELECT 1 FROM pg_operator
+     WHERE oprname = '=' AND oprleft = p1.oid AND oprright = p1.oid);
+ oid | typname 
+-----+---------
+(0 rows)
+
 -- Conversion routines must be provided except in 'c' entries.
 SELECT p1.oid, p1.typname
 FROM pg_type as p1
@@ -63,7 +99,7 @@ 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 = 'b' AND
     (p2.pronargs != 1 OR p2.proretset) AND
-    (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 23);
+    (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 'int4'::regtype);
  oid | typname | oid | proname 
 -----+---------+-----+---------
 (0 rows)
@@ -89,7 +125,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
 FROM pg_type AS p1, pg_proc AS p2
 WHERE p1.typreceive = p2.oid AND p1.typtype = 'b' AND
     (p2.pronargs != 1 OR p2.proretset) AND
-    (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 23);
+    (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 'int4'::regtype);
  oid | typname | oid | proname 
 -----+---------+-----+---------
 (0 rows)
@@ -154,12 +190,15 @@ WHERE p1.relnatts != (SELECT count(*) FROM pg_attribute AS p2
 (0 rows)
 
 -- Cross-check against pg_type entry
+-- NOTE: we allow attstorage to be 'plain' even when typstorage is not;
+-- this is mainly for toast tables.
 SELECT p1.attrelid, p1.attname, p2.oid, p2.typname
 FROM pg_attribute AS p1, pg_type AS p2
 WHERE p1.atttypid = p2.oid AND
     (p1.attlen != p2.typlen OR
      p1.attalign != p2.typalign OR
-     p1.attbyval != p2.typbyval);
+     p1.attbyval != p2.typbyval OR
+     (p1.attstorage != p2.typstorage AND p1.attstorage != 'p'));
  attrelid | attname | oid | typname 
 ----------+---------+-----+---------
 (0 rows)
index 70565e3..c3fd710 100644 (file)
 --
 -- NB: run this test earlier than the create_operator test, because
 -- that test creates some bogus operators...
---
--- NOTE hardwired assumptions about standard types:
---                type bool has OID 16
---                type float8 has OID 701
---
 
 -- **************** pg_proc ****************
 
@@ -29,7 +24,7 @@
 SELECT p1.oid, p1.proname
 FROM pg_proc as p1
 WHERE (p1.prolang = 0 OR p1.prorettype = 0 OR
-    p1.pronargs < 0 OR p1.pronargs > 9)
+       p1.pronargs < 0 OR p1.pronargs > 16)
        AND p1.proname !~ '^pl[^_]+_call_handler$'
        AND p1.proname !~ '^RI_FKey_'
        AND p1.proname !~ 'costestimate$'
@@ -158,7 +153,7 @@ WHERE p1.proimplicit AND
                  t.typname = p1.proname) OR
      NOT ((p1.pronargs = 1 AND p1.proargtypes[0] != prorettype) OR
           (p1.pronargs = 2 AND p1.proargtypes[0] = prorettype AND
-           p1.proargtypes[1] = 23)));
+           p1.proargtypes[1] = 'int4'::regtype)));
 
 -- **************** pg_operator ****************
 
@@ -216,8 +211,8 @@ WHERE p1.oprnegate = p2.oid AND
     (p1.oprkind != p2.oprkind OR
      p1.oprleft != p2.oprleft OR
      p1.oprright != p2.oprright OR
-     p1.oprresult != 16 OR
-     p2.oprresult != 16 OR
+     p1.oprresult != 'bool'::regtype OR
+     p2.oprresult != 'bool'::regtype OR
      p1.oid != p2.oprnegate OR
      p1.oid = p2.oid);
 
@@ -233,8 +228,8 @@ WHERE p1.oprlsortop = p2.oid AND
      p1.oprkind != 'b' OR p2.oprkind != 'b' OR
      p1.oprleft != p2.oprleft OR
      p1.oprleft != p2.oprright OR
-     p1.oprresult != 16 OR
-     p2.oprresult != 16 OR
+     p1.oprresult != 'bool'::regtype OR
+     p2.oprresult != 'bool'::regtype OR
      p1.oprrsortop = 0);
 
 SELECT p1.oid, p1.oprcode, p2.oid, p2.oprcode
@@ -244,8 +239,8 @@ WHERE p1.oprrsortop = p2.oid AND
      p1.oprkind != 'b' OR p2.oprkind != 'b' OR
      p1.oprright != p2.oprleft OR
      p1.oprright != p2.oprright OR
-     p1.oprresult != 16 OR
-     p2.oprresult != 16 OR
+     p1.oprresult != 'bool'::regtype OR
+     p2.oprresult != 'bool'::regtype OR
      p1.oprlsortop = 0);
 
 -- A mergejoinable = operator must have a commutator (usually itself)
@@ -300,8 +295,8 @@ WHERE p1.oprlsortop != p1.oprrsortop AND
 SELECT p1.oid, p1.oprname
 FROM pg_operator AS p1
 WHERE p1.oprcanhash AND NOT
-    (p1.oprkind = 'b' AND p1.oprresult = 16 AND p1.oprleft = p1.oprright AND
-     p1.oprname = '=' AND p1.oprcom = p1.oid);
+    (p1.oprkind = 'b' AND p1.oprresult = 'bool'::regtype AND
+     p1.oprleft = p1.oprright AND p1.oprname = '=' AND p1.oprcom = p1.oid);
 
 -- In 6.5 we accepted hashable array equality operators when the array element
 -- type is hashable.  However, what we actually need to make hashjoin work on
@@ -372,11 +367,11 @@ WHERE p1.oprcode = p2.oid AND
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprrest = p2.oid AND
-    (p1.oprresult != 16 OR
-     p2.prorettype != 701 OR p2.proretset OR
+    (p1.oprresult != 'bool'::regtype OR
+     p2.prorettype != 'float8'::regtype OR p2.proretset OR
      p2.pronargs != 4 OR
-     p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR
-     p2.proargtypes[2] != 0 OR p2.proargtypes[3] != 23);
+     p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 'oid'::regtype OR
+     p2.proargtypes[2] != 0 OR p2.proargtypes[3] != 'int4'::regtype);
 
 -- If oprjoin is set, the operator must be a binary boolean op,
 -- and it must link to a proc with the right signature
@@ -386,10 +381,10 @@ WHERE p1.oprrest = p2.oid AND
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprjoin = p2.oid AND
-    (p1.oprkind != 'b' OR p1.oprresult != 16 OR
-     p2.prorettype != 701 OR p2.proretset OR
+    (p1.oprkind != 'b' OR p1.oprresult != 'bool'::regtype OR
+     p2.prorettype != 'float8'::regtype OR p2.proretset OR
      p2.pronargs != 3 OR
-     p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR
+     p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 'oid'::regtype OR
      p2.proargtypes[2] != 0);
 
 -- **************** pg_aggregate ****************
@@ -507,7 +502,8 @@ WHERE p2.opcamid = p1.oid AND
 SELECT p1.amopclaid, p1.amopopr, p2.oid, p2.oprname
 FROM pg_amop AS p1, pg_operator AS p2
 WHERE p1.amopopr = p2.oid AND
-    (p2.oprkind != 'b' OR p2.oprresult != 16 OR p2.oprleft != p2.oprright);
+    (p2.oprkind != 'b' OR p2.oprresult != 'bool'::regtype OR
+     p2.oprleft != p2.oprright);
 
 -- Check that all operators linked to by opclass entries have selectivity
 -- estimators.  This is not absolutely required, but it seems a reasonable
index 477696e..ca2d24c 100644 (file)
@@ -22,7 +22,9 @@ WHERE (p1.typlen <= 0 AND p1.typlen != -1) OR
     (p1.typtype != 'b' AND p1.typtype != 'c') OR
     NOT p1.typisdefined OR
     (p1.typalign != 'c' AND p1.typalign != 's' AND
-     p1.typalign != 'i' AND p1.typalign != 'd');
+     p1.typalign != 'i' AND p1.typalign != 'd') OR
+    (p1.typstorage != 'p' AND p1.typstorage != 'x' AND
+     p1.typstorage != 'e' AND p1.typstorage != 'm');
 
 -- Look for "pass by value" types that can't be passed by value.
 
@@ -33,6 +35,13 @@ WHERE p1.typbyval AND
     (p1.typlen != 2 OR p1.typalign != 's') AND
     (p1.typlen != 4 OR p1.typalign != 'i');
 
+-- Look for "toastable" types that aren't varlena.
+
+SELECT p1.oid, p1.typname
+FROM pg_type as p1
+WHERE p1.typstorage != 'p' AND
+    (p1.typbyval OR p1.typlen != -1);
+
 -- Look for complex types that do not have a typrelid entry,
 -- or basic types that do.
 
@@ -41,6 +50,24 @@ FROM pg_type as p1
 WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR
     (p1.typtype != 'c' AND p1.typrelid != 0);
 
+-- Look for basic types that don't have an array type.
+-- NOTE: as of 7.3, this check finds SET, smgr, and unknown.
+
+SELECT p1.oid, p1.typname
+FROM pg_type as p1
+WHERE p1.typtype != 'c' AND p1.typname NOT LIKE '\\_%' AND NOT EXISTS
+    (SELECT 1 FROM pg_type as p2
+     WHERE p2.typname = ('_' || p1.typname)::name AND
+           p2.typelem = p1.oid);
+
+-- Look for array types that don't have an equality operator.
+
+SELECT p1.oid, p1.typname
+FROM pg_type as p1
+WHERE p1.typtype != 'c' AND p1.typname LIKE '\\_%' AND NOT EXISTS
+    (SELECT 1 FROM pg_operator
+     WHERE oprname = '=' AND oprleft = p1.oid AND oprright = p1.oid);
+
 -- Conversion routines must be provided except in 'c' entries.
 
 SELECT p1.oid, p1.typname
@@ -58,7 +85,7 @@ 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 = 'b' AND
     (p2.pronargs != 1 OR p2.proretset) AND
-    (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 23);
+    (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 'int4'::regtype);
 
 -- Check for bogus typoutput routines
 -- The first OR subclause detects bogus non-array cases,
@@ -80,7 +107,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
 FROM pg_type AS p1, pg_proc AS p2
 WHERE p1.typreceive = p2.oid AND p1.typtype = 'b' AND
     (p2.pronargs != 1 OR p2.proretset) AND
-    (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 23);
+    (p2.pronargs != 3 OR p2.proretset OR p2.proargtypes[2] != 'int4'::regtype);
 
 -- Check for bogus typsend routines
 -- The first OR subclause detects bogus non-array cases,
@@ -132,10 +159,13 @@ WHERE p1.relnatts != (SELECT count(*) FROM pg_attribute AS p2
                       WHERE p2.attrelid = p1.oid AND p2.attnum > 0);
 
 -- Cross-check against pg_type entry
+-- NOTE: we allow attstorage to be 'plain' even when typstorage is not;
+-- this is mainly for toast tables.
 
 SELECT p1.attrelid, p1.attname, p2.oid, p2.typname
 FROM pg_attribute AS p1, pg_type AS p2
 WHERE p1.atttypid = p2.oid AND
     (p1.attlen != p2.typlen OR
      p1.attalign != p2.typalign OR
-     p1.attbyval != p2.typbyval);
+     p1.attbyval != p2.typbyval OR
+     (p1.attstorage != p2.typstorage AND p1.attstorage != 'p'));