}
/*
- * Construct SELECT statement to acquire the number of rows of a relation.
+ * Construct SELECT statement to acquire the number of rows and the relkind of
+ * a relation.
*
* Note: we just return the remote server's reltuples value, which might
* be off a good deal, but it doesn't seem worth working harder. See
* comments in postgresAcquireSampleRowsFunc.
*/
void
-deparseAnalyzeTuplesSql(StringInfo buf, Relation rel)
+deparseAnalyzeInfoSql(StringInfo buf, Relation rel)
{
StringInfoData relname;
initStringInfo(&relname);
deparseRelation(&relname, rel);
- appendStringInfoString(buf, "SELECT reltuples FROM pg_catalog.pg_class WHERE oid = ");
+ appendStringInfoString(buf, "SELECT reltuples, relkind FROM pg_catalog.pg_class WHERE oid = ");
deparseStringLiteral(buf, relname.data);
appendStringInfoString(buf, "::pg_catalog.regclass");
}
}
/*
- * postgresCountTuplesForForeignTable
+ * postgresGetAnalyzeInfoForForeignTable
* Count tuples in foreign table (just get pg_class.reltuples).
+ *
+ * can_tablesample determines if the remote relation supports acquiring the
+ * sample using TABLESAMPLE.
*/
static double
-postgresCountTuplesForForeignTable(Relation relation)
+postgresGetAnalyzeInfoForForeignTable(Relation relation, bool *can_tablesample)
{
ForeignTable *table;
UserMapping *user;
StringInfoData sql;
PGresult *volatile res = NULL;
volatile double reltuples = -1;
+ volatile char relkind = 0;
+
+ /* assume the remote relation does not support TABLESAMPLE */
+ *can_tablesample = false;
/*
* Get the connection to use. We do the remote access as the table's
* Construct command to get page count for relation.
*/
initStringInfo(&sql);
- deparseAnalyzeTuplesSql(&sql, relation);
+ deparseAnalyzeInfoSql(&sql, relation);
/* In what follows, do not risk leaking any PGresults. */
PG_TRY();
if (PQresultStatus(res) != PGRES_TUPLES_OK)
pgfdw_report_error(ERROR, res, conn, false, sql.data);
- if (PQntuples(res) != 1 || PQnfields(res) != 1)
+ if (PQntuples(res) != 1 || PQnfields(res) != 2)
elog(ERROR, "unexpected result from deparseAnalyzeTuplesSql query");
reltuples = strtod(PQgetvalue(res, 0, 0), NULL);
+ relkind = *(PQgetvalue(res, 0, 1));
}
PG_FINALLY();
{
ReleaseConnection(conn);
+ /* TABLESAMPLE is supported only for regular tables and matviews */
+ *can_tablesample = (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_MATVIEW ||
+ relkind == RELKIND_PARTITIONED_TABLE);
+
return reltuples;
}
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("remote server does not support TABLESAMPLE feature")));
- /*
- * For "auto" method, pick the one we believe is best. For servers with
- * TABLESAMPLE support we pick BERNOULLI, for old servers we fall-back to
- * random() to at least reduce network transfer.
- */
- if (method == ANALYZE_SAMPLE_AUTO)
- {
- if (server_version_num < 95000)
- method = ANALYZE_SAMPLE_RANDOM;
- else
- method = ANALYZE_SAMPLE_BERNOULLI;
- }
-
/*
* If we've decided to do remote sampling, calculate the sampling rate. We
* need to get the number of tuples from the remote server, but skip that
*/
if (method != ANALYZE_SAMPLE_OFF)
{
- reltuples = postgresCountTuplesForForeignTable(relation);
+ bool can_tablesample;
+
+ reltuples = postgresGetAnalyzeInfoForForeignTable(relation,
+ &can_tablesample);
+
+ /*
+ * Make sure we're not choosing TABLESAMPLE when the remote relation does
+ * not support that. But only do this for "auto" - if the user explicitly
+ * requested BERNOULLI/SYSTEM, it's better to fail.
+ */
+ if (!can_tablesample && (method == ANALYZE_SAMPLE_AUTO))
+ method = ANALYZE_SAMPLE_RANDOM;
/*
* Remote's reltuples could be 0 or -1 if the table has never been
}
}
+ /*
+ * For "auto" method, pick the one we believe is best. For servers with
+ * TABLESAMPLE support we pick BERNOULLI, for old servers we fall-back to
+ * random() to at least reduce network transfer.
+ */
+ if (method == ANALYZE_SAMPLE_AUTO)
+ {
+ if (server_version_num < 95000)
+ method = ANALYZE_SAMPLE_RANDOM;
+ else
+ method = ANALYZE_SAMPLE_BERNOULLI;
+ }
+
/*
* Construct cursor that retrieves whole rows from remote.
*/