diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c index 0469a761a9d..d160e3f8571 100644 --- a/contrib/postgres_fdw/postgres_fdw.c +++ b/contrib/postgres_fdw/postgres_fdw.c @@ -323,25 +323,6 @@ typedef struct List *already_used; /* expressions already dealt with */ } ec_member_foreign_arg; -/* Pairs of remote columns with local columns */ -typedef struct -{ - AttrNumber local_attnum; - char *local_attname; - char *remote_attname; - int res_index; -} RemoteAttributeMapping; - -/* Result sets that are returned from a foreign statistics scan */ -typedef struct -{ - PGresult *rel; - PGresult *att; - double livetuples; - double deadtuples; - int version; -} RemoteStatsResults; - /* Column order in relation stats query */ enum RelStatsColumns { @@ -371,6 +352,25 @@ enum AttStatsColumns ATTSTATS_NUM_FIELDS, }; +/* Result sets that are returned from a foreign statistics scan */ +typedef struct +{ + PGresult *rel; /* result for relation stats query */ + PGresult *att; /* result for attribute stats query */ + double livetuples; /* livetuples estimates, for pgstat report */ + double deadtuples; /* deadtuples estimates, for pgstat report */ + int version; /* version of remote server */ +} RemoteStatsResults; + +/* Pairs of remote columns with local columns */ +typedef struct +{ + AttrNumber local_attnum; /* attribute number of local column */ + char *local_attname; /* attribute name of local column */ + char *remote_attname; /* attribute name of remote column */ + int res_index; /* index of row in attribute stats result */ +} RemoteAttributeMapping; + /* * SQL functions */ @@ -566,12 +566,12 @@ static void analyze_row_processor(PGresult *res, int row, PgFdwAnalyzeState *astate); static bool fetch_remote_statistics(Relation relation, List *va_cols, - ForeignTable *table, const char *local_schemaname, const char *local_relname, - int *p_attrcnt, + ForeignTable *table, + RemoteStatsResults *remstats, RemoteAttributeMapping **p_remattrmap, - RemoteStatsResults *remstats); + int *p_attrcnt); static PGresult *fetch_relstats(PGconn *conn, Relation relation); static PGresult *fetch_attstats(PGconn *conn, int server_version_num, const char *remote_schemaname, const char *remote_relname, @@ -586,14 +586,14 @@ static bool match_attrmap(PGresult *res, const char *local_relname, const char *remote_schemaname, const char *remote_relname, - int attrcnt, - RemoteAttributeMapping *remattrmap); + RemoteAttributeMapping *remattrmap, + int attrcnt); static bool import_fetched_statistics(Relation relation, const char *schemaname, const char *relname, - int attrcnt, + RemoteStatsResults *remstats, const RemoteAttributeMapping *remattrmap, - RemoteStatsResults *remstats); + int attrcnt); static char *get_opt_value(PGresult *res, int row, int col); static void set_text_arg(NullableDatum *arg, const char *s); static void set_int32_arg(NullableDatum *arg, const char *s); @@ -5533,12 +5533,12 @@ postgresImportForeignStatistics(Relation relation, List *va_cols, int elevel) starttime = GetCurrentTimestamp(); ok = fetch_remote_statistics(relation, va_cols, - table, schemaname, relname, - &attrcnt, &remattrmap, &remstats); + schemaname, relname, table, + &remstats, &remattrmap, &attrcnt); if (ok) ok = import_fetched_statistics(relation, schemaname, relname, - attrcnt, remattrmap, &remstats); + &remstats, remattrmap, attrcnt); if (ok) { @@ -5564,12 +5564,12 @@ postgresImportForeignStatistics(Relation relation, List *va_cols, int elevel) static bool fetch_remote_statistics(Relation relation, List *va_cols, - ForeignTable *table, const char *local_schemaname, const char *local_relname, - int *p_attrcnt, + ForeignTable *table, + RemoteStatsResults *remstats, RemoteAttributeMapping **p_remattrmap, - RemoteStatsResults *remstats) + int *p_attrcnt) { const char *remote_schemaname = NULL; const char *remote_relname = NULL; @@ -5586,7 +5586,7 @@ fetch_remote_statistics(Relation relation, ListCell *lc; /* - * Assume the remote schema/relation names are the same as the local name + * Assume the remote schema/table names are the same as the local name * unless the foreign table's options tell us otherwise. */ remote_schemaname = local_schemaname; @@ -5604,12 +5604,15 @@ fetch_remote_statistics(Relation relation, /* * Get connection to the foreign server. Connection manager will * establish new connection if necessary. + * + * Note that unlike the sampling case, we only query pg_class and + * pg_stats, so we do the remote access as the current user. */ user = GetUserMapping(GetUserId(), table->serverid); conn = GetConnection(user, false, NULL); remstats->version = server_version_num = PQserverVersion(conn); - /* Fetch relation stats. */ + /* Fetch relation statistics. */ remstats->rel = relstats = fetch_relstats(conn, relation); /* @@ -5637,42 +5640,32 @@ fetch_remote_statistics(Relation relation, } /* - * If the reltuples value > 0, then then we can expect to find attribute - * stats for the remote table. + * If the reltuples value > 0, then we can expect to find attribute stats + * for the remote table. * - * In v14 or latter, if a reltuples value is -1, it means the table has - * never been analyzed, so we wouldn't expect to find the stats for the - * table; fallback to sampling in that case. If the value is 0, it means - * it was empty; in which case skip the stats and import relation stats - * only. + * In v14 or later, if the value is -1, it means the table had never been + * analyzed, so we wouldn't expect to find the stats; fallback to sampling + * in that case. If the value is 0, it means it was empty, in which case + * we don't need the stats, so import relation stats only. * * In versions prior to v14, a value of 0 was ambiguous; it could mean - * that the table had never been analyzed, or that it was empty. Either - * way, we wouldn't expect to find the stats for the table, so we fallback - * to sampling. + * that the table had never been analyzed, or that it was empty. Assuming + * the former, fallback to sampling. */ reltuples = strtod(PQgetvalue(relstats, 0, RELSTATS_RELTUPLES), NULL); - if (((server_version_num < 140000) && (reltuples == 0)) || - ((server_version_num >= 140000) && (reltuples == -1))) - { - ereport(WARNING, - errmsg("could not import statistics for foreign table \"%s.%s\" --- remote table \"%s.%s\" has no relation statistics to import", - local_schemaname, local_relname, - remote_schemaname, remote_relname)); - goto fetch_cleanup; - } - if (reltuples > 0) { StringInfoData column_list; + /* Create mappings of local/remote columns. */ *p_remattrmap = remattrmap = build_remattrmap(relation, va_cols, &attrcnt, &column_list); *p_attrcnt = attrcnt; + /* Try to get attribute stats if needed. */ if (attrcnt > 0) { - /* Fetch attribute stats. */ + /* Fetch attribute statistics. */ remstats->att = attstats = fetch_attstats(conn, server_version_num, remote_schemaname, @@ -5683,10 +5676,19 @@ fetch_remote_statistics(Relation relation, if (!match_attrmap(attstats, local_schemaname, local_relname, remote_schemaname, remote_relname, - attrcnt, remattrmap)) + remattrmap, attrcnt)) goto fetch_cleanup; } } + else if (((server_version_num < 140000) && (reltuples == 0)) || + ((server_version_num >= 140000) && (reltuples == -1))) + { + ereport(WARNING, + errmsg("could not import statistics for foreign table \"%s.%s\" --- remote table \"%s.%s\" has no relation statistics to import", + local_schemaname, local_relname, + remote_schemaname, remote_relname)); + goto fetch_cleanup; + } /* We assume that we have no dead tuple. */ remstats->deadtuples = 0.0; @@ -5700,7 +5702,7 @@ fetch_cleanup: } /* - * Attempt to fetch remote relation stats. + * Attempt to fetch remote relation statistics. */ static PGresult * fetch_relstats(PGconn *conn, Relation relation) @@ -5722,7 +5724,7 @@ fetch_relstats(PGconn *conn, Relation relation) } /* - * Attempt to fetch remote attribute stats. + * Attempt to fetch remote attribute statistics. */ static PGresult * fetch_attstats(PGconn *conn, int server_version_num, @@ -5793,7 +5795,7 @@ fetch_attstats(PGconn *conn, int server_version_num, } /* - * Build the mapping of local columns to remote columns and create a column + * Build the mappings of local columns to remote columns and create a column * list used for constructing the fetch_attstats query. */ static RemoteAttributeMapping * @@ -5812,7 +5814,7 @@ build_remattrmap(Relation relation, List *va_cols, Form_pg_attribute attr = TupleDescAttr(tupdesc, i); char *attname = NameStr(attr->attname); AttrNumber attnum = attr->attnum; - char *remote_attname; + char *colname; List *fc_options; ListCell *lc; @@ -5824,7 +5826,7 @@ build_remattrmap(Relation relation, List *va_cols, continue; /* If the column_name option is not specified, go with attname. */ - remote_attname = attname; + colname = attname; fc_options = GetForeignColumnOptions(RelationGetRelid(relation), attnum); foreach(lc, fc_options) { @@ -5832,24 +5834,24 @@ build_remattrmap(Relation relation, List *va_cols, if (strcmp(def->defname, "column_name") == 0) { - remote_attname = defGetString(def); + colname = defGetString(def); break; } } if (attrcnt > 0) appendStringInfoString(column_list, ", "); - deparseStringLiteral(column_list, remote_attname); + deparseStringLiteral(column_list, colname); remattrmap[attrcnt].local_attnum = attnum; remattrmap[attrcnt].local_attname = pstrdup(attname); - remattrmap[attrcnt].remote_attname = pstrdup(remote_attname); + remattrmap[attrcnt].remote_attname = pstrdup(colname); remattrmap[attrcnt].res_index = -1; attrcnt++; } appendStringInfoChar(column_list, ']'); - /* Sort mapping by remote attribute name if needed. */ + /* Sort mappings by remote_attname if needed. */ if (attrcnt > 1) qsort(remattrmap, attrcnt, sizeof(RemoteAttributeMapping), remattrmap_cmp); @@ -5928,8 +5930,8 @@ match_attrmap(PGresult *res, const char *local_relname, const char *remote_schemaname, const char *remote_relname, - int attrcnt, - RemoteAttributeMapping *remattrmap) + RemoteAttributeMapping *remattrmap, + int attrcnt) { int numrows = PQntuples(res); int row = -1; @@ -6016,9 +6018,9 @@ static bool import_fetched_statistics(Relation relation, const char *schemaname, const char *relname, - int attrcnt, + RemoteStatsResults *remstats, const RemoteAttributeMapping *remattrmap, - RemoteStatsResults *remstats) + int attrcnt) { PGresult *res; NullableDatum args[ATTSTATS_NUM_FIELDS]; @@ -6110,6 +6112,7 @@ import_fetched_statistics(Relation relation, Assert(!args[1].isnull); set_float_arg(&args[2], get_opt_value(res, 0, RELSTATS_RELTUPLES)); Assert(!args[2].isnull); + /* We don't import relallvisible/relallfrozen. */ args[3].value = (Datum) 0; args[3].isnull = true; args[4].value = (Datum) 0; diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml index 0103fdacfdf..502441fefcb 100644 --- a/doc/src/sgml/fdwhandler.sgml +++ b/doc/src/sgml/fdwhandler.sgml @@ -1430,7 +1430,7 @@ ImportForeignStatistics(Relation relation, If the function imports the statistics successfully, it should return true. Otherwise, return false, in which case AnalyzeForeignTable callback function is - called on the foreign table to collect statistics locally, if supported. + called on the foreign table to generate statistics locally, if supported. diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index c28b9dae983..2e2bb7cef81 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -1173,6 +1173,12 @@ examine_attribute(Relation onerel, int attnum, Node *index_expr) return stats; } +/* + * Determine whether the column is analyzable. + * + * If the column is analyzable, its attstattarget value is returned into the + * output parameter p_attstattarget if it isn't NULL. + */ bool attribute_is_analyzable(Relation onerel, int attnum, Form_pg_attribute attr, int *p_attstattarget)