aboutsummaryrefslogtreecommitdiff
path: root/contrib/postgres_fdw/postgres_fdw.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2013-02-22 10:56:06 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2013-02-22 10:56:36 -0500
commitc0c6acdfa055b0c76ea0d1defd4c2c0d5a5c256f (patch)
tree15fa0aeeb52be2d508ce2dfda25a6963924882cf /contrib/postgres_fdw/postgres_fdw.c
parentaf0a4c5924061869ee3db391900ab745107c966f (diff)
downloadpostgresql-c0c6acdfa055b0c76ea0d1defd4c2c0d5a5c256f.tar.gz
postgresql-c0c6acdfa055b0c76ea0d1defd4c2c0d5a5c256f.zip
Fix some planning oversights in postgres_fdw.
Include eval costs of local conditions in remote-estimate mode, and don't assume the remote eval cost is zero in local-estimate mode. (The best we can do with that at the moment is to assume a seqscan, which may well be wildly pessimistic ... but zero won't do at all.) To get a reasonable local estimate, we need to know the relpages count for the remote rel, so improve the ANALYZE code to fetch that rather than just setting the foreign table's relpages field to zero.
Diffstat (limited to 'contrib/postgres_fdw/postgres_fdw.c')
-rw-r--r--contrib/postgres_fdw/postgres_fdw.c87
1 files changed, 80 insertions, 7 deletions
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index a3256179f2b..99dc6aef135 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -259,6 +259,9 @@ postgresGetForeignRelSize(PlannerInfo *root,
int width;
Cost startup_cost;
Cost total_cost;
+ Cost run_cost;
+ QualCost qpqual_cost;
+ Cost cpu_per_tuple;
List *remote_conds;
List *param_conds;
List *local_conds;
@@ -349,6 +352,16 @@ postgresGetForeignRelSize(PlannerInfo *root,
sel *= clauselist_selectivity(root, local_conds,
baserel->relid, JOIN_INNER, NULL);
+ /*
+ * Add in the eval cost of those conditions, too.
+ */
+ cost_qual_eval(&qpqual_cost, param_conds, root);
+ startup_cost += qpqual_cost.startup;
+ total_cost += qpqual_cost.per_tuple * rows;
+ cost_qual_eval(&qpqual_cost, local_conds, root);
+ startup_cost += qpqual_cost.startup;
+ total_cost += qpqual_cost.per_tuple * rows;
+
/* Report estimated numbers to planner. */
baserel->rows = clamp_row_est(rows * sel);
baserel->width = width;
@@ -367,18 +380,25 @@ postgresGetForeignRelSize(PlannerInfo *root,
* estimate of 10 pages, and divide by the column-datatype-based width
* estimate to get the corresponding number of tuples.
*/
- if (baserel->tuples <= 0)
+ if (baserel->pages == 0 && baserel->tuples == 0)
+ {
+ baserel->pages = 10;
baserel->tuples =
(10 * BLCKSZ) / (baserel->width + sizeof(HeapTupleHeaderData));
+ }
set_baserel_size_estimates(root, baserel);
- /*
- * XXX need to do something here to calculate sane startup and total
- * cost estimates ... for the moment, we do this:
- */
+ /* Cost as though this were a seqscan, which is pessimistic. */
startup_cost = 0;
- total_cost = baserel->rows * cpu_tuple_cost;
+ 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;
}
/*
@@ -1068,9 +1088,62 @@ postgresAnalyzeForeignTable(Relation relation,
AcquireSampleRowsFunc *func,
BlockNumber *totalpages)
{
- *totalpages = 0; /* XXX this is probably a bad idea */
+ ForeignTable *table;
+ ForeignServer *server;
+ UserMapping *user;
+ PGconn *conn;
+ StringInfoData sql;
+ PGresult *volatile res = NULL;
+
+ /* Return the row-analysis function pointer */
*func = postgresAcquireSampleRowsFunc;
+ /*
+ * Now we have to get the number of pages. It's annoying that the ANALYZE
+ * API requires us to return that now, because it forces some duplication
+ * of effort between this routine and postgresAcquireSampleRowsFunc. But
+ * it's probably not worth redefining that API at this point.
+ */
+
+ /*
+ * Get the connection to use. We do the remote access as the table's
+ * owner, even if the ANALYZE was started by some other user.
+ */
+ table = GetForeignTable(RelationGetRelid(relation));
+ server = GetForeignServer(table->serverid);
+ user = GetUserMapping(relation->rd_rel->relowner, server->serverid);
+ conn = GetConnection(server, user);
+
+ /*
+ * Construct command to get page count for relation.
+ */
+ initStringInfo(&sql);
+ deparseAnalyzeSizeSql(&sql, relation);
+
+ /* In what follows, do not risk leaking any PGresults. */
+ PG_TRY();
+ {
+ res = PQexec(conn, sql.data);
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ pgfdw_report_error(ERROR, res, false, sql.data);
+
+ if (PQntuples(res) != 1 || PQnfields(res) != 1)
+ elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
+ *totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
+
+ PQclear(res);
+ res = NULL;
+ }
+ PG_CATCH();
+ {
+ if (res)
+ PQclear(res);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ ReleaseConnection(conn);
+
return true;
}