Hello, Before continueing discussion, I tried this patch on the current head.
This patch applies cleanly except one hunk because of a modification just AFTER it, which did no harm. Finally all regression tests suceeded. Attached is the rebased patch of v11 up to the current master. regards, -- Kyotaro Horiguchi NTT Open Source Software Center
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index 5a4d5aa..5a9aec0 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -115,6 +115,11 @@ static void fileGetForeignRelSize(PlannerInfo *root, static void fileGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid); +static ForeignPath *fileReparameterizeForeignPath(PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid, + ForeignPath *path, + Relids required_outer); static ForeignScan *fileGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, @@ -143,7 +148,7 @@ static bool check_selective_binary_conversion(RelOptInfo *baserel, static void estimate_size(PlannerInfo *root, RelOptInfo *baserel, FileFdwPlanState *fdw_private); static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel, - FileFdwPlanState *fdw_private, + FileFdwPlanState *fdw_private, List *join_conds, Cost *startup_cost, Cost *total_cost); static int file_acquire_sample_rows(Relation onerel, int elevel, HeapTuple *rows, int targrows, @@ -161,6 +166,7 @@ file_fdw_handler(PG_FUNCTION_ARGS) fdwroutine->GetForeignRelSize = fileGetForeignRelSize; fdwroutine->GetForeignPaths = fileGetForeignPaths; + fdwroutine->ReparameterizeForeignPath = fileReparameterizeForeignPath; fdwroutine->GetForeignPlan = fileGetForeignPlan; fdwroutine->ExplainForeignScan = fileExplainForeignScan; fdwroutine->BeginForeignScan = fileBeginForeignScan; @@ -509,7 +515,8 @@ fileGetForeignPaths(PlannerInfo *root, (Node *) columns)); /* Estimate costs */ - estimate_costs(root, baserel, fdw_private, + estimate_costs(root, baserel, + fdw_private, NIL, &startup_cost, &total_cost); /* @@ -534,6 +541,41 @@ fileGetForeignPaths(PlannerInfo *root, } /* + * fileReparameterizeForeignPath + * Attempt to modify a given path to have greater parameterization + */ +static ForeignPath * +fileReparameterizeForeignPath(PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid, + ForeignPath *path, + Relids required_outer) +{ + ParamPathInfo *param_info; + FileFdwPlanState *fdw_private = (FileFdwPlanState *) baserel->fdw_private; + Cost startup_cost; + Cost total_cost; + + /* Get the ParamPathInfo */ + param_info = get_baserel_parampathinfo(root, baserel, required_outer); + + /* Redo the cost estimates */ + estimate_costs(root, baserel, + fdw_private, + param_info->ppi_clauses, + &startup_cost, &total_cost); + + /* Make and return the new path */ + return create_foreignscan_path(root, baserel, + param_info->ppi_rows, + startup_cost, + total_cost, + NIL, /* no pathkeys */ + required_outer, + path->fdw_private); +} + +/* * fileGetForeignPlan * Create a ForeignScan plan node for scanning the foreign table */ @@ -962,12 +1004,13 @@ estimate_size(PlannerInfo *root, RelOptInfo *baserel, */ static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel, - FileFdwPlanState *fdw_private, + FileFdwPlanState *fdw_private, List *join_conds, Cost *startup_cost, Cost *total_cost) { BlockNumber pages = fdw_private->pages; double ntuples = fdw_private->ntuples; Cost run_cost = 0; + QualCost join_cost; Cost cpu_per_tuple; /* @@ -978,8 +1021,11 @@ 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; + cost_qual_eval(&join_cost, join_conds, root); + *startup_cost = + (baserel->baserestrictcost.startup + join_cost.startup); + cpu_per_tuple = cpu_tuple_cost * 10 + + (baserel->baserestrictcost.per_tuple + join_cost.per_tuple); run_cost += cpu_per_tuple * ntuples; *total_cost = *startup_cost + run_cost; } diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 7dd43a9..3f7f7c2 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -239,6 +239,11 @@ static void postgresGetForeignRelSize(PlannerInfo *root, static void postgresGetForeignPaths(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid); +static ForeignPath *postgresReparameterizeForeignPath(PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid, + ForeignPath *path, + Relids required_outer); static ForeignScan *postgresGetForeignPlan(PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, @@ -340,6 +345,7 @@ postgres_fdw_handler(PG_FUNCTION_ARGS) /* Functions for scanning foreign tables */ routine->GetForeignRelSize = postgresGetForeignRelSize; routine->GetForeignPaths = postgresGetForeignPaths; + routine->ReparameterizeForeignPath = postgresReparameterizeForeignPath; routine->GetForeignPlan = postgresGetForeignPlan; routine->BeginForeignScan = postgresBeginForeignScan; routine->IterateForeignScan = postgresIterateForeignScan; @@ -727,6 +733,48 @@ postgresGetForeignPaths(PlannerInfo *root, } /* + * postgresReparameterizeForeignPath + * Attempt to modify a given path to have greater parameterization + */ +static ForeignPath * +postgresReparameterizeForeignPath(PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid, + ForeignPath *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 estimates */ + 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 and return 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 */ @@ -1773,11 +1821,8 @@ 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); + Selectivity join_sel; + QualCost join_cost; /* Use rows/width estimates made by set_baserel_size_estimates. */ rows = baserel->rows; @@ -1790,17 +1835,29 @@ estimate_path_cost_size(PlannerInfo *root, 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 are being evaluated remotely, - * too. + * 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; - startup_cost += baserel->baserestrictcost.startup; - cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost.per_tuple; + 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; diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml index 6b5c8b7..ec1492a 100644 --- a/doc/src/sgml/fdwhandler.sgml +++ b/doc/src/sgml/fdwhandler.sgml @@ -162,6 +162,37 @@ GetForeignPaths (PlannerInfo *root, <para> <programlisting> +ForeignPath * +ReparameterizeForeignPath (PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid, + ForeignPath *path, + Relids required_outer); +</programlisting> + + Create an access path for a scan on a foreign table using join + clauses, which is called a <quote>parameterized path</>. + This is called during query planning. + The parameters are as for <function>GetForeignRelSize</>, plus + the <structname>ForeignPath</> (previously produced by + <function>GetForeignPaths</>), and the IDs of all other tables + that provide the join clauses. + </para> + + <para> + This function must generate a parameterized path for a scan on the + foreign table. The parameterized path must contain cost estimates, + and can contain any FDW-private information that is needed to identify + the specific scan method intended. Unlike the other scan-related + functions, this function is optional. + </para> + + <para> + See <xref linkend="fdw-planning"> for additional information. + </para> + + <para> +<programlisting> ForeignScan * GetForeignPlan (PlannerInfo *root, RelOptInfo *baserel, @@ -808,10 +839,10 @@ GetForeignServerByName(const char *name, bool missing_ok); <para> The FDW callback functions <function>GetForeignRelSize</>, - <function>GetForeignPaths</>, <function>GetForeignPlan</>, and - <function>PlanForeignModify</> must fit into the workings of the - <productname>PostgreSQL</> planner. Here are some notes about what - they must do. + <function>GetForeignPaths</>, <function>ReparameterizeForeignPath</>, + <function>GetForeignPlan</>, and <function>PlanForeignModify</> must fit + into the workings of the <productname>PostgreSQL</> planner. Here are + some notes about what they must do. </para> <para> @@ -841,14 +872,17 @@ GetForeignServerByName(const char *name, bool missing_ok); to initialize it to NULL when the <literal>baserel</> node is created. It is useful for passing information forward from <function>GetForeignRelSize</> to <function>GetForeignPaths</> and/or - <function>GetForeignPaths</> to <function>GetForeignPlan</>, thereby - avoiding recalculation. + <function>GetForeignPaths</> to <function>ReparameterizeForeignPath</> + and/or + <function>GetForeignPaths</> or <function>ReparameterizeForeignPath</> + to <function>GetForeignPlan</>, thereby avoiding recalculation. </para> <para> - <function>GetForeignPaths</> can identify the meaning of different - access paths by storing private information in the - <structfield>fdw_private</> field of <structname>ForeignPath</> nodes. + <function>GetForeignPaths</> or <function>ReparameterizeForeignPath</> + can identify the meaning of different access paths by storing private + information in the <structfield>fdw_private</> field of + <structname>ForeignPath</> nodes. <structfield>fdw_private</> is declared as a <type>List</> pointer, but could actually contain anything since the core planner does not touch it. However, best practice is to use a representation that's dumpable @@ -891,10 +925,11 @@ GetForeignServerByName(const char *name, bool missing_ok); <replaceable>sub_expression</>, which it determines can be executed on the remote server given the locally-evaluated value of the <replaceable>sub_expression</>. The actual identification of such a - clause should happen during <function>GetForeignPaths</>, since it would - affect the cost estimate for the path. The path's - <structfield>fdw_private</> field would probably include a pointer to - the identified clause's <structname>RestrictInfo</> node. Then + clause should happen during <function>GetForeignPaths</> or + <function>ReparameterizeForeignPath</>, since it would affect the cost + estimate for the path. The path's <structfield>fdw_private</> field + would probably include a pointer to the identified clause's + <structname>RestrictInfo</> node. Then <function>GetForeignPlan</> would remove that clause from <literal>scan_clauses</>, but add the <replaceable>sub_expression</> to <structfield>fdw_exprs</> to ensure that it gets massaged into executable form. It would probably diff --git a/doc/src/sgml/ref/alter_foreign_table.sgml b/doc/src/sgml/ref/alter_foreign_table.sgml index 4d8cfc5..10461d5 100644 --- a/doc/src/sgml/ref/alter_foreign_table.sgml +++ b/doc/src/sgml/ref/alter_foreign_table.sgml @@ -42,6 +42,10 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab ALTER [ COLUMN ] <replaceable class="PARAMETER">column_name</replaceable> SET ( <replaceable class="PARAMETER">attribute_option</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] ) ALTER [ COLUMN ] <replaceable class="PARAMETER">column_name</replaceable> RESET ( <replaceable class="PARAMETER">attribute_option</replaceable> [, ... ] ) ALTER [ COLUMN ] <replaceable class="PARAMETER">column_name</replaceable> OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ]) + ADD <replaceable class="PARAMETER">table_constraint</replaceable> + DROP CONSTRAINT [ IF EXISTS ] <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ] + INHERIT <replaceable class="PARAMETER">parent_table</replaceable> + NO INHERIT <replaceable class="PARAMETER">parent_table</replaceable> OWNER TO <replaceable class="PARAMETER">new_owner</replaceable> OPTIONS ( [ ADD | SET | DROP ] <replaceable class="PARAMETER">option</replaceable> ['<replaceable class="PARAMETER">value</replaceable>'] [, ... ]) </synopsis> @@ -149,6 +153,50 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab </varlistentry> <varlistentry> + <term><literal>ADD <replaceable class="PARAMETER">table_constraint</replaceable></literal></term> + <listitem> + <para> + This form adds a new constraint to a table using the same syntax as + <xref linkend="SQL-CREATEFOREIGNTABLE">. + Unlike the case when adding a constraint to a regular table, nothing happens + to the underlying storage: this action simply declares that + some new constraint holds for all rows in the foreign table. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>DROP CONSTRAINT [ IF EXISTS ]</literal></term> + <listitem> + <para> + This form drops the specified constraint on a table. + If <literal>IF EXISTS</literal> is specified and the constraint + does not exist, no error is thrown. In this case a notice is issued instead. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>INHERIT <replaceable class="PARAMETER">parent_table</replaceable></literal></term> + <listitem> + <para> + This form adds the target foreign table as a new child of the specified + parent table. The parent table must be an ordinary table. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>NO INHERIT <replaceable class="PARAMETER">parent_table</replaceable></literal></term> + <listitem> + <para> + This form removes the target foreign table from the list of children of + the specified parent table. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>OWNER</literal></term> <listitem> <para> @@ -270,6 +318,24 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab </varlistentry> <varlistentry> + <term><replaceable class="PARAMETER">table_constraint</replaceable></term> + <listitem> + <para> + New table constraint for the table. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="PARAMETER">constraint_name</replaceable></term> + <listitem> + <para> + Name of an existing constraint to drop. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>CASCADE</literal></term> <listitem> <para> @@ -290,6 +356,16 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab </varlistentry> <varlistentry> + <term><replaceable class="PARAMETER">parent_table</replaceable></term> + <listitem> + <para> + A parent table to associate or de-associate with this foreign table. + The parent table must be an ordinary table. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="PARAMETER">new_owner</replaceable></term> <listitem> <para> @@ -319,10 +395,10 @@ ALTER FOREIGN TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceab <para> Consistency with the foreign server is not checked when a column is added or removed with <literal>ADD COLUMN</literal> or - <literal>DROP COLUMN</literal>, a <literal>NOT NULL</> constraint is - added, or a column type is changed with <literal>SET DATA TYPE</>. It is - the user's responsibility to ensure that the table definition matches the - remote side. + <literal>DROP COLUMN</literal>, a <literal>NOT NULL</> constraint or + <literal>CHECK</> constraint is added, or a column type is changed with + <literal>SET DATA TYPE</>. It is the user's responsibility to ensure that + the table definition matches the remote side. </para> <para> diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 69a1e14..eb1352e 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -967,6 +967,13 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> </para> <para> + If a table has any descendant tables that are foreign, a recursive + <literal>SET STORAGE</literal> operation will be rejected since it + is not permitted to add an <literal>oid</literal> system column to + foreign tables. + </para> + + <para> The <literal>TRIGGER</>, <literal>CLUSTER</>, <literal>OWNER</>, and <literal>TABLESPACE</> actions never recurse to descendant tables; that is, they always act as though <literal>ONLY</> were specified. @@ -975,6 +982,19 @@ ALTER TABLE [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> </para> <para> + When adding a <literal>CHECK</> constraint with the <literal>NOT VALID + </literal> option recursively, an inherited constraint on a descendant + table that is foreign will be marked valid without checking + consistency with the foreign server. + </para> + + <para> + A recursive <literal>SET STORAGE</literal> operation will make the + storage mode for a descendant table's column unchanged if the table is + foreign. + </para> + + <para> Changing any part of a system catalog table is not permitted. </para> diff --git a/doc/src/sgml/ref/analyze.sgml b/doc/src/sgml/ref/analyze.sgml index 08d316a..ec257c5 100644 --- a/doc/src/sgml/ref/analyze.sgml +++ b/doc/src/sgml/ref/analyze.sgml @@ -200,6 +200,13 @@ ANALYZE [ VERBOSE ] [ <replaceable class="PARAMETER">table_name</replaceable> [ </para> <para> + The inheritance statistics for a parent table that contains one or more + children that are foreign tables are collected only when explicitly + selected. If the foreign table's wrapper does not support + <command>ANALYZE</command>, the command prints a warning and does nothing. + </para> + + <para> If the table being analyzed is completely empty, <command>ANALYZE</command> will not record new statistics for that table. Any existing statistics will be retained. diff --git a/doc/src/sgml/ref/create_foreign_table.sgml b/doc/src/sgml/ref/create_foreign_table.sgml index 4a8cf38..1755b38 100644 --- a/doc/src/sgml/ref/create_foreign_table.sgml +++ b/doc/src/sgml/ref/create_foreign_table.sgml @@ -19,9 +19,11 @@ <refsynopsisdiv> <synopsis> CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable> ( [ - <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ] [ COLLATE <replaceable>collation</replaceable> ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ] + { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ] [ COLLATE <replaceable>collation</replaceable> ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ] + | <replaceable>table_constraint</replaceable> } [, ... ] ] ) +[ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ] SERVER <replaceable class="parameter">server_name</replaceable> [ OPTIONS ( <replaceable class="PARAMETER">option</replaceable> '<replaceable class="PARAMETER">value</replaceable>' [, ... ] ) ] @@ -30,7 +32,13 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name [ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ] { NOT NULL | NULL | + CHECK ( <replaceable class="PARAMETER">expression</replaceable> ) | DEFAULT <replaceable>default_expr</replaceable> } + +<phrase>and <replaceable class="PARAMETER">table_constraint</replaceable> is:</phrase> + +[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ] +{ CHECK ( <replaceable class="PARAMETER">expression</replaceable> ) } </synopsis> </refsynopsisdiv> @@ -138,6 +146,27 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name </varlistentry> <varlistentry> + <term><literal>CHECK ( <replaceable class="PARAMETER">expression</replaceable> )</literal></term> + <listitem> + <para> + The <literal>CHECK</> clause specifies an expression producing a + Boolean result which each row must satisfy. + Expressions evaluating to TRUE or UNKNOWN succeed. + A check constraint specified as a column constraint should + reference that column's value only, while an expression + appearing in a table constraint can reference multiple columns. + </para> + + <para> + Currently, <literal>CHECK</literal> expressions cannot contain + subqueries nor refer to variables other than columns of the + current row. The system column <literal>tableoid</literal> + may be referenced, but not any other system column. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>DEFAULT <replaceable>default_expr</replaceable></literal></term> <listitem> @@ -159,6 +188,18 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name </varlistentry> <varlistentry> + <term><replaceable class="PARAMETER">parent_table</replaceable></term> + <listitem> + <para> + The name of an existing table from which the new foreign table + automatically inherits all columns. The specified parent table + must be an ordinary table. See <xref linkend="ddl-inherit"> for the + details of table inheritance. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="PARAMETER">server_name</replaceable></term> <listitem> <para> @@ -187,6 +228,24 @@ CREATE FOREIGN TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name </refsect1> + <refsect1> + <title>Notes</title> + + <para> + Constraints on foreign tables are not enforced on insert or update. + Those definitions simply declare the constraints hold for all rows + in the foreign tables. It is the user's responsibility to ensure + that those definitions match the remote side. Such constraints are + used for some kind of query optimization such as constraint exclusion + for partitioned tables (see <xref linkend="ddl-partitioning">). + </para> + + <para> + Since it is not permitted to add an <literal>oid</> system column to + foreign tables, the command will be rejected if any parent tables + have <literal>oid</> system columns. + </para> + </refsect1> <refsect1 id="SQL-CREATEFOREIGNTABLE-examples"> <title>Examples</title> diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 33eef9f..21d32c5 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -2217,6 +2217,12 @@ AddRelationNewConstraints(Relation rel, if (cdef->contype != CONSTR_CHECK) continue; + if (cdef->is_no_inherit && + rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("CHECK constraints on foreign tables cannot be marked NO INHERIT"))); + if (cdef->raw_expr != NULL) { Assert(cdef->cooked_expr == NULL); diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index c09ca7e..f67273c 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -82,6 +82,7 @@ int default_statistics_target = 100; /* A few variables that don't seem worth passing around as parameters */ static MemoryContext anl_context = NULL; +static VacuumMode vac_mode; static BufferAccessStrategy vac_strategy; @@ -115,7 +116,10 @@ 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) +analyze_rel(Oid relid, + VacuumStmt *vacstmt, + VacuumMode vacmode, + BufferAccessStrategy bstrategy) { Relation onerel; int elevel; @@ -129,6 +133,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy) elevel = DEBUG2; /* Set up static variables */ + vac_mode = vacmode; vac_strategy = bstrategy; /* @@ -1452,6 +1457,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, { List *tableOIDs; Relation *rels; + AcquireSampleRowsFunc *acquirefunc; double *relblocks; double totalblocks; int numrows, @@ -1486,6 +1492,8 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, * 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,7 +1515,40 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, } rels[nrels] = childrel; - relblocks[nrels] = (double) RelationGetNumberOfBlocks(childrel); + + if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + { + FdwRoutine *fdwroutine; + BlockNumber relpages = 0; + bool ok = false; + + /* Ignore unless analyzing a specific inheritance tree */ + if (vac_mode != VAC_MODE_SINGLE) + return 0; + + /* Check whether the FDW supports analysis */ + fdwroutine = GetFdwRoutineForRelation(childrel, false); + if (fdwroutine->AnalyzeForeignTable != NULL) + ok = fdwroutine->AnalyzeForeignTable(childrel, + &acquirefunc[nrels], + &relpages); + if (!ok) + { + /* Give up if the FDW doesn't support analysis */ + ereport(WARNING, + (errmsg("skipping analyze of inheritance tree \"%s\" --- cannot analyze foreign table \"%s\"", + RelationGetRelationName(onerel), + RelationGetRelationName(childrel)))); + return 0; + } + relblocks[nrels] = (double) relpages; + } + else + { + acquirefunc[nrels] = acquire_sample_rows; + relblocks[nrels] = (double) RelationGetNumberOfBlocks(childrel); + } + totalblocks += relblocks[nrels]; nrels++; } @@ -1525,6 +1566,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, { Relation childrel = rels[i]; double childblocks = relblocks[i]; + AcquireSampleRowsFunc childacquirefunc = acquirefunc[i]; if (childblocks > 0) { @@ -1540,12 +1582,12 @@ 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); + childrows = childacquirefunc(childrel, + elevel, + rows + numrows, + childtargrows, + &trows, + &tdrows); /* We may need to convert from child's rowtype to parent's */ if (childrows > 0 && diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 341262b..9e09906 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -310,7 +310,8 @@ 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); + 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, @@ -466,10 +467,6 @@ 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, @@ -556,6 +553,30 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId) stmt->relation->relpersistence, &inheritOids, &old_constraints, &parentOidCount); + if (relkind == RELKIND_FOREIGN_TABLE) + { + /* + * Don't allow a foreign table to inherit from parents that have OID + * system columns. + */ + if (parentOidCount > 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot inherit from relation with OIDs"))); + + /* + * Reset the storage parameter for inherited attributes that have + * non-default values. + */ + foreach(listptr, schema) + { + ColumnDef *colDef = lfirst(listptr); + + if (colDef->storage != 0) + colDef->storage = 0; + } + } + /* * Create a tuple descriptor from the relation schema. Note that this * deals with column names, types, and NOT NULL constraints, but not @@ -3065,24 +3086,28 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, * rules. */ ATSimplePermissions(rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE); - ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); + /* Recurse 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); - ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); + /* Recurse 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); - ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); + /* Recurse 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 */ - ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); + /* Recurse 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; @@ -3095,7 +3120,8 @@ 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); + /* 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; @@ -3113,7 +3139,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_ADD_INDEX; break; case AT_AddConstraint: /* ADD CONSTRAINT */ - ATSimplePermissions(rel, ATT_TABLE); + ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) @@ -3127,7 +3153,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_ADD_CONSTR; break; case AT_DropConstraint: /* DROP CONSTRAINT */ - ATSimplePermissions(rel, ATT_TABLE); + ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) @@ -3195,11 +3221,17 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, pass = AT_PASS_MISC; break; case AT_AddInherit: /* INHERIT */ - ATSimplePermissions(rel, ATT_TABLE); + 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); pass = AT_PASS_MISC; @@ -3233,7 +3265,6 @@ 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); @@ -4174,7 +4205,8 @@ ATWrongRelkindError(Relation rel, int allowed_targets) */ static void ATSimpleRecursion(List **wqueue, Relation rel, - AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode) + AlterTableCmd *cmd, bool recurse, + bool include_foreign, LOCKMODE lockmode) { /* * Propagate to children if desired. Non-table relations never have @@ -4202,8 +4234,12 @@ 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); + 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); } } @@ -4493,7 +4529,7 @@ 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); + ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); attrdesc = heap_open(AttributeRelationId, RowExclusiveLock); @@ -4789,6 +4825,11 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, /* find_inheritance_children already got lock */ childrel = heap_open(childrelid, NoLock); + if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE && isOid) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot add OID column to foreign table \"%s\"", + RelationGetRelationName(childrel)))); CheckTableNotInUse(childrel, "ALTER TABLE"); /* Find or create work queue entry for this table */ @@ -5389,7 +5430,7 @@ 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); + ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); /* * get the number of the attribute @@ -5781,7 +5822,14 @@ 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); + ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); + + /* Don't allow ADD CONSTRAINT NOT VALID to be applied to 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 @@ -5792,9 +5840,17 @@ 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. - */ + * + * 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(copyObject(constr)), + list_make1(constr), recursing, /* allow_merge */ !recursing, /* is_local */ is_readd); /* is_internal */ @@ -7274,7 +7330,7 @@ ATExecDropConstraint(Relation rel, const char *constrName, /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) - ATSimplePermissions(rel, ATT_TABLE); + ATSimplePermissions(rel, ATT_TABLE | ATT_FOREIGN_TABLE); conrel = heap_open(ConstraintRelationId, RowExclusiveLock); @@ -7609,7 +7665,10 @@ ATPrepAlterColumnType(List **wqueue, * alter would put them out of step. */ if (recurse) - ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); + { + /* Recurse 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, diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 3d2c739..b5e3ccf 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -61,6 +61,7 @@ int vacuum_multixact_freeze_table_age; /* A few variables that don't seem worth passing around as parameters */ static MemoryContext vac_context = NULL; +static VacuumMode vac_mode; static BufferAccessStrategy vac_strategy; @@ -146,6 +147,20 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast, 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) + vac_mode = VAC_MODE_AUTOVACUUM; + else + { + if (!vacstmt->relation) + vac_mode = VAC_MODE_ALL; + else + vac_mode = VAC_MODE_SINGLE; + } + + /* * If caller didn't give us a buffer strategy object, make one in the * cross-transaction memory context. */ @@ -248,7 +263,7 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast, PushActiveSnapshot(GetTransactionSnapshot()); } - analyze_rel(relid, vacstmt, vac_strategy); + analyze_rel(relid, vacstmt, vac_mode, vac_strategy); if (use_own_xacts) { diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 0410fdd..836fae6 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -1338,11 +1338,12 @@ 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. + * 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); diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 4e05dcd..880595f 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -16,6 +16,7 @@ #include <math.h> +#include "foreign/fdwapi.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" @@ -2062,6 +2063,31 @@ reparameterize_path(PlannerInfo *root, Path *path, case T_SubqueryScan: return create_subqueryscan_path(root, rel, path->pathkeys, required_outer); + case T_ForeignScan: + { + ForeignPath *newpath = NULL; + + /* Let the FDW reparameterize the path node if possible */ + if (rel->fdwroutine->ReparameterizeForeignPath != NULL) + { + Index scan_relid = rel->relid; + RangeTblEntry *rte; + + /* it should be a base rel... */ + Assert(scan_relid > 0); + Assert(rel->rtekind == RTE_RELATION); + rte = planner_rt_fetch(scan_relid, root); + Assert(rte->rtekind == RTE_RELATION); + + newpath = + rel->fdwroutine->ReparameterizeForeignPath(root, + rel, + rte->relid, + (ForeignPath *) path, + required_outer); + } + return (Path *) newpath; + } default: break; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 605c9b4..a206501 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -4213,32 +4213,32 @@ AlterForeignServerStmt: ALTER SERVER name foreign_server_version alter_generic_o CreateForeignTableStmt: CREATE FOREIGN TABLE qualified_name '(' OptTableElementList ')' - SERVER name create_generic_options + 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 = NIL; + n->base.inhRelations = $8; n->base.if_not_exists = false; /* FDW-specific data */ - n->servername = $9; - n->options = $10; + n->servername = $10; + n->options = $11; $$ = (Node *) n; } | CREATE FOREIGN TABLE IF_P NOT EXISTS qualified_name '(' OptTableElementList ')' - SERVER name create_generic_options + 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 = NIL; + n->base.inhRelations = $11; n->base.if_not_exists = true; /* FDW-specific data */ - n->servername = $12; - n->options = $13; + n->servername = $13; + n->options = $14; $$ = (Node *) n; } ; diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 7c1939f..949392a 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -515,12 +515,6 @@ 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; @@ -529,7 +523,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) if (cxt->isforeign) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("constraints are not supported on foreign tables"), + errmsg("primary key or unique constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); if (constraint->keys == NIL) @@ -546,7 +540,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) if (cxt->isforeign) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("constraints are not supported on foreign tables"), + errmsg("foreign key constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); @@ -605,10 +599,14 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) static void transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint) { - if (cxt->isforeign) + 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("constraints are not supported on foreign tables"), + errmsg("primary key, unique, exclusion, or foreign key constraints are not supported on foreign tables"), parser_errposition(cxt->pstate, constraint->location))); diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index d33552a..400e373 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -140,6 +140,15 @@ extern int vacuum_multixact_freeze_min_age; 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,7 +183,9 @@ extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy); /* in commands/analyze.c */ -extern void analyze_rel(Oid relid, VacuumStmt *vacstmt, +extern void analyze_rel(Oid relid, + VacuumStmt *vacstmt, + VacuumMode vacmode, BufferAccessStrategy bstrategy); extern bool std_typanalyze(VacAttrStats *stats); extern double anl_random_fract(void); diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h index 1b735da..5f3996a 100644 --- a/src/include/foreign/fdwapi.h +++ b/src/include/foreign/fdwapi.h @@ -31,6 +31,12 @@ typedef void (*GetForeignPaths_function) (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid); +typedef ForeignPath *(*ReparameterizeForeignPath_function) (PlannerInfo *root, + RelOptInfo *baserel, + Oid foreigntableid, + ForeignPath *path, + Relids required_outer); + typedef ForeignScan *(*GetForeignPlan_function) (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, @@ -117,6 +123,7 @@ typedef struct FdwRoutine /* Functions for scanning foreign tables */ GetForeignRelSize_function GetForeignRelSize; GetForeignPaths_function GetForeignPaths; + ReparameterizeForeignPath_function ReparameterizeForeignPath; GetForeignPlan_function GetForeignPlan; BeginForeignScan_function BeginForeignScan; IterateForeignScan_function IterateForeignScan; diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out index ff203b2..36df338 100644 --- a/src/test/regress/expected/foreign_data.out +++ b/src/test/regress/expected/foreign_data.out @@ -748,16 +748,12 @@ 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 ADD CONSTRAINT ft1_c9_check CHECK (c9 < 0); ALTER FOREIGN TABLE ft1 DROP CONSTRAINT no_const; -- ERROR -ERROR: "ft1" is not a table +ERROR: constraint "no_const" of relation "ft1" does not exist 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 +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; diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql index 0f0869e..8f9ea86 100644 --- a/src/test/regress/sql/foreign_data.sql +++ b/src/test/regress/sql/foreign_data.sql @@ -314,10 +314,10 @@ 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 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_c1_check; +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 '@');
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers