*** a/contrib/file_fdw/file_fdw.c --- b/contrib/file_fdw/file_fdw.c *************** *** 117,122 **** static void fileGetForeignRelSize(PlannerInfo *root, --- 117,126 ---- static void fileGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid); + static ForeignPath *fileReparameterizeForeignPath(PlannerInfo *root, + RelOptInfo *baserel, + Path *path, + Relids required_outer); static ForeignScan *fileGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, *************** *** 145,150 **** static bool check_selective_binary_conversion(RelOptInfo *baserel, --- 149,155 ---- static void estimate_size(PlannerInfo *root, RelOptInfo *baserel, FileFdwPlanState *fdw_private); static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel, + ParamPathInfo *param_info, FileFdwPlanState *fdw_private, Cost *startup_cost, Cost *total_cost); static int file_acquire_sample_rows(Relation onerel, int elevel, *************** *** 163,168 **** file_fdw_handler(PG_FUNCTION_ARGS) --- 168,174 ---- fdwroutine->GetForeignRelSize = fileGetForeignRelSize; fdwroutine->GetForeignPaths = fileGetForeignPaths; + fdwroutine->ReparameterizeForeignPath = fileReparameterizeForeignPath; fdwroutine->GetForeignPlan = fileGetForeignPlan; fdwroutine->ExplainForeignScan = fileExplainForeignScan; fdwroutine->BeginForeignScan = fileBeginForeignScan; *************** *** 517,523 **** fileGetForeignPaths(PlannerInfo *root, (Node *) columns)); /* Estimate costs */ ! estimate_costs(root, baserel, fdw_private, &startup_cost, &total_cost); /* --- 523,530 ---- (Node *) columns)); /* Estimate costs */ ! estimate_costs(root, baserel, ! NULL, fdw_private, &startup_cost, &total_cost); /* *************** *** 542,547 **** fileGetForeignPaths(PlannerInfo *root, --- 549,595 ---- } /* + * fileReparameterizeForeignPath + * Attempt to modify a given path to have greater parameterization + */ + static ForeignPath * + fileReparameterizeForeignPath(PlannerInfo *root, + RelOptInfo *baserel, + Path *path, + Relids required_outer) + { + ForeignPath *newpath = makeNode(ForeignPath); + ParamPathInfo *param_info; + FileFdwPlanState *fdw_private = (FileFdwPlanState *) baserel->fdw_private; + Cost startup_cost; + Cost total_cost; + + /* + * To avoid re-checking whether to selectively perform binary conversion, + * we create a ForeignPath node from by flat-copying the given path node, + * rewriting the param_info and row estimate, and redoing the cost estimate. + */ + memcpy(newpath, (ForeignPath *) path, sizeof(ForeignPath)); + + /* Get the ParamPathInfo */ + param_info = get_baserel_parampathinfo(root, baserel, required_outer); + Assert(param_info); + + newpath->path.param_info = param_info; + newpath->path.rows = param_info->ppi_rows; + + /* Redo the cost estimate */ + estimate_costs(root, baserel, + param_info, fdw_private, + &startup_cost, &total_cost); + + newpath->path.startup_cost = startup_cost; + newpath->path.total_cost = total_cost; + + return newpath; + } + + /* * fileGetForeignPlan * Create a ForeignScan plan node for scanning the foreign table */ *************** *** 970,981 **** estimate_size(PlannerInfo *root, RelOptInfo *baserel, --- 1018,1031 ---- */ static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel, + ParamPathInfo *param_info, FileFdwPlanState *fdw_private, Cost *startup_cost, Cost *total_cost) { BlockNumber pages = fdw_private->pages; double ntuples = fdw_private->ntuples; Cost run_cost = 0; + QualCost qpqual_cost; Cost cpu_per_tuple; /* *************** *** 986,993 **** estimate_costs(PlannerInfo *root, RelOptInfo *baserel, */ run_cost += seq_page_cost * pages; ! *startup_cost = baserel->baserestrictcost.startup; ! cpu_per_tuple = cpu_tuple_cost * 10 + baserel->baserestrictcost.per_tuple; run_cost += cpu_per_tuple * ntuples; *total_cost = *startup_cost + run_cost; } --- 1036,1045 ---- */ run_cost += seq_page_cost * pages; ! get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost); ! ! *startup_cost = qpqual_cost.startup; ! cpu_per_tuple = cpu_tuple_cost * 10 + qpqual_cost.per_tuple; run_cost += cpu_per_tuple * ntuples; *total_cost = *startup_cost + run_cost; } *** a/contrib/postgres_fdw/postgres_fdw.c --- b/contrib/postgres_fdw/postgres_fdw.c *************** *** 241,246 **** static void postgresGetForeignRelSize(PlannerInfo *root, --- 241,250 ---- static void postgresGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid); + static ForeignPath *postgresReparameterizeForeignPath(PlannerInfo *root, + RelOptInfo *baserel, + Path *path, + Relids required_outer); static ForeignScan *postgresGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, *************** *** 342,347 **** postgres_fdw_handler(PG_FUNCTION_ARGS) --- 346,352 ---- /* Functions for scanning foreign tables */ routine->GetForeignRelSize = postgresGetForeignRelSize; routine->GetForeignPaths = postgresGetForeignPaths; + routine->ReparameterizeForeignPath = postgresReparameterizeForeignPath; routine->GetForeignPlan = postgresGetForeignPlan; routine->BeginForeignScan = postgresBeginForeignScan; routine->IterateForeignScan = postgresIterateForeignScan; *************** *** 729,734 **** postgresGetForeignPaths(PlannerInfo *root, --- 734,780 ---- } /* + * postgresReparameterizeForeignPath + * Attempt to modify a given path to have greater parameterization + */ + static ForeignPath * + postgresReparameterizeForeignPath(PlannerInfo *root, + RelOptInfo *baserel, + Path *path, + Relids required_outer) + { + ParamPathInfo *param_info; + double rows; + int width; + Cost startup_cost; + Cost total_cost; + + /* Get the ParamPathInfo */ + param_info = get_baserel_parampathinfo(root, baserel, required_outer); + + /* Redo the cost estimate */ + estimate_path_cost_size(root, baserel, + param_info->ppi_clauses, + &rows, &width, + &startup_cost, &total_cost); + + /* + * ppi_rows currently won't get looked at by anything, but still we + * may as well ensure that it matches our idea of the rowcount. + */ + param_info->ppi_rows = rows; + + /* Make the new path */ + return create_foreignscan_path(root, baserel, + rows, + startup_cost, + total_cost, + NIL, /* no pathkeys */ + required_outer, + NIL); /* no fdw_private list */ + } + + /* * postgresGetForeignPlan * Create ForeignScan plan node which implements selected best path */ *************** *** 1775,1785 **** estimate_path_cost_size(PlannerInfo *root, } else { ! /* ! * We don't support join conditions in this mode (hence, no ! * parameterized paths can be made). ! */ ! Assert(join_conds == NIL); /* Use rows/width estimates made by set_baserel_size_estimates. */ rows = baserel->rows; --- 1821,1828 ---- } else { ! Selectivity join_sel; ! QualCost join_cost; /* Use rows/width estimates made by set_baserel_size_estimates. */ rows = baserel->rows; *************** *** 1792,1808 **** estimate_path_cost_size(PlannerInfo *root, retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel); retrieved_rows = Min(retrieved_rows, baserel->tuples); /* * Cost as though this were a seqscan, which is pessimistic. We ! * effectively imagine the local_conds are being evaluated remotely, ! * too. */ startup_cost = 0; run_cost = 0; run_cost += seq_page_cost * baserel->pages; ! startup_cost += baserel->baserestrictcost.startup; ! cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple; run_cost += cpu_per_tuple * baserel->tuples; total_cost = startup_cost + run_cost; --- 1835,1863 ---- retrieved_rows = clamp_row_est(rows / fpinfo->local_conds_sel); retrieved_rows = Min(retrieved_rows, baserel->tuples); + /* Factor in the selectivity of the join_conds */ + join_sel = clauselist_selectivity(root, + join_conds, + baserel->relid, + JOIN_INNER, + NULL); + + rows = clamp_row_est(rows * join_sel); + /* * Cost as though this were a seqscan, which is pessimistic. We ! * effectively imagine the local_conds and join_conds are being ! * evaluated remotely, too. */ startup_cost = 0; run_cost = 0; run_cost += seq_page_cost * baserel->pages; ! cost_qual_eval(&join_cost, join_conds, root); ! startup_cost += ! (baserel->baserestrictcost.startup + join_cost.startup); ! cpu_per_tuple = cpu_tuple_cost + ! (baserel->baserestrictcost.per_tuple + join_cost.per_tuple); run_cost += cpu_per_tuple * baserel->tuples; total_cost = startup_cost + run_cost; *** a/doc/src/sgml/ddl.sgml --- b/doc/src/sgml/ddl.sgml *************** *** 258,263 **** CREATE TABLE products ( --- 258,274 ---- even if the value came from the default value definition. + + + Note that constraints can be defined on foreign tables too, but such + constraints are not enforced on insert or update. Those constraints are + "assertive", and work only to tell planner that some kind of optimization + such as constraint exclusion can be considerd. This seems useless, but + allows us to use foriegn table as child table (see + ) to off-load to multiple servers. + + + Check Constraints *************** *** 2017,2024 **** CREATE TABLE capitals ( ! In PostgreSQL, a table can inherit from ! zero or more other tables, and a query can reference either all rows of a table or all rows of a table plus all of its descendant tables. The latter behavior is the default. For example, the following query finds the names of all cities, --- 2028,2035 ---- ! In PostgreSQL, a table or foreign table can ! inherit from zero or more other tables, and a query can reference either all rows of a table or all rows of a table plus all of its descendant tables. The latter behavior is the default. For example, the following query finds the names of all cities, *** a/doc/src/sgml/ref/alter_foreign_table.sgml --- b/doc/src/sgml/ref/alter_foreign_table.sgml *************** *** 42,47 **** ALTER FOREIGN TABLE [ IF EXISTS ] namecolumn_name SET ( attribute_option = value [, ... ] ) ALTER [ COLUMN ] column_name RESET ( attribute_option [, ... ] ) ALTER [ COLUMN ] column_name OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ]) + INHERIT parent_table + NO INHERIT parent_table OWNER TO new_owner OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ]) *************** *** 178,183 **** ALTER FOREIGN TABLE [ IF EXISTS ] name + INHERIT parent_table + + + This form adds the target foreign table as a new child of the specified + parent table. The parent table must be a plain table. + + + + + + NO INHERIT parent_table + + + This form removes the target foreign table from the list of children of + the specified parent table. + + + + + OPTIONS ( [ ADD | SET | DROP ] option ['value'] [, ... ] ) *************** *** 306,311 **** ALTER FOREIGN TABLE [ IF EXISTS ] name + + + parent_name + + + A parent table to associate or de-associate with this foreign table. + The parent table must be a plain table. + + + *** a/doc/src/sgml/ref/create_foreign_table.sgml --- b/doc/src/sgml/ref/create_foreign_table.sgml *************** *** 22,27 **** CREATE FOREIGN TABLE [ IF NOT EXISTS ] table_name --- 22,28 ---- column_name data_type [ OPTIONS ( option 'value' [, ... ] ) ] [ COLLATE collation ] [ column_constraint [ ... ] ] [, ... ] ] ) + [ INHERITS ( parent_table [, ... ] ) ] SERVER server_name [ OPTIONS ( option 'value' [, ... ] ) ] *************** *** 159,164 **** CREATE FOREIGN TABLE [ IF NOT EXISTS ] table_name --- 160,177 ---- + parent_table + + + The name of an existing table from which the new foreign table + automatically inherits all columns. The specified parent table + must be a plain table. See for the + details of table inheritance. + + + + + server_name *** a/src/backend/catalog/pg_inherits.c --- b/src/backend/catalog/pg_inherits.c *************** *** 256,261 **** has_subclass(Oid relationId) --- 256,303 ---- /* + * Check wether the inheritance tree contains foreign table(s). + */ + bool + contains_foreign(Oid parentrelId, LOCKMODE lockmode) + { + bool result = false; + List *tableOIDs; + ListCell *lc; + + /* Find all members of the inheritance tree */ + tableOIDs = find_all_inheritors(parentrelId, lockmode, NULL); + + /* There are no children */ + if (list_length(tableOIDs) < 2) + return result; + + foreach(lc, tableOIDs) + { + Oid childOID = lfirst_oid(lc); + Relation childrel; + + /* Parent should not be foreign */ + if (childOID == parentrelId) + continue; + + /* We already got the needed lock */ + childrel = heap_open(childOID, NoLock); + + if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + { + /* Found it */ + result = true; + } + + heap_close(childrel, lockmode); + } + + return result; + } + + + /* * Given two type OIDs, determine whether the first is a complex type * (class type) that inherits from the second. */ *** a/src/backend/commands/analyze.c --- b/src/backend/commands/analyze.c *************** *** 115,121 **** static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull); * analyze_rel() -- analyze one relation */ void ! analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy) { Relation onerel; int elevel; --- 115,122 ---- * analyze_rel() -- analyze one relation */ void ! analyze_rel(Oid relid, VacuumStmt *vacstmt, VacuumMode vacmode, ! BufferAccessStrategy bstrategy) { Relation onerel; int elevel; *************** *** 270,276 **** analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy) /* * If there are child tables, do recursive ANALYZE. */ ! if (onerel->rd_rel->relhassubclass) do_analyze_rel(onerel, vacstmt, acquirefunc, relpages, true, elevel); /* --- 271,279 ---- /* * If there are child tables, do recursive ANALYZE. */ ! if (onerel->rd_rel->relhassubclass && ! (vacmode == VAC_MODE_SINGLE || ! !contains_foreign(RelationGetRelid(onerel), AccessShareLock))) do_analyze_rel(onerel, vacstmt, acquirefunc, relpages, true, elevel); /* *************** *** 1452,1463 **** acquire_inherited_sample_rows(Relation onerel, int elevel, --- 1455,1469 ---- { List *tableOIDs; Relation *rels; + AcquireSampleRowsFunc *acquirefunc; double *relblocks; double totalblocks; + Relation saved_rel; int numrows, nrels, i; ListCell *lc; + bool isAnalyzable = true; /* * Find all members of inheritance set. We only need AccessShareLock on *************** *** 1486,1491 **** acquire_inherited_sample_rows(Relation onerel, int elevel, --- 1492,1499 ---- * BlockNumber, so we use double arithmetic. */ rels = (Relation *) palloc(list_length(tableOIDs) * sizeof(Relation)); + acquirefunc = (AcquireSampleRowsFunc *) palloc(list_length(tableOIDs) + * sizeof(AcquireSampleRowsFunc)); relblocks = (double *) palloc(list_length(tableOIDs) * sizeof(double)); totalblocks = 0; nrels = 0; *************** *** 1507,1518 **** acquire_inherited_sample_rows(Relation onerel, int elevel, } rels[nrels] = childrel; ! relblocks[nrels] = (double) RelationGetNumberOfBlocks(childrel); totalblocks += relblocks[nrels]; nrels++; } /* * Now sample rows from each relation, proportionally to its fraction of * the total block count. (This might be less than desirable if the child * rels have radically different free-space percentages, but it's not --- 1515,1573 ---- } rels[nrels] = childrel; ! ! if (childrel->rd_rel->relkind != RELKIND_FOREIGN_TABLE) ! { ! acquirefunc[nrels] = acquire_sample_rows; ! relblocks[nrels] = (double) RelationGetNumberOfBlocks(childrel); ! } ! else ! { ! /* ! * For a foreign table, call the FDW's hook function to see whether ! * it supports analysis. ! */ ! FdwRoutine *fdwroutine; ! BlockNumber relpages = 0; ! bool ok = false; ! ! fdwroutine = GetFdwRoutineForRelation(childrel, false); ! if (fdwroutine->AnalyzeForeignTable != NULL) ! ok = fdwroutine->AnalyzeForeignTable(childrel, ! &acquirefunc[nrels], ! &relpages); ! if (!ok) ! { ! isAnalyzable = false; ! break; ! } ! ! relblocks[nrels] = (double) relpages; ! } ! totalblocks += relblocks[nrels]; nrels++; } /* + * If there is at least one foreign table that cannot be analyzed, give up. + */ + if (!isAnalyzable) + { + ereport(WARNING, + (errmsg("skipping \"%s\" inheritance tree --- cannot analyze foreign table \"%s\"", + RelationGetRelationName(onerel), + RelationGetRelationName(rels[nrels])))); + for (i = 0; i < nrels; i++) + { + Relation childrel = rels[i]; + + heap_close(childrel, NoLock); + } + return 0; + } + + /* * Now sample rows from each relation, proportionally to its fraction of * the total block count. (This might be less than desirable if the child * rels have radically different free-space percentages, but it's not *************** *** 1525,1530 **** acquire_inherited_sample_rows(Relation onerel, int elevel, --- 1580,1586 ---- { Relation childrel = rels[i]; double childblocks = relblocks[i]; + AcquireSampleRowsFunc childacquirefunc = acquirefunc[i]; if (childblocks > 0) { *************** *** 1540,1551 **** acquire_inherited_sample_rows(Relation onerel, int elevel, tdrows; /* Fetch a random sample of the child's rows */ ! childrows = acquire_sample_rows(childrel, ! elevel, ! rows + numrows, ! childtargrows, ! &trows, ! &tdrows); /* We may need to convert from child's rowtype to parent's */ if (childrows > 0 && --- 1596,1607 ---- tdrows; /* Fetch a random sample of the child's rows */ ! childrows = childacquirefunc(childrel, ! elevel, ! rows + numrows, ! childtargrows, ! &trows, ! &tdrows); /* We may need to convert from child's rowtype to parent's */ if (childrows > 0 && *** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** *** 311,317 **** static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel); static void ATSimplePermissions(Relation rel, int allowed_targets); static void ATWrongRelkindError(Relation rel, int allowed_targets); static void ATSimpleRecursion(List **wqueue, Relation rel, ! AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode); static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode); static List *find_typed_table_dependencies(Oid typeOid, const char *typeName, --- 311,318 ---- static void ATSimplePermissions(Relation rel, int allowed_targets); static void ATWrongRelkindError(Relation rel, int allowed_targets); static void ATSimpleRecursion(List **wqueue, Relation rel, ! AlterTableCmd *cmd, bool recurse, ! bool include_foreign, LOCKMODE lockmode); static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode); static List *find_typed_table_dependencies(Oid typeOid, const char *typeName, *************** *** 467,476 **** DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("ON COMMIT can only be used on temporary tables"))); - if (stmt->constraints != NIL && relkind == RELKIND_FOREIGN_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("constraints are not supported on foreign tables"))); /* * Look up the namespace in which we are supposed to create the relation, --- 468,473 ---- *************** *** 3019,3042 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, * rules. */ ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); ! ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); /* No command-specific prep needed */ pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP; break; case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); ! ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); /* No command-specific prep needed */ pass = AT_PASS_DROP; break; case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); ! ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; break; case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */ ! ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); /* Performs own permission checks */ ATPrepSetStatistics(rel, cmd->name, cmd->def, lockmode); pass = AT_PASS_MISC; --- 3016,3043 ---- * rules. */ ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); ! /* Recurses to child tables that are foreign, too */ ! ATSimpleRecursion(wqueue, rel, cmd, recurse, true, lockmode); /* No command-specific prep needed */ pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP; break; case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); ! /* Recurses to child tables that are foreign, too */ ! ATSimpleRecursion(wqueue, rel, cmd, recurse, true, lockmode); /* No command-specific prep needed */ pass = AT_PASS_DROP; break; case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); ! /* Recurses to child tables that are foreign, too */ ! ATSimpleRecursion(wqueue, rel, cmd, recurse, true, lockmode); /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; break; case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */ ! /* Recurses to child tables that are foreign, too */ ! ATSimpleRecursion(wqueue, rel, cmd, recurse, true, lockmode); /* Performs own permission checks */ ATPrepSetStatistics(rel, cmd->name, cmd->def, lockmode); pass = AT_PASS_MISC; *************** *** 3049,3055 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, break; case AT_SetStorage: /* ALTER COLUMN SET STORAGE */ ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW); ! ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); /* No command-specific prep needed */ pass = AT_PASS_MISC; break; --- 3050,3057 ---- break; case AT_SetStorage: /* ALTER COLUMN SET STORAGE */ ATSimplePermissions(rel, ATT_TABLE | ATT_MATVIEW); ! /* Don't recurse to child tables that are foreign */ ! ATSimpleRecursion(wqueue, rel, cmd, recurse, false, lockmode); /* No command-specific prep needed */ pass = AT_PASS_MISC; break; *************** *** 3067,3073 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_ADD_INDEX; break; case AT_AddConstraint: /* ADD CONSTRAINT */ ! ATSimplePermissions(rel, ATT_TABLE); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) --- 3069,3075 ---- pass = AT_PASS_ADD_INDEX; break; case AT_AddConstraint: /* ADD CONSTRAINT */ ! ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) *************** *** 3081,3087 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_ADD_CONSTR; break; case AT_DropConstraint: /* DROP CONSTRAINT */ ! ATSimplePermissions(rel, ATT_TABLE); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) --- 3083,3089 ---- pass = AT_PASS_ADD_CONSTR; break; case AT_DropConstraint: /* DROP CONSTRAINT */ ! ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) *************** *** 3149,3161 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_MISC; break; case AT_AddInherit: /* INHERIT */ ! ATSimplePermissions(rel, ATT_TABLE); /* This command never recurses */ ATPrepAddInherit(rel); pass = AT_PASS_MISC; break; case AT_AlterConstraint: /* ALTER CONSTRAINT */ ! ATSimplePermissions(rel, ATT_TABLE); pass = AT_PASS_MISC; break; case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */ --- 3151,3169 ---- pass = AT_PASS_MISC; break; case AT_AddInherit: /* INHERIT */ ! ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* This command never recurses */ ATPrepAddInherit(rel); pass = AT_PASS_MISC; break; + case AT_DropInherit: /* NO INHERIT */ + ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + /* This command never recurses */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; + break; case AT_AlterConstraint: /* ALTER CONSTRAINT */ ! ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); pass = AT_PASS_MISC; break; case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */ *************** *** 3184,3190 **** ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, case AT_EnableAlwaysRule: case AT_EnableReplicaRule: case AT_DisableRule: - case AT_DropInherit: /* NO INHERIT */ case AT_AddOf: /* OF */ case AT_DropOf: /* NOT OF */ ATSimplePermissions(rel, ATT_TABLE); --- 3192,3197 ---- *************** *** 4125,4131 **** ATWrongRelkindError(Relation rel, int allowed_targets) */ static void ATSimpleRecursion(List **wqueue, Relation rel, ! AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode) { /* * Propagate to children if desired. Non-table relations never have --- 4132,4139 ---- */ static void ATSimpleRecursion(List **wqueue, Relation rel, ! AlterTableCmd *cmd, bool recurse, ! bool include_foreign, LOCKMODE lockmode) { /* * Propagate to children if desired. Non-table relations never have *************** *** 4153,4160 **** ATSimpleRecursion(List **wqueue, Relation rel, continue; /* find_all_inheritors already got lock */ childrel = relation_open(childrelid, NoLock); ! CheckTableNotInUse(childrel, "ALTER TABLE"); ! ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode); relation_close(childrel, NoLock); } } --- 4161,4172 ---- continue; /* find_all_inheritors already got lock */ childrel = relation_open(childrelid, NoLock); ! if (childrel->rd_rel->relkind != RELKIND_FOREIGN_TABLE ! || include_foreign) ! { ! CheckTableNotInUse(childrel, "ALTER TABLE"); ! ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode); ! } relation_close(childrel, NoLock); } } *************** *** 4444,4450 **** ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, ATT_TABLE); attrdesc = heap_open(AttributeRelationId, RowExclusiveLock); --- 4456,4462 ---- /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); attrdesc = heap_open(AttributeRelationId, RowExclusiveLock); *************** *** 4859,4865 **** ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOC --- 4871,4889 ---- ATPrepAddColumn(wqueue, rel, recurse, false, cmd, lockmode); if (recurse) + { + /* + * Don't allow to add an OID column to inheritance tree that contains + * foreign table(s) + */ + if (contains_foreign(RelationGetRelid(rel), AccessShareLock)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot add OID column to inheritance tree \"%s\" because it contains foreign table(s)", + RelationGetRelationName(rel)))); + cmd->subtype = AT_AddOidsRecurse; + } } /* *************** *** 5340,5346 **** ATExecDropColumn(List **wqueue, Relation rel, const char *colName, /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, ATT_TABLE); /* * get the number of the attribute --- 5364,5370 ---- /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* * get the number of the attribute *************** *** 5732,5738 **** ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, ATT_TABLE); /* * Call AddRelationNewConstraints to do the work, making sure it works on --- 5756,5769 ---- /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); ! ! /* Don't allow ADD constraint NOT VALID on foreign tables */ ! if (tab->relkind == RELKIND_FOREIGN_TABLE && ! constr->skip_validation && !recursing) ! ereport(ERROR, ! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("NOT VALID is not supported on foreign tables"))); /* * Call AddRelationNewConstraints to do the work, making sure it works on *************** *** 5743,5751 **** ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, * omitted from the returned list, which is what we want: we do not need * to do any validation work. That can only happen at child tables, * though, since we disallow merging at the top level. */ newcons = AddRelationNewConstraints(rel, NIL, ! list_make1(copyObject(constr)), recursing, /* allow_merge */ !recursing, /* is_local */ is_readd); /* is_internal */ --- 5774,5790 ---- * omitted from the returned list, which is what we want: we do not need * to do any validation work. That can only happen at child tables, * though, since we disallow merging at the top level. + * + * When propagating a NOT VALID option to children that are foreign tables, + * we quietly ignore the option. Note that this is safe because foreign + * tables don't have any children. */ + constr = (Constraint *) copyObject(constr); + if (tab->relkind == RELKIND_FOREIGN_TABLE && + constr->skip_validation && recursing) + constr->skip_validation = false; newcons = AddRelationNewConstraints(rel, NIL, ! list_make1(constr), recursing, /* allow_merge */ !recursing, /* is_local */ is_readd); /* is_internal */ *************** *** 7225,7231 **** ATExecDropConstraint(Relation rel, const char *constrName, /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, ATT_TABLE); conrel = heap_open(ConstraintRelationId, RowExclusiveLock); --- 7264,7270 ---- /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); conrel = heap_open(ConstraintRelationId, RowExclusiveLock); *************** *** 7560,7566 **** ATPrepAlterColumnType(List **wqueue, * alter would put them out of step. */ if (recurse) ! ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); else if (!recursing && find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL) ereport(ERROR, --- 7599,7608 ---- * alter would put them out of step. */ if (recurse) ! { ! /* Recurses to child tables that are foreign, too */ ! ATSimpleRecursion(wqueue, rel, cmd, recurse, true, lockmode); ! } else if (!recursing && find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL) ereport(ERROR, *** a/src/backend/commands/vacuum.c --- b/src/backend/commands/vacuum.c *************** *** 98,103 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast, --- 98,104 ---- BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel) { const char *stmttype; + VacuumMode vacmode; volatile bool in_outer_xact, use_own_xacts; List *relations; *************** *** 146,151 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast, --- 147,166 ---- ALLOCSET_DEFAULT_MAXSIZE); /* + * Identify vacuum mode. If relid is not InvalidOid, the caller should be + * an autovacuum worker. See the above comments. + */ + if (relid != InvalidOid) + vacmode = VAC_MODE_AUTOVACUUM; + else + { + if (!vacstmt->relation) + vacmode = VAC_MODE_ALL; + else + vacmode = VAC_MODE_SINGLE; + } + + /* * If caller didn't give us a buffer strategy object, make one in the * cross-transaction memory context. */ *************** *** 248,254 **** vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast, PushActiveSnapshot(GetTransactionSnapshot()); } ! analyze_rel(relid, vacstmt, vac_strategy); if (use_own_xacts) { --- 263,269 ---- PushActiveSnapshot(GetTransactionSnapshot()); } ! analyze_rel(relid, vacstmt, vacmode, vac_strategy); if (use_own_xacts) { *** a/src/backend/optimizer/path/costsize.c --- b/src/backend/optimizer/path/costsize.c *************** *** 132,140 **** static MergeScanSelCache *cached_scansel(PlannerInfo *root, static void cost_rescan(PlannerInfo *root, Path *path, Cost *rescan_startup_cost, Cost *rescan_total_cost); static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context); - static void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel, - ParamPathInfo *param_info, - QualCost *qpqual_cost); static bool has_indexed_join_quals(NestPath *joinpath); static double approx_tuple_count(PlannerInfo *root, JoinPath *path, List *quals); --- 132,137 ---- *************** *** 3157,3163 **** cost_qual_eval_walker(Node *node, cost_qual_eval_context *context) * some of the quals. We assume baserestrictcost was previously set by * set_baserel_size_estimates(). */ ! static void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel, ParamPathInfo *param_info, QualCost *qpqual_cost) --- 3154,3160 ---- * some of the quals. We assume baserestrictcost was previously set by * set_baserel_size_estimates(). */ ! void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel, ParamPathInfo *param_info, QualCost *qpqual_cost) *** a/src/backend/optimizer/prep/prepunion.c --- b/src/backend/optimizer/prep/prepunion.c *************** *** 1337,1347 **** expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) /* * Build an RTE for the child, and attach to query's rangetable list. * We copy most fields of the parent's RTE, but replace relation OID, ! * and set inh = false. Also, set requiredPerms to zero since all ! * required permissions checks are done on the original RTE. */ childrte = copyObject(rte); childrte->relid = childOID; childrte->inh = false; childrte->requiredPerms = 0; parse->rtable = lappend(parse->rtable, childrte); --- 1337,1348 ---- /* * Build an RTE for the child, and attach to query's rangetable list. * We copy most fields of the parent's RTE, but replace relation OID, ! * relkind and set inh = false. Also, set requiredPerms to zero since ! * all required permissions checks are done on the original RTE. */ childrte = copyObject(rte); childrte->relid = childOID; + childrte->relkind = newrelation->rd_rel->relkind; childrte->inh = false; childrte->requiredPerms = 0; parse->rtable = lappend(parse->rtable, childrte); *** a/src/backend/optimizer/util/pathnode.c --- b/src/backend/optimizer/util/pathnode.c *************** *** 16,21 **** --- 16,22 ---- #include + #include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" *************** *** 2062,2067 **** reparameterize_path(PlannerInfo *root, Path *path, --- 2063,2073 ---- case T_SubqueryScan: return create_subqueryscan_path(root, rel, path->pathkeys, required_outer); + case T_ForeignScan: + return (Path *) rel->fdwroutine->ReparameterizeForeignPath(root, + rel, + path, + required_outer); default: break; } *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** *** 4209,4240 **** AlterForeignServerStmt: ALTER SERVER name foreign_server_version alter_generic_o CreateForeignTableStmt: CREATE FOREIGN TABLE qualified_name '(' OptTableElementList ')' ! SERVER name create_generic_options { CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); $4->relpersistence = RELPERSISTENCE_PERMANENT; n->base.relation = $4; n->base.tableElts = $6; ! n->base.inhRelations = NIL; n->base.if_not_exists = false; /* FDW-specific data */ ! n->servername = $9; ! n->options = $10; $$ = (Node *) n; } | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name '(' OptTableElementList ')' ! SERVER name create_generic_options { CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); $7->relpersistence = RELPERSISTENCE_PERMANENT; n->base.relation = $7; n->base.tableElts = $9; ! n->base.inhRelations = NIL; n->base.if_not_exists = true; /* FDW-specific data */ ! n->servername = $12; ! n->options = $13; $$ = (Node *) n; } ; --- 4209,4240 ---- CreateForeignTableStmt: CREATE FOREIGN TABLE qualified_name '(' OptTableElementList ')' ! OptInherit SERVER name create_generic_options { CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); $4->relpersistence = RELPERSISTENCE_PERMANENT; n->base.relation = $4; n->base.tableElts = $6; ! n->base.inhRelations = $8; n->base.if_not_exists = false; /* FDW-specific data */ ! n->servername = $10; ! n->options = $11; $$ = (Node *) n; } | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name '(' OptTableElementList ')' ! OptInherit SERVER name create_generic_options { CreateForeignTableStmt *n = makeNode(CreateForeignTableStmt); $7->relpersistence = RELPERSISTENCE_PERMANENT; n->base.relation = $7; n->base.tableElts = $9; ! n->base.inhRelations = $11; n->base.if_not_exists = true; /* FDW-specific data */ ! n->servername = $13; ! n->options = $14; $$ = (Node *) n; } ; *** a/src/backend/parser/parse_utilcmd.c --- b/src/backend/parser/parse_utilcmd.c *************** *** 515,526 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) break; case CONSTR_CHECK: - if (cxt->isforeign) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("constraints are not supported on foreign tables"), - parser_errposition(cxt->pstate, - constraint->location))); cxt->ckconstraints = lappend(cxt->ckconstraints, constraint); break; --- 515,520 ---- *************** *** 529,535 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) if (cxt->isforeign) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); if (constraint->keys == NIL) --- 523,529 ---- if (cxt->isforeign) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("primary key or unique constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); if (constraint->keys == NIL) *************** *** 546,552 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) if (cxt->isforeign) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); --- 540,546 ---- if (cxt->isforeign) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("foreign key constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); *************** *** 605,614 **** transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint) { ! if (cxt->isforeign) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); --- 599,612 ---- static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint) { ! if (cxt->isforeign && ! (constraint->contype == CONSTR_PRIMARY || ! constraint->contype == CONSTR_UNIQUE || ! constraint->contype == CONSTR_EXCLUSION || ! constraint->contype == CONSTR_FOREIGN)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("primary key, unique, exclusion, or foreign key constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); *** a/src/include/catalog/pg_inherits_fn.h --- b/src/include/catalog/pg_inherits_fn.h *************** *** 21,26 **** extern List *find_inheritance_children(Oid parentrelId, LOCKMODE lockmode); --- 21,27 ---- extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **parents); extern bool has_subclass(Oid relationId); + extern bool contains_foreign(Oid parentrelId, LOCKMODE lockmode); extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId); #endif /* PG_INHERITS_FN_H */ *** a/src/include/commands/vacuum.h --- b/src/include/commands/vacuum.h *************** *** 140,145 **** extern int vacuum_multixact_freeze_min_age; --- 140,154 ---- extern int vacuum_multixact_freeze_table_age; + /* Possible modes for vacuum() */ + typedef enum + { + VAC_MODE_ALL, /* Vacuum/analyze all relations */ + VAC_MODE_SINGLE, /* Vacuum/analyze a specific relation */ + VAC_MODE_AUTOVACUUM /* Autovacuum worker */ + } VacuumMode; + + /* in commands/vacuum.c */ extern void vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast, BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel); *************** *** 174,180 **** extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy); /* in commands/analyze.c */ ! extern void analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy); extern bool std_typanalyze(VacAttrStats *stats); extern double anl_random_fract(void); --- 183,189 ---- BufferAccessStrategy bstrategy); /* in commands/analyze.c */ ! extern void analyze_rel(Oid relid, VacuumStmt *vacstmt, VacuumMode vacmode, BufferAccessStrategy bstrategy); extern bool std_typanalyze(VacAttrStats *stats); extern double anl_random_fract(void); *** a/src/include/foreign/fdwapi.h --- b/src/include/foreign/fdwapi.h *************** *** 31,36 **** typedef void (*GetForeignPaths_function) (PlannerInfo *root, --- 31,41 ---- RelOptInfo *baserel, Oid foreigntableid); + typedef ForeignPath *(*ReparameterizeForeignPath_function) (PlannerInfo *root, + RelOptInfo *baserel, + Path *path, + Relids required_outer); + typedef ForeignScan *(*GetForeignPlan_function) (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, *************** *** 117,122 **** typedef struct FdwRoutine --- 122,128 ---- /* Functions for scanning foreign tables */ GetForeignRelSize_function GetForeignRelSize; GetForeignPaths_function GetForeignPaths; + ReparameterizeForeignPath_function ReparameterizeForeignPath; GetForeignPlan_function GetForeignPlan; BeginForeignScan_function BeginForeignScan; IterateForeignScan_function IterateForeignScan; *** a/src/include/optimizer/cost.h --- b/src/include/optimizer/cost.h *************** *** 143,148 **** extern void final_cost_hashjoin(PlannerInfo *root, HashPath *path, --- 143,151 ---- extern void cost_subplan(PlannerInfo *root, SubPlan *subplan, Plan *plan); extern void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root); extern void cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root); + extern void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel, + ParamPathInfo *param_info, + QualCost *qpqual_cost); extern void compute_semi_anti_join_factors(PlannerInfo *root, RelOptInfo *outerrel, RelOptInfo *innerrel, *** a/src/test/regress/expected/foreign_data.out --- b/src/test/regress/expected/foreign_data.out *************** *** 750,765 **** CREATE TABLE use_ft1_column_type (x ft1); ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE integer; -- ERROR ERROR: cannot alter foreign table "ft1" because column "use_ft1_column_type.x" uses its row type DROP TABLE use_ft1_column_type; ! ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0); -- ERROR ! ERROR: constraints are not supported on foreign tables ! LINE 1: ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c... ! ^ ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const; -- ERROR ! ERROR: "ft1" is not a table ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const; ! ERROR: "ft1" is not a table ! ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check; ! ERROR: "ft1" is not a table ALTER FOREIGN TABLE ft1 SET WITH OIDS; -- ERROR ERROR: "ft1" is not a table ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role; --- 750,761 ---- ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE integer; -- ERROR ERROR: cannot alter foreign table "ft1" because column "use_ft1_column_type.x" uses its row type DROP TABLE use_ft1_column_type; ! ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0); ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const; -- ERROR ! ERROR: constraint "no_const" of relation "ft1" does not exist ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const; ! NOTICE: constraint "no_const" of relation "ft1" does not exist, skipping ! ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c9_check; ALTER FOREIGN TABLE ft1 SET WITH OIDS; -- ERROR ERROR: "ft1" is not a table ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role; *** a/src/test/regress/sql/foreign_data.sql --- b/src/test/regress/sql/foreign_data.sql *************** *** 314,323 **** ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET STATISTICS -1; CREATE TABLE use_ft1_column_type (x ft1); ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE integer; -- ERROR DROP TABLE use_ft1_column_type; ! ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0); -- ERROR ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const; -- ERROR ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const; ! ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c1_check; ALTER FOREIGN TABLE ft1 SET WITH OIDS; -- ERROR ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role; ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@'); --- 314,323 ---- CREATE TABLE use_ft1_column_type (x ft1); ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET DATA TYPE integer; -- ERROR DROP TABLE use_ft1_column_type; ! ALTER FOREIGN TABLE ft1 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0); ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const; -- ERROR ALTER FOREIGN TABLE ft1 DROP CONSTRAINT IF EXISTS no_const; ! ALTER FOREIGN TABLE ft1 DROP CONSTRAINT ft1_c9_check; ALTER FOREIGN TABLE ft1 SET WITH OIDS; -- ERROR ALTER FOREIGN TABLE ft1 OWNER TO regress_test_role; ALTER FOREIGN TABLE ft1 OPTIONS (DROP delimiter, SET quote '~', ADD escape '@');