Thread: Distinct types

Distinct types

From
Peter Eisentraut
Date:
Here is an implementation of distinct types, known from SQL99 and
beyond.  They are like domains, except that they don't have defaults or
constraints and they do not allow implicit casting to their base type.
The latter aspect is what makes them distinct types.  They are useful to
create more type-safe database schemas, to prevent using generic types
such as text or int for everything and then mixing them in inappropriate
ways.  This is something domains are not useful for.  Much of the
internals are shared with domains nevertheless.  The difference is
really only the casting behavior.

To create a distinct type, just run

CREATE TYPE mystring AS text;
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/commands/typecmds.c ./src/backend/commands/typecmds.c
*** ../cvs-pgsql/src/backend/commands/typecmds.c    2008-10-21 13:52:23.000000000 +0300
--- ./src/backend/commands/typecmds.c    2008-10-31 13:50:50.000000000 +0200
*************** RemoveTypeById(Oid typeOid)
*** 648,654 ****

  /*
   * DefineDomain
!  *        Registers a new domain.
   */
  void
  DefineDomain(CreateDomainStmt *stmt)
--- 648,654 ----

  /*
   * DefineDomain
!  *        Registers a new domain or distinct type.
   */
  void
  DefineDomain(CreateDomainStmt *stmt)
*************** DefineDomain(CreateDomainStmt *stmt)
*** 721,738 ****
      basetypeoid = HeapTupleGetOid(typeTup);

      /*
!      * Base type must be a plain base type, another domain or an enum. Domains
       * over pseudotypes would create a security hole.  Domains over composite
       * types might be made to work in the future, but not today.
       */
      typtype = baseType->typtype;
      if (typtype != TYPTYPE_BASE &&
          typtype != TYPTYPE_DOMAIN &&
          typtype != TYPTYPE_ENUM)
!         ereport(ERROR,
!                 (errcode(ERRCODE_DATATYPE_MISMATCH),
!                  errmsg("\"%s\" is not a valid base type for a domain",
!                         TypeNameToString(stmt->typename))));

      /* passed by value */
      byValue = baseType->typbyval;
--- 721,747 ----
      basetypeoid = HeapTupleGetOid(typeTup);

      /*
!      * Base type must be a plain base type, another domain, distinct type, or an enum. Domains
       * over pseudotypes would create a security hole.  Domains over composite
       * types might be made to work in the future, but not today.
       */
      typtype = baseType->typtype;
      if (typtype != TYPTYPE_BASE &&
          typtype != TYPTYPE_DOMAIN &&
+         typtype != TYPTYPE_DISTINCT &&
          typtype != TYPTYPE_ENUM)
!     {
!         if (stmt->distinct_type)
!             ereport(ERROR,
!                     (errcode(ERRCODE_DATATYPE_MISMATCH),
!                      errmsg("\"%s\" is not a valid base type for a distinct type",
!                             TypeNameToString(stmt->typename))));
!         else
!             ereport(ERROR,
!                     (errcode(ERRCODE_DATATYPE_MISMATCH),
!                      errmsg("\"%s\" is not a valid base type for a domain",
!                             TypeNameToString(stmt->typename))));
!     }

      /* passed by value */
      byValue = baseType->typbyval;
*************** DefineDomain(CreateDomainStmt *stmt)
*** 938,944 ****
                     InvalidOid,    /* relation oid (n/a here) */
                     0,            /* relation kind (ditto) */
                     internalLength,        /* internal size */
!                    TYPTYPE_DOMAIN,        /* type-type (domain type) */
                     category,    /* type-category */
                     false,        /* domain types are never preferred */
                     delimiter,    /* array element delimiter */
--- 947,953 ----
                     InvalidOid,    /* relation oid (n/a here) */
                     0,            /* relation kind (ditto) */
                     internalLength,        /* internal size */
!                    stmt->distinct_type ? TYPTYPE_DISTINCT : TYPTYPE_DOMAIN,        /* type-type (distinct type or
domain)*/ 
                     category,    /* type-category */
                     false,        /* domain types are never preferred */
                     delimiter,    /* array element delimiter */
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/executor/functions.c ./src/backend/executor/functions.c
*** ../cvs-pgsql/src/backend/executor/functions.c    2008-08-26 01:42:32.000000000 +0300
--- ./src/backend/executor/functions.c    2008-10-30 14:46:05.000000000 +0200
*************** check_sql_fn_retval(Oid func_id, Oid ret
*** 924,929 ****
--- 924,930 ----
      fn_typtype = get_typtype(rettype);

      if (fn_typtype == TYPTYPE_BASE ||
+         fn_typtype == TYPTYPE_DISTINCT ||
          fn_typtype == TYPTYPE_DOMAIN ||
          fn_typtype == TYPTYPE_ENUM ||
          rettype == VOIDOID)
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/nodes/copyfuncs.c ./src/backend/nodes/copyfuncs.c
*** ../cvs-pgsql/src/backend/nodes/copyfuncs.c    2008-10-22 14:35:44.000000000 +0300
--- ./src/backend/nodes/copyfuncs.c    2008-10-30 14:46:05.000000000 +0200
*************** _copyCreateDomainStmt(CreateDomainStmt *
*** 2657,2662 ****
--- 2657,2663 ----
      COPY_NODE_FIELD(domainname);
      COPY_NODE_FIELD(typename);
      COPY_NODE_FIELD(constraints);
+     COPY_SCALAR_FIELD(distinct_type);

      return newnode;
  }
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/nodes/equalfuncs.c ./src/backend/nodes/equalfuncs.c
*** ../cvs-pgsql/src/backend/nodes/equalfuncs.c    2008-10-22 14:35:44.000000000 +0300
--- ./src/backend/nodes/equalfuncs.c    2008-10-30 14:46:05.000000000 +0200
*************** _equalCreateDomainStmt(CreateDomainStmt
*** 1342,1347 ****
--- 1342,1348 ----
      COMPARE_NODE_FIELD(domainname);
      COMPARE_NODE_FIELD(typename);
      COMPARE_NODE_FIELD(constraints);
+     COMPARE_SCALAR_FIELD(distinct_type);

      return true;
  }
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/parser/gram.y ./src/backend/parser/gram.y
*** ../cvs-pgsql/src/backend/parser/gram.y    2008-10-29 13:37:47.000000000 +0200
--- ./src/backend/parser/gram.y    2008-10-30 14:46:05.000000000 +0200
*************** CreateDomainStmt:
*** 5608,5613 ****
--- 5608,5623 ----
                      n->domainname = $3;
                      n->typename = $5;
                      n->constraints = $6;
+                     n->distinct_type = FALSE;
+                     $$ = (Node *)n;
+                 }
+             | CREATE TYPE_P any_name AS Typename
+                 {
+                     CreateDomainStmt *n = makeNode(CreateDomainStmt);
+                     n->domainname = $3;
+                     n->typename = $5;
+                     n->constraints = NIL;
+                     n->distinct_type = TRUE;
                      $$ = (Node *)n;
                  }
          ;
*************** unreserved_keyword:
*** 9439,9445 ****
              | ENABLE_P
              | ENCODING
              | ENCRYPTED
-             | ENUM_P
              | ESCAPE
              | EXCLUDING
              | EXCLUSIVE
--- 9449,9454 ----
*************** col_name_keyword:
*** 9626,9631 ****
--- 9635,9641 ----
              | COALESCE
              | DEC
              | DECIMAL_P
+             | ENUM_P
              | EXISTS
              | EXTRACT
              | FLOAT_P
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/parser/keywords.c ./src/backend/parser/keywords.c
*** ../cvs-pgsql/src/backend/parser/keywords.c    2008-10-28 14:31:25.000000000 +0200
--- ./src/backend/parser/keywords.c    2008-10-30 14:46:05.000000000 +0200
*************** const ScanKeyword ScanKeywords[] = {
*** 157,163 ****
      {"encoding", ENCODING, UNRESERVED_KEYWORD},
      {"encrypted", ENCRYPTED, UNRESERVED_KEYWORD},
      {"end", END_P, RESERVED_KEYWORD},
!     {"enum", ENUM_P, UNRESERVED_KEYWORD},
      {"escape", ESCAPE, UNRESERVED_KEYWORD},
      {"except", EXCEPT, RESERVED_KEYWORD},
      {"excluding", EXCLUDING, UNRESERVED_KEYWORD},
--- 157,163 ----
      {"encoding", ENCODING, UNRESERVED_KEYWORD},
      {"encrypted", ENCRYPTED, UNRESERVED_KEYWORD},
      {"end", END_P, RESERVED_KEYWORD},
!     {"enum", ENUM_P, COL_NAME_KEYWORD},
      {"escape", ESCAPE, UNRESERVED_KEYWORD},
      {"except", EXCEPT, RESERVED_KEYWORD},
      {"excluding", EXCLUDING, UNRESERVED_KEYWORD},
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/parser/parse_coerce.c ./src/backend/parser/parse_coerce.c
*** ../cvs-pgsql/src/backend/parser/parse_coerce.c    2008-10-27 10:14:16.000000000 +0200
--- ./src/backend/parser/parse_coerce.c    2008-10-31 13:52:30.000000000 +0200
*************** find_coercion_pathway(Oid targetTypeId,
*** 1867,1881 ****
      *funcid = InvalidOid;

      /* Perhaps the types are domains; if so, look at their base types */
!     if (OidIsValid(sourceTypeId))
          sourceTypeId = getBaseType(sourceTypeId);
!     if (OidIsValid(targetTypeId))
          targetTypeId = getBaseType(targetTypeId);

      /* Domains are always coercible to and from their base type */
      if (sourceTypeId == targetTypeId)
          return COERCION_PATH_RELABELTYPE;

      /* Look in pg_cast */
      tuple = SearchSysCache(CASTSOURCETARGET,
                             ObjectIdGetDatum(sourceTypeId),
--- 1867,1891 ----
      *funcid = InvalidOid;

      /* Perhaps the types are domains; if so, look at their base types */
!     if (OidIsValid(sourceTypeId) && get_typtype(sourceTypeId) == TYPTYPE_DOMAIN)
          sourceTypeId = getBaseType(sourceTypeId);
!     if (OidIsValid(targetTypeId) && get_typtype(targetTypeId) == TYPTYPE_DOMAIN)
          targetTypeId = getBaseType(targetTypeId);

      /* Domains are always coercible to and from their base type */
      if (sourceTypeId == targetTypeId)
          return COERCION_PATH_RELABELTYPE;

+     /* Distinct types are castable AS ASSIGNMENT to and from their base types */
+     if (ccontext >= COERCION_ASSIGNMENT
+         && ((OidIsValid(sourceTypeId)
+              && get_typtype(sourceTypeId) == TYPTYPE_DISTINCT
+              && getDirectBaseType(sourceTypeId) == targetTypeId)
+             || (OidIsValid(targetTypeId)
+                 && get_typtype(targetTypeId) == TYPTYPE_DISTINCT
+                 && getDirectBaseType(targetTypeId) == sourceTypeId)))
+         return COERCION_PATH_RELABELTYPE;
+
      /* Look in pg_cast */
      tuple = SearchSysCache(CASTSOURCETARGET,
                             ObjectIdGetDatum(sourceTypeId),
*************** find_typmod_coercion_function(Oid typeId
*** 2021,2026 ****
--- 2031,2037 ----
      /* Check for a varlena array type (and not a domain) */
      if (typeForm->typelem != InvalidOid &&
          typeForm->typlen == -1 &&
+         typeForm->typtype != TYPTYPE_DISTINCT &&
          typeForm->typtype != TYPTYPE_DOMAIN)
      {
          /* Yes, switch our attention to the element type */
Only in ./src/backend/parser: parse_coerce.c.~2.170.~
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/utils/adt/format_type.c ./src/backend/utils/adt/format_type.c
*** ../cvs-pgsql/src/backend/utils/adt/format_type.c    2008-03-26 00:42:44.000000000 +0200
--- ./src/backend/utils/adt/format_type.c    2008-10-30 15:30:09.000000000 +0200
*************** format_type_internal(Oid type_oid, int32
*** 147,152 ****
--- 147,153 ----

      if (array_base_type != InvalidOid &&
          typeform->typstorage != 'p' &&
+         typeform->typtype != TYPTYPE_DISTINCT &&
          typeform->typtype != TYPTYPE_DOMAIN)
      {
          /* Switch our attention to the array element type */
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/utils/adt/xml.c ./src/backend/utils/adt/xml.c
*** ../cvs-pgsql/src/backend/utils/adt/xml.c    2008-10-29 11:32:17.000000000 +0200
--- ./src/backend/utils/adt/xml.c    2008-10-30 14:46:05.000000000 +0200
*************** map_sql_typecoll_to_xmlschema_types(List
*** 2861,2867 ****
          }
      }

!     /* add base types of domains */
      foreach(cell0, uniquetypes)
      {
          Oid            typid = lfirst_oid(cell0);
--- 2861,2867 ----
          }
      }

!     /* add base types of distinct types and domains */
      foreach(cell0, uniquetypes)
      {
          Oid            typid = lfirst_oid(cell0);
*************** map_sql_type_to_xmlschema_type(Oid typeo
*** 3045,3051 ****
                  break;

              default:
!                 if (get_typtype(typeoid) == TYPTYPE_DOMAIN)
                  {
                      Oid            base_typeoid;
                      int32        base_typmod = -1;
--- 3045,3051 ----
                  break;

              default:
!                 if (get_typtype(typeoid) == TYPTYPE_DISTINCT || get_typtype(typeoid) == TYPTYPE_DOMAIN)
                  {
                      Oid            base_typeoid;
                      int32        base_typmod = -1;
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/utils/cache/lsyscache.c ./src/backend/utils/cache/lsyscache.c
*** ../cvs-pgsql/src/backend/utils/cache/lsyscache.c    2008-10-02 10:50:16.000000000 +0300
--- ./src/backend/utils/cache/lsyscache.c    2008-10-31 12:21:48.000000000 +0200
*************** get_typdefault(Oid typid)
*** 2032,2038 ****

  /*
   * getBaseType
!  *        If the given type is a domain, return its base type;
   *        otherwise return the type's own OID.
   */
  Oid
--- 2032,2038 ----

  /*
   * getBaseType
!  *        If the given type is a distinct type or domain, return its base type;
   *        otherwise return the type's own OID.
   */
  Oid
*************** getBaseType(Oid typid)
*** 2045,2051 ****

  /*
   * getBaseTypeAndTypmod
!  *        If the given type is a domain, return its base type and typmod;
   *        otherwise return the type's own OID, and leave *typmod unchanged.
   *
   * Note that the "applied typmod" should be -1 for every domain level
--- 2045,2051 ----

  /*
   * getBaseTypeAndTypmod
!  *        If the given type is a distinct type or domain, return its base type and typmod;
   *        otherwise return the type's own OID, and leave *typmod unchanged.
   *
   * Note that the "applied typmod" should be -1 for every domain level
*************** getBaseTypeAndTypmod(Oid typid, int32 *t
*** 2069,2077 ****
          if (!HeapTupleIsValid(tup))
              elog(ERROR, "cache lookup failed for type %u", typid);
          typTup = (Form_pg_type) GETSTRUCT(tup);
!         if (typTup->typtype != TYPTYPE_DOMAIN)
          {
!             /* Not a domain, so done */
              ReleaseSysCache(tup);
              break;
          }
--- 2069,2077 ----
          if (!HeapTupleIsValid(tup))
              elog(ERROR, "cache lookup failed for type %u", typid);
          typTup = (Form_pg_type) GETSTRUCT(tup);
!         if (typTup->typtype != TYPTYPE_DISTINCT && typTup->typtype != TYPTYPE_DOMAIN)
          {
!             /* Not a distinct type or domain, so done */
              ReleaseSysCache(tup);
              break;
          }
*************** getBaseTypeAndTypmod(Oid typid, int32 *t
*** 2087,2092 ****
--- 2087,2116 ----
  }

  /*
+  * getDirectBaseType
+  *        If the given type is a distinct type or domain, return its direct base type;
+  *        otherwise return the type's own OID.
+  */
+ Oid
+ getDirectBaseType(Oid typid)
+ {
+     HeapTuple    tup;
+     Form_pg_type typTup;
+
+     tup = SearchSysCache(TYPEOID,
+                          ObjectIdGetDatum(typid),
+                          0, 0, 0);
+     if (!HeapTupleIsValid(tup))
+         elog(ERROR, "cache lookup failed for type %u", typid);
+     typTup = (Form_pg_type) GETSTRUCT(tup);
+     if (typTup->typtype == TYPTYPE_DISTINCT || typTup->typtype == TYPTYPE_DOMAIN)
+         typid = typTup->typbasetype;
+
+     ReleaseSysCache(tup);
+     return typid;
+ }
+
+ /*
   * get_typavgwidth
   *
   *      Given a type OID and a typmod value (pass -1 if typmod is unknown),
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/utils/fmgr/funcapi.c ./src/backend/utils/fmgr/funcapi.c
*** ../cvs-pgsql/src/backend/utils/fmgr/funcapi.c    2008-08-26 01:42:34.000000000 +0300
--- ./src/backend/utils/fmgr/funcapi.c    2008-10-30 15:28:47.000000000 +0200
*************** get_type_func_class(Oid typid)
*** 643,648 ****
--- 643,649 ----
          case TYPTYPE_COMPOSITE:
              return TYPEFUNC_COMPOSITE;
          case TYPTYPE_BASE:
+         case TYPTYPE_DISTINCT:
          case TYPTYPE_DOMAIN:
          case TYPTYPE_ENUM:
              return TYPEFUNC_SCALAR;
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/bin/pg_dump/pg_dump.c ./src/bin/pg_dump/pg_dump.c
*** ../cvs-pgsql/src/bin/pg_dump/pg_dump.c    2008-10-02 10:50:16.000000000 +0300
--- ./src/bin/pg_dump/pg_dump.c    2008-10-30 17:01:25.000000000 +0200
*************** static void dumpNamespace(Archive *fout,
*** 133,138 ****
--- 133,139 ----
  static void dumpType(Archive *fout, TypeInfo *tinfo);
  static void dumpBaseType(Archive *fout, TypeInfo *tinfo);
  static void dumpEnumType(Archive *fout, TypeInfo *tinfo);
+ static void dumpDistinctType(Archive *fout, TypeInfo *tinfo);
  static void dumpDomain(Archive *fout, TypeInfo *tinfo);
  static void dumpCompositeType(Archive *fout, TypeInfo *tinfo);
  static void dumpShellType(Archive *fout, ShellTypeInfo *stinfo);
*************** dumpType(Archive *fout, TypeInfo *tinfo)
*** 5719,5724 ****
--- 5720,5727 ----
      /* Dump out in proper style */
      if (tinfo->typtype == TYPTYPE_BASE)
          dumpBaseType(fout, tinfo);
+     else if (tinfo->typtype == TYPTYPE_DISTINCT)
+         dumpDistinctType(fout, tinfo);
      else if (tinfo->typtype == TYPTYPE_DOMAIN)
          dumpDomain(fout, tinfo);
      else if (tinfo->typtype == TYPTYPE_COMPOSITE)
*************** dumpBaseType(Archive *fout, TypeInfo *ti
*** 6174,6179 ****
--- 6177,6258 ----
  }

  /*
+  * dumpDistinctType
+  *      writes out to fout the queries to recreate a user-defined distinct type
+  */
+ static void
+ dumpDistinctType(Archive *fout, TypeInfo *tinfo)
+ {
+     PQExpBuffer q = createPQExpBuffer();
+     PQExpBuffer delq = createPQExpBuffer();
+     PQExpBuffer query = createPQExpBuffer();
+     PGresult   *res;
+     int            ntups;
+     char       *typdefn;
+
+     /* Set proper schema search path so type references list correctly */
+     selectSourceSchema(tinfo->dobj.namespace->dobj.name);
+
+     /* Fetch type specific details */
+     appendPQExpBuffer(query, "SELECT pg_catalog.format_type(typbasetype, typtypmod) as typdefn "
+                       "FROM pg_catalog.pg_type "
+                       "WHERE oid = '%u'::pg_catalog.oid",
+                       tinfo->dobj.catId.oid);
+
+     res = PQexec(g_conn, query->data);
+     check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+     /* Expecting a single result only */
+     ntups = PQntuples(res);
+     if (ntups != 1)
+     {
+         write_msg(NULL, "query returned %d rows instead of one: %s\n",
+                   ntups, query->data);
+         exit_nicely();
+     }
+
+     typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
+
+     appendPQExpBuffer(q,
+                       "CREATE TYPE %s AS %s",
+                       fmtId(tinfo->dobj.name),
+                       typdefn);
+
+     PQclear(res);
+
+     appendPQExpBuffer(q, ";\n");
+
+     /*
+      * DROP must be fully qualified in case same name appears in pg_catalog
+      */
+     appendPQExpBuffer(delq, "DROP TYPE %s.",
+                       fmtId(tinfo->dobj.namespace->dobj.name));
+     appendPQExpBuffer(delq, "%s;\n",
+                       fmtId(tinfo->dobj.name));
+
+     ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
+                  tinfo->dobj.name,
+                  tinfo->dobj.namespace->dobj.name,
+                  NULL,
+                  tinfo->rolname, false,
+                  "TYPE", q->data, delq->data, NULL,
+                  tinfo->dobj.dependencies, tinfo->dobj.nDeps,
+                  NULL, NULL);
+
+     /* Dump Type Comments */
+     resetPQExpBuffer(q);
+
+     appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->dobj.name));
+     dumpComment(fout, q->data,
+                 tinfo->dobj.namespace->dobj.name, tinfo->rolname,
+                 tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
+
+     destroyPQExpBuffer(q);
+     destroyPQExpBuffer(delq);
+     destroyPQExpBuffer(query);
+ }
+
+ /*
   * dumpDomain
   *      writes out to fout the queries to recreate a user-defined domain
   */
Only in ./src/bin/pg_dump: pg_dump.c.~1.502.~
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/include/catalog/pg_type.h ./src/include/catalog/pg_type.h
*** ../cvs-pgsql/src/include/catalog/pg_type.h    2008-10-15 14:19:43.000000000 +0300
--- ./src/include/catalog/pg_type.h    2008-10-30 14:46:05.000000000 +0200
*************** CATALOG(pg_type,1247) BKI_BOOTSTRAP
*** 60,66 ****

      /*
       * typtype is 'b' for a base type, 'c' for a composite type (e.g., a
!      * table's rowtype), 'd' for a domain type, 'e' for an enum type, or 'p'
       * for a pseudo-type.  (Use the TYPTYPE macros below.)
       *
       * If typtype is 'c', typrelid is the OID of the class' entry in pg_class.
--- 60,66 ----

      /*
       * typtype is 'b' for a base type, 'c' for a composite type (e.g., a
!      * table's rowtype), 'd' for a domain type, 'D' for a distinct type, 'e' for an enum type, or 'p'
       * for a pseudo-type.  (Use the TYPTYPE macros below.)
       *
       * If typtype is 'c', typrelid is the OID of the class' entry in pg_class.
*************** DATA(insert OID = 3500 ( anyenum        PGNSP
*** 631,636 ****
--- 631,637 ----
  #define  TYPTYPE_BASE        'b' /* base type (ordinary scalar type) */
  #define  TYPTYPE_COMPOSITE    'c' /* composite (e.g., table's rowtype) */
  #define  TYPTYPE_DOMAIN        'd' /* domain over another type */
+ #define  TYPTYPE_DISTINCT    'D' /* distinct type based on another type */
  #define  TYPTYPE_ENUM        'e' /* enumerated type */
  #define  TYPTYPE_PSEUDO        'p' /* pseudo-type */

diff -x CVS -x TAGS -prc ../cvs-pgsql/src/include/nodes/parsenodes.h ./src/include/nodes/parsenodes.h
*** ../cvs-pgsql/src/include/nodes/parsenodes.h    2008-10-15 14:19:43.000000000 +0300
--- ./src/include/nodes/parsenodes.h    2008-10-30 14:46:05.000000000 +0200
*************** typedef struct CreateDomainStmt
*** 1452,1457 ****
--- 1452,1458 ----
      List       *domainname;        /* qualified name (list of Value strings) */
      TypeName   *typename;        /* the base type */
      List       *constraints;    /* constraints (list of Constraint nodes) */
+     bool        distinct_type;    /* create distinct type rather than domain */
  } CreateDomainStmt;

  /* ----------------------
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/include/utils/lsyscache.h ./src/include/utils/lsyscache.h
*** ../cvs-pgsql/src/include/utils/lsyscache.h    2008-10-02 10:50:17.000000000 +0300
--- ./src/include/utils/lsyscache.h    2008-10-31 12:21:50.000000000 +0200
*************** extern void getTypeBinaryOutputInfo(Oid
*** 123,128 ****
--- 123,129 ----
  extern Oid    get_typmodin(Oid typid);
  extern Oid    getBaseType(Oid typid);
  extern Oid    getBaseTypeAndTypmod(Oid typid, int32 *typmod);
+ extern Oid    getDirectBaseType(Oid typid);
  extern int32 get_typavgwidth(Oid typid, int32 typmod);
  extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);
  extern bool get_attstatsslot(HeapTuple statstuple,
Only in ./src/include/utils: lsyscache.h.~1.126.~
diff -x CVS -x TAGS -prc ../cvs-pgsql/src/pl/plpgsql/src/pl_comp.c ./src/pl/plpgsql/src/pl_comp.c
*** ../cvs-pgsql/src/pl/plpgsql/src/pl_comp.c    2008-10-15 14:19:44.000000000 +0300
--- ./src/pl/plpgsql/src/pl_comp.c    2008-10-30 15:19:33.000000000 +0200
*************** build_datatype(HeapTuple typeTup, int32
*** 1725,1730 ****
--- 1725,1731 ----
      switch (typeStruct->typtype)
      {
          case TYPTYPE_BASE:
+         case TYPTYPE_DISTINCT:
          case TYPTYPE_DOMAIN:
          case TYPTYPE_ENUM:
              typ->ttype = PLPGSQL_TTYPE_SCALAR;

Re: Distinct types

From
"Kevin Grittner"
Date:
>>> Peter Eisentraut <Peter.Eisentraut@Sun.COM> wrote: 
> Here is an implementation of distinct types, known from SQL99 and 
> beyond.  They are like domains, except that they don't have defaults
or 
> constraints and they do not allow implicit casting to their base
type. 
> The latter aspect is what makes them distinct types.  They are useful
to 
> create more type-safe database schemas, to prevent using generic
types 
> such as text or int for everything and then mixing them in
inappropriate 
> ways.
I expect I'll be using this.  Thanks!
I notice there's no documentation yet.  Do you have a reference to a
good description of the feature, consistent with your implementation?
If not, a couple questions:
(1)  Can you compare a literal of the base type?
(2)  Can you explicitly cast to the base type?
-Kevin


Re: Distinct types

From
Peter Eisentraut
Date:
On Friday 31 October 2008 17:01:05 Kevin Grittner wrote:
> I notice there's no documentation yet.  Do you have a reference to a
> good description of the feature, consistent with your implementation?

Documentation will need to be added, of course, but there isn't really a lot 
to describe: you create the type and use it.

> If not, a couple questions:
>
> (1)  Can you compare a literal of the base type?

No, unless you create additional casts or operators.

> (2)  Can you explicitly cast to the base type?

There is an implicit AS ASSIGNMENT cast between the base type and the distinct 
type in each direction.


Re: Distinct types

From
Tom Lane
Date:
Peter Eisentraut <peter_e@gmx.net> writes:
> On Friday 31 October 2008 17:01:05 Kevin Grittner wrote:
>> (1)  Can you compare a literal of the base type?

> No, unless you create additional casts or operators.

>> (2)  Can you explicitly cast to the base type?

> There is an implicit AS ASSIGNMENT cast between the base type and the distinct 
> type in each direction.

Hmm ... so out-of-the-box, a distinct type would have no applicable
functions/operators whatsoever.  You couldn't even create an index on
it.  This seems a bit too impoverished to be useful.  And given the
known gotchas with creating functions/operators on domains, I'm not
convinced someone could fix the problem by creating specialized
functions for their distinct type.  Even if they could fix it,
having to set up a custom btree opclass in order to have an index
seems to take this out of the "easy to use" category.
        regards, tom lane


Re: Distinct types

From
Jeff Davis
Date:
On Sat, 2008-11-01 at 16:38 -0400, Tom Lane wrote:
> Peter Eisentraut <peter_e@gmx.net> writes:
> > On Friday 31 October 2008 17:01:05 Kevin Grittner wrote:
> >> (1)  Can you compare a literal of the base type?
> 
> > No, unless you create additional casts or operators.
> 
> >> (2)  Can you explicitly cast to the base type?
> 
> > There is an implicit AS ASSIGNMENT cast between the base type and the distinct 
> > type in each direction.
> 
> Hmm ... so out-of-the-box, a distinct type would have no applicable
> functions/operators whatsoever.  You couldn't even create an index on
> it.  This seems a bit too impoverished to be useful.  And given the

I didn't have any problem creating and using an index on a distinct type
at all.

Regards,Jeff Davis

postgres=# create type mytype as int;
CREATE DOMAIN
postgres=# create table mytable (foo mytype);
CREATE TABLE
postgres=# insert into mytable values (1),(2);
INSERT 0 2
postgres=# create index myindex on mytable(foo);
CREATE INDEX
postgres=# set enable_seqscan = f;
SET
postgres=# explain select * from mytable order by foo desc;                                  QUERY
PLAN                                    
---------------------------------------------------------------------------------Index Scan Backward using myindex on
mytable (cost=0.00..12.28 rows=2
 
width=4)
(1 row)

postgres=# select * from mytable order by foo desc;foo 
-----  2  1
(2 rows)





Re: Distinct types

From
Jeff Davis
Date:
On Fri, 2008-11-07 at 09:39 -0800, Jeff Davis wrote:
> On Sat, 2008-11-01 at 16:38 -0400, Tom Lane wrote:
> > Peter Eisentraut <peter_e@gmx.net> writes:
> > > On Friday 31 October 2008 17:01:05 Kevin Grittner wrote:
> > >> (1)  Can you compare a literal of the base type?
> > 
> > > No, unless you create additional casts or operators.
> > 
> > >> (2)  Can you explicitly cast to the base type?
> > 
> > > There is an implicit AS ASSIGNMENT cast between the base type and the distinct 
> > > type in each direction.
> > 
> > Hmm ... so out-of-the-box, a distinct type would have no applicable
> > functions/operators whatsoever.  You couldn't even create an index on
> > it.  This seems a bit too impoverished to be useful.  And given the
> 
> I didn't have any problem creating and using an index on a distinct type
> at all.
> 

Oh, I see, it doesn't have an equality operator for itself. That is
obviously limiting.

Regards,Jeff Davis



Re: Distinct types

From
Andrew Dunstan
Date:

Jeff Davis wrote:
>
> postgres=# create type mytype as int;
> CREATE DOMAIN
>
>   

Is that really the return message we want?

cheers

andrew


Re: Distinct types

From
Tom Lane
Date:
Jeff Davis <pgsql@j-davis.com> writes:
> On Sat, 2008-11-01 at 16:38 -0400, Tom Lane wrote:
>> Peter Eisentraut <peter_e@gmx.net> writes:
>>> There is an implicit AS ASSIGNMENT cast between the base type and the distinct 
>>> type in each direction.
>> 
>> Hmm ... so out-of-the-box, a distinct type would have no applicable
>> functions/operators whatsoever.  You couldn't even create an index on
>> it.  This seems a bit too impoverished to be useful.  And given the

> I didn't have any problem creating and using an index on a distinct type
> at all.

[ scratches head... ]  That should have failed, if the cast is really AS
ASSIGNMENT and not implicit.  If it is implicit, how distinct is the type?
        regards, tom lane


Re: Distinct types

From
Tom Lane
Date:
Andrew Dunstan <andrew@dunslane.net> writes:
> Jeff Davis wrote:
>> postgres=# create type mytype as int;
>> CREATE DOMAIN

> Is that really the return message we want?

That's an artifact of the fact that the patch tries to piggyback on
the DOMAIN infrastructure instead of implementing its own statement
type etc.

The overly close relationship between domains and distinct types seems
to also explain the fact that CREATE INDEX fails to fail.  It looks like
getBaseType() will happily smash a distinct type to its base, and Peter
has put defenses to prevent that into some but not all call sites of
getBaseType.  I'm not sure why he did that rather than having the check
inside getBaseType itself; if there's a principled explanation for
having some of them behave differently, it sure isn't given in the
patch.
        regards, tom lane


Re: Distinct types

From
Peter Eisentraut
Date:
Tom Lane wrote:
> That's an artifact of the fact that the patch tries to piggyback on
> the DOMAIN infrastructure instead of implementing its own statement
> type etc.

This piggybacking is intentional in some way.  If you read the 
commentary on the SQL99 standard, distinct types were specifically 
invented as a better replacement for the domains introduced in SQL92, 
with the only functional difference being the casting behavior (and that 
you can use distinct types as function argument types, but PostgreSQL 
supports that for domains as well).  So even though the names of the 
object classes are different, they are really intended to be quite similar.



Re: Distinct types

From
Bruce Momjian
Date:
Added to TODO:
Allow the creation of "distinct" types
       * http://archives.postgresql.org/pgsql-hackers/2008-10/msg01647.php 

---------------------------------------------------------------------------

Peter Eisentraut wrote:
> Here is an implementation of distinct types, known from SQL99 and 
> beyond.  They are like domains, except that they don't have defaults or 
> constraints and they do not allow implicit casting to their base type. 
> The latter aspect is what makes them distinct types.  They are useful to 
> create more type-safe database schemas, to prevent using generic types 
> such as text or int for everything and then mixing them in inappropriate 
> ways.  This is something domains are not useful for.  Much of the 
> internals are shared with domains nevertheless.  The difference is 
> really only the casting behavior.
> 
> To create a distinct type, just run
> 
> CREATE TYPE mystring AS text;

> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/commands/typecmds.c ./src/backend/commands/typecmds.c
> *** ../cvs-pgsql/src/backend/commands/typecmds.c    2008-10-21 13:52:23.000000000 +0300
> --- ./src/backend/commands/typecmds.c    2008-10-31 13:50:50.000000000 +0200
> *************** RemoveTypeById(Oid typeOid)
> *** 648,654 ****
>   
>   /*
>    * DefineDomain
> !  *        Registers a new domain.
>    */
>   void
>   DefineDomain(CreateDomainStmt *stmt)
> --- 648,654 ----
>   
>   /*
>    * DefineDomain
> !  *        Registers a new domain or distinct type.
>    */
>   void
>   DefineDomain(CreateDomainStmt *stmt)
> *************** DefineDomain(CreateDomainStmt *stmt)
> *** 721,738 ****
>       basetypeoid = HeapTupleGetOid(typeTup);
>   
>       /*
> !      * Base type must be a plain base type, another domain or an enum. Domains
>        * over pseudotypes would create a security hole.  Domains over composite
>        * types might be made to work in the future, but not today.
>        */
>       typtype = baseType->typtype;
>       if (typtype != TYPTYPE_BASE &&
>           typtype != TYPTYPE_DOMAIN &&
>           typtype != TYPTYPE_ENUM)
> !         ereport(ERROR,
> !                 (errcode(ERRCODE_DATATYPE_MISMATCH),
> !                  errmsg("\"%s\" is not a valid base type for a domain",
> !                         TypeNameToString(stmt->typename))));
>   
>       /* passed by value */
>       byValue = baseType->typbyval;
> --- 721,747 ----
>       basetypeoid = HeapTupleGetOid(typeTup);
>   
>       /*
> !      * Base type must be a plain base type, another domain, distinct type, or an enum. Domains
>        * over pseudotypes would create a security hole.  Domains over composite
>        * types might be made to work in the future, but not today.
>        */
>       typtype = baseType->typtype;
>       if (typtype != TYPTYPE_BASE &&
>           typtype != TYPTYPE_DOMAIN &&
> +         typtype != TYPTYPE_DISTINCT &&
>           typtype != TYPTYPE_ENUM)
> !     {
> !         if (stmt->distinct_type)
> !             ereport(ERROR,
> !                     (errcode(ERRCODE_DATATYPE_MISMATCH),
> !                      errmsg("\"%s\" is not a valid base type for a distinct type",
> !                             TypeNameToString(stmt->typename))));
> !         else
> !             ereport(ERROR,
> !                     (errcode(ERRCODE_DATATYPE_MISMATCH),
> !                      errmsg("\"%s\" is not a valid base type for a domain",
> !                             TypeNameToString(stmt->typename))));
> !     }
>   
>       /* passed by value */
>       byValue = baseType->typbyval;
> *************** DefineDomain(CreateDomainStmt *stmt)
> *** 938,944 ****
>                      InvalidOid,    /* relation oid (n/a here) */
>                      0,            /* relation kind (ditto) */
>                      internalLength,        /* internal size */
> !                    TYPTYPE_DOMAIN,        /* type-type (domain type) */
>                      category,    /* type-category */
>                      false,        /* domain types are never preferred */
>                      delimiter,    /* array element delimiter */
> --- 947,953 ----
>                      InvalidOid,    /* relation oid (n/a here) */
>                      0,            /* relation kind (ditto) */
>                      internalLength,        /* internal size */
> !                    stmt->distinct_type ? TYPTYPE_DISTINCT : TYPTYPE_DOMAIN,        /* type-type (distinct type or
domain)*/
 
>                      category,    /* type-category */
>                      false,        /* domain types are never preferred */
>                      delimiter,    /* array element delimiter */
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/executor/functions.c ./src/backend/executor/functions.c
> *** ../cvs-pgsql/src/backend/executor/functions.c    2008-08-26 01:42:32.000000000 +0300
> --- ./src/backend/executor/functions.c    2008-10-30 14:46:05.000000000 +0200
> *************** check_sql_fn_retval(Oid func_id, Oid ret
> *** 924,929 ****
> --- 924,930 ----
>       fn_typtype = get_typtype(rettype);
>   
>       if (fn_typtype == TYPTYPE_BASE ||
> +         fn_typtype == TYPTYPE_DISTINCT ||
>           fn_typtype == TYPTYPE_DOMAIN ||
>           fn_typtype == TYPTYPE_ENUM ||
>           rettype == VOIDOID)
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/nodes/copyfuncs.c ./src/backend/nodes/copyfuncs.c
> *** ../cvs-pgsql/src/backend/nodes/copyfuncs.c    2008-10-22 14:35:44.000000000 +0300
> --- ./src/backend/nodes/copyfuncs.c    2008-10-30 14:46:05.000000000 +0200
> *************** _copyCreateDomainStmt(CreateDomainStmt *
> *** 2657,2662 ****
> --- 2657,2663 ----
>       COPY_NODE_FIELD(domainname);
>       COPY_NODE_FIELD(typename);
>       COPY_NODE_FIELD(constraints);
> +     COPY_SCALAR_FIELD(distinct_type);
>   
>       return newnode;
>   }
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/nodes/equalfuncs.c ./src/backend/nodes/equalfuncs.c
> *** ../cvs-pgsql/src/backend/nodes/equalfuncs.c    2008-10-22 14:35:44.000000000 +0300
> --- ./src/backend/nodes/equalfuncs.c    2008-10-30 14:46:05.000000000 +0200
> *************** _equalCreateDomainStmt(CreateDomainStmt 
> *** 1342,1347 ****
> --- 1342,1348 ----
>       COMPARE_NODE_FIELD(domainname);
>       COMPARE_NODE_FIELD(typename);
>       COMPARE_NODE_FIELD(constraints);
> +     COMPARE_SCALAR_FIELD(distinct_type);
>   
>       return true;
>   }
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/parser/gram.y ./src/backend/parser/gram.y
> *** ../cvs-pgsql/src/backend/parser/gram.y    2008-10-29 13:37:47.000000000 +0200
> --- ./src/backend/parser/gram.y    2008-10-30 14:46:05.000000000 +0200
> *************** CreateDomainStmt:
> *** 5608,5613 ****
> --- 5608,5623 ----
>                       n->domainname = $3;
>                       n->typename = $5;
>                       n->constraints = $6;
> +                     n->distinct_type = FALSE;
> +                     $$ = (Node *)n;
> +                 }
> +             | CREATE TYPE_P any_name AS Typename
> +                 {
> +                     CreateDomainStmt *n = makeNode(CreateDomainStmt);
> +                     n->domainname = $3;
> +                     n->typename = $5;
> +                     n->constraints = NIL;
> +                     n->distinct_type = TRUE;
>                       $$ = (Node *)n;
>                   }
>           ;
> *************** unreserved_keyword:
> *** 9439,9445 ****
>               | ENABLE_P
>               | ENCODING
>               | ENCRYPTED
> -             | ENUM_P
>               | ESCAPE
>               | EXCLUDING
>               | EXCLUSIVE
> --- 9449,9454 ----
> *************** col_name_keyword:
> *** 9626,9631 ****
> --- 9635,9641 ----
>               | COALESCE
>               | DEC
>               | DECIMAL_P
> +             | ENUM_P
>               | EXISTS
>               | EXTRACT
>               | FLOAT_P
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/parser/keywords.c ./src/backend/parser/keywords.c
> *** ../cvs-pgsql/src/backend/parser/keywords.c    2008-10-28 14:31:25.000000000 +0200
> --- ./src/backend/parser/keywords.c    2008-10-30 14:46:05.000000000 +0200
> *************** const ScanKeyword ScanKeywords[] = {
> *** 157,163 ****
>       {"encoding", ENCODING, UNRESERVED_KEYWORD},
>       {"encrypted", ENCRYPTED, UNRESERVED_KEYWORD},
>       {"end", END_P, RESERVED_KEYWORD},
> !     {"enum", ENUM_P, UNRESERVED_KEYWORD},
>       {"escape", ESCAPE, UNRESERVED_KEYWORD},
>       {"except", EXCEPT, RESERVED_KEYWORD},
>       {"excluding", EXCLUDING, UNRESERVED_KEYWORD},
> --- 157,163 ----
>       {"encoding", ENCODING, UNRESERVED_KEYWORD},
>       {"encrypted", ENCRYPTED, UNRESERVED_KEYWORD},
>       {"end", END_P, RESERVED_KEYWORD},
> !     {"enum", ENUM_P, COL_NAME_KEYWORD},
>       {"escape", ESCAPE, UNRESERVED_KEYWORD},
>       {"except", EXCEPT, RESERVED_KEYWORD},
>       {"excluding", EXCLUDING, UNRESERVED_KEYWORD},
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/parser/parse_coerce.c ./src/backend/parser/parse_coerce.c
> *** ../cvs-pgsql/src/backend/parser/parse_coerce.c    2008-10-27 10:14:16.000000000 +0200
> --- ./src/backend/parser/parse_coerce.c    2008-10-31 13:52:30.000000000 +0200
> *************** find_coercion_pathway(Oid targetTypeId, 
> *** 1867,1881 ****
>       *funcid = InvalidOid;
>   
>       /* Perhaps the types are domains; if so, look at their base types */
> !     if (OidIsValid(sourceTypeId))
>           sourceTypeId = getBaseType(sourceTypeId);
> !     if (OidIsValid(targetTypeId))
>           targetTypeId = getBaseType(targetTypeId);
>   
>       /* Domains are always coercible to and from their base type */
>       if (sourceTypeId == targetTypeId)
>           return COERCION_PATH_RELABELTYPE;
>   
>       /* Look in pg_cast */
>       tuple = SearchSysCache(CASTSOURCETARGET,
>                              ObjectIdGetDatum(sourceTypeId),
> --- 1867,1891 ----
>       *funcid = InvalidOid;
>   
>       /* Perhaps the types are domains; if so, look at their base types */
> !     if (OidIsValid(sourceTypeId) && get_typtype(sourceTypeId) == TYPTYPE_DOMAIN)
>           sourceTypeId = getBaseType(sourceTypeId);
> !     if (OidIsValid(targetTypeId) && get_typtype(targetTypeId) == TYPTYPE_DOMAIN)
>           targetTypeId = getBaseType(targetTypeId);
>   
>       /* Domains are always coercible to and from their base type */
>       if (sourceTypeId == targetTypeId)
>           return COERCION_PATH_RELABELTYPE;
>   
> +     /* Distinct types are castable AS ASSIGNMENT to and from their base types */
> +     if (ccontext >= COERCION_ASSIGNMENT
> +         && ((OidIsValid(sourceTypeId)
> +              && get_typtype(sourceTypeId) == TYPTYPE_DISTINCT
> +              && getDirectBaseType(sourceTypeId) == targetTypeId)
> +             || (OidIsValid(targetTypeId)
> +                 && get_typtype(targetTypeId) == TYPTYPE_DISTINCT
> +                 && getDirectBaseType(targetTypeId) == sourceTypeId)))
> +         return COERCION_PATH_RELABELTYPE;
> + 
>       /* Look in pg_cast */
>       tuple = SearchSysCache(CASTSOURCETARGET,
>                              ObjectIdGetDatum(sourceTypeId),
> *************** find_typmod_coercion_function(Oid typeId
> *** 2021,2026 ****
> --- 2031,2037 ----
>       /* Check for a varlena array type (and not a domain) */
>       if (typeForm->typelem != InvalidOid &&
>           typeForm->typlen == -1 &&
> +         typeForm->typtype != TYPTYPE_DISTINCT &&
>           typeForm->typtype != TYPTYPE_DOMAIN)
>       {
>           /* Yes, switch our attention to the element type */
> Only in ./src/backend/parser: parse_coerce.c.~2.170.~
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/utils/adt/format_type.c ./src/backend/utils/adt/format_type.c
> *** ../cvs-pgsql/src/backend/utils/adt/format_type.c    2008-03-26 00:42:44.000000000 +0200
> --- ./src/backend/utils/adt/format_type.c    2008-10-30 15:30:09.000000000 +0200
> *************** format_type_internal(Oid type_oid, int32
> *** 147,152 ****
> --- 147,153 ----
>   
>       if (array_base_type != InvalidOid &&
>           typeform->typstorage != 'p' &&
> +         typeform->typtype != TYPTYPE_DISTINCT &&
>           typeform->typtype != TYPTYPE_DOMAIN)
>       {
>           /* Switch our attention to the array element type */
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/utils/adt/xml.c ./src/backend/utils/adt/xml.c
> *** ../cvs-pgsql/src/backend/utils/adt/xml.c    2008-10-29 11:32:17.000000000 +0200
> --- ./src/backend/utils/adt/xml.c    2008-10-30 14:46:05.000000000 +0200
> *************** map_sql_typecoll_to_xmlschema_types(List
> *** 2861,2867 ****
>           }
>       }
>   
> !     /* add base types of domains */
>       foreach(cell0, uniquetypes)
>       {
>           Oid            typid = lfirst_oid(cell0);
> --- 2861,2867 ----
>           }
>       }
>   
> !     /* add base types of distinct types and domains */
>       foreach(cell0, uniquetypes)
>       {
>           Oid            typid = lfirst_oid(cell0);
> *************** map_sql_type_to_xmlschema_type(Oid typeo
> *** 3045,3051 ****
>                   break;
>   
>               default:
> !                 if (get_typtype(typeoid) == TYPTYPE_DOMAIN)
>                   {
>                       Oid            base_typeoid;
>                       int32        base_typmod = -1;
> --- 3045,3051 ----
>                   break;
>   
>               default:
> !                 if (get_typtype(typeoid) == TYPTYPE_DISTINCT || get_typtype(typeoid) == TYPTYPE_DOMAIN)
>                   {
>                       Oid            base_typeoid;
>                       int32        base_typmod = -1;
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/utils/cache/lsyscache.c ./src/backend/utils/cache/lsyscache.c
> *** ../cvs-pgsql/src/backend/utils/cache/lsyscache.c    2008-10-02 10:50:16.000000000 +0300
> --- ./src/backend/utils/cache/lsyscache.c    2008-10-31 12:21:48.000000000 +0200
> *************** get_typdefault(Oid typid)
> *** 2032,2038 ****
>   
>   /*
>    * getBaseType
> !  *        If the given type is a domain, return its base type;
>    *        otherwise return the type's own OID.
>    */
>   Oid
> --- 2032,2038 ----
>   
>   /*
>    * getBaseType
> !  *        If the given type is a distinct type or domain, return its base type;
>    *        otherwise return the type's own OID.
>    */
>   Oid
> *************** getBaseType(Oid typid)
> *** 2045,2051 ****
>   
>   /*
>    * getBaseTypeAndTypmod
> !  *        If the given type is a domain, return its base type and typmod;
>    *        otherwise return the type's own OID, and leave *typmod unchanged.
>    *
>    * Note that the "applied typmod" should be -1 for every domain level
> --- 2045,2051 ----
>   
>   /*
>    * getBaseTypeAndTypmod
> !  *        If the given type is a distinct type or domain, return its base type and typmod;
>    *        otherwise return the type's own OID, and leave *typmod unchanged.
>    *
>    * Note that the "applied typmod" should be -1 for every domain level
> *************** getBaseTypeAndTypmod(Oid typid, int32 *t
> *** 2069,2077 ****
>           if (!HeapTupleIsValid(tup))
>               elog(ERROR, "cache lookup failed for type %u", typid);
>           typTup = (Form_pg_type) GETSTRUCT(tup);
> !         if (typTup->typtype != TYPTYPE_DOMAIN)
>           {
> !             /* Not a domain, so done */
>               ReleaseSysCache(tup);
>               break;
>           }
> --- 2069,2077 ----
>           if (!HeapTupleIsValid(tup))
>               elog(ERROR, "cache lookup failed for type %u", typid);
>           typTup = (Form_pg_type) GETSTRUCT(tup);
> !         if (typTup->typtype != TYPTYPE_DISTINCT && typTup->typtype != TYPTYPE_DOMAIN)
>           {
> !             /* Not a distinct type or domain, so done */
>               ReleaseSysCache(tup);
>               break;
>           }
> *************** getBaseTypeAndTypmod(Oid typid, int32 *t
> *** 2087,2092 ****
> --- 2087,2116 ----
>   }
>   
>   /*
> +  * getDirectBaseType
> +  *        If the given type is a distinct type or domain, return its direct base type;
> +  *        otherwise return the type's own OID.
> +  */
> + Oid
> + getDirectBaseType(Oid typid)
> + {
> +     HeapTuple    tup;
> +     Form_pg_type typTup;
> + 
> +     tup = SearchSysCache(TYPEOID,
> +                          ObjectIdGetDatum(typid),
> +                          0, 0, 0);
> +     if (!HeapTupleIsValid(tup))
> +         elog(ERROR, "cache lookup failed for type %u", typid);
> +     typTup = (Form_pg_type) GETSTRUCT(tup);
> +     if (typTup->typtype == TYPTYPE_DISTINCT || typTup->typtype == TYPTYPE_DOMAIN)
> +         typid = typTup->typbasetype;
> +         
> +     ReleaseSysCache(tup);
> +     return typid;
> + }
> + 
> + /*
>    * get_typavgwidth
>    *
>    *      Given a type OID and a typmod value (pass -1 if typmod is unknown),
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/backend/utils/fmgr/funcapi.c ./src/backend/utils/fmgr/funcapi.c
> *** ../cvs-pgsql/src/backend/utils/fmgr/funcapi.c    2008-08-26 01:42:34.000000000 +0300
> --- ./src/backend/utils/fmgr/funcapi.c    2008-10-30 15:28:47.000000000 +0200
> *************** get_type_func_class(Oid typid)
> *** 643,648 ****
> --- 643,649 ----
>           case TYPTYPE_COMPOSITE:
>               return TYPEFUNC_COMPOSITE;
>           case TYPTYPE_BASE:
> +         case TYPTYPE_DISTINCT:
>           case TYPTYPE_DOMAIN:
>           case TYPTYPE_ENUM:
>               return TYPEFUNC_SCALAR;
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/bin/pg_dump/pg_dump.c ./src/bin/pg_dump/pg_dump.c
> *** ../cvs-pgsql/src/bin/pg_dump/pg_dump.c    2008-10-02 10:50:16.000000000 +0300
> --- ./src/bin/pg_dump/pg_dump.c    2008-10-30 17:01:25.000000000 +0200
> *************** static void dumpNamespace(Archive *fout,
> *** 133,138 ****
> --- 133,139 ----
>   static void dumpType(Archive *fout, TypeInfo *tinfo);
>   static void dumpBaseType(Archive *fout, TypeInfo *tinfo);
>   static void dumpEnumType(Archive *fout, TypeInfo *tinfo);
> + static void dumpDistinctType(Archive *fout, TypeInfo *tinfo);
>   static void dumpDomain(Archive *fout, TypeInfo *tinfo);
>   static void dumpCompositeType(Archive *fout, TypeInfo *tinfo);
>   static void dumpShellType(Archive *fout, ShellTypeInfo *stinfo);
> *************** dumpType(Archive *fout, TypeInfo *tinfo)
> *** 5719,5724 ****
> --- 5720,5727 ----
>       /* Dump out in proper style */
>       if (tinfo->typtype == TYPTYPE_BASE)
>           dumpBaseType(fout, tinfo);
> +     else if (tinfo->typtype == TYPTYPE_DISTINCT)
> +         dumpDistinctType(fout, tinfo);
>       else if (tinfo->typtype == TYPTYPE_DOMAIN)
>           dumpDomain(fout, tinfo);
>       else if (tinfo->typtype == TYPTYPE_COMPOSITE)
> *************** dumpBaseType(Archive *fout, TypeInfo *ti
> *** 6174,6179 ****
> --- 6177,6258 ----
>   }
>   
>   /*
> +  * dumpDistinctType
> +  *      writes out to fout the queries to recreate a user-defined distinct type
> +  */
> + static void
> + dumpDistinctType(Archive *fout, TypeInfo *tinfo)
> + {
> +     PQExpBuffer q = createPQExpBuffer();
> +     PQExpBuffer delq = createPQExpBuffer();
> +     PQExpBuffer query = createPQExpBuffer();
> +     PGresult   *res;
> +     int            ntups;
> +     char       *typdefn;
> + 
> +     /* Set proper schema search path so type references list correctly */
> +     selectSourceSchema(tinfo->dobj.namespace->dobj.name);
> + 
> +     /* Fetch type specific details */
> +     appendPQExpBuffer(query, "SELECT pg_catalog.format_type(typbasetype, typtypmod) as typdefn "
> +                       "FROM pg_catalog.pg_type "
> +                       "WHERE oid = '%u'::pg_catalog.oid",
> +                       tinfo->dobj.catId.oid);
> + 
> +     res = PQexec(g_conn, query->data);
> +     check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
> + 
> +     /* Expecting a single result only */
> +     ntups = PQntuples(res);
> +     if (ntups != 1)
> +     {
> +         write_msg(NULL, "query returned %d rows instead of one: %s\n",
> +                   ntups, query->data);
> +         exit_nicely();
> +     }
> + 
> +     typdefn = PQgetvalue(res, 0, PQfnumber(res, "typdefn"));
> + 
> +     appendPQExpBuffer(q,
> +                       "CREATE TYPE %s AS %s",
> +                       fmtId(tinfo->dobj.name),
> +                       typdefn);
> + 
> +     PQclear(res);
> + 
> +     appendPQExpBuffer(q, ";\n");
> + 
> +     /*
> +      * DROP must be fully qualified in case same name appears in pg_catalog
> +      */
> +     appendPQExpBuffer(delq, "DROP TYPE %s.",
> +                       fmtId(tinfo->dobj.namespace->dobj.name));
> +     appendPQExpBuffer(delq, "%s;\n",
> +                       fmtId(tinfo->dobj.name));
> + 
> +     ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
> +                  tinfo->dobj.name,
> +                  tinfo->dobj.namespace->dobj.name,
> +                  NULL,
> +                  tinfo->rolname, false,
> +                  "TYPE", q->data, delq->data, NULL,
> +                  tinfo->dobj.dependencies, tinfo->dobj.nDeps,
> +                  NULL, NULL);
> + 
> +     /* Dump Type Comments */
> +     resetPQExpBuffer(q);
> + 
> +     appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->dobj.name));
> +     dumpComment(fout, q->data,
> +                 tinfo->dobj.namespace->dobj.name, tinfo->rolname,
> +                 tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
> + 
> +     destroyPQExpBuffer(q);
> +     destroyPQExpBuffer(delq);
> +     destroyPQExpBuffer(query);
> + }
> + 
> + /*
>    * dumpDomain
>    *      writes out to fout the queries to recreate a user-defined domain
>    */
> Only in ./src/bin/pg_dump: pg_dump.c.~1.502.~
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/include/catalog/pg_type.h ./src/include/catalog/pg_type.h
> *** ../cvs-pgsql/src/include/catalog/pg_type.h    2008-10-15 14:19:43.000000000 +0300
> --- ./src/include/catalog/pg_type.h    2008-10-30 14:46:05.000000000 +0200
> *************** CATALOG(pg_type,1247) BKI_BOOTSTRAP
> *** 60,66 ****
>   
>       /*
>        * typtype is 'b' for a base type, 'c' for a composite type (e.g., a
> !      * table's rowtype), 'd' for a domain type, 'e' for an enum type, or 'p'
>        * for a pseudo-type.  (Use the TYPTYPE macros below.)
>        *
>        * If typtype is 'c', typrelid is the OID of the class' entry in pg_class.
> --- 60,66 ----
>   
>       /*
>        * typtype is 'b' for a base type, 'c' for a composite type (e.g., a
> !      * table's rowtype), 'd' for a domain type, 'D' for a distinct type, 'e' for an enum type, or 'p'
>        * for a pseudo-type.  (Use the TYPTYPE macros below.)
>        *
>        * If typtype is 'c', typrelid is the OID of the class' entry in pg_class.
> *************** DATA(insert OID = 3500 ( anyenum        PGNSP 
> *** 631,636 ****
> --- 631,637 ----
>   #define  TYPTYPE_BASE        'b' /* base type (ordinary scalar type) */
>   #define  TYPTYPE_COMPOSITE    'c' /* composite (e.g., table's rowtype) */
>   #define  TYPTYPE_DOMAIN        'd' /* domain over another type */
> + #define  TYPTYPE_DISTINCT    'D' /* distinct type based on another type */
>   #define  TYPTYPE_ENUM        'e' /* enumerated type */
>   #define  TYPTYPE_PSEUDO        'p' /* pseudo-type */
>   
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/include/nodes/parsenodes.h ./src/include/nodes/parsenodes.h
> *** ../cvs-pgsql/src/include/nodes/parsenodes.h    2008-10-15 14:19:43.000000000 +0300
> --- ./src/include/nodes/parsenodes.h    2008-10-30 14:46:05.000000000 +0200
> *************** typedef struct CreateDomainStmt
> *** 1452,1457 ****
> --- 1452,1458 ----
>       List       *domainname;        /* qualified name (list of Value strings) */
>       TypeName   *typename;        /* the base type */
>       List       *constraints;    /* constraints (list of Constraint nodes) */
> +     bool        distinct_type;    /* create distinct type rather than domain */
>   } CreateDomainStmt;
>   
>   /* ----------------------
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/include/utils/lsyscache.h ./src/include/utils/lsyscache.h
> *** ../cvs-pgsql/src/include/utils/lsyscache.h    2008-10-02 10:50:17.000000000 +0300
> --- ./src/include/utils/lsyscache.h    2008-10-31 12:21:50.000000000 +0200
> *************** extern void getTypeBinaryOutputInfo(Oid 
> *** 123,128 ****
> --- 123,129 ----
>   extern Oid    get_typmodin(Oid typid);
>   extern Oid    getBaseType(Oid typid);
>   extern Oid    getBaseTypeAndTypmod(Oid typid, int32 *typmod);
> + extern Oid    getDirectBaseType(Oid typid);
>   extern int32 get_typavgwidth(Oid typid, int32 typmod);
>   extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);
>   extern bool get_attstatsslot(HeapTuple statstuple,
> Only in ./src/include/utils: lsyscache.h.~1.126.~
> diff -x CVS -x TAGS -prc ../cvs-pgsql/src/pl/plpgsql/src/pl_comp.c ./src/pl/plpgsql/src/pl_comp.c
> *** ../cvs-pgsql/src/pl/plpgsql/src/pl_comp.c    2008-10-15 14:19:44.000000000 +0300
> --- ./src/pl/plpgsql/src/pl_comp.c    2008-10-30 15:19:33.000000000 +0200
> *************** build_datatype(HeapTuple typeTup, int32 
> *** 1725,1730 ****
> --- 1725,1731 ----
>       switch (typeStruct->typtype)
>       {
>           case TYPTYPE_BASE:
> +         case TYPTYPE_DISTINCT:
>           case TYPTYPE_DOMAIN:
>           case TYPTYPE_ENUM:
>               typ->ttype = PLPGSQL_TTYPE_SCALAR;

> 
> -- 
> Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
> To make changes to your subscription:
> http://www.postgresql.org/mailpref/pgsql-hackers

--  Bruce Momjian  <bruce@momjian.us>        http://momjian.us EnterpriseDB
http://enterprisedb.com
 + If your life is a hard drive, Christ can be your backup. +