On Tue, Mar 13, 2012 at 08:24:47AM -0700, David Fetter wrote:
> Folks,
> 
> This is for 9.3, of course.
> 
> I noticed that CREATE FOREIGN TABLE (LIKE some_table) doesn't work.  I
> believe it should, as it would:
> 
> - Remove a POLA violation
> - Make data loading into an extant table even easier, especially if
>   there need to be filtering or other cleanup steps
> 
> Come to think of it, which CREATE TABLE options are inappropriate to
> CREATE FOREIGN TABLE?
> 
> Cheers,
> David.

Here's a WIP patch (lots of cut/paste, no docs, no tests), but it does
work.  Still to do in addition: decide whether ALTER FOREIGN TABLE
should also handle LIKE.

Cheers,
David.
-- 
David Fetter <da...@fetter.org> http://fetter.org/
Phone: +1 415 235 3778  AIM: dfetter666  Yahoo!: dfetter
Skype: davidfetter      XMPP: david.fet...@gmail.com
iCal: webcal://www.tripit.com/feed/ical/people/david74/tripit.ics

Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 5cde225..c634e19 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2727,6 +2727,16 @@ _copyTableLikeClause(const TableLikeClause *from)
        return newnode;
 }
 
+static ForeignTableLikeClause *
+_copyForeignTableLikeClause(const ForeignTableLikeClause *from)
+{
+       ForeignTableLikeClause *newnode = makeNode(ForeignTableLikeClause);
+
+       COPY_NODE_FIELD(relation);
+
+       return newnode;
+}
+
 static DefineStmt *
 _copyDefineStmt(const DefineStmt *from)
 {
@@ -4126,6 +4136,9 @@ copyObject(const void *from)
                case T_TableLikeClause:
                        retval = _copyTableLikeClause(from);
                        break;
+               case T_ForeignTableLikeClause:
+                       retval = _copyForeignTableLikeClause(from);
+                       break;
                case T_DefineStmt:
                        retval = _copyDefineStmt(from);
                        break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index d2a79eb..55cc2db 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1170,6 +1170,14 @@ _equalTableLikeClause(const TableLikeClause *a, const 
TableLikeClause *b)
 }
 
 static bool
+_equalForeignTableLikeClause(const ForeignTableLikeClause *a, const 
ForeignTableLikeClause *b)
+{
+       COMPARE_NODE_FIELD(relation);
+
+       return true;
+}
+
+static bool
 _equalDefineStmt(const DefineStmt *a, const DefineStmt *b)
 {
        COMPARE_SCALAR_FIELD(kind);
@@ -2685,6 +2693,9 @@ equal(const void *a, const void *b)
                case T_TableLikeClause:
                        retval = _equalTableLikeClause(a, b);
                        break;
+               case T_ForeignTableLikeClause:
+                       retval = _equalForeignTableLikeClause(a, b);
+                       break;
                case T_DefineStmt:
                        retval = _equalDefineStmt(a, b);
                        break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 51181a9..88599ba 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2057,6 +2057,14 @@ _outTableLikeClause(StringInfo str, const 
TableLikeClause *node)
 }
 
 static void
+_outForeignTableLikeClause(StringInfo str, const ForeignTableLikeClause *node)
+{
+       WRITE_NODE_TYPE("FOREIGNTABLELIKECLAUSE");
+
+       WRITE_NODE_FIELD(relation);
+}
+
+static void
 _outLockingClause(StringInfo str, const LockingClause *node)
 {
        WRITE_NODE_TYPE("LOCKINGCLAUSE");
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index feb28a4..34e5bfc 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -373,7 +373,7 @@ static void processCASbits(int cas_bits, int location, 
const char *constrType,
 %type <vsetstmt> set_rest set_rest_more SetResetClause FunctionSetResetClause
 
 %type <node>   TableElement TypedTableElement ConstraintElem TableFuncElement
-                               ForeignTableElement
+                               ForeignTableElement ForeignTableLikeClause
 %type <node>   columnDef columnOptions
 %type <defelt> def_elem reloption_elem old_aggr_elem
 %type <node>   def_arg columnElem where_clause where_or_current_clause
@@ -3950,6 +3950,16 @@ ForeignTableElementList:
 
 ForeignTableElement:
                        columnDef                                       { $$ = 
$1; }
+            | ForeignTableLikeClause   { $$ = $1; }
+               ;
+
+ForeignTableLikeClause:
+                       LIKE qualified_name
+                               {
+                                       ForeignTableLikeClause *n = 
makeNode(ForeignTableLikeClause);
+                                       n->relation = $2;
+                                       $$ = (Node *)n;
+                               }
                ;
 
 /*****************************************************************************
diff --git a/src/backend/parser/parse_utilcmd.c 
b/src/backend/parser/parse_utilcmd.c
index 43f5634..f430c08 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -104,6 +104,8 @@ static void transformTableConstraint(CreateStmtContext *cxt,
                                                 Constraint *constraint);
 static void transformTableLikeClause(CreateStmtContext *cxt,
                                         TableLikeClause *table_like_clause);
+static void transformForeignTableLikeClause(CreateStmtContext *cxt,
+                                        ForeignTableLikeClause 
*foreign_table_like_clause);
 static void transformOfType(CreateStmtContext *cxt,
                                TypeName *ofTypename);
 static char *chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt);
@@ -238,6 +240,10 @@ transformCreateStmt(CreateStmt *stmt, const char 
*queryString)
                                transformTableLikeClause(&cxt, (TableLikeClause 
*) element);
                                break;
 
+                       case T_ForeignTableLikeClause:
+                               transformForeignTableLikeClause(&cxt, 
(ForeignTableLikeClause *) element);
+                               break;
+
                        default:
                                elog(ERROR, "unrecognized node type: %d",
                                         (int) nodeTag(element));
@@ -888,6 +894,113 @@ transformTableLikeClause(CreateStmtContext *cxt, 
TableLikeClause *table_like_cla
        heap_close(relation, NoLock);
 }
 
+/*
+ * transformForeignTableLikeClause
+ *
+ * Change the LIKE <srctable> portion of a CREATE FOREIGN TABLE
+ * statement into column definitions which recreate the user defined
+ * column portions of <srctable>.
+ */
+static void
+transformForeignTableLikeClause(CreateStmtContext *cxt, ForeignTableLikeClause 
*foreign_table_like_clause)
+{
+       AttrNumber      parent_attno;
+       Relation        relation;
+       TupleDesc       tupleDesc;
+       TupleConstr *constr;
+       AclResult       aclresult;
+       char       *comment;
+       ParseCallbackState pcbstate;
+
+       setup_parser_errposition_callback(&pcbstate, cxt->pstate, 
foreign_table_like_clause->relation->location);
+
+       relation = relation_openrv(foreign_table_like_clause->relation, 
AccessShareLock);
+
+       if (relation->rd_rel->relkind != RELKIND_RELATION
+               && relation->rd_rel->relkind != RELKIND_VIEW
+               && relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE
+               && relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("\"%s\" is not a table, view, composite 
type, or foreign table",
+                                               
foreign_table_like_clause->relation->relname)));
+
+       cancel_parser_errposition_callback(&pcbstate);
+
+       /*
+        * Check for privileges
+        */
+       if (relation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
+       {
+               aclresult = pg_type_aclcheck(relation->rd_rel->reltype, 
GetUserId(),
+                                                                        
ACL_USAGE);
+               if (aclresult != ACLCHECK_OK)
+                       aclcheck_error(aclresult, ACL_KIND_TYPE,
+                                                  
RelationGetRelationName(relation));
+       }
+       else
+       {
+               aclresult = pg_class_aclcheck(RelationGetRelid(relation), 
GetUserId(),
+                                                                 ACL_SELECT);
+               if (aclresult != ACLCHECK_OK)
+                       aclcheck_error(aclresult, ACL_KIND_CLASS,
+                                                  
RelationGetRelationName(relation));
+       }
+
+       tupleDesc = RelationGetDescr(relation);
+       constr = tupleDesc->constr;
+
+       /*
+        * Insert the copied attributes into the cxt for the new table 
definition.
+        */
+       for (parent_attno = 1; parent_attno <= tupleDesc->natts;
+                parent_attno++)
+       {
+               Form_pg_attribute attribute = tupleDesc->attrs[parent_attno - 
1];
+               char       *attributeName = NameStr(attribute->attname);
+               ColumnDef  *def;
+
+               /*
+                * Ignore dropped columns in the parent.
+                */
+               if (attribute->attisdropped)
+                       continue;
+
+               /*
+                * Create a new column, which is marked as NOT inherited.
+                *
+                * For constraints, ONLY the NOT NULL constraint is inherited 
by the
+                * new column definition per SQL99.
+                */
+               def = makeNode(ColumnDef);
+               def->colname = pstrdup(attributeName);
+               def->typeName = makeTypeNameFromOid(attribute->atttypid,
+                                                                               
        attribute->atttypmod);
+               def->inhcount = 0;
+               def->is_local = true;
+               def->is_not_null = attribute->attnotnull;
+               def->is_from_type = false;
+               def->storage = 0;
+               def->raw_default = NULL;
+               def->cooked_default = NULL;
+               def->collClause = NULL;
+               def->collOid = attribute->attcollation;
+               def->constraints = NIL;
+
+               /*
+                * Add to column list
+                */
+               cxt->columns = lappend(cxt->columns, def);
+       }
+
+       /*
+        * Close the parent rel, but keep our AccessShareLock on it until xact
+        * commit.      That will prevent someone else from deleting or 
ALTERing the
+        * parent before the child is committed.
+        */
+       heap_close(relation, NoLock);
+}
+
 static void
 transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
 {
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 905458f..f33cb20 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -394,6 +394,7 @@ typedef enum NodeTag
        T_XmlSerialize,
        T_WithClause,
        T_CommonTableExpr,
+       T_ForeignTableLikeClause,
 
        /*
         * TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ab55639..ffee4ae 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1675,6 +1675,15 @@ typedef struct CreateForeignTableStmt
        List       *options;
 } CreateForeignTableStmt;
 
+/*
+ * ForeignTableLikeClause - CREATE FOREIGN TABLE ( ... LIKE ... ) clause
+ */
+typedef struct ForeignTableLikeClause
+{
+       NodeTag         type;
+       RangeVar   *relation;
+} ForeignTableLikeClause;
+
 /* ----------------------
  *             Create/Drop USER MAPPING Statements
  * ----------------------
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to