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 set_rest set_rest_more SetResetClause FunctionSetResetClause %type TableElement TypedTableElement ConstraintElem TableFuncElement - ForeignTableElement + ForeignTableElement ForeignTableLikeClause %type columnDef columnOptions %type def_elem reloption_elem old_aggr_elem %type 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 portion of a CREATE FOREIGN TABLE + * statement into column definitions which recreate the user defined + * column portions of . + */ +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 * ----------------------