Remove the "opaque" pseudo-type and associated compatibility hacks.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 5 Mar 2020 20:48:56 +0000 (15:48 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 5 Mar 2020 20:48:56 +0000 (15:48 -0500)
A long time ago, it was necessary to declare datatype I/O functions,
triggers, and language handler support functions in a very type-unsafe
way involving a single pseudo-type "opaque".  We got rid of those
conventions in 7.3, but there was still support in various places to
automatically convert such functions to the modern declaration style,
to be able to transparently re-load dumps from pre-7.3 servers.
It seems unnecessary to continue to support that anymore, so take out
the hacks; whereupon the "opaque" pseudo-type itself is no longer
needed and can be dropped.

This is part of a group of patches removing various server-side kluges
for transparently upgrading pre-8.0 dump files.  Since we've had few
complaints about dropping pg_dump's support for dumping from pre-8.0
servers (commit 64f3524e2), it seems okay to now remove these kluges.

Discussion: https://postgr.es/m/4110.1583255415@sss.pgh.pa.us

22 files changed:
doc/src/sgml/datatype.sgml
doc/src/sgml/ref/create_language.sgml
doc/src/sgml/ref/create_trigger.sgml
doc/src/sgml/ref/create_type.sgml
src/backend/commands/functioncmds.c
src/backend/commands/proclang.c
src/backend/commands/trigger.c
src/backend/commands/typecmds.c
src/backend/utils/adt/pseudotypes.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/include/catalog/catversion.h
src/include/catalog/pg_proc.dat
src/include/catalog/pg_type.dat
src/include/commands/defrem.h
src/pl/plperl/plperl.c
src/pl/plpgsql/src/pl_handler.c
src/pl/plpython/plpy_main.c
src/test/regress/expected/create_type.out
src/test/regress/expected/opr_sanity.out
src/test/regress/sql/create_type.sql
src/test/regress/sql/opr_sanity.sql

index d1d033178ffcd2e4a3c92c3ad12a77b947caac46..410eaedcb7f78be8dc044002db7f794cc3760a09 100644 (file)
@@ -4827,10 +4827,6 @@ SELECT * FROM pg_attribute
     <primary>unknown</primary>
    </indexterm>
 
-   <indexterm zone="datatype-pseudo">
-    <primary>opaque</primary>
-   </indexterm>
-
    <para>
     The <productname>PostgreSQL</productname> type system contains a
     number of special-purpose entries that are collectively called
@@ -4953,12 +4949,6 @@ SELECT * FROM pg_attribute
         <entry>Identifies a not-yet-resolved type, e.g. of an undecorated
          string literal.</entry>
        </row>
-
-       <row>
-        <entry><type>opaque</type></entry>
-        <entry>An obsolete type name that formerly served many of the above
-         purposes.</entry>
-       </row>
       </tbody>
      </tgroup>
     </table>
index af9d115edd54f958186745cc681189a2d15996de..2243ee6a6c6f574a5fd49d6df3b5cc30df460f64 100644 (file)
@@ -211,16 +211,6 @@ CREATE [ OR REPLACE ] [ TRUSTED ] [ PROCEDURAL ] LANGUAGE <replaceable class="pa
    database, which will cause it to be available automatically in
    all subsequently-created databases.
   </para>
-
-  <para>
-   In <productname>PostgreSQL</productname> versions before 7.3, it was
-   necessary to declare handler functions as returning the placeholder
-   type <type>opaque</type>, rather than <type>language_handler</type>.
-   To support loading
-   of old dump files, <command>CREATE LANGUAGE</command> will accept a function
-   declared as returning <type>opaque</type>, but it will issue a notice and
-   change the function's declared return type to <type>language_handler</type>.
-  </para>
  </refsect1>
 
  <refsect1 id="sql-createlanguage-examples">
index 3339a4b4e16a6e7f63330bbf4bc6e728f29e2b3e..3b8f25ea4a5d788a72b2f5283629743ddc577917 100644 (file)
@@ -543,15 +543,6 @@ UPDATE OF <replaceable>column_name1</replaceable> [, <replaceable>column_name2</
    row-level triggers with transition relations cannot be defined on
    partitions or inheritance child tables.
   </para>
-
-  <para>
-   In <productname>PostgreSQL</productname> versions before 7.3, it was
-   necessary to declare trigger functions as returning the placeholder
-   type <type>opaque</type>, rather than <type>trigger</type>.  To support loading
-   of old dump files, <command>CREATE TRIGGER</command> will accept a function
-   declared as returning <type>opaque</type>, but it will issue a notice and
-   change the function's declared return type to <type>trigger</type>.
-  </para>
  </refsect1>
 
  <refsect1 id="sql-createtrigger-examples">
index 175315f3d7e456332a2b9c667773727e2d3e5f46..111f8e65d29d7f7576799ddcb4dea7168feb6aff 100644 (file)
@@ -823,18 +823,6 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
    function is written in C.
   </para>
 
-  <para>
-   In <productname>PostgreSQL</productname> versions before 7.3, it
-   was customary to avoid creating a shell type at all, by replacing the
-   functions' forward references to the type name with the placeholder
-   pseudo-type <type>opaque</type>.  The <type>cstring</type> arguments and
-   results also had to be declared as <type>opaque</type> before 7.3.  To
-   support loading of old dump files, <command>CREATE TYPE</command> will
-   accept I/O functions declared using <type>opaque</type>, but it will issue
-   a notice and change the function declarations to use the correct
-   types.
-  </para>
-
  </refsect1>
 
  <refsect1>
index 6d824a5ebfb64c1198c2f3905612891de0e49aa9..43a23c69af30e42a9636720eb988c3cacdb09f8e 100644 (file)
@@ -1399,93 +1399,6 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
        return address;
 }
 
-/*
- * SetFunctionReturnType - change declared return type of a function
- *
- * This is presently only used for adjusting legacy functions that return
- * OPAQUE to return whatever we find their correct definition should be.
- * The caller should emit a suitable warning explaining what we did.
- */
-void
-SetFunctionReturnType(Oid funcOid, Oid newRetType)
-{
-       Relation        pg_proc_rel;
-       HeapTuple       tup;
-       Form_pg_proc procForm;
-       ObjectAddress func_address;
-       ObjectAddress type_address;
-
-       pg_proc_rel = table_open(ProcedureRelationId, RowExclusiveLock);
-
-       tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(funcOid));
-       if (!HeapTupleIsValid(tup)) /* should not happen */
-               elog(ERROR, "cache lookup failed for function %u", funcOid);
-       procForm = (Form_pg_proc) GETSTRUCT(tup);
-
-       if (procForm->prorettype != OPAQUEOID)  /* caller messed up */
-               elog(ERROR, "function %u doesn't return OPAQUE", funcOid);
-
-       /* okay to overwrite copied tuple */
-       procForm->prorettype = newRetType;
-
-       /* update the catalog and its indexes */
-       CatalogTupleUpdate(pg_proc_rel, &tup->t_self, tup);
-
-       table_close(pg_proc_rel, RowExclusiveLock);
-
-       /*
-        * Also update the dependency to the new type. Opaque is a pinned type, so
-        * there is no old dependency record for it that we would need to remove.
-        */
-       ObjectAddressSet(type_address, TypeRelationId, newRetType);
-       ObjectAddressSet(func_address, ProcedureRelationId, funcOid);
-       recordDependencyOn(&func_address, &type_address, DEPENDENCY_NORMAL);
-}
-
-
-/*
- * SetFunctionArgType - change declared argument type of a function
- *
- * As above, but change an argument's type.
- */
-void
-SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType)
-{
-       Relation        pg_proc_rel;
-       HeapTuple       tup;
-       Form_pg_proc procForm;
-       ObjectAddress func_address;
-       ObjectAddress type_address;
-
-       pg_proc_rel = table_open(ProcedureRelationId, RowExclusiveLock);
-
-       tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(funcOid));
-       if (!HeapTupleIsValid(tup)) /* should not happen */
-               elog(ERROR, "cache lookup failed for function %u", funcOid);
-       procForm = (Form_pg_proc) GETSTRUCT(tup);
-
-       if (argIndex < 0 || argIndex >= procForm->pronargs ||
-               procForm->proargtypes.values[argIndex] != OPAQUEOID)
-               elog(ERROR, "function %u doesn't take OPAQUE", funcOid);
-
-       /* okay to overwrite copied tuple */
-       procForm->proargtypes.values[argIndex] = newArgType;
-
-       /* update the catalog and its indexes */
-       CatalogTupleUpdate(pg_proc_rel, &tup->t_self, tup);
-
-       table_close(pg_proc_rel, RowExclusiveLock);
-
-       /*
-        * Also update the dependency to the new type. Opaque is a pinned type, so
-        * there is no old dependency record for it that we would need to remove.
-        */
-       ObjectAddressSet(type_address, TypeRelationId, newArgType);
-       ObjectAddressSet(func_address, ProcedureRelationId, funcOid);
-       recordDependencyOn(&func_address, &type_address, DEPENDENCY_NORMAL);
-}
-
-
 
 /*
  * CREATE CAST
index 9d72edbfec56ad77e94963172a2397d4b3c52d32..21ceeb7e9a113b539454cd80adb41471dc9dd1b8 100644 (file)
@@ -74,27 +74,10 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
        handlerOid = LookupFuncName(stmt->plhandler, 0, NULL, false);
        funcrettype = get_func_rettype(handlerOid);
        if (funcrettype != LANGUAGE_HANDLEROID)
-       {
-               /*
-                * We allow OPAQUE just so we can load old dump files.  When we see a
-                * handler function declared OPAQUE, change it to LANGUAGE_HANDLER.
-                * (This is probably obsolete and removable?)
-                */
-               if (funcrettype == OPAQUEOID)
-               {
-                       ereport(WARNING,
-                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                        errmsg("changing return type of function %s from %s to %s",
-                                                       NameListToString(stmt->plhandler),
-                                                       "opaque", "language_handler")));
-                       SetFunctionReturnType(handlerOid, LANGUAGE_HANDLEROID);
-               }
-               else
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                        errmsg("function %s must return type %s",
-                                                       NameListToString(stmt->plhandler), "language_handler")));
-       }
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("function %s must return type %s",
+                                               NameListToString(stmt->plhandler), "language_handler")));
 
        /* validate the inline function */
        if (stmt->plinline)
index 35b4bb35106c6ab6cf6498d18d9a813e9e0a5f12..b408efb11ea04b15623264bf3dee4192f4a11c5a 100644 (file)
@@ -694,25 +694,10 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
        }
        funcrettype = get_func_rettype(funcoid);
        if (funcrettype != TRIGGEROID)
-       {
-               /*
-                * We allow OPAQUE just so we can load old dump files.  When we see a
-                * trigger function declared OPAQUE, change it to TRIGGER.
-                */
-               if (funcrettype == OPAQUEOID)
-               {
-                       ereport(WARNING,
-                                       (errmsg("changing return type of function %s from %s to %s",
-                                                       NameListToString(stmt->funcname),
-                                                       "opaque", "trigger")));
-                       SetFunctionReturnType(funcoid, TRIGGEROID);
-               }
-               else
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                        errmsg("function %s must return type %s",
-                                                       NameListToString(stmt->funcname), "trigger")));
-       }
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("function %s must return type %s",
+                                               NameListToString(stmt->funcname), "trigger")));
 
        /*
         * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a
index 99528bf1d58e5b793717524455b3773919ff12d7..d732a3af4506324e487f04c79183ade33adebb51 100644 (file)
@@ -163,7 +163,6 @@ DefineType(ParseState *pstate, List *names, List *parameters)
        char       *array_type;
        Oid                     array_oid;
        Oid                     typoid;
-       Oid                     resulttype;
        ListCell   *pl;
        ObjectAddress address;
 
@@ -196,8 +195,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
 #endif
 
        /*
-        * Look to see if type already exists (presumably as a shell; if not,
-        * TypeCreate will complain).
+        * Look to see if type already exists.
         */
        typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
                                                         CStringGetDatum(typeName),
@@ -211,35 +209,37 @@ DefineType(ParseState *pstate, List *names, List *parameters)
        {
                if (moveArrayTypeName(typoid, typeName, typeNamespace))
                        typoid = InvalidOid;
+               else
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                        errmsg("type \"%s\" already exists", typeName)));
        }
 
        /*
-        * If it doesn't exist, create it as a shell, so that the OID is known for
-        * use in the I/O function definitions.
+        * If this command is a parameterless CREATE TYPE, then we're just here to
+        * make a shell type, so do that (or fail if there already is a shell).
         */
-       if (!OidIsValid(typoid))
+       if (parameters == NIL)
        {
-               address = TypeShellMake(typeName, typeNamespace, GetUserId());
-               typoid = address.objectId;
-               /* Make new shell type visible for modification below */
-               CommandCounterIncrement();
-
-               /*
-                * If the command was a parameterless CREATE TYPE, we're done ---
-                * creating the shell type was all we're supposed to do.
-                */
-               if (parameters == NIL)
-                       return address;
-       }
-       else
-       {
-               /* Complain if dummy CREATE TYPE and entry already exists */
-               if (parameters == NIL)
+               if (OidIsValid(typoid))
                        ereport(ERROR,
                                        (errcode(ERRCODE_DUPLICATE_OBJECT),
                                         errmsg("type \"%s\" already exists", typeName)));
+
+               address = TypeShellMake(typeName, typeNamespace, GetUserId());
+               return address;
        }
 
+       /*
+        * Otherwise, we must already have a shell type, since there is no other
+        * way that the I/O functions could have been created.
+        */
+       if (!OidIsValid(typoid))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                errmsg("type \"%s\" does not exist", typeName),
+                                errhint("Create the type as a shell type, then create its I/O functions, then do a full CREATE TYPE.")));
+
        /* Extract the parameters from the parameter list */
        foreach(pl, parameters)
        {
@@ -444,63 +444,6 @@ DefineType(ParseState *pstate, List *names, List *parameters)
        if (sendName)
                sendOid = findTypeSendFunction(sendName, typoid);
 
-       /*
-        * Verify that I/O procs return the expected thing.  If we see OPAQUE,
-        * complain and change it to the correct type-safe choice.
-        */
-       resulttype = get_func_rettype(inputOid);
-       if (resulttype != typoid)
-       {
-               if (resulttype == OPAQUEOID)
-               {
-                       /* backwards-compatibility hack */
-                       ereport(WARNING,
-                                       (errmsg("changing return type of function %s from %s to %s",
-                                                       NameListToString(inputName), "opaque", typeName)));
-                       SetFunctionReturnType(inputOid, typoid);
-               }
-               else
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                        errmsg("type input function %s must return type %s",
-                                                       NameListToString(inputName), typeName)));
-       }
-       resulttype = get_func_rettype(outputOid);
-       if (resulttype != CSTRINGOID)
-       {
-               if (resulttype == OPAQUEOID)
-               {
-                       /* backwards-compatibility hack */
-                       ereport(WARNING,
-                                       (errmsg("changing return type of function %s from %s to %s",
-                                                       NameListToString(outputName), "opaque", "cstring")));
-                       SetFunctionReturnType(outputOid, CSTRINGOID);
-               }
-               else
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                        errmsg("type output function %s must return type %s",
-                                                       NameListToString(outputName), "cstring")));
-       }
-       if (receiveOid)
-       {
-               resulttype = get_func_rettype(receiveOid);
-               if (resulttype != typoid)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                        errmsg("type receive function %s must return type %s",
-                                                       NameListToString(receiveName), typeName)));
-       }
-       if (sendOid)
-       {
-               resulttype = get_func_rettype(sendOid);
-               if (resulttype != BYTEAOID)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                        errmsg("type send function %s must return type %s",
-                                                       NameListToString(sendName), "bytea")));
-       }
-
        /*
         * Convert typmodin/out function proc names to OIDs.
         */
@@ -1404,16 +1347,9 @@ DefineRange(CreateRangeStmt *stmt)
        }
 
        /*
-        * If it doesn't exist, create it as a shell, so that the OID is known for
-        * use in the range function definitions.
+        * Unlike DefineType(), we don't insist on a shell type existing first, as
+        * it's only needed if the user wants to specify a canonical function.
         */
-       if (!OidIsValid(typoid))
-       {
-               address = TypeShellMake(typeName, typeNamespace, GetUserId());
-               typoid = address.objectId;
-               /* Make new shell type visible for modification below */
-               CommandCounterIncrement();
-       }
 
        /* Extract the parameters from the parameter list */
        foreach(lc, stmt->params)
@@ -1502,8 +1438,15 @@ DefineRange(CreateRangeStmt *stmt)
 
        /* Identify support functions, if provided */
        if (rangeCanonicalName != NIL)
+       {
+               if (!OidIsValid(typoid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                        errmsg("cannot specify a canonical function without a pre-created shell type"),
+                                        errhint("Create the type as a shell type, then create its canonicalization function, then do a full CREATE TYPE.")));
                rangeCanonical = findRangeCanonicalFunction(rangeCanonicalName,
                                                                                                        typoid);
+       }
        else
                rangeCanonical = InvalidOid;
 
@@ -1555,7 +1498,8 @@ DefineRange(CreateRangeStmt *stmt)
                                   0,                   /* Array dimensions of typbasetype */
                                   false,               /* Type NOT NULL */
                                   InvalidOid); /* type's collation (ranges never have one) */
-       Assert(typoid == address.objectId);
+       Assert(typoid == InvalidOid || typoid == address.objectId);
+       typoid = address.objectId;
 
        /* Create the entry in pg_range */
        RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
@@ -1695,63 +1639,32 @@ findTypeInputFunction(List *procname, Oid typeOid)
 
        /*
         * Input functions can take a single argument of type CSTRING, or three
-        * arguments (string, typioparam OID, typmod).
-        *
-        * For backwards compatibility we allow OPAQUE in place of CSTRING; if we
-        * see this, we issue a warning and fix up the pg_proc entry.
+        * arguments (string, typioparam OID, typmod).  They must return the
+        * target type.
         */
        argList[0] = CSTRINGOID;
 
        procOid = LookupFuncName(procname, 1, argList, true);
-       if (OidIsValid(procOid))
-               return procOid;
-
-       argList[1] = OIDOID;
-       argList[2] = INT4OID;
-
-       procOid = LookupFuncName(procname, 3, argList, true);
-       if (OidIsValid(procOid))
-               return procOid;
-
-       /* No luck, try it with OPAQUE */
-       argList[0] = OPAQUEOID;
-
-       procOid = LookupFuncName(procname, 1, argList, true);
-
        if (!OidIsValid(procOid))
        {
                argList[1] = OIDOID;
                argList[2] = INT4OID;
 
                procOid = LookupFuncName(procname, 3, argList, true);
+               if (!OidIsValid(procOid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                        errmsg("function %s does not exist",
+                                                       func_signature_string(procname, 1, NIL, argList))));
        }
 
-       if (OidIsValid(procOid))
-       {
-               /* Found, but must complain and fix the pg_proc entry */
-               ereport(WARNING,
-                               (errmsg("changing argument type of function %s from \"opaque\" to \"cstring\"",
-                                               NameListToString(procname))));
-               SetFunctionArgType(procOid, 0, CSTRINGOID);
-
-               /*
-                * Need CommandCounterIncrement since DefineType will likely try to
-                * alter the pg_proc tuple again.
-                */
-               CommandCounterIncrement();
-
-               return procOid;
-       }
-
-       /* Use CSTRING (preferred) in the error message */
-       argList[0] = CSTRINGOID;
-
-       ereport(ERROR,
-                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
-                        errmsg("function %s does not exist",
-                                       func_signature_string(procname, 1, NIL, argList))));
+       if (get_func_rettype(procOid) != typeOid)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("type input function %s must return type %s",
+                                               NameListToString(procname), format_type_be(typeOid))));
 
-       return InvalidOid;                      /* keep compiler quiet */
+       return procOid;
 }
 
 static Oid
@@ -1761,48 +1674,25 @@ findTypeOutputFunction(List *procname, Oid typeOid)
        Oid                     procOid;
 
        /*
-        * Output functions can take a single argument of the type.
-        *
-        * For backwards compatibility we allow OPAQUE in place of the actual type
-        * name; if we see this, we issue a warning and fix up the pg_proc entry.
+        * Output functions always take a single argument of the type and return
+        * cstring.
         */
        argList[0] = typeOid;
 
        procOid = LookupFuncName(procname, 1, argList, true);
-       if (OidIsValid(procOid))
-               return procOid;
-
-       /* No luck, try it with OPAQUE */
-       argList[0] = OPAQUEOID;
-
-       procOid = LookupFuncName(procname, 1, argList, true);
-
-       if (OidIsValid(procOid))
-       {
-               /* Found, but must complain and fix the pg_proc entry */
-               ereport(WARNING,
-                               (errmsg("changing argument type of function %s from \"opaque\" to %s",
-                                               NameListToString(procname), format_type_be(typeOid))));
-               SetFunctionArgType(procOid, 0, typeOid);
-
-               /*
-                * Need CommandCounterIncrement since DefineType will likely try to
-                * alter the pg_proc tuple again.
-                */
-               CommandCounterIncrement();
-
-               return procOid;
-       }
-
-       /* Use type name, not OPAQUE, in the failure message. */
-       argList[0] = typeOid;
+       if (!OidIsValid(procOid))
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                errmsg("function %s does not exist",
+                                               func_signature_string(procname, 1, NIL, argList))));
 
-       ereport(ERROR,
-                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
-                        errmsg("function %s does not exist",
-                                       func_signature_string(procname, 1, NIL, argList))));
+       if (get_func_rettype(procOid) != CSTRINGOID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("type output function %s must return type %s",
+                                               NameListToString(procname), "cstring")));
 
-       return InvalidOid;                      /* keep compiler quiet */
+       return procOid;
 }
 
 static Oid
@@ -1813,27 +1703,32 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
 
        /*
         * Receive functions can take a single argument of type INTERNAL, or three
-        * arguments (internal, typioparam OID, typmod).
+        * arguments (internal, typioparam OID, typmod).  They must return the
+        * target type.
         */
        argList[0] = INTERNALOID;
 
        procOid = LookupFuncName(procname, 1, argList, true);
-       if (OidIsValid(procOid))
-               return procOid;
-
-       argList[1] = OIDOID;
-       argList[2] = INT4OID;
+       if (!OidIsValid(procOid))
+       {
+               argList[1] = OIDOID;
+               argList[2] = INT4OID;
 
-       procOid = LookupFuncName(procname, 3, argList, true);
-       if (OidIsValid(procOid))
-               return procOid;
+               procOid = LookupFuncName(procname, 3, argList, true);
+               if (!OidIsValid(procOid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                        errmsg("function %s does not exist",
+                                                       func_signature_string(procname, 1, NIL, argList))));
+       }
 
-       ereport(ERROR,
-                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
-                        errmsg("function %s does not exist",
-                                       func_signature_string(procname, 1, NIL, argList))));
+       if (get_func_rettype(procOid) != typeOid)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("type receive function %s must return type %s",
+                                               NameListToString(procname), format_type_be(typeOid))));
 
-       return InvalidOid;                      /* keep compiler quiet */
+       return procOid;
 }
 
 static Oid
@@ -1843,20 +1738,25 @@ findTypeSendFunction(List *procname, Oid typeOid)
        Oid                     procOid;
 
        /*
-        * Send functions can take a single argument of the type.
+        * Send functions always take a single argument of the type and return
+        * bytea.
         */
        argList[0] = typeOid;
 
        procOid = LookupFuncName(procname, 1, argList, true);
-       if (OidIsValid(procOid))
-               return procOid;
+       if (!OidIsValid(procOid))
+               ereport(ERROR,
+                               (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                errmsg("function %s does not exist",
+                                               func_signature_string(procname, 1, NIL, argList))));
 
-       ereport(ERROR,
-                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
-                        errmsg("function %s does not exist",
-                                       func_signature_string(procname, 1, NIL, argList))));
+       if (get_func_rettype(procOid) != BYTEAOID)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("type send function %s must return type %s",
+                                               NameListToString(procname), "bytea")));
 
-       return InvalidOid;                      /* keep compiler quiet */
+       return procOid;
 }
 
 static Oid
index ad0e363c70749097cb80d5fb96de35a4d9732740..4653fc33e6d848fe995e6fd5dedfefbcf28f251b 100644 (file)
@@ -415,7 +415,6 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(fdw_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(index_am_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(tsm_handler);
 PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
-PSEUDOTYPE_DUMMY_IO_FUNCS(opaque);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
 PSEUDOTYPE_DUMMY_IO_FUNCS(table_am_handler);
index caa25e3e758a403db7f0ad26f405be7abaa48073..a12c8d011bcc02b410c65bb617e8b97f04891ab0 100644 (file)
@@ -82,10 +82,9 @@ typedef struct
 
 typedef enum OidOptions
 {
-       zeroAsOpaque = 1,
-       zeroAsAny = 2,
-       zeroAsStar = 4,
-       zeroAsNone = 8
+       zeroIsError = 1,
+       zeroAsStar = 2,
+       zeroAsNone = 4
 } OidOptions;
 
 /* global decls */
@@ -122,8 +121,6 @@ static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
 static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
 
 
-char           g_opaque_type[10];      /* name for the opaque type */
-
 /* placeholders for the delimiters for comments */
 char           g_comment_start[10];
 char           g_comment_end[10];
@@ -404,7 +401,6 @@ main(int argc, char **argv)
 
        strcpy(g_comment_start, "-- ");
        g_comment_end[0] = '\0';
-       strcpy(g_opaque_type, "opaque");
 
        progname = get_progname(argv[0]);
 
@@ -10736,7 +10732,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
        {
                char       *elemType;
 
-               elemType = getFormattedTypeName(fout, tyinfo->typelem, zeroAsOpaque);
+               elemType = getFormattedTypeName(fout, tyinfo->typelem, zeroIsError);
                appendPQExpBuffer(q, ",\n    ELEMENT = %s", elemType);
                free(elemType);
        }
@@ -11547,7 +11543,7 @@ format_function_arguments_old(Archive *fout,
                const char *argname;
 
                typid = allargtypes ? atooid(allargtypes[j]) : finfo->argtypes[j];
-               typname = getFormattedTypeName(fout, typid, zeroAsOpaque);
+               typname = getFormattedTypeName(fout, typid, zeroIsError);
 
                if (argmodes)
                {
@@ -11616,7 +11612,7 @@ format_function_signature(Archive *fout, FuncInfo *finfo, bool honor_quotes)
                        appendPQExpBufferStr(&fn, ", ");
 
                typname = getFormattedTypeName(fout, finfo->argtypes[j],
-                                                                          zeroAsOpaque);
+                                                                          zeroIsError);
                appendPQExpBufferStr(&fn, typname);
                free(typname);
        }
@@ -12021,7 +12017,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
        else
        {
                rettypename = getFormattedTypeName(fout, finfo->prorettype,
-                                                                                  zeroAsOpaque);
+                                                                                  zeroIsError);
                appendPQExpBuffer(q, " RETURNS %s%s",
                                                  (proretset[0] == 't') ? "SETOF " : "",
                                                  rettypename);
@@ -13740,7 +13736,7 @@ format_aggregate_signature(AggInfo *agginfo, Archive *fout, bool honor_quotes)
                        char       *typname;
 
                        typname = getFormattedTypeName(fout, agginfo->aggfn.argtypes[j],
-                                                                                  zeroAsOpaque);
+                                                                                  zeroIsError);
 
                        appendPQExpBuffer(&buf, "%s%s",
                                                          (j > 0) ? ", " : "",
@@ -18363,11 +18359,7 @@ getFormattedTypeName(Archive *fout, Oid oid, OidOptions opts)
 
        if (oid == 0)
        {
-               if ((opts & zeroAsOpaque) != 0)
-                       return pg_strdup(g_opaque_type);
-               else if ((opts & zeroAsAny) != 0)
-                       return pg_strdup("'any'");
-               else if ((opts & zeroAsStar) != 0)
+               if ((opts & zeroAsStar) != 0)
                        return pg_strdup("*");
                else if ((opts & zeroAsNone) != 0)
                        return pg_strdup("NONE");
index 21004e5078b0a439f6750865a32a801fe9ecc645..852020b66c8a68d9742462ff0c36ed775d80ca93 100644 (file)
@@ -640,8 +640,6 @@ typedef struct _extensionMemberId
 extern char g_comment_start[10];
 extern char g_comment_end[10];
 
-extern char g_opaque_type[10]; /* name for the opaque type */
-
 /*
  *     common utility functions
  */
index d4fe84a03793b18f1cc2bc67f50ad07c9b193a04..6ad90bac3ddccdb039cf3f53d8e6ae8a0b2df5bb 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     202003031
+#define CATALOG_VERSION_NO     202003051
 
 #endif
index 07a86c7b7b3fee1a30fea60a6cffb5db59517350..7fb574f9dcc855eb5c2823c62d5cf368f98bf928 100644 (file)
 { oid => '2305', descr => 'I/O',
   proname => 'internal_out', prorettype => 'cstring', proargtypes => 'internal',
   prosrc => 'internal_out' },
-{ oid => '2306', descr => 'I/O',
-  proname => 'opaque_in', proisstrict => 'f', prorettype => 'opaque',
-  proargtypes => 'cstring', prosrc => 'opaque_in' },
-{ oid => '2307', descr => 'I/O',
-  proname => 'opaque_out', prorettype => 'cstring', proargtypes => 'opaque',
-  prosrc => 'opaque_out' },
 { oid => '2312', descr => 'I/O',
   proname => 'anyelement_in', prorettype => 'anyelement',
   proargtypes => 'cstring', prosrc => 'anyelement_in' },
   proname => 'anyelement_out', prorettype => 'cstring',
   proargtypes => 'anyelement', prosrc => 'anyelement_out' },
 { oid => '2398', descr => 'I/O',
-  proname => 'shell_in', proisstrict => 'f', prorettype => 'opaque',
+  proname => 'shell_in', proisstrict => 'f', prorettype => 'void',
   proargtypes => 'cstring', prosrc => 'shell_in' },
 { oid => '2399', descr => 'I/O',
-  proname => 'shell_out', prorettype => 'cstring', proargtypes => 'opaque',
+  proname => 'shell_out', prorettype => 'cstring', proargtypes => 'void',
   prosrc => 'shell_out' },
 { oid => '2597', descr => 'I/O',
   proname => 'domain_in', proisstrict => 'f', provolatile => 's',
index 4cf2b9df7bba8cbc28e5c7f1a93ddf10346cccce..b00597d6ffc5aa2e471dd4bd952812aeb3db9600 100644 (file)
   typtype => 'p', typcategory => 'P', typinput => 'internal_in',
   typoutput => 'internal_out', typreceive => '-', typsend => '-',
   typalign => 'ALIGNOF_POINTER' },
-{ oid => '2282', descr => 'obsolete, deprecated pseudo-type',
-  typname => 'opaque', typlen => '4', typbyval => 't', typtype => 'p',
-  typcategory => 'P', typinput => 'opaque_in', typoutput => 'opaque_out',
-  typreceive => '-', typsend => '-', typalign => 'i' },
 { oid => '2283', descr => 'pseudo-type representing a polymorphic base type',
   typname => 'anyelement', typlen => '4', typbyval => 't', typtype => 'p',
   typcategory => 'P', typinput => 'anyelement_in',
index dede9d788ec92287f8539219fe2399420018e04c..5cd6975a225e5b8a260f046bbdf9090cb73021d3 100644 (file)
@@ -54,8 +54,6 @@ extern Oid    ResolveOpClass(List *opclass, Oid attrType,
 /* commands/functioncmds.c */
 extern ObjectAddress CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt);
 extern void RemoveFunctionById(Oid funcOid);
-extern void SetFunctionReturnType(Oid funcOid, Oid newRetType);
-extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType);
 extern ObjectAddress AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt);
 extern ObjectAddress CreateCast(CreateCastStmt *stmt);
 extern void DropCastById(Oid castOid);
index a65bce07135e5303a2d749b1f06f734b068ef62b..5fdf303fe6b329492d1c56bce98fd920438acb59 100644 (file)
@@ -2000,9 +2000,7 @@ plperl_validator(PG_FUNCTION_ARGS)
        /* except for TRIGGER, EVTTRIGGER, RECORD, or VOID */
        if (functyptype == TYPTYPE_PSEUDO)
        {
-               /* we assume OPAQUE with no arguments means a trigger */
-               if (proc->prorettype == TRIGGEROID ||
-                       (proc->prorettype == OPAQUEOID && proc->pronargs == 0))
+               if (proc->prorettype == TRIGGEROID)
                        is_trigger = true;
                else if (proc->prorettype == EVTTRIGGEROID)
                        is_event_trigger = true;
index b83087e8d2f1ed9dce4993292dfdd1b77d8e57e1..b434818e5415c58560ac61c40e9b649f4fa69e8d 100644 (file)
@@ -421,12 +421,10 @@ plpgsql_validator(PG_FUNCTION_ARGS)
        functyptype = get_typtype(proc->prorettype);
 
        /* Disallow pseudotype result */
-       /* except for TRIGGER, RECORD, VOID, or polymorphic */
+       /* except for TRIGGER, EVTTRIGGER, RECORD, VOID, or polymorphic */
        if (functyptype == TYPTYPE_PSEUDO)
        {
-               /* we assume OPAQUE with no arguments means a trigger */
-               if (proc->prorettype == TRIGGEROID ||
-                       (proc->prorettype == OPAQUEOID && proc->pronargs == 0))
+               if (proc->prorettype == TRIGGEROID)
                        is_dml_trigger = true;
                else if (proc->prorettype == EVTTRIGGEROID)
                        is_event_trigger = true;
index 882d69e14a107ea77ff5b825193b572cd0cf20cd..3eedaa80da7d4909c34ee1e01e987875c169b466 100644 (file)
@@ -379,9 +379,7 @@ plpython2_inline_handler(PG_FUNCTION_ARGS)
 static bool
 PLy_procedure_is_trigger(Form_pg_proc procStruct)
 {
-       return (procStruct->prorettype == TRIGGEROID ||
-                       (procStruct->prorettype == OPAQUEOID &&
-                        procStruct->pronargs == 0));
+       return (procStruct->prorettype == TRIGGEROID);
 }
 
 static void
index 8309756030d444df8f13efded78e8c9f9068988d..eb55e255d6f2e6be7838c0b86585981b4461c571 100644 (file)
@@ -83,8 +83,10 @@ SELECT * FROM default_test;
  zippo | 42
 (1 row)
 
+-- We need a shell type to test some CREATE TYPE failure cases with
+CREATE TYPE bogus_type;
 -- invalid: non-lowercase quoted identifiers
-CREATE TYPE case_int42 (
+CREATE TYPE bogus_type (
        "Internallength" = 4,
        "Input" = int42_in,
        "Output" = int42_out,
@@ -111,6 +113,20 @@ WARNING:  type attribute "Passedbyvalue" not recognized
 LINE 7:  "Passedbyvalue"
          ^
 ERROR:  type input function must be specified
+-- invalid: input/output function incompatibility
+CREATE TYPE bogus_type (INPUT = array_in,
+    OUTPUT = array_out,
+    ELEMENT = int,
+    INTERNALLENGTH = 32);
+ERROR:  type input function array_in must return type bogus_type
+DROP TYPE bogus_type;
+-- It no longer is possible to issue CREATE TYPE without making a shell first
+CREATE TYPE bogus_type (INPUT = array_in,
+    OUTPUT = array_out,
+    ELEMENT = int,
+    INTERNALLENGTH = 32);
+ERROR:  type "bogus_type" does not exist
+HINT:  Create the type as a shell type, then create its I/O functions, then do a full CREATE TYPE.
 -- Test stand-alone composite type
 CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42);
 CREATE FUNCTION get_default_test() RETURNS SETOF default_test_row AS '
@@ -137,28 +153,25 @@ ERROR:  type "text_w_default" already exists
 DROP TYPE default_test_row CASCADE;
 NOTICE:  drop cascades to function get_default_test()
 DROP TABLE default_test;
--- Check type create with input/output incompatibility
-CREATE TYPE not_existing_type (INPUT = array_in,
-    OUTPUT = array_out,
-    ELEMENT = int,
-    INTERNALLENGTH = 32);
-ERROR:  function array_out(not_existing_type) does not exist
--- Check dependency transfer of opaque functions when creating a new type
-CREATE FUNCTION base_fn_in(cstring) RETURNS opaque AS 'boolin'
+-- Check dependencies are established when creating a new type
+CREATE TYPE base_type;
+CREATE FUNCTION base_fn_in(cstring) RETURNS base_type AS 'boolin'
     LANGUAGE internal IMMUTABLE STRICT;
-CREATE FUNCTION base_fn_out(opaque) RETURNS opaque AS 'boolout'
+NOTICE:  return type base_type is only a shell
+CREATE FUNCTION base_fn_out(base_type) RETURNS cstring AS 'boolout'
     LANGUAGE internal IMMUTABLE STRICT;
+NOTICE:  argument type base_type is only a shell
 CREATE TYPE base_type(INPUT = base_fn_in, OUTPUT = base_fn_out);
-WARNING:  changing argument type of function base_fn_out from "opaque" to base_type
-WARNING:  changing return type of function base_fn_in from opaque to base_type
-WARNING:  changing return type of function base_fn_out from opaque to cstring
 DROP FUNCTION base_fn_in(cstring); -- error
 ERROR:  cannot drop function base_fn_in(cstring) because other objects depend on it
 DETAIL:  type base_type depends on function base_fn_in(cstring)
 function base_fn_out(base_type) depends on type base_type
 HINT:  Use DROP ... CASCADE to drop the dependent objects too.
-DROP FUNCTION base_fn_out(opaque); -- error
-ERROR:  function base_fn_out(opaque) does not exist
+DROP FUNCTION base_fn_out(base_type); -- error
+ERROR:  cannot drop function base_fn_out(base_type) because other objects depend on it
+DETAIL:  type base_type depends on function base_fn_out(base_type)
+function base_fn_in(cstring) depends on type base_type
+HINT:  Use DROP ... CASCADE to drop the dependent objects too.
 DROP TYPE base_type; -- error
 ERROR:  cannot drop type base_type because other objects depend on it
 DETAIL:  function base_fn_in(cstring) depends on type base_type
index fb6c029e3d2afad7eb2e4dcc118b6f044e2735f9..40468e8f4972d4b0f70615af3a62d3b6a75ecaa2 100644 (file)
@@ -384,7 +384,7 @@ FROM pg_proc as p1
 WHERE  p1.prorettype = 'cstring'::regtype
     AND NOT EXISTS(SELECT 1 FROM pg_type WHERE typoutput = p1.oid)
     AND NOT EXISTS(SELECT 1 FROM pg_type WHERE typmodout = p1.oid)
-    AND p1.oid != 'shell_out(opaque)'::regprocedure
+    AND p1.oid != 'shell_out(void)'::regprocedure
 ORDER BY 1;
  oid  |   proname    
 ------+--------------
index 3d1deba97cfc2cc2c579ed7351c9dd222de41405..68b04fd4feb88fa8f6f288f577f27ef0bec0cfe3 100644 (file)
@@ -84,8 +84,11 @@ INSERT INTO default_test DEFAULT VALUES;
 
 SELECT * FROM default_test;
 
+-- We need a shell type to test some CREATE TYPE failure cases with
+CREATE TYPE bogus_type;
+
 -- invalid: non-lowercase quoted identifiers
-CREATE TYPE case_int42 (
+CREATE TYPE bogus_type (
        "Internallength" = 4,
        "Input" = int42_in,
        "Output" = int42_out,
@@ -94,6 +97,20 @@ CREATE TYPE case_int42 (
        "Passedbyvalue"
 );
 
+-- invalid: input/output function incompatibility
+CREATE TYPE bogus_type (INPUT = array_in,
+    OUTPUT = array_out,
+    ELEMENT = int,
+    INTERNALLENGTH = 32);
+
+DROP TYPE bogus_type;
+
+-- It no longer is possible to issue CREATE TYPE without making a shell first
+CREATE TYPE bogus_type (INPUT = array_in,
+    OUTPUT = array_out,
+    ELEMENT = int,
+    INTERNALLENGTH = 32);
+
 -- Test stand-alone composite type
 
 CREATE TYPE default_test_row AS (f1 text_w_default, f2 int42);
@@ -119,20 +136,15 @@ DROP TYPE default_test_row CASCADE;
 
 DROP TABLE default_test;
 
--- Check type create with input/output incompatibility
-CREATE TYPE not_existing_type (INPUT = array_in,
-    OUTPUT = array_out,
-    ELEMENT = int,
-    INTERNALLENGTH = 32);
-
--- Check dependency transfer of opaque functions when creating a new type
-CREATE FUNCTION base_fn_in(cstring) RETURNS opaque AS 'boolin'
+-- Check dependencies are established when creating a new type
+CREATE TYPE base_type;
+CREATE FUNCTION base_fn_in(cstring) RETURNS base_type AS 'boolin'
     LANGUAGE internal IMMUTABLE STRICT;
-CREATE FUNCTION base_fn_out(opaque) RETURNS opaque AS 'boolout'
+CREATE FUNCTION base_fn_out(base_type) RETURNS cstring AS 'boolout'
     LANGUAGE internal IMMUTABLE STRICT;
 CREATE TYPE base_type(INPUT = base_fn_in, OUTPUT = base_fn_out);
 DROP FUNCTION base_fn_in(cstring); -- error
-DROP FUNCTION base_fn_out(opaque); -- error
+DROP FUNCTION base_fn_out(base_type); -- error
 DROP TYPE base_type; -- error
 DROP TYPE base_type CASCADE;
 
index 8351b6469abe2f23de5b3a0c349a5bf0f4dd87a8..f06f245db3b3e2abacb8e7c76176669d96f8ad48 100644 (file)
@@ -309,7 +309,7 @@ FROM pg_proc as p1
 WHERE  p1.prorettype = 'cstring'::regtype
     AND NOT EXISTS(SELECT 1 FROM pg_type WHERE typoutput = p1.oid)
     AND NOT EXISTS(SELECT 1 FROM pg_type WHERE typmodout = p1.oid)
-    AND p1.oid != 'shell_out(opaque)'::regprocedure
+    AND p1.oid != 'shell_out(void)'::regprocedure
 ORDER BY 1;
 
 -- Check for length inconsistencies between the various argument-info arrays.