mirror of
https://github.com/postgres/postgres.git
synced 2026-07-12 19:15:32 -04:00
postgres_fdw: Remove SPI from postgresImportForeignStatistics.
Previously, this function imported remote statistics by executing SQL
functions like pg_restore_relation_stats and pg_restore_attribute_stats
via SPI (in read-write mode). As the SQL functions take a schema name
and a relation name as two separate arguments, rather than a single OID
argument, if the containing schema was concurrently renamed, the
callback function would throw an error like this:
ERROR: schema "foo" does not exist
To fix, 1) provide new interface functions to import remote statistics
that are directly callable from FDWs and take a single OID, and 2)
modify the callback function to use the interface functions instead when
importing remote statistics.
For #1, this commit does a bit of refactoring to
relation_statistics_update and attribute_statistics_update, which are
the workhorse functions for pg_restore_relation_stats and
pg_restore_attribute_stats respectively: since they also take a schema
name and a relation name, separate the guts of them into new functions
so that they take a single OID and are callable not only from the
workhorse functions but from the interface functions introduced by #1.
Oversight in commit 28972b6fc.
Reported-by: Robert Haas <robertmhaas@gmail.com>
Suggested-by: Robert Haas <robertmhaas@gmail.com>
Author: Corey Huinker <corey.huinker@gmail.com>
Co-authored-by: Etsuro Fujita <etsuro.fujita@gmail.com>
Discussion: https://postgr.es/m/CA%2BTgmoYqMtWb4zLUkT98oFnEkJ%3DWz0Pw-ggDJrp9wnSXPzUaeQ%40mail.gmail.com
Backpatch-through: 19
This commit is contained in:
parent
5594f209c3
commit
54cd6fc831
7 changed files with 481 additions and 311 deletions
|
|
@ -13067,6 +13067,43 @@ ANALYZE dtest_table;
|
||||||
ANALYZE VERBOSE dtest_ftable; -- should work
|
ANALYZE VERBOSE dtest_ftable; -- should work
|
||||||
INFO: importing statistics for foreign table "public.dtest_ftable"
|
INFO: importing statistics for foreign table "public.dtest_ftable"
|
||||||
INFO: finished importing statistics for foreign table "public.dtest_ftable"
|
INFO: finished importing statistics for foreign table "public.dtest_ftable"
|
||||||
|
-- dtest_ftable's stats should now exactly match dtest_table's
|
||||||
|
-- compare values, should match
|
||||||
|
SELECT relpages, reltuples FROM pg_class
|
||||||
|
WHERE oid = 'public.dtest_table'::regclass
|
||||||
|
EXCEPT
|
||||||
|
SELECT relpages, reltuples FROM pg_class
|
||||||
|
WHERE oid = 'public.dtest_ftable'::regclass;
|
||||||
|
relpages | reltuples
|
||||||
|
----------+-----------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
-- compare the rowcounts, should get 0 rows back
|
||||||
|
SELECT COUNT(*) FROM pg_stats
|
||||||
|
WHERE schemaname = 'public' AND tablename = 'dtest_table'
|
||||||
|
EXCEPT
|
||||||
|
SELECT COUNT(*) FROM pg_stats
|
||||||
|
WHERE schemaname = 'public' AND tablename = 'dtest_ftable';
|
||||||
|
count
|
||||||
|
-------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
-- test only a few stats columns common to integer types
|
||||||
|
SELECT attname, inherited, null_frac, avg_width, n_distinct,
|
||||||
|
most_common_vals::text as mcv, most_common_freqs,
|
||||||
|
histogram_bounds::text as hb, correlation
|
||||||
|
FROM pg_stats
|
||||||
|
WHERE schemaname = 'public' AND tablename = 'dtest_table'
|
||||||
|
EXCEPT
|
||||||
|
SELECT attname, inherited, null_frac, avg_width, n_distinct,
|
||||||
|
most_common_vals::text as mcv, most_common_freqs,
|
||||||
|
histogram_bounds::text as hb, correlation
|
||||||
|
FROM pg_stats
|
||||||
|
WHERE schemaname = 'public' AND tablename = 'dtest_ftable';
|
||||||
|
attname | inherited | null_frac | avg_width | n_distinct | mcv | most_common_freqs | hb | correlation
|
||||||
|
---------+-----------+-----------+-----------+------------+-----+-------------------+----+-------------
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
-- cleanup
|
-- cleanup
|
||||||
DROP FOREIGN TABLE simport_ftable;
|
DROP FOREIGN TABLE simport_ftable;
|
||||||
DROP FOREIGN TABLE simport_fview;
|
DROP FOREIGN TABLE simport_fview;
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@
|
||||||
#include "commands/vacuum.h"
|
#include "commands/vacuum.h"
|
||||||
#include "executor/execAsync.h"
|
#include "executor/execAsync.h"
|
||||||
#include "executor/instrument.h"
|
#include "executor/instrument.h"
|
||||||
#include "executor/spi.h"
|
|
||||||
#include "foreign/fdwapi.h"
|
#include "foreign/fdwapi.h"
|
||||||
#include "funcapi.h"
|
#include "funcapi.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
|
@ -47,6 +46,7 @@
|
||||||
#include "storage/latch.h"
|
#include "storage/latch.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/float.h"
|
#include "utils/float.h"
|
||||||
|
#include "utils/fmgroids.h"
|
||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
|
|
@ -337,9 +337,9 @@ typedef struct
|
||||||
{
|
{
|
||||||
PGresult *rel;
|
PGresult *rel;
|
||||||
PGresult *att;
|
PGresult *att;
|
||||||
int server_version_num;
|
|
||||||
double livetuples;
|
double livetuples;
|
||||||
double deadtuples;
|
double deadtuples;
|
||||||
|
int version;
|
||||||
} RemoteStatsResults;
|
} RemoteStatsResults;
|
||||||
|
|
||||||
/* Column order in relation stats query */
|
/* Column order in relation stats query */
|
||||||
|
|
@ -371,136 +371,6 @@ enum AttStatsColumns
|
||||||
ATTSTATS_NUM_FIELDS,
|
ATTSTATS_NUM_FIELDS,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Relation stats import query */
|
|
||||||
static const char *relimport_sql =
|
|
||||||
"SELECT pg_catalog.pg_restore_relation_stats(\n"
|
|
||||||
"\t'version', $1,\n"
|
|
||||||
"\t'schemaname', $2,\n"
|
|
||||||
"\t'relname', $3,\n"
|
|
||||||
"\t'relpages', $4::integer,\n"
|
|
||||||
"\t'reltuples', $5::real)";
|
|
||||||
|
|
||||||
/* Argument order in relation stats import query */
|
|
||||||
enum RelImportSqlArgs
|
|
||||||
{
|
|
||||||
RELIMPORT_SQL_VERSION = 0,
|
|
||||||
RELIMPORT_SQL_SCHEMANAME,
|
|
||||||
RELIMPORT_SQL_RELNAME,
|
|
||||||
RELIMPORT_SQL_RELPAGES,
|
|
||||||
RELIMPORT_SQL_RELTUPLES,
|
|
||||||
RELIMPORT_SQL_NUM_FIELDS
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Argument types in relation stats import query */
|
|
||||||
static const Oid relimport_argtypes[RELIMPORT_SQL_NUM_FIELDS] =
|
|
||||||
{
|
|
||||||
INT4OID, TEXTOID, TEXTOID, TEXTOID,
|
|
||||||
TEXTOID,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Attribute stats import query */
|
|
||||||
static const char *attimport_sql =
|
|
||||||
"SELECT pg_catalog.pg_restore_attribute_stats(\n"
|
|
||||||
"\t'version', $1,\n"
|
|
||||||
"\t'schemaname', $2,\n"
|
|
||||||
"\t'relname', $3,\n"
|
|
||||||
"\t'attnum', $4,\n"
|
|
||||||
"\t'inherited', false::boolean,\n"
|
|
||||||
"\t'null_frac', $5::real,\n"
|
|
||||||
"\t'avg_width', $6::integer,\n"
|
|
||||||
"\t'n_distinct', $7::real,\n"
|
|
||||||
"\t'most_common_vals', $8,\n"
|
|
||||||
"\t'most_common_freqs', $9::real[],\n"
|
|
||||||
"\t'histogram_bounds', $10,\n"
|
|
||||||
"\t'correlation', $11::real,\n"
|
|
||||||
"\t'most_common_elems', $12,\n"
|
|
||||||
"\t'most_common_elem_freqs', $13::real[],\n"
|
|
||||||
"\t'elem_count_histogram', $14::real[],\n"
|
|
||||||
"\t'range_length_histogram', $15,\n"
|
|
||||||
"\t'range_empty_frac', $16::real,\n"
|
|
||||||
"\t'range_bounds_histogram', $17)";
|
|
||||||
|
|
||||||
/* Argument order in attribute stats import query */
|
|
||||||
enum AttImportSqlArgs
|
|
||||||
{
|
|
||||||
ATTIMPORT_SQL_VERSION = 0,
|
|
||||||
ATTIMPORT_SQL_SCHEMANAME,
|
|
||||||
ATTIMPORT_SQL_RELNAME,
|
|
||||||
ATTIMPORT_SQL_ATTNUM,
|
|
||||||
ATTIMPORT_SQL_NULL_FRAC,
|
|
||||||
ATTIMPORT_SQL_AVG_WIDTH,
|
|
||||||
ATTIMPORT_SQL_N_DISTINCT,
|
|
||||||
ATTIMPORT_SQL_MOST_COMMON_VALS,
|
|
||||||
ATTIMPORT_SQL_MOST_COMMON_FREQS,
|
|
||||||
ATTIMPORT_SQL_HISTOGRAM_BOUNDS,
|
|
||||||
ATTIMPORT_SQL_CORRELATION,
|
|
||||||
ATTIMPORT_SQL_MOST_COMMON_ELEMS,
|
|
||||||
ATTIMPORT_SQL_MOST_COMMON_ELEM_FREQS,
|
|
||||||
ATTIMPORT_SQL_ELEM_COUNT_HISTOGRAM,
|
|
||||||
ATTIMPORT_SQL_RANGE_LENGTH_HISTOGRAM,
|
|
||||||
ATTIMPORT_SQL_RANGE_EMPTY_FRAC,
|
|
||||||
ATTIMPORT_SQL_RANGE_BOUNDS_HISTOGRAM,
|
|
||||||
ATTIMPORT_SQL_NUM_FIELDS
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Argument types in attribute stats import query */
|
|
||||||
static const Oid attimport_argtypes[ATTIMPORT_SQL_NUM_FIELDS] =
|
|
||||||
{
|
|
||||||
INT4OID, TEXTOID, TEXTOID, INT2OID,
|
|
||||||
TEXTOID, TEXTOID, TEXTOID, TEXTOID,
|
|
||||||
TEXTOID, TEXTOID, TEXTOID, TEXTOID,
|
|
||||||
TEXTOID, TEXTOID, TEXTOID, TEXTOID,
|
|
||||||
TEXTOID,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The mapping of attribute stats query columns to the positional arguments in
|
|
||||||
* the prepared pg_restore_attribute_stats() statement.
|
|
||||||
*/
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
enum AttStatsColumns res_field;
|
|
||||||
enum AttImportSqlArgs arg_num;
|
|
||||||
} AttrResultArgMap;
|
|
||||||
|
|
||||||
#define NUM_MAPPED_ATTIMPORT_ARGS 13
|
|
||||||
|
|
||||||
static const AttrResultArgMap attr_result_arg_map[NUM_MAPPED_ATTIMPORT_ARGS] =
|
|
||||||
{
|
|
||||||
{ATTSTATS_NULL_FRAC, ATTIMPORT_SQL_NULL_FRAC},
|
|
||||||
{ATTSTATS_AVG_WIDTH, ATTIMPORT_SQL_AVG_WIDTH},
|
|
||||||
{ATTSTATS_N_DISTINCT, ATTIMPORT_SQL_N_DISTINCT},
|
|
||||||
{ATTSTATS_MOST_COMMON_VALS, ATTIMPORT_SQL_MOST_COMMON_VALS},
|
|
||||||
{ATTSTATS_MOST_COMMON_FREQS, ATTIMPORT_SQL_MOST_COMMON_FREQS},
|
|
||||||
{ATTSTATS_HISTOGRAM_BOUNDS, ATTIMPORT_SQL_HISTOGRAM_BOUNDS},
|
|
||||||
{ATTSTATS_CORRELATION, ATTIMPORT_SQL_CORRELATION},
|
|
||||||
{ATTSTATS_MOST_COMMON_ELEMS, ATTIMPORT_SQL_MOST_COMMON_ELEMS},
|
|
||||||
{ATTSTATS_MOST_COMMON_ELEM_FREQS, ATTIMPORT_SQL_MOST_COMMON_ELEM_FREQS},
|
|
||||||
{ATTSTATS_ELEM_COUNT_HISTOGRAM, ATTIMPORT_SQL_ELEM_COUNT_HISTOGRAM},
|
|
||||||
{ATTSTATS_RANGE_LENGTH_HISTOGRAM, ATTIMPORT_SQL_RANGE_LENGTH_HISTOGRAM},
|
|
||||||
{ATTSTATS_RANGE_EMPTY_FRAC, ATTIMPORT_SQL_RANGE_EMPTY_FRAC},
|
|
||||||
{ATTSTATS_RANGE_BOUNDS_HISTOGRAM, ATTIMPORT_SQL_RANGE_BOUNDS_HISTOGRAM},
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Attribute stats clear query */
|
|
||||||
static const char *attclear_sql =
|
|
||||||
"SELECT pg_catalog.pg_clear_attribute_stats($1, $2, $3, false)";
|
|
||||||
|
|
||||||
/* Argument order in attribute stats clear query */
|
|
||||||
enum AttClearSqlArgs
|
|
||||||
{
|
|
||||||
ATTCLEAR_SQL_SCHEMANAME = 0,
|
|
||||||
ATTCLEAR_SQL_RELNAME,
|
|
||||||
ATTCLEAR_SQL_ATTNAME,
|
|
||||||
ATTCLEAR_SQL_NUM_FIELDS
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Argument types in attribute stats clear query */
|
|
||||||
static const Oid attclear_argtypes[ATTCLEAR_SQL_NUM_FIELDS] =
|
|
||||||
{
|
|
||||||
TEXTOID, TEXTOID, TEXTOID,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SQL functions
|
* SQL functions
|
||||||
*/
|
*/
|
||||||
|
|
@ -718,14 +588,18 @@ static bool match_attrmap(PGresult *res,
|
||||||
const char *remote_relname,
|
const char *remote_relname,
|
||||||
int attrcnt,
|
int attrcnt,
|
||||||
RemoteAttributeMapping *remattrmap);
|
RemoteAttributeMapping *remattrmap);
|
||||||
static bool import_fetched_statistics(const char *schemaname,
|
static bool import_fetched_statistics(Relation relation,
|
||||||
|
const char *schemaname,
|
||||||
const char *relname,
|
const char *relname,
|
||||||
int attrcnt,
|
int attrcnt,
|
||||||
const RemoteAttributeMapping *remattrmap,
|
const RemoteAttributeMapping *remattrmap,
|
||||||
RemoteStatsResults *remstats);
|
RemoteStatsResults *remstats);
|
||||||
static void map_field_to_arg(PGresult *res, int row, int field,
|
static char *get_opt_value(PGresult *res, int row, int col);
|
||||||
int arg, Datum *values, char *nulls);
|
static void set_text_arg(NullableDatum *arg, const char *s);
|
||||||
static bool import_spi_query_ok(void);
|
static void set_int32_arg(NullableDatum *arg, const char *s);
|
||||||
|
static void set_uint32_arg(NullableDatum *arg, const char *s);
|
||||||
|
static void set_float_arg(NullableDatum *arg, const char *s);
|
||||||
|
static void set_floatarr_arg(NullableDatum *arg, const char *s);
|
||||||
static void produce_tuple_asynchronously(AsyncRequest *areq, bool fetch);
|
static void produce_tuple_asynchronously(AsyncRequest *areq, bool fetch);
|
||||||
static void fetch_more_data_begin(AsyncRequest *areq);
|
static void fetch_more_data_begin(AsyncRequest *areq);
|
||||||
static void complete_pending_request(AsyncRequest *areq);
|
static void complete_pending_request(AsyncRequest *areq);
|
||||||
|
|
@ -5668,7 +5542,7 @@ postgresImportForeignStatistics(Relation relation, List *va_cols, int elevel)
|
||||||
&attrcnt, &remattrmap, &remstats);
|
&attrcnt, &remattrmap, &remstats);
|
||||||
|
|
||||||
if (ok)
|
if (ok)
|
||||||
ok = import_fetched_statistics(schemaname, relname,
|
ok = import_fetched_statistics(relation, schemaname, relname,
|
||||||
attrcnt, remattrmap, &remstats);
|
attrcnt, remattrmap, &remstats);
|
||||||
|
|
||||||
if (ok)
|
if (ok)
|
||||||
|
|
@ -5738,7 +5612,7 @@ fetch_remote_statistics(Relation relation,
|
||||||
*/
|
*/
|
||||||
user = GetUserMapping(GetUserId(), table->serverid);
|
user = GetUserMapping(GetUserId(), table->serverid);
|
||||||
conn = GetConnection(user, false, NULL);
|
conn = GetConnection(user, false, NULL);
|
||||||
remstats->server_version_num = server_version_num = PQserverVersion(conn);
|
remstats->version = server_version_num = PQserverVersion(conn);
|
||||||
|
|
||||||
/* Fetch relation stats. */
|
/* Fetch relation stats. */
|
||||||
remstats->rel = relstats = fetch_relstats(conn, relation);
|
remstats->rel = relstats = fetch_relstats(conn, relation);
|
||||||
|
|
@ -6144,191 +6018,232 @@ match_attrmap(PGresult *res,
|
||||||
* Import fetched statistics into the local statistics tables.
|
* Import fetched statistics into the local statistics tables.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
import_fetched_statistics(const char *schemaname,
|
import_fetched_statistics(Relation relation,
|
||||||
|
const char *schemaname,
|
||||||
const char *relname,
|
const char *relname,
|
||||||
int attrcnt,
|
int attrcnt,
|
||||||
const RemoteAttributeMapping *remattrmap,
|
const RemoteAttributeMapping *remattrmap,
|
||||||
RemoteStatsResults *remstats)
|
RemoteStatsResults *remstats)
|
||||||
{
|
{
|
||||||
SPIPlanPtr attimport_plan = NULL;
|
PGresult *res;
|
||||||
SPIPlanPtr attclear_plan = NULL;
|
NullableDatum args[ATTSTATS_NUM_FIELDS];
|
||||||
Datum values[ATTIMPORT_SQL_NUM_FIELDS];
|
|
||||||
char nulls[ATTIMPORT_SQL_NUM_FIELDS];
|
|
||||||
int spirc;
|
|
||||||
bool ok = false;
|
|
||||||
|
|
||||||
/* Assign all the invariant parameters common to relation/attribute stats */
|
/* Set the 'version' parameter, which is common to both statistics. */
|
||||||
values[ATTIMPORT_SQL_VERSION] = Int32GetDatum(remstats->server_version_num);
|
args[0].value = UInt32GetDatum(remstats->version);
|
||||||
nulls[ATTIMPORT_SQL_VERSION] = ' ';
|
args[0].isnull = false;
|
||||||
|
|
||||||
values[ATTIMPORT_SQL_SCHEMANAME] = CStringGetTextDatum(schemaname);
|
|
||||||
nulls[ATTIMPORT_SQL_SCHEMANAME] = ' ';
|
|
||||||
|
|
||||||
values[ATTIMPORT_SQL_RELNAME] = CStringGetTextDatum(relname);
|
|
||||||
nulls[ATTIMPORT_SQL_RELNAME] = ' ';
|
|
||||||
|
|
||||||
SPI_connect();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We import attribute statistics first, if any, because those are more
|
* We import attribute statistics first, if any, because those are more
|
||||||
* prone to errors. This avoids making a modification of pg_class that
|
* prone to errors. This avoids making a modification of pg_class that
|
||||||
* will just get rolled back by a failed attribute import.
|
* will just get rolled back by a failed attribute import.
|
||||||
*/
|
*/
|
||||||
if (remstats->att != NULL)
|
res = remstats->att;
|
||||||
|
if (res != NULL)
|
||||||
{
|
{
|
||||||
Assert(PQnfields(remstats->att) == ATTSTATS_NUM_FIELDS);
|
Assert(PQnfields(res) == ATTSTATS_NUM_FIELDS);
|
||||||
Assert(PQntuples(remstats->att) >= 1);
|
Assert(PQntuples(res) >= 1);
|
||||||
|
|
||||||
attimport_plan = SPI_prepare(attimport_sql, ATTIMPORT_SQL_NUM_FIELDS,
|
|
||||||
attimport_argtypes);
|
|
||||||
if (attimport_plan == NULL)
|
|
||||||
elog(ERROR, "failed to prepare attimport_sql query");
|
|
||||||
|
|
||||||
attclear_plan = SPI_prepare(attclear_sql, ATTCLEAR_SQL_NUM_FIELDS,
|
|
||||||
attclear_argtypes);
|
|
||||||
if (attclear_plan == NULL)
|
|
||||||
elog(ERROR, "failed to prepare attclear_sql query");
|
|
||||||
|
|
||||||
nulls[ATTIMPORT_SQL_ATTNUM] = ' ';
|
|
||||||
|
|
||||||
for (int mapidx = 0; mapidx < attrcnt; mapidx++)
|
for (int mapidx = 0; mapidx < attrcnt; mapidx++)
|
||||||
{
|
{
|
||||||
int row = remattrmap[mapidx].res_index;
|
int row = remattrmap[mapidx].res_index;
|
||||||
Datum *values2 = values + 1;
|
AttrNumber attnum = remattrmap[mapidx].local_attnum;
|
||||||
char *nulls2 = nulls + 1;
|
|
||||||
|
|
||||||
/* All mappings should have been assigned a result set row. */
|
/* All mappings should have been assigned a result set row. */
|
||||||
Assert(row >= 0);
|
Assert(row >= 0);
|
||||||
|
|
||||||
/*
|
/* Check for user-requested abort. */
|
||||||
* Check for user-requested abort.
|
|
||||||
*/
|
|
||||||
CHECK_FOR_INTERRUPTS();
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
/*
|
/* Clear existing attribute statistics. */
|
||||||
* First, clear existing attribute stats.
|
delete_attribute_statistics(relation, attnum, false);
|
||||||
*
|
|
||||||
* We can re-use the values/nulls because the number of parameters
|
|
||||||
* is less and the first two params are the same as the second and
|
|
||||||
* third ones in attimport_sql.
|
|
||||||
*/
|
|
||||||
values2[ATTCLEAR_SQL_ATTNAME] =
|
|
||||||
CStringGetTextDatum(remattrmap[mapidx].local_attname);
|
|
||||||
|
|
||||||
spirc = SPI_execute_plan(attclear_plan, values2, nulls2, false, 1);
|
/* Set the remaining parameters. */
|
||||||
if (spirc != SPI_OK_SELECT)
|
set_float_arg(&args[1],
|
||||||
elog(ERROR, "failed to execute attclear_sql query for column \"%s\" of foreign table \"%s.%s\"",
|
get_opt_value(res, row, ATTSTATS_NULL_FRAC));
|
||||||
remattrmap[mapidx].local_attname, schemaname, relname);
|
set_int32_arg(&args[2],
|
||||||
|
get_opt_value(res, row, ATTSTATS_AVG_WIDTH));
|
||||||
|
set_float_arg(&args[3],
|
||||||
|
get_opt_value(res, row, ATTSTATS_N_DISTINCT));
|
||||||
|
set_text_arg(&args[4],
|
||||||
|
get_opt_value(res, row, ATTSTATS_MOST_COMMON_VALS));
|
||||||
|
set_floatarr_arg(&args[5],
|
||||||
|
get_opt_value(res, row, ATTSTATS_MOST_COMMON_FREQS));
|
||||||
|
set_text_arg(&args[6],
|
||||||
|
get_opt_value(res, row, ATTSTATS_HISTOGRAM_BOUNDS));
|
||||||
|
set_float_arg(&args[7],
|
||||||
|
get_opt_value(res, row, ATTSTATS_CORRELATION));
|
||||||
|
set_text_arg(&args[8],
|
||||||
|
get_opt_value(res, row, ATTSTATS_MOST_COMMON_ELEMS));
|
||||||
|
set_floatarr_arg(&args[9],
|
||||||
|
get_opt_value(res, row, ATTSTATS_MOST_COMMON_ELEM_FREQS));
|
||||||
|
set_floatarr_arg(&args[10],
|
||||||
|
get_opt_value(res, row, ATTSTATS_ELEM_COUNT_HISTOGRAM));
|
||||||
|
set_text_arg(&args[11],
|
||||||
|
get_opt_value(res, row, ATTSTATS_RANGE_LENGTH_HISTOGRAM));
|
||||||
|
set_float_arg(&args[12],
|
||||||
|
get_opt_value(res, row, ATTSTATS_RANGE_EMPTY_FRAC));
|
||||||
|
set_text_arg(&args[13],
|
||||||
|
get_opt_value(res, row, ATTSTATS_RANGE_BOUNDS_HISTOGRAM));
|
||||||
|
|
||||||
values[ATTIMPORT_SQL_ATTNUM] =
|
/* Try to import the statistics. */
|
||||||
Int16GetDatum(remattrmap[mapidx].local_attnum);
|
if (!import_attribute_statistics(relation, attnum, false,
|
||||||
|
&args[0], &args[1], &args[2],
|
||||||
/* Loop through all mappable columns to set remaining arguments */
|
&args[3], &args[4], &args[5],
|
||||||
for (int i = 0; i < NUM_MAPPED_ATTIMPORT_ARGS; i++)
|
&args[6], &args[7], &args[8],
|
||||||
map_field_to_arg(remstats->att, row,
|
&args[9], &args[10], &args[11],
|
||||||
attr_result_arg_map[i].res_field,
|
&args[12], &args[13]))
|
||||||
attr_result_arg_map[i].arg_num,
|
|
||||||
values, nulls);
|
|
||||||
|
|
||||||
spirc = SPI_execute_plan(attimport_plan, values, nulls, false, 1);
|
|
||||||
if (spirc != SPI_OK_SELECT)
|
|
||||||
elog(ERROR, "failed to execute attimport_sql query for column \"%s\" of foreign table \"%s.%s\"",
|
|
||||||
remattrmap[mapidx].local_attname, schemaname, relname);
|
|
||||||
|
|
||||||
if (!import_spi_query_ok())
|
|
||||||
{
|
{
|
||||||
ereport(WARNING,
|
ereport(WARNING,
|
||||||
errmsg("could not import statistics for foreign table \"%s.%s\" --- attribute statistics import failed for column \"%s\" of this foreign table",
|
errmsg("could not import statistics for foreign table \"%s.%s\" --- attribute statistics import failed for column \"%s\" of this foreign table",
|
||||||
schemaname, relname,
|
schemaname, relname,
|
||||||
remattrmap[mapidx].local_attname));
|
remattrmap[mapidx].local_attname));
|
||||||
goto import_cleanup;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Import relation stats. We only perform this once, so there is no point
|
* Import relation statistics.
|
||||||
* in preparing the statement.
|
|
||||||
*
|
|
||||||
* We can re-use the values/nulls because the number of parameters is less
|
|
||||||
* and the first three params are the same as attimport_sql.
|
|
||||||
*/
|
*/
|
||||||
Assert(remstats->rel != NULL);
|
res = remstats->rel;
|
||||||
Assert(PQnfields(remstats->rel) == RELSTATS_NUM_FIELDS);
|
Assert(res != NULL);
|
||||||
Assert(PQntuples(remstats->rel) == 1);
|
Assert(PQnfields(res) == RELSTATS_NUM_FIELDS);
|
||||||
map_field_to_arg(remstats->rel, 0, RELSTATS_RELPAGES,
|
Assert(PQntuples(res) == 1);
|
||||||
RELIMPORT_SQL_RELPAGES, values, nulls);
|
|
||||||
map_field_to_arg(remstats->rel, 0, RELSTATS_RELTUPLES,
|
|
||||||
RELIMPORT_SQL_RELTUPLES, values, nulls);
|
|
||||||
|
|
||||||
spirc = SPI_execute_with_args(relimport_sql,
|
/* Set the remaining parameters. */
|
||||||
RELIMPORT_SQL_NUM_FIELDS,
|
set_uint32_arg(&args[1], get_opt_value(res, 0, RELSTATS_RELPAGES));
|
||||||
relimport_argtypes,
|
Assert(!args[1].isnull);
|
||||||
values, nulls, false, 1);
|
set_float_arg(&args[2], get_opt_value(res, 0, RELSTATS_RELTUPLES));
|
||||||
if (spirc != SPI_OK_SELECT)
|
Assert(!args[2].isnull);
|
||||||
elog(ERROR, "failed to execute relimport_sql query for foreign table \"%s.%s\"",
|
args[3].value = (Datum) 0;
|
||||||
schemaname, relname);
|
args[3].isnull = true;
|
||||||
|
args[4].value = (Datum) 0;
|
||||||
|
args[4].isnull = true;
|
||||||
|
|
||||||
if (!import_spi_query_ok())
|
/* Try to import the statistics. */
|
||||||
|
if (!import_relation_statistics(relation, &args[0], &args[1],
|
||||||
|
&args[2], &args[3], &args[4]))
|
||||||
{
|
{
|
||||||
ereport(WARNING,
|
ereport(WARNING,
|
||||||
errmsg("could not import statistics for foreign table \"%s.%s\" --- relation statistics import failed for this foreign table",
|
errmsg("could not import statistics for foreign table \"%s.%s\" --- relation statistics import failed for this foreign table",
|
||||||
schemaname, relname));
|
schemaname, relname));
|
||||||
goto import_cleanup;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = true;
|
return true;
|
||||||
|
|
||||||
import_cleanup:
|
|
||||||
if (attimport_plan)
|
|
||||||
SPI_freeplan(attimport_plan);
|
|
||||||
if (attclear_plan)
|
|
||||||
SPI_freeplan(attclear_plan);
|
|
||||||
SPI_finish();
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Move a string value from a result set to a Text value of a Datum array.
|
* Conenience routine to fetch the value for the row/column of the PGresult
|
||||||
*/
|
*/
|
||||||
static void
|
static char *
|
||||||
map_field_to_arg(PGresult *res, int row, int field,
|
get_opt_value(PGresult *res, int row, int col)
|
||||||
int arg, Datum *values, char *nulls)
|
|
||||||
{
|
{
|
||||||
if (PQgetisnull(res, row, field))
|
if (PQgetisnull(res, row, col))
|
||||||
|
return NULL;
|
||||||
|
return PQgetvalue(res, row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convenience routine for setting optional text arguments
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
set_text_arg(NullableDatum *arg, const char *s)
|
||||||
|
{
|
||||||
|
if (s)
|
||||||
{
|
{
|
||||||
values[arg] = (Datum) 0;
|
arg->value = CStringGetTextDatum(s);
|
||||||
nulls[arg] = 'n';
|
arg->isnull = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const char *s = PQgetvalue(res, row, field);
|
arg->value = (Datum) 0;
|
||||||
|
arg->isnull = true;
|
||||||
values[arg] = CStringGetTextDatum(s);
|
|
||||||
nulls[arg] = ' ';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check the 1x1 result set of a pg_restore_*_stats() command for success.
|
* Convenience routine for setting optional int32 arguments
|
||||||
*/
|
*/
|
||||||
static bool
|
void
|
||||||
import_spi_query_ok(void)
|
set_int32_arg(NullableDatum *arg, const char *s)
|
||||||
{
|
{
|
||||||
TupleDesc tupdesc;
|
if (s)
|
||||||
Datum dat;
|
{
|
||||||
bool isnull;
|
int32 val = pg_strtoint32(s);
|
||||||
|
|
||||||
Assert(SPI_tuptable != NULL);
|
arg->value = Int32GetDatum(val);
|
||||||
Assert(SPI_processed == 1);
|
arg->isnull = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arg->value = (Datum) 0;
|
||||||
|
arg->isnull = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tupdesc = SPI_tuptable->tupdesc;
|
/*
|
||||||
Assert(tupdesc->natts == 1);
|
* Convenience routine for setting optional uint32 arguments
|
||||||
Assert(TupleDescAttr(tupdesc, 0)->atttypid == BOOLOID);
|
*/
|
||||||
dat = SPI_getbinval(SPI_tuptable->vals[0], tupdesc, 1, &isnull);
|
void
|
||||||
Assert(!isnull);
|
set_uint32_arg(NullableDatum *arg, const char *s)
|
||||||
|
{
|
||||||
|
if (s)
|
||||||
|
{
|
||||||
|
uint32 val = uint32in_subr(s, NULL, "uint32", NULL);
|
||||||
|
|
||||||
return DatumGetBool(dat);
|
arg->value = UInt32GetDatum(val);
|
||||||
|
arg->isnull = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arg->value = (Datum) 0;
|
||||||
|
arg->isnull = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convenience routine for setting optional float arguments
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
set_float_arg(NullableDatum *arg, const char *s)
|
||||||
|
{
|
||||||
|
if (s)
|
||||||
|
{
|
||||||
|
float4 val = float4in_internal((char *) s, NULL, "float", s, NULL);
|
||||||
|
|
||||||
|
arg->value = Float4GetDatum(val);
|
||||||
|
arg->isnull = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arg->value = (Datum) 0;
|
||||||
|
arg->isnull = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convenience routine for setting optional float[] arguments
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
set_floatarr_arg(NullableDatum *arg, const char *s)
|
||||||
|
{
|
||||||
|
if (s)
|
||||||
|
{
|
||||||
|
FmgrInfo flinfo;
|
||||||
|
Datum val;
|
||||||
|
|
||||||
|
fmgr_info(F_ARRAY_IN, &flinfo);
|
||||||
|
val = InputFunctionCall(&flinfo, (char *) s, FLOAT4OID, -1);
|
||||||
|
|
||||||
|
arg->value = val;
|
||||||
|
arg->isnull = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arg->value = (Datum) 0;
|
||||||
|
arg->isnull = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -4652,6 +4652,34 @@ ANALYZE dtest_table;
|
||||||
|
|
||||||
ANALYZE VERBOSE dtest_ftable; -- should work
|
ANALYZE VERBOSE dtest_ftable; -- should work
|
||||||
|
|
||||||
|
-- dtest_ftable's stats should now exactly match dtest_table's
|
||||||
|
-- compare values, should match
|
||||||
|
SELECT relpages, reltuples FROM pg_class
|
||||||
|
WHERE oid = 'public.dtest_table'::regclass
|
||||||
|
EXCEPT
|
||||||
|
SELECT relpages, reltuples FROM pg_class
|
||||||
|
WHERE oid = 'public.dtest_ftable'::regclass;
|
||||||
|
|
||||||
|
-- compare the rowcounts, should get 0 rows back
|
||||||
|
SELECT COUNT(*) FROM pg_stats
|
||||||
|
WHERE schemaname = 'public' AND tablename = 'dtest_table'
|
||||||
|
EXCEPT
|
||||||
|
SELECT COUNT(*) FROM pg_stats
|
||||||
|
WHERE schemaname = 'public' AND tablename = 'dtest_ftable';
|
||||||
|
|
||||||
|
-- test only a few stats columns common to integer types
|
||||||
|
SELECT attname, inherited, null_frac, avg_width, n_distinct,
|
||||||
|
most_common_vals::text as mcv, most_common_freqs,
|
||||||
|
histogram_bounds::text as hb, correlation
|
||||||
|
FROM pg_stats
|
||||||
|
WHERE schemaname = 'public' AND tablename = 'dtest_table'
|
||||||
|
EXCEPT
|
||||||
|
SELECT attname, inherited, null_frac, avg_width, n_distinct,
|
||||||
|
most_common_vals::text as mcv, most_common_freqs,
|
||||||
|
histogram_bounds::text as hb, correlation
|
||||||
|
FROM pg_stats
|
||||||
|
WHERE schemaname = 'public' AND tablename = 'dtest_ftable';
|
||||||
|
|
||||||
-- cleanup
|
-- cleanup
|
||||||
DROP FOREIGN TABLE simport_ftable;
|
DROP FOREIGN TABLE simport_ftable;
|
||||||
DROP FOREIGN TABLE simport_fview;
|
DROP FOREIGN TABLE simport_fview;
|
||||||
|
|
|
||||||
|
|
@ -1438,8 +1438,9 @@ ImportForeignStatistics(Relation relation,
|
||||||
<productname>PostgreSQL</productname> is found in
|
<productname>PostgreSQL</productname> is found in
|
||||||
<filename>src/backend/command/analyze.c</filename>.
|
<filename>src/backend/command/analyze.c</filename>.
|
||||||
It's recommended to import table-level and column-level statistics for the
|
It's recommended to import table-level and column-level statistics for the
|
||||||
foreign table using <function>pg_restore_relation_stats</function> and
|
foreign table using <function>import_relation_statistics</function>,
|
||||||
<function>pg_restore_attribute_stats</function>, respectively.
|
<function>import_attribute_statistics</function>, and
|
||||||
|
<function>delete_attribute_statistics</function>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,11 @@ static struct StatsArgInfo cleararginfo[] =
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool attribute_statistics_update(FunctionCallInfo fcinfo);
|
static bool attribute_statistics_update(FunctionCallInfo fcinfo);
|
||||||
|
static bool attribute_statistics_update_internal(Oid reloid,
|
||||||
|
const char *attname,
|
||||||
|
AttrNumber attnum,
|
||||||
|
bool inherited,
|
||||||
|
FunctionCallInfo fcinfo);
|
||||||
static void upsert_pg_statistic(Relation starel, HeapTuple oldtup,
|
static void upsert_pg_statistic(Relation starel, HeapTuple oldtup,
|
||||||
const Datum *values, const bool *nulls, const bool *replaces);
|
const Datum *values, const bool *nulls, const bool *replaces);
|
||||||
static bool delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit);
|
static bool delete_pg_statistic(Oid reloid, AttrNumber attnum, bool stainherit);
|
||||||
|
|
@ -136,38 +141,6 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
|
||||||
bool inherited;
|
bool inherited;
|
||||||
Oid locked_table = InvalidOid;
|
Oid locked_table = InvalidOid;
|
||||||
|
|
||||||
Relation starel;
|
|
||||||
HeapTuple statup;
|
|
||||||
|
|
||||||
Oid atttypid = InvalidOid;
|
|
||||||
int32 atttypmod;
|
|
||||||
char atttyptype;
|
|
||||||
Oid atttypcoll = InvalidOid;
|
|
||||||
Oid eq_opr = InvalidOid;
|
|
||||||
Oid lt_opr = InvalidOid;
|
|
||||||
|
|
||||||
Oid elemtypid = InvalidOid;
|
|
||||||
Oid elem_eq_opr = InvalidOid;
|
|
||||||
|
|
||||||
FmgrInfo array_in_fn;
|
|
||||||
|
|
||||||
bool do_mcv = !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) &&
|
|
||||||
!PG_ARGISNULL(MOST_COMMON_VALS_ARG);
|
|
||||||
bool do_histogram = !PG_ARGISNULL(HISTOGRAM_BOUNDS_ARG);
|
|
||||||
bool do_correlation = !PG_ARGISNULL(CORRELATION_ARG);
|
|
||||||
bool do_mcelem = !PG_ARGISNULL(MOST_COMMON_ELEMS_ARG) &&
|
|
||||||
!PG_ARGISNULL(MOST_COMMON_ELEM_FREQS_ARG);
|
|
||||||
bool do_dechist = !PG_ARGISNULL(ELEM_COUNT_HISTOGRAM_ARG);
|
|
||||||
bool do_bounds_histogram = !PG_ARGISNULL(RANGE_BOUNDS_HISTOGRAM_ARG);
|
|
||||||
bool do_range_length_histogram = !PG_ARGISNULL(RANGE_LENGTH_HISTOGRAM_ARG) &&
|
|
||||||
!PG_ARGISNULL(RANGE_EMPTY_FRAC_ARG);
|
|
||||||
|
|
||||||
Datum values[Natts_pg_statistic] = {0};
|
|
||||||
bool nulls[Natts_pg_statistic] = {0};
|
|
||||||
bool replaces[Natts_pg_statistic] = {0};
|
|
||||||
|
|
||||||
bool result = true;
|
|
||||||
|
|
||||||
stats_check_required_arg(fcinfo, attarginfo, ATTRELSCHEMA_ARG);
|
stats_check_required_arg(fcinfo, attarginfo, ATTRELSCHEMA_ARG);
|
||||||
stats_check_required_arg(fcinfo, attarginfo, ATTRELNAME_ARG);
|
stats_check_required_arg(fcinfo, attarginfo, ATTRELNAME_ARG);
|
||||||
|
|
||||||
|
|
@ -231,6 +204,50 @@ attribute_statistics_update(FunctionCallInfo fcinfo)
|
||||||
stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG);
|
stats_check_required_arg(fcinfo, attarginfo, INHERITED_ARG);
|
||||||
inherited = PG_GETARG_BOOL(INHERITED_ARG);
|
inherited = PG_GETARG_BOOL(INHERITED_ARG);
|
||||||
|
|
||||||
|
return attribute_statistics_update_internal(reloid, attname, attnum,
|
||||||
|
inherited, fcinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Workhorse function for attribute_statistics_update.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
attribute_statistics_update_internal(Oid reloid,
|
||||||
|
const char *attname, AttrNumber attnum,
|
||||||
|
bool inherited, FunctionCallInfo fcinfo)
|
||||||
|
{
|
||||||
|
Relation starel;
|
||||||
|
HeapTuple statup;
|
||||||
|
|
||||||
|
Oid atttypid = InvalidOid;
|
||||||
|
int32 atttypmod;
|
||||||
|
char atttyptype;
|
||||||
|
Oid atttypcoll = InvalidOid;
|
||||||
|
Oid eq_opr = InvalidOid;
|
||||||
|
Oid lt_opr = InvalidOid;
|
||||||
|
|
||||||
|
Oid elemtypid = InvalidOid;
|
||||||
|
Oid elem_eq_opr = InvalidOid;
|
||||||
|
|
||||||
|
FmgrInfo array_in_fn;
|
||||||
|
|
||||||
|
bool do_mcv = !PG_ARGISNULL(MOST_COMMON_FREQS_ARG) &&
|
||||||
|
!PG_ARGISNULL(MOST_COMMON_VALS_ARG);
|
||||||
|
bool do_histogram = !PG_ARGISNULL(HISTOGRAM_BOUNDS_ARG);
|
||||||
|
bool do_correlation = !PG_ARGISNULL(CORRELATION_ARG);
|
||||||
|
bool do_mcelem = !PG_ARGISNULL(MOST_COMMON_ELEMS_ARG) &&
|
||||||
|
!PG_ARGISNULL(MOST_COMMON_ELEM_FREQS_ARG);
|
||||||
|
bool do_dechist = !PG_ARGISNULL(ELEM_COUNT_HISTOGRAM_ARG);
|
||||||
|
bool do_bounds_histogram = !PG_ARGISNULL(RANGE_BOUNDS_HISTOGRAM_ARG);
|
||||||
|
bool do_range_length_histogram = !PG_ARGISNULL(RANGE_LENGTH_HISTOGRAM_ARG) &&
|
||||||
|
!PG_ARGISNULL(RANGE_EMPTY_FRAC_ARG);
|
||||||
|
|
||||||
|
Datum values[Natts_pg_statistic] = {0};
|
||||||
|
bool nulls[Natts_pg_statistic] = {0};
|
||||||
|
bool replaces[Natts_pg_statistic] = {0};
|
||||||
|
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check argument sanity. If some arguments are unusable, emit a WARNING
|
* Check argument sanity. If some arguments are unusable, emit a WARNING
|
||||||
* and set the corresponding argument to NULL in fcinfo.
|
* and set the corresponding argument to NULL in fcinfo.
|
||||||
|
|
@ -689,3 +706,96 @@ pg_restore_attribute_stats(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
PG_RETURN_BOOL(result);
|
PG_RETURN_BOOL(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Import attribute statistics from NullableDatum inputs for all statitical
|
||||||
|
* values.
|
||||||
|
*
|
||||||
|
* For now, the 'version' argument is ignored. In the future it can be used
|
||||||
|
* to interpret older statistics properly.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
import_attribute_statistics(Relation rel, AttrNumber attnum, bool inherited,
|
||||||
|
const NullableDatum *version,
|
||||||
|
const NullableDatum *null_frac,
|
||||||
|
const NullableDatum *avg_width,
|
||||||
|
const NullableDatum *n_distinct,
|
||||||
|
const NullableDatum *most_common_vals,
|
||||||
|
const NullableDatum *most_common_freqs,
|
||||||
|
const NullableDatum *histogram_bounds,
|
||||||
|
const NullableDatum *correlation,
|
||||||
|
const NullableDatum *most_common_elems,
|
||||||
|
const NullableDatum *most_common_elem_freqs,
|
||||||
|
const NullableDatum *elem_count_histogram,
|
||||||
|
const NullableDatum *range_length_histogram,
|
||||||
|
const NullableDatum *range_empty_frac,
|
||||||
|
const NullableDatum *range_bounds_histogram)
|
||||||
|
{
|
||||||
|
LOCAL_FCINFO(newfcinfo, NUM_ATTRIBUTE_STATS_ARGS);
|
||||||
|
Oid reloid = RelationGetRelid(rel);
|
||||||
|
char *relname = RelationGetRelationName(rel);
|
||||||
|
char *attname = get_attname(reloid, attnum, true);
|
||||||
|
|
||||||
|
Assert(null_frac);
|
||||||
|
Assert(avg_width);
|
||||||
|
Assert(n_distinct);
|
||||||
|
Assert(most_common_vals);
|
||||||
|
Assert(most_common_freqs);
|
||||||
|
Assert(histogram_bounds);
|
||||||
|
Assert(correlation);
|
||||||
|
Assert(most_common_elems);
|
||||||
|
Assert(most_common_elem_freqs);
|
||||||
|
Assert(elem_count_histogram);
|
||||||
|
Assert(range_length_histogram);
|
||||||
|
Assert(range_empty_frac);
|
||||||
|
Assert(range_bounds_histogram);
|
||||||
|
|
||||||
|
/* annoyingly, get_attname doesn't check attisdropped */
|
||||||
|
if (attname == NULL ||
|
||||||
|
!SearchSysCacheExistsAttName(reloid, attname))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||||
|
errmsg("column %d of relation \"%s\" does not exist",
|
||||||
|
attnum, relname)));
|
||||||
|
|
||||||
|
InitFunctionCallInfoData(*newfcinfo, NULL, NUM_ATTRIBUTE_STATS_ARGS,
|
||||||
|
InvalidOid, NULL, NULL);
|
||||||
|
|
||||||
|
newfcinfo->args[ATTRELSCHEMA_ARG].value =
|
||||||
|
CStringGetTextDatum(get_namespace_name(RelationGetNamespace(rel)));
|
||||||
|
newfcinfo->args[ATTRELSCHEMA_ARG].isnull = false;
|
||||||
|
newfcinfo->args[ATTRELNAME_ARG].value = CStringGetTextDatum(relname);
|
||||||
|
newfcinfo->args[ATTRELNAME_ARG].isnull = false;
|
||||||
|
newfcinfo->args[ATTNAME_ARG].value = CStringGetTextDatum(attname);
|
||||||
|
newfcinfo->args[ATTNAME_ARG].isnull = false;
|
||||||
|
newfcinfo->args[ATTNUM_ARG].value = Int16GetDatum(attnum);
|
||||||
|
newfcinfo->args[ATTNUM_ARG].isnull = false;
|
||||||
|
newfcinfo->args[INHERITED_ARG].value = BoolGetDatum(inherited);
|
||||||
|
newfcinfo->args[INHERITED_ARG].isnull = false;
|
||||||
|
|
||||||
|
newfcinfo->args[NULL_FRAC_ARG] = *null_frac;
|
||||||
|
newfcinfo->args[AVG_WIDTH_ARG] = *avg_width;
|
||||||
|
newfcinfo->args[N_DISTINCT_ARG] = *n_distinct;
|
||||||
|
newfcinfo->args[MOST_COMMON_VALS_ARG] = *most_common_vals;
|
||||||
|
newfcinfo->args[MOST_COMMON_FREQS_ARG] = *most_common_freqs;
|
||||||
|
newfcinfo->args[HISTOGRAM_BOUNDS_ARG] = *histogram_bounds;
|
||||||
|
newfcinfo->args[CORRELATION_ARG] = *correlation;
|
||||||
|
newfcinfo->args[MOST_COMMON_ELEMS_ARG] = *most_common_elems;
|
||||||
|
newfcinfo->args[MOST_COMMON_ELEM_FREQS_ARG] = *most_common_elem_freqs;
|
||||||
|
newfcinfo->args[ELEM_COUNT_HISTOGRAM_ARG] = *elem_count_histogram;
|
||||||
|
newfcinfo->args[RANGE_LENGTH_HISTOGRAM_ARG] = *range_length_histogram;
|
||||||
|
newfcinfo->args[RANGE_EMPTY_FRAC_ARG] = *range_empty_frac;
|
||||||
|
newfcinfo->args[RANGE_BOUNDS_HISTOGRAM_ARG] = *range_bounds_histogram;
|
||||||
|
|
||||||
|
return attribute_statistics_update_internal(reloid, attname, attnum,
|
||||||
|
inherited, newfcinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Delete attribute statistics.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
delete_attribute_statistics(Relation rel, AttrNumber attnum, bool inherited)
|
||||||
|
{
|
||||||
|
return delete_pg_statistic(RelationGetRelid(rel), attnum, inherited);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
#include "catalog/indexing.h"
|
#include "catalog/indexing.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
|
#include "statistics/statistics.h"
|
||||||
#include "statistics/stat_utils.h"
|
#include "statistics/stat_utils.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/fmgroids.h"
|
#include "utils/fmgroids.h"
|
||||||
|
|
@ -57,6 +58,8 @@ static struct StatsArgInfo relarginfo[] =
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool relation_statistics_update(FunctionCallInfo fcinfo);
|
static bool relation_statistics_update(FunctionCallInfo fcinfo);
|
||||||
|
static bool relation_statistics_update_internal(Oid reloid,
|
||||||
|
FunctionCallInfo fcinfo);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Internal function for modifying statistics for a relation.
|
* Internal function for modifying statistics for a relation.
|
||||||
|
|
@ -64,25 +67,9 @@ static bool relation_statistics_update(FunctionCallInfo fcinfo);
|
||||||
static bool
|
static bool
|
||||||
relation_statistics_update(FunctionCallInfo fcinfo)
|
relation_statistics_update(FunctionCallInfo fcinfo)
|
||||||
{
|
{
|
||||||
bool result = true;
|
|
||||||
char *nspname;
|
char *nspname;
|
||||||
char *relname;
|
char *relname;
|
||||||
Oid reloid;
|
Oid reloid;
|
||||||
Relation crel;
|
|
||||||
BlockNumber relpages = 0;
|
|
||||||
bool update_relpages = false;
|
|
||||||
float reltuples = 0;
|
|
||||||
bool update_reltuples = false;
|
|
||||||
BlockNumber relallvisible = 0;
|
|
||||||
bool update_relallvisible = false;
|
|
||||||
BlockNumber relallfrozen = 0;
|
|
||||||
bool update_relallfrozen = false;
|
|
||||||
HeapTuple ctup;
|
|
||||||
Form_pg_class pgcform;
|
|
||||||
int replaces[4] = {0};
|
|
||||||
Datum values[4] = {0};
|
|
||||||
bool nulls[4] = {0};
|
|
||||||
int nreplaces = 0;
|
|
||||||
Oid locked_table = InvalidOid;
|
Oid locked_table = InvalidOid;
|
||||||
|
|
||||||
stats_check_required_arg(fcinfo, relarginfo, RELSCHEMA_ARG);
|
stats_check_required_arg(fcinfo, relarginfo, RELSCHEMA_ARG);
|
||||||
|
|
@ -101,6 +88,32 @@ relation_statistics_update(FunctionCallInfo fcinfo)
|
||||||
ShareUpdateExclusiveLock, 0,
|
ShareUpdateExclusiveLock, 0,
|
||||||
RangeVarCallbackForStats, &locked_table);
|
RangeVarCallbackForStats, &locked_table);
|
||||||
|
|
||||||
|
return relation_statistics_update_internal(reloid, fcinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Workhorse function for relation_statistics_update.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
relation_statistics_update_internal(Oid reloid, FunctionCallInfo fcinfo)
|
||||||
|
{
|
||||||
|
BlockNumber relpages = 0;
|
||||||
|
bool update_relpages = false;
|
||||||
|
float reltuples = 0;
|
||||||
|
bool update_reltuples = false;
|
||||||
|
BlockNumber relallvisible = 0;
|
||||||
|
bool update_relallvisible = false;
|
||||||
|
BlockNumber relallfrozen = 0;
|
||||||
|
bool update_relallfrozen = false;
|
||||||
|
Relation crel;
|
||||||
|
HeapTuple ctup;
|
||||||
|
Form_pg_class pgcform;
|
||||||
|
int replaces[4] = {0};
|
||||||
|
Datum values[4] = {0};
|
||||||
|
bool nulls[4] = {0};
|
||||||
|
int nreplaces = 0;
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
if (!PG_ARGISNULL(RELPAGES_ARG))
|
if (!PG_ARGISNULL(RELPAGES_ARG))
|
||||||
{
|
{
|
||||||
relpages = PG_GETARG_UINT32(RELPAGES_ARG);
|
relpages = PG_GETARG_UINT32(RELPAGES_ARG);
|
||||||
|
|
@ -241,3 +254,44 @@ pg_restore_relation_stats(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
PG_RETURN_BOOL(result);
|
PG_RETURN_BOOL(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Import relation statistics from NullableDatum inputs for all statitical
|
||||||
|
* values.
|
||||||
|
*
|
||||||
|
* For now, the 'version' argument is ignored. In the future it can be used
|
||||||
|
* to interpret older statistics properly.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
import_relation_statistics(Relation rel,
|
||||||
|
const NullableDatum *version,
|
||||||
|
const NullableDatum *relpages,
|
||||||
|
const NullableDatum *reltuples,
|
||||||
|
const NullableDatum *relallvisible,
|
||||||
|
const NullableDatum *relallfrozen)
|
||||||
|
{
|
||||||
|
LOCAL_FCINFO(newfcinfo, NUM_RELATION_STATS_ARGS);
|
||||||
|
|
||||||
|
Assert(relpages);
|
||||||
|
Assert(reltuples);
|
||||||
|
Assert(relallvisible);
|
||||||
|
Assert(relallfrozen);
|
||||||
|
|
||||||
|
InitFunctionCallInfoData(*newfcinfo, NULL, NUM_RELATION_STATS_ARGS,
|
||||||
|
InvalidOid, NULL, NULL);
|
||||||
|
|
||||||
|
newfcinfo->args[RELSCHEMA_ARG].value =
|
||||||
|
CStringGetTextDatum(get_namespace_name(RelationGetNamespace(rel)));
|
||||||
|
newfcinfo->args[RELSCHEMA_ARG].isnull = false;
|
||||||
|
newfcinfo->args[RELNAME_ARG].value =
|
||||||
|
CStringGetTextDatum(RelationGetRelationName(rel));
|
||||||
|
newfcinfo->args[RELNAME_ARG].isnull = false;
|
||||||
|
|
||||||
|
newfcinfo->args[RELPAGES_ARG] = *relpages;
|
||||||
|
newfcinfo->args[RELTUPLES_ARG] = *reltuples;
|
||||||
|
newfcinfo->args[RELALLVISIBLE_ARG] = *relallvisible;
|
||||||
|
newfcinfo->args[RELALLFROZEN_ARG] = *relallfrozen;
|
||||||
|
|
||||||
|
return relation_statistics_update_internal(RelationGetRelid(rel),
|
||||||
|
newfcinfo);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -128,4 +128,29 @@ extern StatisticExtInfo *choose_best_statistics(List *stats, char requiredkind,
|
||||||
int nclauses);
|
int nclauses);
|
||||||
extern HeapTuple statext_expressions_load(Oid stxoid, bool inh, int idx);
|
extern HeapTuple statext_expressions_load(Oid stxoid, bool inh, int idx);
|
||||||
|
|
||||||
|
extern bool import_relation_statistics(Relation rel,
|
||||||
|
const NullableDatum *version,
|
||||||
|
const NullableDatum *relpages,
|
||||||
|
const NullableDatum *reltuples,
|
||||||
|
const NullableDatum *relallvisible,
|
||||||
|
const NullableDatum *relallfrozen);
|
||||||
|
extern bool import_attribute_statistics(Relation rel,
|
||||||
|
AttrNumber attnum, bool inherited,
|
||||||
|
const NullableDatum *version,
|
||||||
|
const NullableDatum *null_frac,
|
||||||
|
const NullableDatum *avg_width,
|
||||||
|
const NullableDatum *n_distinct,
|
||||||
|
const NullableDatum *most_common_vals,
|
||||||
|
const NullableDatum *most_common_freqs,
|
||||||
|
const NullableDatum *histogram_bounds,
|
||||||
|
const NullableDatum *correlation,
|
||||||
|
const NullableDatum *most_common_elems,
|
||||||
|
const NullableDatum *most_common_elem_freqs,
|
||||||
|
const NullableDatum *elem_count_histogram,
|
||||||
|
const NullableDatum *range_length_histogram,
|
||||||
|
const NullableDatum *range_empty_frac,
|
||||||
|
const NullableDatum *range_bounds_histogram);
|
||||||
|
extern bool delete_attribute_statistics(Relation rel,
|
||||||
|
AttrNumber attnum, bool inherited);
|
||||||
|
|
||||||
#endif /* STATISTICS_H */
|
#endif /* STATISTICS_H */
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue