deparse: Support DefineStmt commands
authorAbhijit Menon-Sen <ams@2ndQuadrant.com>
Mon, 5 May 2014 06:50:58 +0000 (12:20 +0530)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Tue, 7 Apr 2015 17:09:38 +0000 (14:09 -0300)
CREATE AGGREGATE
CREATE COLLATION
CREATE OPERATOR
CREATE TEXT SEARCH CONFIGURATION
CREATE TEXT SEARCH PARSER
CREATE TEXT SEARCH DICTIONARY
CREATE TEXT SEARCH TEMPLATE
CREATE TYPE

src/backend/tcop/deparse_utility.c

index 3439d15f995315180a4ec943e2cc15e3d27779e3..83dd59b29c715c73fc872023c3855a3e48d1125e 100644 (file)
@@ -735,6 +735,878 @@ get_persistence_str(char persistence)
    }
 }
 
+/*
+ * Form an ObjElem to be used as a single argument in an aggregate argument
+ * list
+ */
+static ObjElem *
+form_agg_argument(int idx, char *modes, char **names, Oid *types)
+{
+   ObjTree    *arg;
+
+   arg = new_objtree_VA("%{mode}s %{name}s %{type}T", 0);
+
+   append_string_object(arg, "mode",
+                        (modes && modes[idx] == 'v') ? "VARIADIC" : "");
+   append_string_object(arg, "name", names ? names[idx] : "");
+   append_object_object(arg, "type",
+                        new_objtree_for_type(types[idx], -1));
+
+   return new_object_object(arg);
+}
+
+static ObjTree *
+deparse_DefineStmt_Aggregate(Oid objectId, DefineStmt *define)
+{
+   HeapTuple   aggTup;
+   HeapTuple   procTup;
+   ObjTree    *stmt;
+   ObjTree    *tmp;
+   List       *list;
+   Datum       initval;
+   bool        isnull;
+   Form_pg_aggregate agg;
+   Form_pg_proc proc;
+
+   aggTup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(objectId));
+   if (!HeapTupleIsValid(aggTup))
+       elog(ERROR, "cache lookup failed for aggregate with OID %u", objectId);
+   agg = (Form_pg_aggregate) GETSTRUCT(aggTup);
+
+   procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(agg->aggfnoid));
+   if (!HeapTupleIsValid(procTup))
+       elog(ERROR, "cache lookup failed for procedure with OID %u",
+            agg->aggfnoid);
+   proc = (Form_pg_proc) GETSTRUCT(procTup);
+
+   stmt = new_objtree_VA("CREATE AGGREGATE %{identity}D(%{types}s) "
+                         "(%{elems:, }s)", 0);
+
+   append_object_object(stmt, "identity",
+                        new_objtree_for_qualname(proc->pronamespace,
+                                                 NameStr(proc->proname)));
+
+   /*
+    * Add the argument list.  There are three cases to consider:
+    *
+    * 1. no arguments, in which case the signature is (*).
+    *
+    * 2. Not an ordered-set aggregate.  In this case there's one or more
+    * arguments.
+    *
+    * 3. Ordered-set aggregates. These have zero or more direct arguments, and
+    * one or more ordered arguments.
+    *
+    * We don't need to consider default values or table parameters, and the
+    * only mode that we need to consider is VARIADIC.
+    */
+
+   if (proc->pronargs == 0)
+   {
+       append_string_object(stmt, "agg type", "noargs");
+       append_string_object(stmt, "types", "*");
+   }
+   else if (!AGGKIND_IS_ORDERED_SET(agg->aggkind))
+   {
+       int         i;
+       int         nargs;
+       Oid        *types;
+       char       *modes;
+       char      **names;
+
+       nargs = get_func_arg_info(procTup, &types, &names, &modes);
+
+       /* only direct arguments in this case */
+       list = NIL;
+       for (i = 0; i < nargs; i++)
+       {
+           list = lappend(list, form_agg_argument(i, modes, names, types));
+       }
+
+       tmp = new_objtree_VA("%{direct:, }s", 1,
+                            "direct", ObjTypeArray, list);
+       append_object_object(stmt, "types", tmp);
+       append_string_object(stmt, "agg type", "plain");
+   }
+   else
+   {
+       int         i;
+       int         nargs;
+       Oid        *types;
+       char       *modes;
+       char      **names;
+
+       nargs = get_func_arg_info(procTup, &types, &names, &modes);
+
+       tmp = new_objtree_VA("%{direct:, }s ORDER BY %{aggregated:, }s", 0);
+
+       /* direct arguments ... */
+       list = NIL;
+       for (i = 0; i < agg->aggnumdirectargs; i++)
+       {
+           list = lappend(list, form_agg_argument(i, modes, names, types));
+       }
+       append_array_object(tmp, "direct", list);
+
+       /*
+        * ... and aggregated arguments.  If the last direct argument is
+        * VARIADIC, we need to repeat it here rather than searching for more
+        * arguments.  (See aggr_args production in gram.y for an explanation.)
+        */
+       if (modes && modes[agg->aggnumdirectargs - 1] == 'v')
+       {
+           list = list_make1(form_agg_argument(agg->aggnumdirectargs - 1,
+                                               modes, names, types));
+       }
+       else
+       {
+           list = NIL;
+           for (i = agg->aggnumdirectargs; i < nargs; i++)
+           {
+               list = lappend(list, form_agg_argument(i, modes, names, types));
+           }
+       }
+       append_array_object(tmp, "aggregated", list);
+
+       append_object_object(stmt, "types", tmp);
+       append_string_object(stmt, "agg type", "ordered-set");
+   }
+
+   /*
+    * Now add the definition clause
+    */
+   list = NIL;
+
+   tmp = new_objtree_VA("SFUNC=%{procedure}D", 0);
+   append_object_object(tmp, "procedure",
+                        new_objtree_for_qualname_id(ProcedureRelationId,
+                                                    agg->aggtransfn));
+   list = lappend(list, new_object_object(tmp));
+
+   tmp = new_objtree_VA("STYPE=%{type}T", 0);
+   append_object_object(tmp, "type",
+                        new_objtree_for_type(agg->aggtranstype, -1));
+   list = lappend(list, new_object_object(tmp));
+
+   if (agg->aggtransspace != 0)
+   {
+       tmp = new_objtree_VA("SSPACE=%{space}n", 1,
+                            "space", ObjTypeInteger,
+                            agg->aggtransspace);
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (OidIsValid(agg->aggfinalfn))
+   {
+       tmp = new_objtree_VA("FINALFUNC=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        agg->aggfinalfn));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (agg->aggfinalextra)
+   {
+       tmp = new_objtree_VA("FINALFUNC_EXTRA=true", 0);
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   initval = SysCacheGetAttr(AGGFNOID, aggTup,
+                             Anum_pg_aggregate_agginitval,
+                             &isnull);
+   if (!isnull)
+   {
+       tmp = new_objtree_VA("INITCOND=%{initval}L",
+                            1, "initval", ObjTypeString,
+                            TextDatumGetCString(initval));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (OidIsValid(agg->aggmtransfn))
+   {
+       tmp = new_objtree_VA("MSFUNC=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        agg->aggmtransfn));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (OidIsValid(agg->aggmtranstype))
+   {
+       tmp = new_objtree_VA("MSTYPE=%{type}T", 0);
+       append_object_object(tmp, "type",
+                            new_objtree_for_type(agg->aggmtranstype, -1));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (agg->aggmtransspace != 0)
+   {
+       tmp = new_objtree_VA("MSSPACE=%{space}n", 1,
+                            "space", ObjTypeInteger,
+                            agg->aggmtransspace);
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (OidIsValid(agg->aggminvtransfn))
+   {
+       tmp = new_objtree_VA("MINVFUNC=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        agg->aggminvtransfn));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (OidIsValid(agg->aggmfinalfn))
+   {
+       tmp = new_objtree_VA("MFINALFUNC=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        agg->aggmfinalfn));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (agg->aggmfinalextra)
+   {
+       tmp = new_objtree_VA("MFINALFUNC_EXTRA=true", 0);
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   initval = SysCacheGetAttr(AGGFNOID, aggTup,
+                             Anum_pg_aggregate_aggminitval,
+                             &isnull);
+   if (!isnull)
+   {
+       tmp = new_objtree_VA("MINITCOND=%{initval}L",
+                            1, "initval", ObjTypeString,
+                            TextDatumGetCString(initval));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (agg->aggkind == AGGKIND_HYPOTHETICAL)
+   {
+       tmp = new_objtree_VA("HYPOTHETICAL=true", 0);
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (OidIsValid(agg->aggsortop))
+   {
+       Oid sortop = agg->aggsortop;
+       Form_pg_operator op;
+       HeapTuple tup;
+
+       tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(sortop));
+       if (!HeapTupleIsValid(tup))
+           elog(ERROR, "cache lookup failed for operator with OID %u", sortop);
+       op = (Form_pg_operator) GETSTRUCT(tup);
+
+       tmp = new_objtree_VA("SORTOP=%{operator}D", 0);
+       append_object_object(tmp, "operator",
+                            new_objtree_for_qualname(op->oprnamespace,
+                                                     NameStr(op->oprname)));
+       list = lappend(list, new_object_object(tmp));
+
+       ReleaseSysCache(tup);
+   }
+
+   append_array_object(stmt, "elems", list);
+
+   ReleaseSysCache(procTup);
+   ReleaseSysCache(aggTup);
+
+   return stmt;
+}
+
+static ObjTree *
+deparse_DefineStmt_Collation(Oid objectId, DefineStmt *define)
+{
+   HeapTuple   colTup;
+   ObjTree    *stmt;
+   Form_pg_collation colForm;
+
+   colTup = SearchSysCache1(COLLOID, ObjectIdGetDatum(objectId));
+   if (!HeapTupleIsValid(colTup))
+       elog(ERROR, "cache lookup failed for collation with OID %u", objectId);
+   colForm = (Form_pg_collation) GETSTRUCT(colTup);
+
+   stmt = new_objtree_VA("CREATE COLLATION %{identity}D "
+                         "(LC_COLLATE = %{collate}L,"
+                         " LC_CTYPE = %{ctype}L)", 0);
+
+   append_object_object(stmt, "identity",
+                        new_objtree_for_qualname(colForm->collnamespace,
+                                                 NameStr(colForm->collname)));
+   append_string_object(stmt, "collate", NameStr(colForm->collcollate));
+   append_string_object(stmt, "ctype", NameStr(colForm->collctype));
+
+   ReleaseSysCache(colTup);
+
+   return stmt;
+}
+
+static ObjTree *
+deparse_DefineStmt_Operator(Oid objectId, DefineStmt *define)
+{
+   HeapTuple   oprTup;
+   ObjTree    *stmt;
+   ObjTree    *tmp;
+   List       *list;
+   Form_pg_operator oprForm;
+
+   oprTup = SearchSysCache1(OPEROID, ObjectIdGetDatum(objectId));
+   if (!HeapTupleIsValid(oprTup))
+       elog(ERROR, "cache lookup failed for operator with OID %u", objectId);
+   oprForm = (Form_pg_operator) GETSTRUCT(oprTup);
+
+   stmt = new_objtree_VA("CREATE OPERATOR %{identity}O (%{elems:, }s)", 0);
+
+   append_object_object(stmt, "identity",
+                        new_objtree_for_qualname(oprForm->oprnamespace,
+                                                 NameStr(oprForm->oprname)));
+
+   list = NIL;
+
+   tmp = new_objtree_VA("PROCEDURE=%{procedure}D", 0);
+   append_object_object(tmp, "procedure",
+                        new_objtree_for_qualname_id(ProcedureRelationId,
+                                                    oprForm->oprcode));
+   list = lappend(list, new_object_object(tmp));
+
+   if (OidIsValid(oprForm->oprleft))
+   {
+       tmp = new_objtree_VA("LEFTARG=%{type}T", 0);
+       append_object_object(tmp, "type",
+                            new_objtree_for_type(oprForm->oprleft, -1));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (OidIsValid(oprForm->oprright))
+   {
+       tmp = new_objtree_VA("RIGHTARG=%{type}T", 0);
+       append_object_object(tmp, "type",
+                            new_objtree_for_type(oprForm->oprright, -1));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (OidIsValid(oprForm->oprcom))
+   {
+       tmp = new_objtree_VA("COMMUTATOR=%{oper}D", 0);
+       append_object_object(tmp, "oper",
+                            new_objtree_for_qualname_id(OperatorRelationId,
+                                                        oprForm->oprcom));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (OidIsValid(oprForm->oprnegate))
+   {
+       tmp = new_objtree_VA("NEGATOR=%{oper}D", 0);
+       append_object_object(tmp, "oper",
+                            new_objtree_for_qualname_id(OperatorRelationId,
+                                                        oprForm->oprnegate));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (OidIsValid(oprForm->oprrest))
+   {
+       tmp = new_objtree_VA("RESTRICT=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        oprForm->oprrest));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (OidIsValid(oprForm->oprjoin))
+   {
+       tmp = new_objtree_VA("JOIN=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        oprForm->oprjoin));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   if (oprForm->oprcanmerge)
+       list = lappend(list, new_object_object(new_objtree_VA("MERGES", 0)));
+   if (oprForm->oprcanhash)
+       list = lappend(list, new_object_object(new_objtree_VA("HASHES", 0)));
+
+   append_array_object(stmt, "elems", list);
+
+   ReleaseSysCache(oprTup);
+
+   return stmt;
+}
+
+static ObjTree *
+deparse_DefineStmt_TSConfig(Oid objectId, DefineStmt *define,
+                           ObjectAddress copied)
+{
+   HeapTuple   tscTup;
+   HeapTuple   tspTup;
+   ObjTree    *stmt;
+   ObjTree    *tmp;
+   Form_pg_ts_config tscForm;
+   Form_pg_ts_parser tspForm;
+   List       *list;
+
+   tscTup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(objectId));
+   if (!HeapTupleIsValid(tscTup))
+       elog(ERROR, "cache lookup failed for text search configuration "
+            "with OID %u", objectId);
+   tscForm = (Form_pg_ts_config) GETSTRUCT(tscTup);
+
+   tspTup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(tscForm->cfgparser));
+   if (!HeapTupleIsValid(tspTup))
+       elog(ERROR, "cache lookup failed for text search parser with OID %u",
+            tscForm->cfgparser);
+   tspForm = (Form_pg_ts_parser) GETSTRUCT(tspTup);
+
+   stmt = new_objtree_VA("CREATE TEXT SEARCH CONFIGURATION %{identity}D "
+                         "(%{elems:, }s)", 0);
+
+   append_object_object(stmt, "identity",
+                        new_objtree_for_qualname(tscForm->cfgnamespace,
+                                                 NameStr(tscForm->cfgname)));
+
+   /*
+    * If tsconfig was copied from another one, define it like so.
+    */
+   list = NIL;
+   if (copied.objectId != InvalidOid)
+   {
+       tmp = new_objtree_VA("COPY=%{tsconfig}D", 0);
+       append_object_object(tmp, "tsconfig",
+                            new_objtree_for_qualname_id(TSConfigRelationId,
+                                                        copied.objectId));
+   }
+   else
+   {
+       tmp = new_objtree_VA("PARSER=%{parser}D", 0);
+       append_object_object(tmp, "parser",
+                            new_objtree_for_qualname(tspForm->prsnamespace,
+                                                     NameStr(tspForm->prsname)));
+   }
+   list = lappend(list, new_object_object(tmp));
+   append_array_object(stmt, "elems", list);
+
+   ReleaseSysCache(tspTup);
+   ReleaseSysCache(tscTup);
+
+   return stmt;
+}
+
+static ObjTree *
+deparse_DefineStmt_TSParser(Oid objectId, DefineStmt *define)
+{
+   HeapTuple   tspTup;
+   ObjTree    *stmt;
+   ObjTree    *tmp;
+   List       *list;
+   Form_pg_ts_parser tspForm;
+
+   tspTup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(objectId));
+   if (!HeapTupleIsValid(tspTup))
+       elog(ERROR, "cache lookup failed for text search parser with OID %u",
+            objectId);
+   tspForm = (Form_pg_ts_parser) GETSTRUCT(tspTup);
+
+   stmt = new_objtree_VA("CREATE TEXT SEARCH PARSER %{identity}D "
+                         "(%{elems:, }s)", 0);
+
+   append_object_object(stmt, "identity",
+                        new_objtree_for_qualname(tspForm->prsnamespace,
+                                                 NameStr(tspForm->prsname)));
+
+   list = NIL;
+
+   tmp = new_objtree_VA("START=%{procedure}D", 0);
+   append_object_object(tmp, "procedure",
+                        new_objtree_for_qualname_id(ProcedureRelationId,
+                                                    tspForm->prsstart));
+   list = lappend(list, new_object_object(tmp));
+
+   tmp = new_objtree_VA("GETTOKEN=%{procedure}D", 0);
+   append_object_object(tmp, "procedure",
+                        new_objtree_for_qualname_id(ProcedureRelationId,
+                                                    tspForm->prstoken));
+   list = lappend(list, new_object_object(tmp));
+
+   tmp = new_objtree_VA("END=%{procedure}D", 0);
+   append_object_object(tmp, "procedure",
+                        new_objtree_for_qualname_id(ProcedureRelationId,
+                                                    tspForm->prsend));
+   list = lappend(list, new_object_object(tmp));
+
+   tmp = new_objtree_VA("LEXTYPES=%{procedure}D", 0);
+   append_object_object(tmp, "procedure",
+                        new_objtree_for_qualname_id(ProcedureRelationId,
+                                                    tspForm->prslextype));
+   list = lappend(list, new_object_object(tmp));
+
+   if (OidIsValid(tspForm->prsheadline))
+   {
+       tmp = new_objtree_VA("HEADLINE=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        tspForm->prsheadline));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   append_array_object(stmt, "elems", list);
+
+   ReleaseSysCache(tspTup);
+
+   return stmt;
+}
+
+static ObjTree *
+deparse_DefineStmt_TSDictionary(Oid objectId, DefineStmt *define)
+{
+   HeapTuple   tsdTup;
+   ObjTree    *stmt;
+   ObjTree    *tmp;
+   List       *list;
+   Datum       options;
+   bool        isnull;
+   Form_pg_ts_dict tsdForm;
+
+   tsdTup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(objectId));
+   if (!HeapTupleIsValid(tsdTup))
+       elog(ERROR, "cache lookup failed for text search dictionary "
+            "with OID %u", objectId);
+   tsdForm = (Form_pg_ts_dict) GETSTRUCT(tsdTup);
+
+   stmt = new_objtree_VA("CREATE TEXT SEARCH DICTIONARY %{identity}D "
+                         "(%{elems:, }s)", 0);
+
+   append_object_object(stmt, "identity",
+                        new_objtree_for_qualname(tsdForm->dictnamespace,
+                                                 NameStr(tsdForm->dictname)));
+
+   list = NIL;
+
+   tmp = new_objtree_VA("TEMPLATE=%{template}D", 0);
+   append_object_object(tmp, "template",
+                        new_objtree_for_qualname_id(TSTemplateRelationId,
+                                                    tsdForm->dicttemplate));
+   list = lappend(list, new_object_object(tmp));
+
+   options = SysCacheGetAttr(TSDICTOID, tsdTup,
+                             Anum_pg_ts_dict_dictinitoption,
+                             &isnull);
+   if (!isnull)
+   {
+       tmp = new_objtree_VA("%{options}s", 0);
+       append_string_object(tmp, "options", TextDatumGetCString(options));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   append_array_object(stmt, "elems", list);
+
+   ReleaseSysCache(tsdTup);
+
+   return stmt;
+}
+
+static ObjTree *
+deparse_DefineStmt_TSTemplate(Oid objectId, DefineStmt *define)
+{
+   HeapTuple   tstTup;
+   ObjTree    *stmt;
+   ObjTree    *tmp;
+   List       *list;
+   Form_pg_ts_template tstForm;
+
+   tstTup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(objectId));
+   if (!HeapTupleIsValid(tstTup))
+       elog(ERROR, "cache lookup failed for text search template with OID %u",
+            objectId);
+   tstForm = (Form_pg_ts_template) GETSTRUCT(tstTup);
+
+   stmt = new_objtree_VA("CREATE TEXT SEARCH TEMPLATE %{identity}D "
+                         "(%{elems:, }s)", 0);
+
+   append_object_object(stmt, "identity",
+                        new_objtree_for_qualname(tstForm->tmplnamespace,
+                                                 NameStr(tstForm->tmplname)));
+
+   list = NIL;
+
+   if (OidIsValid(tstForm->tmplinit))
+   {
+       tmp = new_objtree_VA("INIT=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        tstForm->tmplinit));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   tmp = new_objtree_VA("LEXIZE=%{procedure}D", 0);
+   append_object_object(tmp, "procedure",
+                        new_objtree_for_qualname_id(ProcedureRelationId,
+                                                    tstForm->tmpllexize));
+   list = lappend(list, new_object_object(tmp));
+
+   append_array_object(stmt, "elems", list);
+
+   ReleaseSysCache(tstTup);
+
+   return stmt;
+}
+
+static ObjTree *
+deparse_DefineStmt_Type(Oid objectId, DefineStmt *define)
+{
+   HeapTuple   typTup;
+   ObjTree    *stmt;
+   ObjTree    *tmp;
+   List       *list;
+   char       *str;
+   Datum       dflt;
+   bool        isnull;
+   Form_pg_type typForm;
+
+   typTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(objectId));
+   if (!HeapTupleIsValid(typTup))
+       elog(ERROR, "cache lookup failed for type with OID %u", objectId);
+   typForm = (Form_pg_type) GETSTRUCT(typTup);
+
+   /* Shortcut processing for shell types. */
+   if (!typForm->typisdefined)
+   {
+       stmt = new_objtree_VA("CREATE TYPE %{identity}D", 0);
+       append_object_object(stmt, "identity",
+                            new_objtree_for_qualname(typForm->typnamespace,
+                                                     NameStr(typForm->typname)));
+       ReleaseSysCache(typTup);
+       return stmt;
+   }
+
+   stmt = new_objtree_VA("CREATE TYPE %{identity}D (%{elems:, }s)", 0);
+
+   append_object_object(stmt, "identity",
+                        new_objtree_for_qualname(typForm->typnamespace,
+                                                 NameStr(typForm->typname)));
+
+   list = NIL;
+
+   /* INPUT */
+   tmp = new_objtree_VA("INPUT=%{procedure}D", 0);
+   append_object_object(tmp, "procedure",
+                        new_objtree_for_qualname_id(ProcedureRelationId,
+                                                    typForm->typinput));
+   list = lappend(list, new_object_object(tmp));
+
+   /* OUTPUT */
+   tmp = new_objtree_VA("OUTPUT=%{procedure}D", 0);
+   append_object_object(tmp, "procedure",
+                        new_objtree_for_qualname_id(ProcedureRelationId,
+                                                    typForm->typoutput));
+   list = lappend(list, new_object_object(tmp));
+
+   /* RECEIVE */
+   if (OidIsValid(typForm->typreceive))
+   {
+       tmp = new_objtree_VA("RECEIVE=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        typForm->typreceive));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   /* SEND */
+   if (OidIsValid(typForm->typsend))
+   {
+       tmp = new_objtree_VA("SEND=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        typForm->typsend));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   /* TYPMOD_IN */
+   if (OidIsValid(typForm->typmodin))
+   {
+       tmp = new_objtree_VA("TYPMOD_IN=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        typForm->typmodin));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   /* TYPMOD_OUT */
+   if (OidIsValid(typForm->typmodout))
+   {
+       tmp = new_objtree_VA("TYPMOD_OUT=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        typForm->typmodout));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   /* ANALYZE */
+   if (OidIsValid(typForm->typanalyze))
+   {
+       tmp = new_objtree_VA("ANALYZE=%{procedure}D", 0);
+       append_object_object(tmp, "procedure",
+                            new_objtree_for_qualname_id(ProcedureRelationId,
+                                                        typForm->typanalyze));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   /* INTERNALLENGTH */
+   if (typForm->typlen == -1)
+   {
+       tmp = new_objtree_VA("INTERNALLENGTH=VARIABLE", 0);
+   }
+   else
+   {
+       tmp = new_objtree_VA("INTERNALLENGTH=%{typlen}n", 1,
+                            "typlen", ObjTypeInteger, typForm->typlen);
+   }
+   list = lappend(list, new_object_object(tmp));
+
+   /* PASSEDBYVALUE */
+   if (typForm->typbyval)
+       list = lappend(list, new_object_object(new_objtree_VA("PASSEDBYVALUE", 0)));
+
+   /* ALIGNMENT */
+   tmp = new_objtree_VA("ALIGNMENT=%{align}s", 0);
+   switch (typForm->typalign)
+   {
+       case 'd':
+           str = "pg_catalog.float8";
+           break;
+       case 'i':
+           str = "pg_catalog.int4";
+           break;
+       case 's':
+           str = "pg_catalog.int2";
+           break;
+       case 'c':
+           str = "pg_catalog.bpchar";
+           break;
+       default:
+           elog(ERROR, "invalid alignment %c", typForm->typalign);
+   }
+   append_string_object(tmp, "align", str);
+   list = lappend(list, new_object_object(tmp));
+
+   tmp = new_objtree_VA("STORAGE=%{storage}s", 0);
+   switch (typForm->typstorage)
+   {
+       case 'p':
+           str = "plain";
+           break;
+       case 'e':
+           str = "external";
+           break;
+       case 'x':
+           str = "extended";
+           break;
+       case 'm':
+           str = "main";
+           break;
+       default:
+           elog(ERROR, "invalid storage specifier %c", typForm->typstorage);
+   }
+   append_string_object(tmp, "storage", str);
+   list = lappend(list, new_object_object(tmp));
+
+   /* CATEGORY */
+   tmp = new_objtree_VA("CATEGORY=%{category}L", 0);
+   append_string_object(tmp, "category",
+                        psprintf("%c", typForm->typcategory));
+   list = lappend(list, new_object_object(tmp));
+
+   /* PREFERRED */
+   if (typForm->typispreferred)
+       list = lappend(list, new_object_object(new_objtree_VA("PREFERRED=true", 0)));
+
+   /* DEFAULT */
+   dflt = SysCacheGetAttr(TYPEOID, typTup,
+                          Anum_pg_type_typdefault,
+                          &isnull);
+   if (!isnull)
+   {
+       tmp = new_objtree_VA("DEFAULT=%{default}L", 0);
+       append_string_object(tmp, "default", TextDatumGetCString(dflt));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   /* ELEMENT */
+   if (OidIsValid(typForm->typelem))
+   {
+       tmp = new_objtree_VA("ELEMENT=%{elem}T", 0);
+       append_object_object(tmp, "elem",
+                            new_objtree_for_type(typForm->typelem, -1));
+       list = lappend(list, new_object_object(tmp));
+   }
+
+   /* DELIMITER */
+   tmp = new_objtree_VA("DELIMITER=%{delim}L", 0);
+   append_string_object(tmp, "delim",
+                        psprintf("%c", typForm->typdelim));
+   list = lappend(list, new_object_object(tmp));
+
+   /* COLLATABLE */
+   if (OidIsValid(typForm->typcollation))
+       list = lappend(list,
+                      new_object_object(new_objtree_VA("COLLATABLE=true", 0)));
+
+   append_array_object(stmt, "elems", list);
+
+   ReleaseSysCache(typTup);
+
+   return stmt;
+}
+
+static ObjTree *
+deparse_DefineStmt(Oid objectId, Node *parsetree, ObjectAddress secondaryObj)
+{
+   DefineStmt *define = (DefineStmt *) parsetree;
+   ObjTree    *defStmt;
+
+   switch (define->kind)
+   {
+       case OBJECT_AGGREGATE:
+           defStmt = deparse_DefineStmt_Aggregate(objectId, define);
+           break;
+
+       case OBJECT_COLLATION:
+           defStmt = deparse_DefineStmt_Collation(objectId, define);
+           break;
+
+       case OBJECT_OPERATOR:
+           defStmt = deparse_DefineStmt_Operator(objectId, define);
+           break;
+
+       case OBJECT_TSCONFIGURATION:
+           defStmt = deparse_DefineStmt_TSConfig(objectId, define, secondaryObj);
+           break;
+
+       case OBJECT_TSPARSER:
+           defStmt = deparse_DefineStmt_TSParser(objectId, define);
+           break;
+
+       case OBJECT_TSDICTIONARY:
+           defStmt = deparse_DefineStmt_TSDictionary(objectId, define);
+           break;
+
+       case OBJECT_TSTEMPLATE:
+           defStmt = deparse_DefineStmt_TSTemplate(objectId, define);
+           break;
+
+       case OBJECT_TYPE:
+           defStmt = deparse_DefineStmt_Type(objectId, define);
+           break;
+
+       default:
+           elog(ERROR, "unsupported object kind");
+           return NULL;
+   }
+
+   return defStmt;
+}
+
 /*
  * deparse_CreateExtensionStmt
  *     deparse a CreateExtensionStmt
@@ -4211,7 +5083,8 @@ deparse_simple_command(StashedCommand *cmd)
 
            /* other local objects */
        case T_DefineStmt:
-           elog(ERROR, "unimplemented deparse of %s", CreateCommandTag(parsetree));
+           command = deparse_DefineStmt(objectId, parsetree,
+                                        cmd->d.simple.secondaryObject);
            break;
 
        case T_IndexStmt: