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