Commit cea49fe8 authored by Tom Lane's avatar Tom Lane

Dept of second thoughts: improve the API for AnalyzeForeignTable.

If we make the initially-called function return the table physical-size
estimate, acquire_inherited_sample_rows will be able to use that to
allocate numbers of samples among child tables, when the day comes that
we want to support foreign tables in inheritance trees.
parent 263d9de6
...@@ -125,7 +125,9 @@ static void fileBeginForeignScan(ForeignScanState *node, int eflags); ...@@ -125,7 +125,9 @@ static void fileBeginForeignScan(ForeignScanState *node, int eflags);
static TupleTableSlot *fileIterateForeignScan(ForeignScanState *node); static TupleTableSlot *fileIterateForeignScan(ForeignScanState *node);
static void fileReScanForeignScan(ForeignScanState *node); static void fileReScanForeignScan(ForeignScanState *node);
static void fileEndForeignScan(ForeignScanState *node); static void fileEndForeignScan(ForeignScanState *node);
static AcquireSampleRowsFunc fileAnalyzeForeignTable(Relation relation); static bool fileAnalyzeForeignTable(Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages);
/* /*
* Helper functions * Helper functions
...@@ -141,8 +143,7 @@ static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel, ...@@ -141,8 +143,7 @@ static void estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
Cost *startup_cost, Cost *total_cost); Cost *startup_cost, Cost *total_cost);
static int file_acquire_sample_rows(Relation onerel, int elevel, static int file_acquire_sample_rows(Relation onerel, int elevel,
HeapTuple *rows, int targrows, HeapTuple *rows, int targrows,
double *totalrows, double *totaldeadrows, double *totalrows, double *totaldeadrows);
BlockNumber *totalpages);
/* /*
...@@ -656,10 +657,39 @@ fileEndForeignScan(ForeignScanState *node) ...@@ -656,10 +657,39 @@ fileEndForeignScan(ForeignScanState *node)
* fileAnalyzeForeignTable * fileAnalyzeForeignTable
* Test whether analyzing this foreign table is supported * Test whether analyzing this foreign table is supported
*/ */
static AcquireSampleRowsFunc static bool
fileAnalyzeForeignTable(Relation relation) fileAnalyzeForeignTable(Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages)
{ {
return file_acquire_sample_rows; char *filename;
List *options;
struct stat stat_buf;
/* Fetch options of foreign table */
fileGetOptions(RelationGetRelid(relation), &filename, &options);
/*
* Get size of the file. (XXX if we fail here, would it be better to
* just return false to skip analyzing the table?)
*/
if (stat(filename, &stat_buf) < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m",
filename)));
/*
* Convert size to pages. Must return at least 1 so that we can tell
* later on that pg_class.relpages is not default.
*/
*totalpages = (stat_buf.st_size + (BLCKSZ - 1)) / BLCKSZ;
if (*totalpages < 1)
*totalpages = 1;
*func = file_acquire_sample_rows;
return true;
} }
/* /*
...@@ -780,8 +810,7 @@ estimate_costs(PlannerInfo *root, RelOptInfo *baserel, ...@@ -780,8 +810,7 @@ estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
* which must have at least targrows entries. * which must have at least targrows entries.
* The actual number of rows selected is returned as the function result. * The actual number of rows selected is returned as the function result.
* We also count the total number of rows in the file and return it into * We also count the total number of rows in the file and return it into
* *totalrows, and return the file's physical size in *totalpages. * *totalrows. Note that *totaldeadrows is always set to 0.
* Note that *totaldeadrows is always set to 0.
* *
* Note that the returned list of rows is not always in order by physical * Note that the returned list of rows is not always in order by physical
* position in the file. Therefore, correlation estimates derived later * position in the file. Therefore, correlation estimates derived later
...@@ -791,8 +820,7 @@ estimate_costs(PlannerInfo *root, RelOptInfo *baserel, ...@@ -791,8 +820,7 @@ estimate_costs(PlannerInfo *root, RelOptInfo *baserel,
static int static int
file_acquire_sample_rows(Relation onerel, int elevel, file_acquire_sample_rows(Relation onerel, int elevel,
HeapTuple *rows, int targrows, HeapTuple *rows, int targrows,
double *totalrows, double *totaldeadrows, double *totalrows, double *totaldeadrows)
BlockNumber *totalpages)
{ {
int numrows = 0; int numrows = 0;
double rowstoskip = -1; /* -1 means not set yet */ double rowstoskip = -1; /* -1 means not set yet */
...@@ -802,7 +830,6 @@ file_acquire_sample_rows(Relation onerel, int elevel, ...@@ -802,7 +830,6 @@ file_acquire_sample_rows(Relation onerel, int elevel,
bool *nulls; bool *nulls;
bool found; bool found;
char *filename; char *filename;
struct stat stat_buf;
List *options; List *options;
CopyState cstate; CopyState cstate;
ErrorContextCallback errcontext; ErrorContextCallback errcontext;
...@@ -819,22 +846,6 @@ file_acquire_sample_rows(Relation onerel, int elevel, ...@@ -819,22 +846,6 @@ file_acquire_sample_rows(Relation onerel, int elevel,
/* Fetch options of foreign table */ /* Fetch options of foreign table */
fileGetOptions(RelationGetRelid(onerel), &filename, &options); fileGetOptions(RelationGetRelid(onerel), &filename, &options);
/*
* Get size of the file.
*/
if (stat(filename, &stat_buf) < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m",
filename)));
/*
* Convert size to pages for use in I/O cost estimate.
*/
*totalpages = (stat_buf.st_size + (BLCKSZ - 1)) / BLCKSZ;
if (*totalpages < 1)
*totalpages = 1;
/* /*
* Create CopyState from FDW options. * Create CopyState from FDW options.
*/ */
...@@ -931,10 +942,10 @@ file_acquire_sample_rows(Relation onerel, int elevel, ...@@ -931,10 +942,10 @@ file_acquire_sample_rows(Relation onerel, int elevel,
* Emit some interesting relation info * Emit some interesting relation info
*/ */
ereport(elevel, ereport(elevel,
(errmsg("\"%s\": scanned %u pages containing %.0f rows; " (errmsg("\"%s\": file contains %.0f rows; "
"%d rows in sample", "%d rows in sample",
RelationGetRelationName(onerel), RelationGetRelationName(onerel),
*totalpages, *totalrows, numrows))); *totalrows, numrows)));
return numrows; return numrows;
} }
...@@ -279,15 +279,19 @@ EndForeignScan (ForeignScanState *node); ...@@ -279,15 +279,19 @@ EndForeignScan (ForeignScanState *node);
<para> <para>
<programlisting> <programlisting>
AcquireSampleRowsFunc bool
AnalyzeForeignTable (Relation relation); AnalyzeForeignTable (Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages);
</programlisting> </programlisting>
This function is called when <xref linkend="sql-analyze"> is executed on This function is called when <xref linkend="sql-analyze"> is executed on
a foreign table. If the FDW supports collecting statistics for this a foreign table. If the FDW can collect statistics for this
foreign table, it should return a pointer to a function that will collect foreign table, it should return <literal>true</>, and provide a pointer
sample rows from the table. Otherwise, return <literal>NULL</>. If the to a function that will collect sample rows from the table in
FDW does not support collecting statistics for any tables, the <parameter>func</>, plus the estimated size of the table in pages in
<parameter>totalpages</>. Otherwise, return <literal>false</>.
If the FDW does not support collecting statistics for any tables, the
<function>AnalyzeForeignTable</> pointer can be set to <literal>NULL</>. <function>AnalyzeForeignTable</> pointer can be set to <literal>NULL</>.
</para> </para>
...@@ -298,18 +302,16 @@ int ...@@ -298,18 +302,16 @@ int
AcquireSampleRowsFunc (Relation relation, int elevel, AcquireSampleRowsFunc (Relation relation, int elevel,
HeapTuple *rows, int targrows, HeapTuple *rows, int targrows,
double *totalrows, double *totalrows,
double *totaldeadrows, double *totaldeadrows);
BlockNumber *totalpages);
</programlisting> </programlisting>
A random sample of up to <parameter>targrows</> rows should be collected A random sample of up to <parameter>targrows</> rows should be collected
from the table and stored into the caller-provided <parameter>rows</> from the table and stored into the caller-provided <parameter>rows</>
array. The actual number of rows collected must be returned. In array. The actual number of rows collected must be returned. In
addition, store estimates of the total numbers of live rows, dead rows, addition, store estimates of the total numbers of live and dead rows in
and pages in the table into the output parameters the table into the output parameters <parameter>totalrows</> and
<parameter>totalrows</>, <parameter>totaldeadrows</>, and <parameter>totaldeadrows</>. (Set <parameter>totaldeadrows</> to zero
<parameter>totalpages</>. These numbers will be recorded in the table's if the FDW does not have any concept of dead rows.)
<structname>pg_class</> entry for future use.
</para> </para>
<para> <para>
......
...@@ -84,7 +84,8 @@ static BufferAccessStrategy vac_strategy; ...@@ -84,7 +84,8 @@ static BufferAccessStrategy vac_strategy;
static void do_analyze_rel(Relation onerel, VacuumStmt *vacstmt, static void do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
AcquireSampleRowsFunc acquirefunc, bool inh, int elevel); AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
bool inh, int elevel);
static void BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, static void BlockSampler_Init(BlockSampler bs, BlockNumber nblocks,
int samplesize); int samplesize);
static bool BlockSampler_HasMore(BlockSampler bs); static bool BlockSampler_HasMore(BlockSampler bs);
...@@ -97,8 +98,7 @@ static VacAttrStats *examine_attribute(Relation onerel, int attnum, ...@@ -97,8 +98,7 @@ static VacAttrStats *examine_attribute(Relation onerel, int attnum,
Node *index_expr); Node *index_expr);
static int acquire_sample_rows(Relation onerel, int elevel, static int acquire_sample_rows(Relation onerel, int elevel,
HeapTuple *rows, int targrows, HeapTuple *rows, int targrows,
double *totalrows, double *totaldeadrows, double *totalrows, double *totaldeadrows);
BlockNumber *totalpages);
static int compare_rows(const void *a, const void *b); static int compare_rows(const void *a, const void *b);
static int acquire_inherited_sample_rows(Relation onerel, int elevel, static int acquire_inherited_sample_rows(Relation onerel, int elevel,
HeapTuple *rows, int targrows, HeapTuple *rows, int targrows,
...@@ -117,7 +117,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy) ...@@ -117,7 +117,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
{ {
Relation onerel; Relation onerel;
int elevel; int elevel;
AcquireSampleRowsFunc acquirefunc; AcquireSampleRowsFunc acquirefunc = NULL;
BlockNumber relpages = 0;
/* Select logging level */ /* Select logging level */
if (vacstmt->options & VACOPT_VERBOSE) if (vacstmt->options & VACOPT_VERBOSE)
...@@ -182,6 +183,27 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy) ...@@ -182,6 +183,27 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
return; return;
} }
/*
* Silently ignore tables that are temp tables of other backends ---
* trying to analyze these is rather pointless, since their contents are
* probably not up-to-date on disk. (We don't throw a warning here; it
* would just lead to chatter during a database-wide ANALYZE.)
*/
if (RELATION_IS_OTHER_TEMP(onerel))
{
relation_close(onerel, ShareUpdateExclusiveLock);
return;
}
/*
* We can ANALYZE any table except pg_statistic. See update_attstats
*/
if (RelationGetRelid(onerel) == StatisticRelationId)
{
relation_close(onerel, ShareUpdateExclusiveLock);
return;
}
/* /*
* Check that it's a plain table or foreign table; we used to do this * Check that it's a plain table or foreign table; we used to do this
* in get_rel_oids() but seems safer to check after we've locked the * in get_rel_oids() but seems safer to check after we've locked the
...@@ -191,6 +213,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy) ...@@ -191,6 +213,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
{ {
/* Regular table, so we'll use the regular row acquisition function */ /* Regular table, so we'll use the regular row acquisition function */
acquirefunc = acquire_sample_rows; acquirefunc = acquire_sample_rows;
/* Also get regular table's size */
relpages = RelationGetNumberOfBlocks(onerel);
} }
else if (onerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) else if (onerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
{ {
...@@ -199,15 +223,16 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy) ...@@ -199,15 +223,16 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
* supports analysis. * supports analysis.
*/ */
FdwRoutine *fdwroutine; FdwRoutine *fdwroutine;
bool ok = false;
fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(onerel)); fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(onerel));
if (fdwroutine->AnalyzeForeignTable != NULL) if (fdwroutine->AnalyzeForeignTable != NULL)
acquirefunc = fdwroutine->AnalyzeForeignTable(onerel); ok = fdwroutine->AnalyzeForeignTable(onerel,
else &acquirefunc,
acquirefunc = NULL; &relpages);
if (acquirefunc == NULL) if (!ok)
{ {
ereport(WARNING, ereport(WARNING,
(errmsg("skipping \"%s\" --- cannot analyze this foreign table", (errmsg("skipping \"%s\" --- cannot analyze this foreign table",
...@@ -227,27 +252,6 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy) ...@@ -227,27 +252,6 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
return; return;
} }
/*
* Silently ignore tables that are temp tables of other backends ---
* trying to analyze these is rather pointless, since their contents are
* probably not up-to-date on disk. (We don't throw a warning here; it
* would just lead to chatter during a database-wide ANALYZE.)
*/
if (RELATION_IS_OTHER_TEMP(onerel))
{
relation_close(onerel, ShareUpdateExclusiveLock);
return;
}
/*
* We can ANALYZE any table except pg_statistic. See update_attstats
*/
if (RelationGetRelid(onerel) == StatisticRelationId)
{
relation_close(onerel, ShareUpdateExclusiveLock);
return;
}
/* /*
* OK, let's do it. First let other backends know I'm in ANALYZE. * OK, let's do it. First let other backends know I'm in ANALYZE.
*/ */
...@@ -258,13 +262,13 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy) ...@@ -258,13 +262,13 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
/* /*
* Do the normal non-recursive ANALYZE. * Do the normal non-recursive ANALYZE.
*/ */
do_analyze_rel(onerel, vacstmt, acquirefunc, false, elevel); do_analyze_rel(onerel, vacstmt, acquirefunc, relpages, false, elevel);
/* /*
* If there are child tables, do recursive ANALYZE. * If there are child tables, do recursive ANALYZE.
*/ */
if (onerel->rd_rel->relhassubclass) if (onerel->rd_rel->relhassubclass)
do_analyze_rel(onerel, vacstmt, acquirefunc, true, elevel); do_analyze_rel(onerel, vacstmt, acquirefunc, relpages, true, elevel);
/* /*
* Close source relation now, but keep lock so that no one deletes it * Close source relation now, but keep lock so that no one deletes it
...@@ -293,7 +297,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy) ...@@ -293,7 +297,8 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
*/ */
static void static void
do_analyze_rel(Relation onerel, VacuumStmt *vacstmt, do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
AcquireSampleRowsFunc acquirefunc, bool inh, int elevel) AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
bool inh, int elevel)
{ {
int attr_cnt, int attr_cnt,
tcnt, tcnt,
...@@ -308,7 +313,6 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt, ...@@ -308,7 +313,6 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
numrows; numrows;
double totalrows, double totalrows,
totaldeadrows; totaldeadrows;
BlockNumber totalpages;
HeapTuple *rows; HeapTuple *rows;
PGRUsage ru0; PGRUsage ru0;
TimestampTz starttime = 0; TimestampTz starttime = 0;
...@@ -485,17 +489,13 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt, ...@@ -485,17 +489,13 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
*/ */
rows = (HeapTuple *) palloc(targrows * sizeof(HeapTuple)); rows = (HeapTuple *) palloc(targrows * sizeof(HeapTuple));
if (inh) if (inh)
{
numrows = acquire_inherited_sample_rows(onerel, elevel, numrows = acquire_inherited_sample_rows(onerel, elevel,
rows, targrows, rows, targrows,
&totalrows, &totaldeadrows); &totalrows, &totaldeadrows);
totalpages = 0; /* not needed in this path */
}
else else
numrows = (*acquirefunc) (onerel, elevel, numrows = (*acquirefunc) (onerel, elevel,
rows, targrows, rows, targrows,
&totalrows, &totaldeadrows, &totalrows, &totaldeadrows);
&totalpages);
/* /*
* Compute the statistics. Temporary results during the calculations for * Compute the statistics. Temporary results during the calculations for
...@@ -576,7 +576,7 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt, ...@@ -576,7 +576,7 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
*/ */
if (!inh) if (!inh)
vac_update_relstats(onerel, vac_update_relstats(onerel,
totalpages, relpages,
totalrows, totalrows,
visibilitymap_count(onerel), visibilitymap_count(onerel),
hasindex, hasindex,
...@@ -1032,7 +1032,6 @@ BlockSampler_Next(BlockSampler bs) ...@@ -1032,7 +1032,6 @@ BlockSampler_Next(BlockSampler bs)
* The actual number of rows selected is returned as the function result. * The actual number of rows selected is returned as the function result.
* We also estimate the total numbers of live and dead rows in the table, * We also estimate the total numbers of live and dead rows in the table,
* and return them into *totalrows and *totaldeadrows, respectively. * and return them into *totalrows and *totaldeadrows, respectively.
* Also, the number of pages in the relation is returned into *totalpages.
* *
* The returned list of tuples is in order by physical position in the table. * The returned list of tuples is in order by physical position in the table.
* (We will rely on this later to derive correlation estimates.) * (We will rely on this later to derive correlation estimates.)
...@@ -1061,8 +1060,7 @@ BlockSampler_Next(BlockSampler bs) ...@@ -1061,8 +1060,7 @@ BlockSampler_Next(BlockSampler bs)
static int static int
acquire_sample_rows(Relation onerel, int elevel, acquire_sample_rows(Relation onerel, int elevel,
HeapTuple *rows, int targrows, HeapTuple *rows, int targrows,
double *totalrows, double *totaldeadrows, double *totalrows, double *totaldeadrows)
BlockNumber *totalpages)
{ {
int numrows = 0; /* # rows now in reservoir */ int numrows = 0; /* # rows now in reservoir */
double samplerows = 0; /* total # rows collected */ double samplerows = 0; /* total # rows collected */
...@@ -1077,7 +1075,6 @@ acquire_sample_rows(Relation onerel, int elevel, ...@@ -1077,7 +1075,6 @@ acquire_sample_rows(Relation onerel, int elevel,
Assert(targrows > 0); Assert(targrows > 0);
totalblocks = RelationGetNumberOfBlocks(onerel); totalblocks = RelationGetNumberOfBlocks(onerel);
*totalpages = totalblocks;
/* Need a cutoff xmin for HeapTupleSatisfiesVacuum */ /* Need a cutoff xmin for HeapTupleSatisfiesVacuum */
OldestXmin = GetOldestXmin(onerel->rd_rel->relisshared, true); OldestXmin = GetOldestXmin(onerel->rd_rel->relisshared, true);
...@@ -1438,7 +1435,7 @@ compare_rows(const void *a, const void *b) ...@@ -1438,7 +1435,7 @@ compare_rows(const void *a, const void *b)
/* /*
* acquire_inherited_sample_rows -- acquire sample rows from inheritance tree * acquire_inherited_sample_rows -- acquire sample rows from inheritance tree
* *
* This has largely the same API as acquire_sample_rows, except that rows are * This has the same API as acquire_sample_rows, except that rows are
* collected from all inheritance children as well as the specified table. * collected from all inheritance children as well as the specified table.
* We fail and return zero if there are no inheritance children. * We fail and return zero if there are no inheritance children.
*/ */
...@@ -1481,11 +1478,6 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, ...@@ -1481,11 +1478,6 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
/* /*
* Count the blocks in all the relations. The result could overflow * Count the blocks in all the relations. The result could overflow
* BlockNumber, so we use double arithmetic. * BlockNumber, so we use double arithmetic.
*
* XXX eventually we will probably want to allow child tables that are
* foreign tables. Since we can't do RelationGetNumberOfBlocks on a
* foreign table, it's not very clear what fraction of the total to assign
* to it here.
*/ */
rels = (Relation *) palloc(list_length(tableOIDs) * sizeof(Relation)); rels = (Relation *) palloc(list_length(tableOIDs) * sizeof(Relation));
relblocks = (double *) palloc(list_length(tableOIDs) * sizeof(double)); relblocks = (double *) palloc(list_length(tableOIDs) * sizeof(double));
...@@ -1540,7 +1532,6 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, ...@@ -1540,7 +1532,6 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
int childrows; int childrows;
double trows, double trows,
tdrows; tdrows;
BlockNumber tpages;
/* Fetch a random sample of the child's rows */ /* Fetch a random sample of the child's rows */
childrows = acquire_sample_rows(childrel, childrows = acquire_sample_rows(childrel,
...@@ -1548,8 +1539,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, ...@@ -1548,8 +1539,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
rows + numrows, rows + numrows,
childtargrows, childtargrows,
&trows, &trows,
&tdrows, &tdrows);
&tpages);
/* We may need to convert from child's rowtype to parent's */ /* We may need to convert from child's rowtype to parent's */
if (childrows > 0 && if (childrows > 0 &&
......
...@@ -53,11 +53,11 @@ typedef void (*EndForeignScan_function) (ForeignScanState *node); ...@@ -53,11 +53,11 @@ typedef void (*EndForeignScan_function) (ForeignScanState *node);
typedef int (*AcquireSampleRowsFunc) (Relation relation, int elevel, typedef int (*AcquireSampleRowsFunc) (Relation relation, int elevel,
HeapTuple *rows, int targrows, HeapTuple *rows, int targrows,
double *totalrows, double *totalrows,
double *totaldeadrows, double *totaldeadrows);
BlockNumber *totalpages);
typedef AcquireSampleRowsFunc (*AnalyzeForeignTable_function) (Relation relation);
typedef bool (*AnalyzeForeignTable_function) (Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages);
/* /*
* FdwRoutine is the struct returned by a foreign-data wrapper's handler * FdwRoutine is the struct returned by a foreign-data wrapper's handler
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment