deparse: Support CREATE SCHEMA/TABLE/SEQUENCE/INDEX/TRIGGER
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Thu, 25 Sep 2014 18:57:48 +0000 (15:57 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 13 Oct 2014 03:19:46 +0000 (00:19 -0300)
src/backend/commands/sequence.c
src/backend/commands/tablecmds.c
src/backend/tcop/deparse_utility.c
src/backend/utils/adt/ruleutils.c
src/include/commands/sequence.h
src/include/utils/ruleutils.h

index 3b89dd009bcbec95f7689f3e7af494fa8a69b881..0c5d5d754f6631b2480c27cb168472062ce5ee79 100644 (file)
@@ -1492,6 +1492,40 @@ process_owned_by(Relation seqrel, List *owned_by)
        relation_close(tablerel, NoLock);
 }
 
+/*
+ * Return sequence parameters, detailed
+ */
+Form_pg_sequence
+get_sequence_values(Oid sequenceId)
+{
+   Buffer      buf;
+   SeqTable    elm;
+   Relation    seqrel;
+   HeapTupleData seqtuple;
+   Form_pg_sequence seq;
+   Form_pg_sequence retSeq;
+
+   retSeq = palloc(sizeof(FormData_pg_sequence));
+
+   /* open and AccessShareLock sequence */
+   init_sequence(sequenceId, &elm, &seqrel);
+
+   if (pg_class_aclcheck(sequenceId, GetUserId(),
+                         ACL_SELECT | ACL_UPDATE | ACL_USAGE) != ACLCHECK_OK)
+       ereport(ERROR,
+               (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                errmsg("permission denied for sequence %s",
+                       RelationGetRelationName(seqrel))));
+
+   seq = read_seq_tuple(elm, seqrel, &buf, &seqtuple);
+
+   memcpy(retSeq, seq, sizeof(FormData_pg_sequence));
+
+   UnlockReleaseBuffer(buf);
+   relation_close(seqrel, NoLock);
+
+   return retSeq;
+}
 
 /*
  * Return sequence parameters, for use by information schema
index 2e289a460fbb397e73b0209aa6726d4f8621c35a..0e4379a39dc43721dfc127fdd948a4e0aaa543ff 100644 (file)
@@ -7940,7 +7940,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
                if (!list_member_oid(tab->changedConstraintOids,
                                     foundObject.objectId))
                {
-                   char       *defstring = pg_get_constraintdef_string(foundObject.objectId);
+                   char       *defstring = pg_get_constraintdef_string(foundObject.objectId,
+                                                                       true);
 
                    /*
                     * Put NORMAL dependencies at the front of the list and
index aa1a93609e08b71c07c7e7bfce38ae8a451474dd..49f5a3ee8567b0fccc9d6dc32858cc932f705ba5 100644 (file)
@@ -543,6 +543,223 @@ new_objtree_for_qualname_id(Oid classId, Oid objectId)
    return qualified;
 }
 
+/*
+ * Return the string representation of the given RELPERSISTENCE value
+ */
+static char *
+get_persistence_str(char persistence)
+{
+   switch (persistence)
+   {
+       case RELPERSISTENCE_TEMP:
+           return "TEMPORARY";
+       case RELPERSISTENCE_UNLOGGED:
+           return "UNLOGGED";
+       case RELPERSISTENCE_PERMANENT:
+           return "";
+       default:
+           return "???";
+   }
+}
+
+/*
+ * deparse_CreateTrigStmt
+ *     Deparse a CreateTrigStmt (CREATE TRIGGER)
+ *
+ * Given a trigger OID and the parsetree that created it, return an ObjTree
+ * representing the creation command.
+ */
+static ObjTree *
+deparse_CreateTrigStmt(Oid objectId, Node *parsetree)
+{
+   CreateTrigStmt *node = (CreateTrigStmt *) parsetree;
+   Relation    pg_trigger;
+   HeapTuple   trigTup;
+   Form_pg_trigger trigForm;
+   ObjTree    *trigger;
+   ObjTree    *tmp;
+   int         tgnargs;
+   List       *list;
+   List       *events;
+
+   pg_trigger = heap_open(TriggerRelationId, AccessShareLock);
+
+   trigTup = get_catalog_object_by_oid(pg_trigger, objectId);
+   trigForm = (Form_pg_trigger) GETSTRUCT(trigTup);
+
+   /*
+    * Some of the elements only make sense for CONSTRAINT TRIGGERs, but it
+    * seems simpler to use a single fmt string for both kinds of triggers.
+    */
+   trigger =
+       new_objtree_VA("CREATE %{constraint}s TRIGGER %{name}I %{time}s %{events: OR }s "
+                      "ON %{relation}D %{from_table}s %{constraint_attrs: }s "
+                      "FOR EACH %{for_each}s %{when}s EXECUTE PROCEDURE %{function}s",
+                      2,
+                      "name", ObjTypeString, node->trigname,
+                      "constraint", ObjTypeString,
+                      node->isconstraint ? "CONSTRAINT" : "");
+
+   if (node->timing == TRIGGER_TYPE_BEFORE)
+       append_string_object(trigger, "time", "BEFORE");
+   else if (node->timing == TRIGGER_TYPE_AFTER)
+       append_string_object(trigger, "time", "AFTER");
+   else if (node->timing == TRIGGER_TYPE_INSTEAD)
+       append_string_object(trigger, "time", "INSTEAD OF");
+   else
+       elog(ERROR, "unrecognized trigger timing value %d", node->timing);
+
+   /*
+    * Decode the events that the trigger fires for.  The output is a list;
+    * in most cases it will just be a string with the even name, but when
+    * there's an UPDATE with a list of columns, we return a JSON object.
+    */
+   events = NIL;
+   if (node->events & TRIGGER_TYPE_INSERT)
+       events = lappend(events, new_string_object("INSERT"));
+   if (node->events & TRIGGER_TYPE_DELETE)
+       events = lappend(events, new_string_object("DELETE"));
+   if (node->events & TRIGGER_TYPE_TRUNCATE)
+       events = lappend(events, new_string_object("TRUNCATE"));
+   if (node->events & TRIGGER_TYPE_UPDATE)
+   {
+       if (node->columns == NIL)
+       {
+           events = lappend(events, new_string_object("UPDATE"));
+       }
+       else
+       {
+           ObjTree    *update;
+           ListCell   *cell;
+           List       *cols = NIL;
+
+           /*
+            * Currently only UPDATE OF can be objects in the output JSON, but
+            * we add a "kind" element so that user code can distinguish
+            * possible future new event types.
+            */
+           update = new_objtree_VA("UPDATE OF %{columns:, }I",
+                                   1, "kind", ObjTypeString, "update_of");
+
+           foreach(cell, node->columns)
+           {
+               char   *colname = strVal(lfirst(cell));
+
+               cols = lappend(cols,
+                              new_string_object(colname));
+           }
+
+           append_array_object(update, "columns", cols);
+
+           events = lappend(events,
+                            new_object_object(update));
+       }
+   }
+   append_array_object(trigger, "events", events);
+
+   tmp = new_objtree_for_qualname_id(RelationRelationId,
+                                     trigForm->tgrelid);
+   append_object_object(trigger, "relation", tmp);
+
+   tmp = new_objtree_VA("FROM %{relation}D", 0);
+   if (trigForm->tgconstrrelid)
+   {
+       ObjTree    *rel;
+
+       rel = new_objtree_for_qualname_id(RelationRelationId,
+                                         trigForm->tgconstrrelid);
+       append_object_object(tmp, "relation", rel);
+   }
+   else
+       append_bool_object(tmp, "present", false);
+   append_object_object(trigger, "from_table", tmp);
+
+   list = NIL;
+   if (node->deferrable)
+       list = lappend(list,
+                      new_string_object("DEFERRABLE"));
+   if (node->initdeferred)
+       list = lappend(list,
+                      new_string_object("INITIALLY DEFERRED"));
+   append_array_object(trigger, "constraint_attrs", list);
+
+   append_string_object(trigger, "for_each",
+                        node->row ? "ROW" : "STATEMENT");
+
+   tmp = new_objtree_VA("WHEN (%{clause}s)", 0);
+   if (node->whenClause)
+   {
+       Node       *whenClause;
+       Datum       value;
+       bool        isnull;
+
+       value = fastgetattr(trigTup, Anum_pg_trigger_tgqual,
+                           RelationGetDescr(pg_trigger), &isnull);
+       if (isnull)
+           elog(ERROR, "bogus NULL tgqual");
+
+       whenClause = stringToNode(TextDatumGetCString(value));
+       append_string_object(tmp, "clause",
+                            pg_get_trigger_whenclause(trigForm,
+                                                      whenClause,
+                                                      false));
+   }
+   else
+       append_bool_object(tmp, "present", false);
+   append_object_object(trigger, "when", tmp);
+
+   tmp = new_objtree_VA("%{funcname}D(%{args:, }L)",
+                        1, "funcname", ObjTypeObject,
+                        new_objtree_for_qualname_id(ProcedureRelationId,
+                                                    trigForm->tgfoid));
+   list = NIL;
+   tgnargs = trigForm->tgnargs;
+   if (tgnargs > 0)
+   {
+       bytea  *tgargs;
+       char   *argstr;
+       bool    isnull;
+       int     findx;
+       int     lentgargs;
+       char   *p;
+
+       tgargs = DatumGetByteaP(fastgetattr(trigTup,
+                                           Anum_pg_trigger_tgargs,
+                                           RelationGetDescr(pg_trigger),
+                                           &isnull));
+       if (isnull)
+           elog(ERROR, "invalid NULL tgargs");
+       argstr = (char *) VARDATA(tgargs);
+       lentgargs = VARSIZE_ANY_EXHDR(tgargs);
+
+       p = argstr;
+       for (findx = 0; findx < tgnargs; findx++)
+       {
+           size_t  tlen;
+
+           /* verify that the argument encoding is correct */
+           tlen = strlen(p);
+           if (p + tlen >= argstr + lentgargs)
+               ereport(ERROR,
+                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("invalid argument string (%s) for trigger \"%s\"",
+                               argstr, NameStr(trigForm->tgname))));
+
+           list = lappend(list, new_string_object(p));
+
+           p += tlen + 1;
+       }
+   }
+
+   append_array_object(tmp, "args", list);     /* might be NIL */
+
+   append_object_object(trigger, "function", tmp);
+
+   heap_close(pg_trigger, AccessShareLock);
+
+   return trigger;
+}
+
 /*
  * deparse_ColumnDef
  *     Subroutine for CREATE TABLE deparsing
@@ -769,6 +986,333 @@ deparseTableElements(Relation relation, List *tableElements, List *dpcontext,
    return elements;
 }
 
+/*
+ * obtainConstraints
+ *     Subroutine for CREATE TABLE/CREATE DOMAIN deparsing
+ *
+ * Given a table OID or domain OID, obtain its constraints and append them to
+ * the given elements list.  The updated list is returned.
+ *
+ * This works for typed tables, regular tables, and domains.
+ *
+ * Note that CONSTRAINT_FOREIGN constraints are always ignored.
+ */
+static List *
+obtainConstraints(List *elements, Oid relationId, Oid domainId)
+{
+   Relation    conRel;
+   ScanKeyData key;
+   SysScanDesc scan;
+   HeapTuple   tuple;
+   ObjTree    *tmp;
+
+   /* only one may be valid */
+   Assert(OidIsValid(relationId) ^ OidIsValid(domainId));
+
+   /*
+    * scan pg_constraint to fetch all constraints linked to the given
+    * relation.
+    */
+   conRel = heap_open(ConstraintRelationId, AccessShareLock);
+   if (OidIsValid(relationId))
+   {
+       ScanKeyInit(&key,
+                   Anum_pg_constraint_conrelid,
+                   BTEqualStrategyNumber, F_OIDEQ,
+                   ObjectIdGetDatum(relationId));
+       scan = systable_beginscan(conRel, ConstraintRelidIndexId,
+                                 true, NULL, 1, &key);
+   }
+   else
+   {
+       Assert(OidIsValid(domainId));
+       ScanKeyInit(&key,
+                   Anum_pg_constraint_contypid,
+                   BTEqualStrategyNumber, F_OIDEQ,
+                   ObjectIdGetDatum(domainId));
+       scan = systable_beginscan(conRel, ConstraintTypidIndexId,
+                                 true, NULL, 1, &key);
+   }
+
+   /*
+    * For each constraint, add a node to the list of table elements.  In
+    * these nodes we include not only the printable information ("fmt"), but
+    * also separate attributes to indicate the type of constraint, for
+    * automatic processing.
+    */
+   while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+   {
+       Form_pg_constraint constrForm;
+       char       *contype;
+
+       constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
+
+       switch (constrForm->contype)
+       {
+           case CONSTRAINT_CHECK:
+               contype = "check";
+               break;
+           case CONSTRAINT_FOREIGN:
+               continue;   /* not here */
+           case CONSTRAINT_PRIMARY:
+               contype = "primary key";
+               break;
+           case CONSTRAINT_UNIQUE:
+               contype = "unique";
+               break;
+           case CONSTRAINT_TRIGGER:
+               contype = "trigger";
+               break;
+           case CONSTRAINT_EXCLUSION:
+               contype = "exclusion";
+               break;
+           default:
+               elog(ERROR, "unrecognized constraint type");
+       }
+
+       /*
+        * "type" and "contype" are not part of the printable output, but are
+        * useful to programmatically distinguish these from columns and among
+        * different constraint types.
+        *
+        * XXX it might be useful to also list the column names in a PK, etc.
+        */
+       tmp = new_objtree_VA("CONSTRAINT %{name}I %{definition}s",
+                            4,
+                            "type", ObjTypeString, "constraint",
+                            "contype", ObjTypeString, contype,
+                        "name", ObjTypeString, NameStr(constrForm->conname),
+                            "definition", ObjTypeString,
+                         pg_get_constraintdef_string(HeapTupleGetOid(tuple),
+                                                     false));
+       elements = lappend(elements, new_object_object(tmp));
+   }
+
+   systable_endscan(scan);
+   heap_close(conRel, AccessShareLock);
+
+   return elements;
+}
+
+/*
+ * deparse_CreateStmt
+ *     Deparse a CreateStmt (CREATE TABLE)
+ *
+ * Given a table OID and the parsetree that created it, return an ObjTree
+ * representing the creation command.
+ */
+static ObjTree *
+deparse_CreateStmt(Oid objectId, Node *parsetree)
+{
+   CreateStmt *node = (CreateStmt *) parsetree;
+   Relation    relation = relation_open(objectId, AccessShareLock);
+   List       *dpcontext;
+   ObjTree    *createStmt;
+   ObjTree    *tmp;
+   List       *list;
+   ListCell   *cell;
+   char       *fmtstr;
+
+   /*
+    * Typed tables use a slightly different format string: we must not put
+    * table_elements with parents directly in the fmt string, because if
+    * there are no options the parens must not be emitted; and also, typed
+    * tables do not allow for inheritance.
+    */
+   if (node->ofTypename)
+       fmtstr = "CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D "
+           "OF %{of_type}T %{table_elements}s "
+           "%{on_commit}s WITH (%{with:, }s) %{tablespace}s";
+   else
+       fmtstr = "CREATE %{persistence}s TABLE %{if_not_exists}s %{identity}D "
+           "(%{table_elements:, }s) %{inherits}s "
+           "%{on_commit}s WITH (%{with:, }s) %{tablespace}s";
+
+   createStmt =
+       new_objtree_VA(fmtstr, 1,
+                      "persistence", ObjTypeString,
+                      get_persistence_str(relation->rd_rel->relpersistence));
+
+   tmp = new_objtree_for_qualname(relation->rd_rel->relnamespace,
+                                  RelationGetRelationName(relation));
+   append_object_object(createStmt, "identity", tmp);
+
+   append_string_object(createStmt, "if_not_exists",
+                        node->if_not_exists ? "IF NOT EXISTS" : "");
+
+   dpcontext = deparse_context_for(RelationGetRelationName(relation),
+                                   objectId);
+
+   if (node->ofTypename)
+   {
+       List       *tableelts = NIL;
+
+       /*
+        * We can't put table elements directly in the fmt string as an array
+        * surrounded by parens here, because an empty clause would cause a
+        * syntax error.  Therefore, we use an indirection element and set
+        * present=false when there are no elements.
+        */
+       append_string_object(createStmt, "table_kind", "typed");
+
+       tmp = new_objtree_for_type(relation->rd_rel->reloftype, -1);
+       append_object_object(createStmt, "of_type", tmp);
+
+       tableelts = deparseTableElements(relation, node->tableElts, dpcontext,
+                                        true,      /* typed table */
+                                        false);    /* not composite */
+       tableelts = obtainConstraints(tableelts, objectId, InvalidOid);
+       if (tableelts == NIL)
+           tmp = new_objtree_VA("", 1,
+                                "present", ObjTypeBool, false);
+       else
+           tmp = new_objtree_VA("(%{elements:, }s)", 1,
+                                "elements", ObjTypeArray, tableelts);
+       append_object_object(createStmt, "table_elements", tmp);
+   }
+   else
+   {
+       List       *tableelts = NIL;
+
+       /*
+        * There is no need to process LIKE clauses separately; they have
+        * already been transformed into columns and constraints.
+        */
+       append_string_object(createStmt, "table_kind", "plain");
+
+       /*
+        * Process table elements: column definitions and constraints.  Only
+        * the column definitions are obtained from the parse node itself.  To
+        * get constraints we rely on pg_constraint, because the parse node
+        * might be missing some things such as the name of the constraints.
+        */
+       tableelts = deparseTableElements(relation, node->tableElts, dpcontext,
+                                        false,     /* not typed table */
+                                        false);    /* not composite */
+       tableelts = obtainConstraints(tableelts, objectId, InvalidOid);
+
+       append_array_object(createStmt, "table_elements", tableelts);
+
+       /*
+        * Add inheritance specification.  We cannot simply scan the list of
+        * parents from the parser node, because that may lack the actual
+        * qualified names of the parent relations.  Rather than trying to
+        * re-resolve them from the information in the parse node, it seems
+        * more accurate and convenient to grab it from pg_inherits.
+        */
+       tmp = new_objtree_VA("INHERITS (%{parents:, }D)", 0);
+       if (list_length(node->inhRelations) > 0)
+       {
+           List       *parents = NIL;
+           Relation    inhRel;
+           SysScanDesc scan;
+           ScanKeyData key;
+           HeapTuple   tuple;
+
+           inhRel = heap_open(InheritsRelationId, RowExclusiveLock);
+
+           ScanKeyInit(&key,
+                       Anum_pg_inherits_inhrelid,
+                       BTEqualStrategyNumber, F_OIDEQ,
+                       ObjectIdGetDatum(objectId));
+
+           scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId,
+                                     true, NULL, 1, &key);
+
+           while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+           {
+               ObjTree    *parent;
+               Form_pg_inherits formInh = (Form_pg_inherits) GETSTRUCT(tuple);
+
+               parent = new_objtree_for_qualname_id(RelationRelationId,
+                                                    formInh->inhparent);
+               parents = lappend(parents, new_object_object(parent));
+           }
+
+           systable_endscan(scan);
+           heap_close(inhRel, RowExclusiveLock);
+
+           append_array_object(tmp, "parents", parents);
+       }
+       else
+       {
+           append_null_object(tmp, "parents");
+           append_bool_object(tmp, "present", false);
+       }
+       append_object_object(createStmt, "inherits", tmp);
+   }
+
+   tmp = new_objtree_VA("TABLESPACE %{tablespace}I", 0);
+   if (node->tablespacename)
+       append_string_object(tmp, "tablespace", node->tablespacename);
+   else
+   {
+       append_null_object(tmp, "tablespace");
+       append_bool_object(tmp, "present", false);
+   }
+   append_object_object(createStmt, "tablespace", tmp);
+
+   tmp = new_objtree_VA("ON COMMIT %{on_commit_value}s", 0);
+   switch (node->oncommit)
+   {
+       case ONCOMMIT_DROP:
+           append_string_object(tmp, "on_commit_value", "DROP");
+           break;
+
+       case ONCOMMIT_DELETE_ROWS:
+           append_string_object(tmp, "on_commit_value", "DELETE ROWS");
+           break;
+
+       case ONCOMMIT_PRESERVE_ROWS:
+           append_string_object(tmp, "on_commit_value", "PRESERVE ROWS");
+           break;
+
+       case ONCOMMIT_NOOP:
+           append_null_object(tmp, "on_commit_value");
+           append_bool_object(tmp, "present", false);
+           break;
+   }
+   append_object_object(createStmt, "on_commit", tmp);
+
+   /*
+    * WITH clause.  We always emit one, containing at least the OIDS option.
+    * That way we don't depend on the default value for default_with_oids.
+    * We can skip emitting other options if there don't appear in the parse
+    * node.
+    */
+   tmp = new_objtree_VA("oids=%{value}s", 2,
+                        "option", ObjTypeString, "oids",
+                        "value", ObjTypeString,
+                        relation->rd_rel->relhasoids ? "ON" : "OFF");
+   list = list_make1(new_object_object(tmp));
+   foreach(cell, node->options)
+   {
+       DefElem *opt = (DefElem *) lfirst(cell);
+       char   *fmt;
+       char   *value;
+
+       /* already handled above */
+       if (strcmp(opt->defname, "oids") == 0)
+           continue;
+
+       if (opt->defnamespace)
+           fmt = psprintf("%s.%s=%%{value}s", opt->defnamespace, opt->defname);
+       else
+           fmt = psprintf("%s=%%{value}s", opt->defname);
+       value = opt->arg ? defGetString(opt) :
+           defGetBoolean(opt) ? "TRUE" : "FALSE";
+       tmp = new_objtree_VA(fmt, 2,
+                            "option", ObjTypeString, opt->defname,
+                            "value", ObjTypeString, value);
+       list = lappend(list, new_object_object(tmp));
+   }
+   append_array_object(createStmt, "with", list);
+
+   relation_close(relation, AccessShareLock);
+
+   return createStmt;
+}
+
 /*
  * deparse_CompositeTypeStmt
  *     Deparse a CompositeTypeStmt (CREATE TYPE AS)
@@ -837,6 +1381,404 @@ deparse_CreateEnumStmt(Oid objectId, Node *parsetree)
    return enumtype;
 }
 
+static inline ObjElem *
+deparse_Seq_Cache(ObjTree *parent, Form_pg_sequence seqdata)
+{
+   ObjTree    *tmp;
+   char       *tmpstr;
+
+   tmpstr = psprintf(INT64_FORMAT, seqdata->cache_value);
+   tmp = new_objtree_VA("CACHE %{value}s",
+                        2,
+                        "clause", ObjTypeString, "cache",
+                        "value", ObjTypeString, tmpstr);
+   return new_object_object(tmp);
+}
+
+static inline ObjElem *
+deparse_Seq_Cycle(ObjTree *parent, Form_pg_sequence seqdata)
+{
+   ObjTree    *tmp;
+
+   tmp = new_objtree_VA("%{no}s CYCLE",
+                        2,
+                        "clause", ObjTypeString, "cycle",
+                        "no", ObjTypeString,
+                        seqdata->is_cycled ? "" : "NO");
+   return new_object_object(tmp);
+}
+
+static inline ObjElem *
+deparse_Seq_IncrementBy(ObjTree *parent, Form_pg_sequence seqdata)
+{
+   ObjTree    *tmp;
+   char       *tmpstr;
+
+   tmpstr = psprintf(INT64_FORMAT, seqdata->increment_by);
+   tmp = new_objtree_VA("INCREMENT BY %{value}s",
+                        2,
+                        "clause", ObjTypeString, "increment_by",
+                        "value", ObjTypeString, tmpstr);
+   return new_object_object(tmp);
+}
+
+static inline ObjElem *
+deparse_Seq_Minvalue(ObjTree *parent, Form_pg_sequence seqdata)
+{
+   ObjTree    *tmp;
+   char       *tmpstr;
+
+   tmpstr = psprintf(INT64_FORMAT, seqdata->min_value);
+   tmp = new_objtree_VA("MINVALUE %{value}s",
+                        2,
+                        "clause", ObjTypeString, "minvalue",
+                        "value", ObjTypeString, tmpstr);
+   return new_object_object(tmp);
+}
+
+static inline ObjElem *
+deparse_Seq_Maxvalue(ObjTree *parent, Form_pg_sequence seqdata)
+{
+   ObjTree    *tmp;
+   char       *tmpstr;
+
+   tmpstr = psprintf(INT64_FORMAT, seqdata->max_value);
+   tmp = new_objtree_VA("MAXVALUE %{value}s",
+                        2,
+                        "clause", ObjTypeString, "maxvalue",
+                        "value", ObjTypeString, tmpstr);
+   return new_object_object(tmp);
+}
+
+static inline ObjElem *
+deparse_Seq_Startwith(ObjTree *parent, Form_pg_sequence seqdata)
+{
+   ObjTree    *tmp;
+   char       *tmpstr;
+
+   tmpstr = psprintf(INT64_FORMAT, seqdata->start_value);
+   tmp = new_objtree_VA("START WITH %{value}s",
+                        2,
+                        "clause", ObjTypeString, "start",
+                        "value", ObjTypeString, tmpstr);
+   return new_object_object(tmp);
+}
+
+static inline ObjElem *
+deparse_Seq_Restart(ObjTree *parent, Form_pg_sequence seqdata)
+{
+   ObjTree    *tmp;
+   char       *tmpstr;
+
+   tmpstr = psprintf(INT64_FORMAT, seqdata->last_value);
+   tmp = new_objtree_VA("RESTART %{value}s",
+                        2,
+                        "clause", ObjTypeString, "restart",
+                        "value", ObjTypeString, tmpstr);
+   return new_object_object(tmp);
+}
+
+static ObjElem *
+deparse_Seq_OwnedBy(ObjTree *parent, Oid sequenceId)
+{
+   ObjTree    *ownedby = NULL;
+   Relation    depRel;
+   SysScanDesc scan;
+   ScanKeyData keys[3];
+   HeapTuple   tuple;
+
+   depRel = heap_open(DependRelationId, AccessShareLock);
+   ScanKeyInit(&keys[0],
+               Anum_pg_depend_classid,
+               BTEqualStrategyNumber, F_OIDEQ,
+               ObjectIdGetDatum(RelationRelationId));
+   ScanKeyInit(&keys[1],
+               Anum_pg_depend_objid,
+               BTEqualStrategyNumber, F_OIDEQ,
+               ObjectIdGetDatum(sequenceId));
+   ScanKeyInit(&keys[2],
+               Anum_pg_depend_objsubid,
+               BTEqualStrategyNumber, F_INT4EQ,
+               Int32GetDatum(0));
+
+   scan = systable_beginscan(depRel, DependDependerIndexId, true,
+                             NULL, 3, keys);
+   while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+   {
+       Oid         ownerId;
+       Form_pg_depend depform;
+       ObjTree    *tmp;
+       char       *colname;
+
+       depform = (Form_pg_depend) GETSTRUCT(tuple);
+
+       /* only consider AUTO dependencies on pg_class */
+       if (depform->deptype != DEPENDENCY_AUTO)
+           continue;
+       if (depform->refclassid != RelationRelationId)
+           continue;
+       if (depform->refobjsubid <= 0)
+           continue;
+
+       ownerId = depform->refobjid;
+       colname = get_attname(ownerId, depform->refobjsubid);
+       if (colname == NULL)
+           continue;
+
+       tmp = new_objtree_for_qualname_id(RelationRelationId, ownerId);
+       append_string_object(tmp, "attrname", colname);
+       ownedby = new_objtree_VA("OWNED BY %{owner}D",
+                                2,
+                                "clause", ObjTypeString, "owned",
+                                "owner", ObjTypeObject, tmp);
+   }
+
+   systable_endscan(scan);
+   relation_close(depRel, AccessShareLock);
+
+   /*
+    * If there's no owner column, emit an empty OWNED BY element, set up so
+    * that it won't print anything.
+    */
+   if (!ownedby)
+       /* XXX this shouldn't happen ... */
+       ownedby = new_objtree_VA("OWNED BY %{owner}D",
+                                3,
+                                "clause", ObjTypeString, "owned",
+                                "owner", ObjTypeNull,
+                                "present", ObjTypeBool, false);
+   return new_object_object(ownedby);
+}
+
+/*
+ * deparse_CreateSeqStmt
+ *     deparse a CreateSeqStmt
+ *
+ * Given a sequence OID and the parsetree that created it, return an ObjTree
+ * representing the creation command.
+ */
+static ObjTree *
+deparse_CreateSeqStmt(Oid objectId, Node *parsetree)
+{
+   ObjTree    *createSeq;
+   ObjTree    *tmp;
+   Relation    relation = relation_open(objectId, AccessShareLock);
+   Form_pg_sequence seqdata;
+   List       *elems = NIL;
+
+   seqdata = get_sequence_values(objectId);
+
+   createSeq =
+       new_objtree_VA("CREATE %{persistence}s SEQUENCE %{identity}D "
+                      "%{definition: }s",
+                      1,
+                      "persistence", ObjTypeString,
+                      get_persistence_str(relation->rd_rel->relpersistence));
+
+   tmp = new_objtree_for_qualname(relation->rd_rel->relnamespace,
+                                  RelationGetRelationName(relation));
+   append_object_object(createSeq, "identity", tmp);
+
+   /* definition elements */
+   elems = lappend(elems, deparse_Seq_Cache(createSeq, seqdata));
+   elems = lappend(elems, deparse_Seq_Cycle(createSeq, seqdata));
+   elems = lappend(elems, deparse_Seq_IncrementBy(createSeq, seqdata));
+   elems = lappend(elems, deparse_Seq_Minvalue(createSeq, seqdata));
+   elems = lappend(elems, deparse_Seq_Maxvalue(createSeq, seqdata));
+   elems = lappend(elems, deparse_Seq_Startwith(createSeq, seqdata));
+   elems = lappend(elems, deparse_Seq_Restart(createSeq, seqdata));
+   /* we purposefully do not emit OWNED BY here */
+
+   append_array_object(createSeq, "definition", elems);
+
+   relation_close(relation, AccessShareLock);
+
+   return createSeq;
+}
+
+/*
+ * deparse_AlterSeqStmt
+ *     deparse an AlterSeqStmt
+ *
+ * Given a sequence OID and a parsetree that modified it, return an ObjTree
+ * representing the alter command.
+ */
+static ObjTree *
+deparse_AlterSeqStmt(Oid objectId, Node *parsetree)
+{
+   ObjTree    *alterSeq;
+   ObjTree    *tmp;
+   Relation    relation = relation_open(objectId, AccessShareLock);
+   Form_pg_sequence seqdata;
+   List       *elems = NIL;
+   ListCell   *cell;
+
+   seqdata = get_sequence_values(objectId);
+
+   alterSeq =
+       new_objtree_VA("ALTER SEQUENCE %{identity}D %{definition: }s", 0);
+   tmp = new_objtree_for_qualname(relation->rd_rel->relnamespace,
+                                  RelationGetRelationName(relation));
+   append_object_object(alterSeq, "identity", tmp);
+
+   foreach(cell, ((AlterSeqStmt *) parsetree)->options)
+   {
+       DefElem *elem = (DefElem *) lfirst(cell);
+       ObjElem *newelm;
+
+       if (strcmp(elem->defname, "cache") == 0)
+           newelm = deparse_Seq_Cache(alterSeq, seqdata);
+       else if (strcmp(elem->defname, "cycle") == 0)
+           newelm = deparse_Seq_Cycle(alterSeq, seqdata);
+       else if (strcmp(elem->defname, "increment") == 0)
+           newelm = deparse_Seq_IncrementBy(alterSeq, seqdata);
+       else if (strcmp(elem->defname, "minvalue") == 0)
+           newelm = deparse_Seq_Minvalue(alterSeq, seqdata);
+       else if (strcmp(elem->defname, "maxvalue") == 0)
+           newelm = deparse_Seq_Maxvalue(alterSeq, seqdata);
+       else if (strcmp(elem->defname, "start") == 0)
+           newelm = deparse_Seq_Startwith(alterSeq, seqdata);
+       else if (strcmp(elem->defname, "restart") == 0)
+           newelm = deparse_Seq_Restart(alterSeq, seqdata);
+       else if (strcmp(elem->defname, "owned_by") == 0)
+           newelm = deparse_Seq_OwnedBy(alterSeq, objectId);
+       else
+           elog(ERROR, "invalid sequence option %s", elem->defname);
+
+       elems = lappend(elems, newelm);
+   }
+
+   append_array_object(alterSeq, "definition", elems);
+
+   relation_close(relation, AccessShareLock);
+
+   return alterSeq;
+}
+
+/*
+ * deparse_IndexStmt
+ *     deparse an IndexStmt
+ *
+ * Given an index OID and the parsetree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * If the index corresponds to a constraint, NULL is returned.
+ */
+static ObjTree *
+deparse_IndexStmt(Oid objectId, Node *parsetree)
+{
+   IndexStmt  *node = (IndexStmt *) parsetree;
+   ObjTree    *indexStmt;
+   ObjTree    *tmp;
+   Relation    idxrel;
+   Relation    heaprel;
+   char       *index_am;
+   char       *definition;
+   char       *reloptions;
+   char       *tablespace;
+   char       *whereClause;
+
+   if (node->primary || node->isconstraint)
+   {
+       /*
+        * indexes for PRIMARY KEY and other constraints are output
+        * separately; return empty here.
+        */
+       return NULL;
+   }
+
+   idxrel = relation_open(objectId, AccessShareLock);
+   heaprel = relation_open(idxrel->rd_index->indrelid, AccessShareLock);
+
+   pg_get_indexdef_detailed(objectId,
+                            &index_am, &definition, &reloptions,
+                            &tablespace, &whereClause);
+
+   indexStmt =
+       new_objtree_VA("CREATE %{unique}s INDEX %{concurrently}s %{name}I "
+                      "ON %{table}D USING %{index_am}s (%{definition}s) "
+                      "%{with}s %{tablespace}s %{where_clause}s",
+                      5,
+                      "unique", ObjTypeString, node->unique ? "UNIQUE" : "",
+                      "concurrently", ObjTypeString,
+                      node->concurrent ? "CONCURRENTLY" : "",
+                      "name", ObjTypeString, RelationGetRelationName(idxrel),
+                      "definition", ObjTypeString, definition,
+                      "index_am", ObjTypeString, index_am);
+
+   tmp = new_objtree_for_qualname(heaprel->rd_rel->relnamespace,
+                                  RelationGetRelationName(heaprel));
+   append_object_object(indexStmt, "table", tmp);
+
+   /* reloptions */
+   tmp = new_objtree_VA("WITH (%{opts}s)", 0);
+   if (reloptions)
+       append_string_object(tmp, "opts", reloptions);
+   else
+       append_bool_object(tmp, "present", false);
+   append_object_object(indexStmt, "with", tmp);
+
+   /* tablespace */
+   tmp = new_objtree_VA("TABLESPACE %{tablespace}s", 0);
+   if (tablespace)
+       append_string_object(tmp, "tablespace", tablespace);
+   else
+       append_bool_object(tmp, "present", false);
+   append_object_object(indexStmt, "tablespace", tmp);
+
+   /* WHERE clause */
+   tmp = new_objtree_VA("WHERE %{where}s", 0);
+   if (whereClause)
+       append_string_object(tmp, "where", whereClause);
+   else
+       append_bool_object(tmp, "present", false);
+   append_object_object(indexStmt, "where_clause", tmp);
+
+   heap_close(idxrel, AccessShareLock);
+   heap_close(heaprel, AccessShareLock);
+
+   return indexStmt;
+}
+
+/*
+ * deparse_CreateSchemaStmt
+ *     deparse a CreateSchemaStmt
+ *
+ * Given a schema OID and the parsetree that created it, return an ObjTree
+ * representing the creation command.
+ *
+ * Note we don't output the schema elements given in the creation command.
+ * They must be output separately.  (In the current implementation,
+ * CreateSchemaCommand passes them back to ProcessUtility, which will lead to
+ * this file if appropriate.)
+ */
+static ObjTree *
+deparse_CreateSchemaStmt(Oid objectId, Node *parsetree)
+{
+   CreateSchemaStmt *node = (CreateSchemaStmt *) parsetree;
+   ObjTree    *createSchema;
+   ObjTree    *auth;
+
+   createSchema =
+       new_objtree_VA("CREATE SCHEMA %{if_not_exists}s %{name}I %{authorization}s",
+                      2,
+                      "name", ObjTypeString, node->schemaname,
+                      "if_not_exists", ObjTypeString,
+                      node->if_not_exists ? "IF NOT EXISTS" : "");
+
+   auth = new_objtree_VA("AUTHORIZATION %{authorization_role}I", 0);
+   if (node->authid)
+       append_string_object(auth, "authorization_role", node->authid);
+   else
+   {
+       append_null_object(auth, "authorization_role");
+       append_bool_object(auth, "present", false);
+   }
+   append_object_object(createSchema, "authorization", auth);
+
+   return createSchema;
+}
+
 /*
  * Handle deparsing of simple commands.
  *
@@ -859,11 +1801,11 @@ deparse_simple_command(StashedCommand *cmd)
    switch (nodeTag(parsetree))
    {
        case T_CreateSchemaStmt:
-           command = NULL;
+           command = deparse_CreateSchemaStmt(objectId, parsetree);
            break;
 
        case T_CreateStmt:
-           command = NULL;
+           command = deparse_CreateStmt(objectId, parsetree);
            break;
 
        case T_CreateForeignTableStmt:
@@ -886,7 +1828,7 @@ deparse_simple_command(StashedCommand *cmd)
            break;
 
        case T_IndexStmt:
-           command = NULL;
+           command = deparse_IndexStmt(objectId, parsetree);
            break;
 
        case T_CreateExtensionStmt:
@@ -967,11 +1909,11 @@ deparse_simple_command(StashedCommand *cmd)
            break;
 
        case T_CreateSeqStmt:
-           command = NULL;
+           command = deparse_CreateSeqStmt(objectId, parsetree);
            break;
 
        case T_AlterSeqStmt:
-           command = NULL;
+           command = deparse_AlterSeqStmt(objectId, parsetree);
            break;
 
        case T_CreateTableAsStmt:
@@ -983,7 +1925,7 @@ deparse_simple_command(StashedCommand *cmd)
            break;
 
        case T_CreateTrigStmt:
-           command = NULL;
+           command = deparse_CreateTrigStmt(objectId, parsetree);
            break;
 
        case T_CreatePLangStmt:
index 1fc7e340a06b83a6cc7903bb45dd303b18c875a7..1907a086c35b5c8976d5aaa3c5bead4b6a2f9774 100644 (file)
@@ -821,59 +821,12 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
    if (!isnull)
    {
        Node       *qual;
-       char        relkind;
-       deparse_context context;
-       deparse_namespace dpns;
-       RangeTblEntry *oldrte;
-       RangeTblEntry *newrte;
-
-       appendStringInfoString(&buf, "WHEN (");
+       char       *qualstr;
 
        qual = stringToNode(TextDatumGetCString(value));
+       qualstr = pg_get_trigger_whenclause(trigrec, qual, pretty);
 
-       relkind = get_rel_relkind(trigrec->tgrelid);
-
-       /* Build minimal OLD and NEW RTEs for the rel */
-       oldrte = makeNode(RangeTblEntry);
-       oldrte->rtekind = RTE_RELATION;
-       oldrte->relid = trigrec->tgrelid;
-       oldrte->relkind = relkind;
-       oldrte->alias = makeAlias("old", NIL);
-       oldrte->eref = oldrte->alias;
-       oldrte->lateral = false;
-       oldrte->inh = false;
-       oldrte->inFromCl = true;
-
-       newrte = makeNode(RangeTblEntry);
-       newrte->rtekind = RTE_RELATION;
-       newrte->relid = trigrec->tgrelid;
-       newrte->relkind = relkind;
-       newrte->alias = makeAlias("new", NIL);
-       newrte->eref = newrte->alias;
-       newrte->lateral = false;
-       newrte->inh = false;
-       newrte->inFromCl = true;
-
-       /* Build two-element rtable */
-       memset(&dpns, 0, sizeof(dpns));
-       dpns.rtable = list_make2(oldrte, newrte);
-       dpns.ctes = NIL;
-       set_rtable_names(&dpns, NIL, NULL);
-       set_simple_column_names(&dpns);
-
-       /* Set up context with one-deep namespace stack */
-       context.buf = &buf;
-       context.namespaces = list_make1(&dpns);
-       context.windowClause = NIL;
-       context.windowTList = NIL;
-       context.varprefix = true;
-       context.prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
-       context.wrapColumn = WRAP_COLUMN_DEFAULT;
-       context.indentLevel = PRETTYINDENT_STD;
-
-       get_rule_expr(qual, &context, false);
-
-       appendStringInfoString(&buf, ") ");
+       appendStringInfo(&buf, "WHEN (%s) ", qualstr);
    }
 
    appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
@@ -914,6 +867,63 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
    return buf.data;
 }
 
+char *
+pg_get_trigger_whenclause(Form_pg_trigger trigrec, Node *whenClause, bool pretty)
+{
+   StringInfoData buf;
+   char        relkind;
+   deparse_context context;
+   deparse_namespace dpns;
+   RangeTblEntry *oldrte;
+   RangeTblEntry *newrte;
+
+   initStringInfo(&buf);
+
+   relkind = get_rel_relkind(trigrec->tgrelid);
+
+   /* Build minimal OLD and NEW RTEs for the rel */
+   oldrte = makeNode(RangeTblEntry);
+   oldrte->rtekind = RTE_RELATION;
+   oldrte->relid = trigrec->tgrelid;
+   oldrte->relkind = relkind;
+   oldrte->alias = makeAlias("old", NIL);
+   oldrte->eref = oldrte->alias;
+   oldrte->lateral = false;
+   oldrte->inh = false;
+   oldrte->inFromCl = true;
+
+   newrte = makeNode(RangeTblEntry);
+   newrte->rtekind = RTE_RELATION;
+   newrte->relid = trigrec->tgrelid;
+   newrte->relkind = relkind;
+   newrte->alias = makeAlias("new", NIL);
+   newrte->eref = newrte->alias;
+   newrte->lateral = false;
+   newrte->inh = false;
+   newrte->inFromCl = true;
+
+   /* Build two-element rtable */
+   memset(&dpns, 0, sizeof(dpns));
+   dpns.rtable = list_make2(oldrte, newrte);
+   dpns.ctes = NIL;
+   set_rtable_names(&dpns, NIL, NULL);
+   set_simple_column_names(&dpns);
+
+   /* Set up context with one-deep namespace stack */
+   context.buf = &buf;
+   context.namespaces = list_make1(&dpns);
+   context.windowClause = NIL;
+   context.windowTList = NIL;
+   context.varprefix = true;
+   context.prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
+   context.wrapColumn = WRAP_COLUMN_DEFAULT;
+   context.indentLevel = PRETTYINDENT_STD;
+
+   get_rule_expr(whenClause, &context, false);
+
+   return buf.data;
+}
+
 /* ----------
  * get_indexdef            - Get the definition of an index
  *
@@ -977,6 +987,8 @@ pg_get_indexdef_columns(Oid indexrelid, bool pretty)
  *
  * This is now used for exclusion constraints as well: if excludeOps is not
  * NULL then it points to an array of exclusion operator OIDs.
+ *
+ * XXX if you change this function, see pg_get_indexdef_detailed too.
  */
 static char *
 pg_get_indexdef_worker(Oid indexrelid, int colno,
@@ -1256,6 +1268,245 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
    return buf.data;
 }
 
+/*
+ * Return an index definition, split in several pieces.
+ *
+ * There is a huge lot of code that's a dupe of pg_get_indexdef_worker, but
+ * control flow is different enough that it doesn't seem worth keeping them
+ * together.
+ */
+void
+pg_get_indexdef_detailed(Oid indexrelid,
+                        char **index_am,
+                        char **definition,
+                        char **reloptions,
+                        char **tablespace,
+                        char **whereClause)
+{
+   HeapTuple   ht_idx;
+   HeapTuple   ht_idxrel;
+   HeapTuple   ht_am;
+   Form_pg_index idxrec;
+   Form_pg_class idxrelrec;
+   Form_pg_am  amrec;
+   List       *indexprs;
+   ListCell   *indexpr_item;
+   List       *context;
+   Oid         indrelid;
+   int         keyno;
+   Datum       indcollDatum;
+   Datum       indclassDatum;
+   Datum       indoptionDatum;
+   bool        isnull;
+   oidvector  *indcollation;
+   oidvector  *indclass;
+   int2vector *indoption;
+   StringInfoData definitionBuf;
+   char       *sep;
+
+   /*
+    * Fetch the pg_index tuple by the Oid of the index
+    */
+   ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
+   if (!HeapTupleIsValid(ht_idx))
+       elog(ERROR, "cache lookup failed for index %u", indexrelid);
+   idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
+
+   indrelid = idxrec->indrelid;
+   Assert(indexrelid == idxrec->indexrelid);
+
+   /* Must get indcollation, indclass, and indoption the hard way */
+   indcollDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                  Anum_pg_index_indcollation, &isnull);
+   Assert(!isnull);
+   indcollation = (oidvector *) DatumGetPointer(indcollDatum);
+
+   indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                   Anum_pg_index_indclass, &isnull);
+   Assert(!isnull);
+   indclass = (oidvector *) DatumGetPointer(indclassDatum);
+
+   indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                    Anum_pg_index_indoption, &isnull);
+   Assert(!isnull);
+   indoption = (int2vector *) DatumGetPointer(indoptionDatum);
+
+   /*
+    * Fetch the pg_class tuple of the index relation
+    */
+   ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid));
+   if (!HeapTupleIsValid(ht_idxrel))
+       elog(ERROR, "cache lookup failed for relation %u", indexrelid);
+   idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
+
+   /*
+    * Fetch the pg_am tuple of the index' access method
+    */
+   ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
+   if (!HeapTupleIsValid(ht_am))
+       elog(ERROR, "cache lookup failed for access method %u",
+            idxrelrec->relam);
+   amrec = (Form_pg_am) GETSTRUCT(ht_am);
+
+   /*
+    * Get the index expressions, if any.  (NOTE: we do not use the relcache
+    * versions of the expressions and predicate, because we want to display
+    * non-const-folded expressions.)
+    */
+   if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs))
+   {
+       Datum       exprsDatum;
+       bool        isnull;
+       char       *exprsString;
+
+       exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                    Anum_pg_index_indexprs, &isnull);
+       Assert(!isnull);
+       exprsString = TextDatumGetCString(exprsDatum);
+       indexprs = (List *) stringToNode(exprsString);
+       pfree(exprsString);
+   }
+   else
+       indexprs = NIL;
+
+   indexpr_item = list_head(indexprs);
+
+   context = deparse_context_for(get_relation_name(indrelid), indrelid);
+
+   initStringInfo(&definitionBuf);
+
+   /* output index AM */
+   *index_am = pstrdup(quote_identifier(NameStr(amrec->amname)));
+
+   /*
+    * Output index definition.  Note the outer parens must be supplied by
+    * caller.
+    */
+   sep = "";
+   for (keyno = 0; keyno < idxrec->indnatts; keyno++)
+   {
+       AttrNumber  attnum = idxrec->indkey.values[keyno];
+       int16       opt = indoption->values[keyno];
+       Oid         keycoltype;
+       Oid         keycolcollation;
+       Oid         indcoll;
+
+       appendStringInfoString(&definitionBuf, sep);
+       sep = ", ";
+
+       if (attnum != 0)
+       {
+           /* Simple index column */
+           char       *attname;
+           int32       keycoltypmod;
+
+           attname = get_relid_attribute_name(indrelid, attnum);
+           appendStringInfoString(&definitionBuf, quote_identifier(attname));
+           get_atttypetypmodcoll(indrelid, attnum,
+                                 &keycoltype, &keycoltypmod,
+                                 &keycolcollation);
+       }
+       else
+       {
+           /* expressional index */
+           Node       *indexkey;
+           char       *str;
+
+           if (indexpr_item == NULL)
+               elog(ERROR, "too few entries in indexprs list");
+           indexkey = (Node *) lfirst(indexpr_item);
+           indexpr_item = lnext(indexpr_item);
+           /* Deparse */
+           str = deparse_expression_pretty(indexkey, context, false, false,
+                                           0, 0);
+
+           /* Need parens if it's not a bare function call */
+           if (indexkey && IsA(indexkey, FuncExpr) &&
+               ((FuncExpr *) indexkey)->funcformat == COERCE_EXPLICIT_CALL)
+               appendStringInfoString(&definitionBuf, str);
+           else
+               appendStringInfo(&definitionBuf, "(%s)", str);
+
+           keycoltype = exprType(indexkey);
+           keycolcollation = exprCollation(indexkey);
+       }
+
+       /* Add collation, even if default */
+       indcoll = indcollation->values[keyno];
+       if (OidIsValid(indcoll))
+           appendStringInfo(&definitionBuf, " COLLATE %s",
+                            generate_collation_name((indcoll)));
+
+       /* Add the operator class name, even if default */
+       get_opclass_name(indclass->values[keyno], InvalidOid, &definitionBuf);
+
+       /* Add options if relevant */
+       if (amrec->amcanorder)
+       {
+           /* if it supports sort ordering, report DESC and NULLS opts */
+           if (opt & INDOPTION_DESC)
+           {
+               appendStringInfoString(&definitionBuf, " DESC");
+               /* NULLS FIRST is the default in this case */
+               if (!(opt & INDOPTION_NULLS_FIRST))
+                   appendStringInfoString(&definitionBuf, " NULLS LAST");
+           }
+           else
+           {
+               if (opt & INDOPTION_NULLS_FIRST)
+                   appendStringInfoString(&definitionBuf, " NULLS FIRST");
+           }
+       }
+
+       /* XXX excludeOps thingy was here; do we need anything? */
+   }
+   *definition = definitionBuf.data;
+
+   /* output reloptions */
+   *reloptions = flatten_reloptions(indexrelid);
+
+   /* output tablespace */
+   {
+       Oid         tblspc;
+
+       tblspc = get_rel_tablespace(indexrelid);
+       if (OidIsValid(tblspc))
+           *tablespace = pstrdup(quote_identifier(get_tablespace_name(tblspc)));
+       else
+           *tablespace = NULL;
+   }
+
+   /* report index predicate, if any */
+   if (!heap_attisnull(ht_idx, Anum_pg_index_indpred))
+   {
+       Node       *node;
+       Datum       predDatum;
+       bool        isnull;
+       char       *predString;
+
+       /* Convert text string to node tree */
+       predDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                   Anum_pg_index_indpred, &isnull);
+       Assert(!isnull);
+       predString = TextDatumGetCString(predDatum);
+       node = (Node *) stringToNode(predString);
+       pfree(predString);
+
+       /* Deparse */
+       *whereClause =
+           deparse_expression_pretty(node, context, false, false,
+                                     0, 0);
+   }
+   else
+       *whereClause = NULL;
+
+   /* Clean up */
+   ReleaseSysCache(ht_idx);
+   ReleaseSysCache(ht_idxrel);
+   ReleaseSysCache(ht_am);
+
+   /* all done */
+}
 
 /*
  * pg_get_constraintdef
@@ -1290,9 +1541,9 @@ pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
 
 /* Internal version that returns a palloc'd C string; no pretty-printing */
 char *
-pg_get_constraintdef_string(Oid constraintId)
+pg_get_constraintdef_string(Oid constraintId, bool fullCommand)
 {
-   return pg_get_constraintdef_worker(constraintId, true, 0);
+   return pg_get_constraintdef_worker(constraintId, fullCommand, 0);
 }
 
 /*
index 914d155c9f1790895996c46e98356c8bd0fbf5f9..984631a77b03bbe34ec190b4e42f2fc1c7bcd028 100644 (file)
@@ -70,6 +70,7 @@ extern Datum setval3_oid(PG_FUNCTION_ARGS);
 extern Datum lastval(PG_FUNCTION_ARGS);
 
 extern Datum pg_sequence_parameters(PG_FUNCTION_ARGS);
+extern Form_pg_sequence get_sequence_values(Oid sequenceId);
 
 extern Oid DefineSequence(CreateSeqStmt *stmt);
 extern Oid AlterSequence(AlterSeqStmt *stmt);
index 05f0fc4bd1ef3fdba9b9f78d140965c8e09c9ebc..0503816659cafbaefea4967055356a85fb517600 100644 (file)
@@ -13,6 +13,7 @@
 #ifndef RULEUTILS_H
 #define RULEUTILS_H
 
+#include "catalog/pg_trigger.h"
 #include "nodes/nodes.h"
 #include "nodes/parsenodes.h"
 #include "nodes/pg_list.h"
 
 extern char *pg_get_indexdef_string(Oid indexrelid);
 extern char *pg_get_indexdef_columns(Oid indexrelid, bool pretty);
+extern void pg_get_indexdef_detailed(Oid indexrelid,
+                        char **index_am,
+                        char **definition,
+                        char **reloptions,
+                        char **tablespace,
+                        char **whereClause);
+extern char *pg_get_trigger_whenclause(Form_pg_trigger trigrec,
+                         Node *whenClause, bool pretty);
+extern char *pg_get_constraintdef_string(Oid constraintId, bool fullCommand);
 
-extern char *pg_get_constraintdef_string(Oid constraintId);
 extern char *deparse_expression(Node *expr, List *dpcontext,
                   bool forceprefix, bool showimplicit);
 extern List *deparse_context_for(const char *aliasname, Oid relid);