deparse: support CREATE TABLE AS
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Sat, 14 Feb 2015 18:30:04 +0000 (15:30 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Tue, 7 Apr 2015 17:09:39 +0000 (14:09 -0300)
src/backend/tcop/deparse_utility.c
src/backend/utils/adt/ruleutils.c
src/include/utils/ruleutils.h

index c84ed08b3a6873e134a2210580ee48742cce892c..9c0c6848ee2d07aae273d7f6b5cdce8645703bb6 100644 (file)
@@ -4333,6 +4333,135 @@ deparse_RuleStmt(Oid objectId, Node *parsetree)
    return ruleStmt;
 }
 
+static ObjTree *
+deparse_CreateTableAsStmt(Oid objectId, Node *parsetree)
+{
+   CreateTableAsStmt *node = (CreateTableAsStmt *) parsetree;
+   Relation    relation = relation_open(objectId, AccessShareLock);
+   ObjTree    *createStmt;
+   ObjTree    *tmp;
+   ObjTree    *tmp2;
+   char       *fmt;
+   List       *list;
+   ListCell   *cell;
+
+   /*
+    * Reject unsupported case right away.
+    */
+   if (((Query *) (node->query))->commandType == CMD_UTILITY)
+       elog(ERROR, "unimplemented deparse of CREATE TABLE AS EXECUTE");
+
+   /*
+    * Note that INSERT INTO is deparsed as CREATE TABLE AS.  They are
+    * functionally equivalent synonyms so there is no harm from this.
+    */
+   if (node->relkind == OBJECT_MATVIEW)
+       fmt = "CREATE %{persistence}s MATERIALIZED VIEW %{if_not_exists}s "
+           "%{identity}D %{columns}s %{with_clause}s %{tablespace}s "
+           "AS %{query}s %{with_no_data}s";
+   else
+       fmt = "CREATE %{persistence}s TABLE %{if_not_exists}s "
+           "%{identity}D %{columns}s %{with_clause}s %{on_commit}s %{tablespace}s "
+           "AS %{query}s %{with_no_data}s";
+
+   createStmt =
+       new_objtree_VA(fmt, 2,
+                      "persistence", ObjTypeString,
+                      get_persistence_str(node->into->rel->relpersistence),
+                      "if_not_exists", ObjTypeString,
+                      node->if_not_exists ? "IF NOT EXISTS" : "");
+   append_object_object(createStmt, "identity",
+                        new_objtree_for_qualname_id(RelationRelationId,
+                                                    objectId));
+
+   /* Add an ON COMMIT clause.  CREATE MATERIALIZED VIEW doesn't have one */
+   if (node->relkind == OBJECT_TABLE)
+   {
+       append_object_object(createStmt, "on_commit",
+                            deparse_OnCommitClause(node->into->onCommit));
+   }
+
+   /* add a TABLESPACE clause */
+   tmp = new_objtree_VA("TABLESPACE %{tablespace}I", 0);
+   if (node->into->tableSpaceName)
+       append_string_object(tmp, "tablespace", node->into->tableSpaceName);
+   else
+   {
+       append_null_object(tmp, "tablespace");
+       append_bool_object(tmp, "present", false);
+   }
+   append_object_object(createStmt, "tablespace", tmp);
+
+   /* add a WITH NO DATA clause */
+   tmp = new_objtree_VA("WITH NO DATA", 1,
+                        "present", ObjTypeBool,
+                        node->into->skipData ? true : false);
+   append_object_object(createStmt, "with_no_data", tmp);
+
+   /* add the column list, if any */
+   if (node->into->colNames == NIL)
+       tmp = new_objtree_VA("", 1,
+                            "present", ObjTypeBool, false);
+   else
+   {
+       ListCell *cell;
+
+       list = NIL;
+       foreach (cell, node->into->colNames)
+           list = lappend(list, new_string_object(strVal(lfirst(cell))));
+
+       tmp = new_objtree_VA("(%{columns:, }I)", 1,
+                            "columns", ObjTypeArray, list);
+   }
+   append_object_object(createStmt, "columns", tmp);
+
+   /* add the query */
+   Assert(IsA(node->query, Query));
+   append_string_object(createStmt, "query",
+                        pg_get_createtableas_def((Query *) node->query));
+
+
+   /*
+    * WITH clause.  In CREATE TABLE AS, like for CREATE TABLE, we always emit
+    * one, containing at least the OIDS option; CREATE MATERIALIZED VIEW
+    * doesn't support the oids option, so we have to make the clause reduce to
+    * empty if no other options are specified.
+    */
+   tmp = new_objtree_VA("WITH (%{with:, }s)", 0);
+   if (node->relkind == OBJECT_MATVIEW && node->into->options == NIL)
+       append_bool_object(tmp, "present", false);
+   else
+   {
+       if (node->relkind == OBJECT_TABLE)
+       {
+           tmp2 = new_objtree_VA("oids=%{value}s", 2,
+                                 "option", ObjTypeString, "oids",
+                                 "value", ObjTypeString,
+                                 relation->rd_rel->relhasoids ? "ON" : "OFF");
+           list = list_make1(new_object_object(tmp2));
+       }
+       else
+           list = NIL;
+
+       foreach (cell, node->into->options)
+       {
+           DefElem *opt = (DefElem *) lfirst(cell);
+
+           if (strcmp(opt->defname, "oids") == 0)
+               continue;
+
+           tmp2 = deparse_DefElem(opt, false);
+           list = lappend(list, new_object_object(tmp2));
+       }
+       append_array_object(tmp, "with", list);
+   }
+   append_object_object(createStmt, "with_clause", tmp);
+
+   relation_close(relation, AccessShareLock);
+
+   return createStmt;
+}
+
 /*
  * deparse_CreateSchemaStmt
  *     deparse a CreateSchemaStmt
@@ -5595,8 +5724,7 @@ deparse_simple_command(StashedCommand *cmd)
            break;
 
        case T_CreateTableAsStmt:
-           /* XXX handle at least the CREATE MATVIEW case? */
-           elog(ERROR, "unimplemented deparse of %s", CreateCommandTag(parsetree));
+           command = deparse_CreateTableAsStmt(objectId, parsetree);
            break;
 
        case T_RefreshMatViewStmt:
index 04d8f8b2bb297c7b438134de228f229150d787a7..a2b8af2d87d8a83555c800c3864dc639420bbef8 100644 (file)
@@ -751,6 +751,21 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
    return buf.data;
 }
 
+/*
+ * get_createtableas_def   - Get the accompanying query for a CREATE TABLE AS
+ */
+char *
+pg_get_createtableas_def(Query *query)
+{
+   StringInfoData buf;
+
+   initStringInfo(&buf);
+
+   get_query_def(query, &buf, NIL, NULL, 0, 0, 0);
+
+   return buf.data;
+}
+
 /* ----------
  * get_triggerdef          - Get the definition of a trigger
  * ----------
index 7b22ea04210b72ed937ce9647b3b78ffcffd673a..2c020e9afc658d3aaf4029998600e47659484fa0 100644 (file)
@@ -33,6 +33,7 @@ extern char *pg_get_constraintdef_string(Oid constraintId, bool fullCommand);
 extern void pg_get_ruledef_details(Datum ev_qual, Datum ev_action,
                       char **whereClause, List **actions);
 extern char *pg_get_viewdef_internal(Oid viewoid);
+extern char *pg_get_createtableas_def(Query *query);
 
 extern char *deparse_expression(Node *expr, List *dpcontext,
                   bool forceprefix, bool showimplicit);