#include "postgres.h"
#include "pgstat.h"
+#include "miscadmin.h"
+#include "access/transam.h"
+#include "access/xact.h"
#include "catalog/namespace.h"
#include "commands/vacuum.h"
#include "executor/executor.h"
#include "utils/timestamp.h"
#if PG_VERSION_NUM >= 90500
#include "nodes/makefuncs.h"
+#if PG_VERSION_NUM >= 100000
+#include "utils/varlena.h"
+#include "utils/regproc.h"
+#if PG_VERSION_NUM >= 130000
+#include "common/hashfn.h"
+#endif
+#endif
#endif
#endif
#endif
static bool online_analyze_enable = true;
+static bool online_analyze_local_tracking = false;
static bool online_analyze_verbose = true;
static double online_analyze_scale_factor = 0.1;
static int online_analyze_threshold = 50;
static ProcessUtility_hook_type oldProcessUtilityHook = NULL;
#endif
+#if PG_VERSION_NUM >= 120000
+#define VACOPT_NOWAIT VACOPT_SKIP_LOCKED
+#endif
+
+typedef enum CmdKind
+{
+ CK_SELECT = CMD_SELECT,
+ CK_UPDATE = CMD_UPDATE,
+ CK_INSERT = CMD_INSERT,
+ CK_DELETE = CMD_DELETE,
+ CK_TRUNCATE,
+ CK_FASTTRUNCATE,
+ CK_CREATE,
+ CK_ANALYZE,
+ CK_VACUUM
+} CmdKind;
+
+
typedef enum
{
OATT_ALL = 0x03,
int nTables;
Oid *tables;
char *tableStr;
+ bool inited;
} TableList;
-static TableList excludeTables = {0, NULL, NULL};
-static TableList includeTables = {0, NULL, NULL};
+static TableList excludeTables = {0, NULL, NULL, false};
+static TableList includeTables = {0, NULL, NULL, false};
typedef struct OnlineAnalyzeTableStat {
Oid tableid;
bool rereadStat;
PgStat_Counter n_tuples;
- PgStat_Counter changes_since_analyze;
- TimestampTz autovac_analyze_timestamp;
- TimestampTz analyze_timestamp;
+ PgStat_Counter mod_since_analyze;
+ TimestampTz last_autoanalyze_time;
+ TimestampTz last_analyze_time;
} OnlineAnalyzeTableStat;
static MemoryContext onlineAnalyzeMemoryContext = NULL;
static void relstatsInit(void);
+#if PG_VERSION_NUM < 100000
static int
oid_cmp(const void *a, const void *b)
{
return 0;
return (*(Oid*)a > *(Oid*)b) ? 1 : -1;
}
+#endif
static const char *
tableListAssign(const char * newval, bool doit, TableList *tbl)
if (!SplitIdentifierString(rawname, ',', &namelist))
goto cleanup;
+ /*
+ * follow work could be done only in normal processing because of
+ * accsess to system catalog
+ */
+#if PG_VERSION_NUM >= 170000
+ if (MyProcNumber == INVALID_PROC_NUMBER ||
+#else
+ if (MyBackendId == InvalidBackendId ||
+#endif
+ !IsUnderPostmaster ||
+ !IsTransactionState())
+ {
+ includeTables.inited = false;
+ excludeTables.inited = false;
+ return newval;
+ }
+
if (doit)
{
nOids = list_length(namelist);
foreach(l, namelist)
{
char *curname = (char *) lfirst(l);
-#if PG_VERSION_NUM >= 90200
+#if PG_VERSION_NUM >= 160000
+ Oid relOid = RangeVarGetRelid(makeRangeVarFromNameList(
+ stringToQualifiedNameList(curname, NULL)), NoLock, true);
+#elif PG_VERSION_NUM >= 90200
Oid relOid = RangeVarGetRelid(makeRangeVarFromNameList(
stringToQualifiedNameList(curname)), NoLock, true);
#else
static void
includeTablesAssign(const char *newval, void *extra)
{
- tableListAssign(newval, true, &excludeTables);
+ tableListAssign(newval, true, &includeTables);
}
#else /* PG_VERSION_NUM < 90100 */
#endif
+static void
+lateInit()
+{
+ TableList *tl[] = {&includeTables, &excludeTables};
+ int i;
+
+#if PG_VERSION_NUM >= 170000
+ if (MyProcNumber == INVALID_PROC_NUMBER ||
+#else
+ if (MyBackendId == InvalidBackendId ||
+#endif
+ !IsUnderPostmaster ||
+ !IsTransactionState())
+ return; /* we aren't in connected state */
+
+ for(i=0; i<lengthof(tl); i++)
+ {
+ TableList *tbl = tl[i];
+
+ if (tbl->inited == false)
+ tableListAssign(tbl->tableStr, true, tbl);
+ tbl->inited = true;
+ }
+}
+
static const char*
tableListShow(TableList *tbl)
{
int i,
len;
+ lateInit();
+
len = 1 /* \0 */ + tbl->nTables * (2 * NAMEDATALEN + 2 /* ', ' */ + 1 /* . */);
ptr = val = palloc(len);
*ptr ='\0';
#endif
static void
-makeAnalyze(Oid relOid, CmdType operation, int64 naffected)
+makeAnalyze(Oid relOid, CmdKind operation, int64 naffected)
{
TimestampTz now = GetCurrentTimestamp();
Relation rel;
else
rstat = &dummyrstat; /* found == false for following if */
- if (found == false || rstat->rereadStat == true || naffected == 0)
+ if (!found)
{
+ MemSet(rstat, 0, sizeof(*rstat));
+ rstat->tableid = relOid;
+ newTable = true;
+ }
- if (!found)
- {
- MemSet(rstat, 0, sizeof(*rstat));
- rstat->tableid = relOid;
- }
- Assert(rstat->tableid == relOid);
+ if (operation == CK_VACUUM)
+ {
+ /* force reread because vacuum could change n_tuples */
+ rstat->rereadStat = true;
+ return;
+ }
+ else if (operation == CK_ANALYZE)
+ {
+ /* only analyze */
+ rstat->mod_since_analyze = 0;
+ rstat->last_analyze_time = now;
+ if (newTable)
+ rstat->rereadStat = true;
+ return;
+ }
+
+ Assert(rstat->tableid == relOid);
+
+ if (
+ /* do not reread data if it was a truncation */
+ operation != CK_TRUNCATE && operation != CK_FASTTRUNCATE &&
+ /* read for persistent table and for temp teble if it allowed */
+ (reltype == OATT_PERSISTENT || online_analyze_local_tracking == false) &&
+ /* read only for new table or we know that it's needed */
+ (newTable == true || rstat->rereadStat == true)
+ )
+ {
+ rstat->rereadStat = false;
tabentry = pgstat_fetch_stat_tabentry(relOid);
if (tabentry)
{
- rstat->n_tuples = tabentry->n_dead_tuples + tabentry->n_live_tuples;
- rstat->changes_since_analyze =
-#if PG_VERSION_NUM >= 90000
+ rstat->n_tuples =
+#if PG_VERSION_NUM >= 160000
+ tabentry->dead_tuples + tabentry->live_tuples;
+#else
+ tabentry->n_dead_tuples + tabentry->n_live_tuples;
+#endif
+
+ rstat->mod_since_analyze =
+#if PG_VERSION_NUM >= 160000
+ tabentry->mod_since_analyze;
+#elif PG_VERSION_NUM >= 90000
tabentry->changes_since_analyze;
#else
tabentry->n_live_tuples + tabentry->n_dead_tuples -
tabentry->last_anl_tuples;
#endif
- rstat->autovac_analyze_timestamp =
+
+ rstat->last_autoanalyze_time =
+#if PG_VERSION_NUM >= 160000
+ tabentry->last_autoanalyze_time;
+#else
tabentry->autovac_analyze_timestamp;
- rstat->analyze_timestamp = tabentry->analyze_timestamp;
- rstat->rereadStat = false;
- }
- else
- {
- newTable = true;
- rstat->rereadStat = true;
+#endif
+
+ rstat->last_analyze_time =
+#if PG_VERSION_NUM >= 160000
+ tabentry->last_analyze_time;
+#else
+ tabentry->analyze_timestamp;
+#endif
}
}
- if (newTable || (
+ if (newTable ||
+ /* force analyze after truncate, fasttruncate already did analyze */
+ operation == CK_TRUNCATE || (
/* do not analyze too often, if both stamps are exceeded the go */
- TimestampDifferenceExceeds(rstat->analyze_timestamp, now, online_analyze_min_interval) &&
- TimestampDifferenceExceeds(rstat->autovac_analyze_timestamp, now, online_analyze_min_interval) &&
+ TimestampDifferenceExceeds(rstat->last_analyze_time, now, online_analyze_min_interval) &&
+ TimestampDifferenceExceeds(rstat->last_autoanalyze_time, now, online_analyze_min_interval) &&
/* do not analyze too small tables */
- rstat->n_tuples + rstat->changes_since_analyze + naffected > online_analyze_lower_limit &&
+ rstat->n_tuples + rstat->mod_since_analyze + naffected > online_analyze_lower_limit &&
/* be in sync with relation_needs_vacanalyze */
- ((double)(rstat->changes_since_analyze + naffected)) >=
+ ((double)(rstat->mod_since_analyze + naffected)) >=
online_analyze_scale_factor * ((double)rstat->n_tuples) +
(double)online_analyze_threshold))
{
VacuumParams vacstmt;
#endif
TimestampTz startStamp, endStamp;
+ int flags;
+#ifdef PGPRO_EE
+ /* ATX is not compatible with online_analyze */
+ if (getNestLevelATX() != 0)
+ return;
+#endif
memset(&startStamp, 0, sizeof(startStamp)); /* keep compiler quiet */
vacstmt.log_min_duration = -1;
#endif
+
if (online_analyze_verbose)
startStamp = GetCurrentTimestamp();
+ flags = VACOPT_ANALYZE | VACOPT_NOWAIT |
+ ((online_analyze_verbose) ? VACOPT_VERBOSE : 0);
+
+#if PG_VERSION_NUM >= 120000
+ vacstmt.options = flags;
+#endif
analyze_rel(relOid,
#if PG_VERSION_NUM < 90500
&vacstmt
#endif
#else
makeRangeVarFromOid(relOid),
- VACOPT_ANALYZE | ((online_analyze_verbose) ? VACOPT_VERBOSE : 0),
+#if PG_VERSION_NUM < 120000
+ flags,
+#endif
&vacstmt, NULL, true, GetAccessStrategy(BAS_VACUUM)
#endif
);
+ /* Make changes visible to subsequent calls */
+ CommandCounterIncrement();
+
if (online_analyze_verbose)
{
long secs;
((double)secs) + ((double)microsecs)/1.0e6);
}
- rstat->autovac_analyze_timestamp = now;
- rstat->changes_since_analyze = 0;
- rstat->rereadStat = true;
+ rstat->last_autoanalyze_time = now;
+ rstat->mod_since_analyze = 0;
+
+ switch(operation)
+ {
+ case CK_CREATE:
+ case CK_INSERT:
+ case CK_UPDATE:
+ rstat->n_tuples += naffected;
+ /* FALLTHROUGH */
+ case CK_DELETE:
+ rstat->rereadStat = (reltype == OATT_PERSISTENT);
+ break;
+ case CK_TRUNCATE:
+ case CK_FASTTRUNCATE:
+ rstat->rereadStat = false;
+ rstat->n_tuples = 0;
+ break;
+ default:
+ break;
+ }
/* update last analyze timestamp in local memory of backend */
if (tabentry)
{
+#if PG_VERSION_NUM >= 160000
+ tabentry->last_analyze_time = now;
+ tabentry->mod_since_analyze = 0;
+#else
tabentry->analyze_timestamp = now;
tabentry->changes_since_analyze = 0;
+#endif
}
#if 0
/* force reload stat for new table */
{
#if PG_VERSION_NUM >= 90000
if (tabentry)
+#if PG_VERSION_NUM >= 160000
+ tabentry->mod_since_analyze += naffected;
+#else
tabentry->changes_since_analyze += naffected;
#endif
- rstat->changes_since_analyze += naffected;
+#endif
+ switch(operation)
+ {
+ case CK_CREATE:
+ case CK_INSERT:
+ rstat->mod_since_analyze += naffected;
+ rstat->n_tuples += naffected;
+ break;
+ case CK_UPDATE:
+ rstat->mod_since_analyze += 2 * naffected;
+ rstat->n_tuples += naffected;
+ break;
+ case CK_DELETE:
+ rstat->mod_since_analyze += naffected;
+ break;
+ case CK_TRUNCATE:
+ case CK_FASTTRUNCATE:
+ rstat->mod_since_analyze = 0;
+ rstat->n_tuples = 0;
+ break;
+ default:
+ break;
+ }
}
/* Reset local cache if we are over limit */
relstatsInit();
}
+static Const*
+isFastTruncateCall(QueryDesc *queryDesc)
+{
+ TargetEntry *te;
+ FuncExpr *fe;
+ Const *constval;
+
+ if (!(
+ queryDesc->plannedstmt &&
+ queryDesc->operation == CMD_SELECT &&
+ queryDesc->plannedstmt->planTree &&
+ queryDesc->plannedstmt->planTree->targetlist &&
+ list_length(queryDesc->plannedstmt->planTree->targetlist) == 1
+ ))
+ return NULL;
+
+ te = linitial(queryDesc->plannedstmt->planTree->targetlist);
+
+ if (!IsA(te, TargetEntry))
+ return NULL;
+
+ fe = (FuncExpr*)te->expr;
+
+ if (!(
+ fe && IsA(fe, FuncExpr) &&
+ fe->funcid >= FirstNormalObjectId &&
+ fe->funcretset == false &&
+ fe->funcresulttype == VOIDOID &&
+ fe->funcvariadic == false &&
+ list_length(fe->args) == 1
+ ))
+ return NULL;
+
+ constval = linitial(fe->args);
+
+ if (!(
+ IsA(constval,Const) &&
+ constval->consttype == TEXTOID &&
+ strcmp(get_func_name(fe->funcid), "fasttruncate") == 0
+ ))
+ return NULL;
+
+ return constval;
+}
+
+
extern PGDLLIMPORT void onlineAnalyzeHooker(QueryDesc *queryDesc);
void
onlineAnalyzeHooker(QueryDesc *queryDesc)
{
int64 naffected = -1;
+ Const *constval;
if (queryDesc->estate)
naffected = queryDesc->estate->es_processed;
+ lateInit();
+
+#if PG_VERSION_NUM >= 90200
+ if (online_analyze_enable &&
+ (constval = isFastTruncateCall(queryDesc)) != NULL)
+ {
+ Datum tblnamed = constval->constvalue;
+ char *tblname = text_to_cstring(DatumGetTextP(tblnamed));
+#if PG_VERSION_NUM >= 160000
+ RangeVar *tblvar =
+ makeRangeVarFromNameList(stringToQualifiedNameList(tblname, NULL));
+#else
+ RangeVar *tblvar =
+ makeRangeVarFromNameList(stringToQualifiedNameList(tblname));
+#endif
+
+ makeAnalyze(RangeVarGetRelid(tblvar,
+ NoLock,
+ false),
+ CK_FASTTRUNCATE, -1);
+ }
+#endif
+
if (online_analyze_enable && queryDesc->plannedstmt &&
(queryDesc->operation == CMD_INSERT ||
queryDesc->operation == CMD_UPDATE ||
RangeTblEntry *rte = list_nth(queryDesc->plannedstmt->rtable, n-1);
if (rte->rtekind == RTE_RELATION)
- makeAnalyze(rte->relid, queryDesc->operation, naffected);
+ makeAnalyze(rte->relid, (CmdKind)queryDesc->operation, naffected);
}
}
}
standard_ExecutorEnd(queryDesc);
}
+static List *toremove = NIL;
+
+/*
+ * removeTable called on transaction end, see call RegisterXactCallback() below
+ */
+static void
+removeTable(XactEvent event, void *arg)
+{
+ ListCell *cell;
+
+ switch(event)
+ {
+ case XACT_EVENT_COMMIT:
+ break;
+ case XACT_EVENT_ABORT:
+ toremove = NIL;
+ default:
+ return;
+ }
+
+ foreach(cell, toremove)
+ {
+ Oid relOid = lfirst_oid(cell);
+
+ hash_search(relstats, &relOid, HASH_REMOVE, NULL);
+ }
+
+ toremove = NIL;
+}
+
+#if PG_VERSION_NUM >= 120000
+static int
+parse_vacuum_opt(VacuumStmt *vacstmt)
+{
+ int options = vacstmt->is_vacuumcmd ? VACOPT_VACUUM : VACOPT_ANALYZE;
+ ListCell *lc;
+
+ foreach(lc, vacstmt->options)
+ {
+ DefElem *opt = (DefElem *) lfirst(lc);
+
+ /* Parse common options for VACUUM and ANALYZE */
+ if (strcmp(opt->defname, "verbose") == 0)
+ options |= VACOPT_VERBOSE;
+ else if (strcmp(opt->defname, "skip_locked") == 0)
+ options |= VACOPT_SKIP_LOCKED;
+ else if (strcmp(opt->defname, "analyze") == 0)
+ options |= VACOPT_ANALYZE;
+ else if (strcmp(opt->defname, "freeze") == 0)
+ options |= VACOPT_FREEZE;
+ else if (strcmp(opt->defname, "full") == 0)
+ options |= VACOPT_FULL;
+ else if (strcmp(opt->defname, "disable_page_skipping") == 0)
+ options |= VACOPT_DISABLE_PAGE_SKIPPING;
+ }
+
+ return options;
+}
+#endif
+
+
#if PG_VERSION_NUM >= 90200
static void
-onlineAnalyzeHookerUtility(Node *parsetree, const char *queryString,
+onlineAnalyzeHookerUtility(
+#if PG_VERSION_NUM >= 100000
+ PlannedStmt *pstmt,
+#else
+ Node *parsetree,
+#endif
+ const char *queryString,
+#if PG_VERSION_NUM >= 140000
+ bool readOnlyTree,
+#endif
#if PG_VERSION_NUM >= 90300
ProcessUtilityContext context, ParamListInfo params,
+#if PG_VERSION_NUM >= 100000
+ QueryEnvironment *queryEnv,
+#endif
#else
ParamListInfo params, bool isTopLevel,
#endif
- DestReceiver *dest, char *completionTag) {
- RangeVar *tblname = NULL;
+ DestReceiver *dest,
+#if PG_VERSION_NUM >= 130000
+ QueryCompletion *completionTag
+#else
+ char *completionTag
+#endif
+) {
+ List *tblnames = NIL;
+ CmdKind op = CK_INSERT;
+#if PG_VERSION_NUM >= 100000
+ Node *parsetree = NULL;
+
+ if (pstmt->commandType == CMD_UTILITY)
+ parsetree = pstmt->utilityStmt;
+#endif
+
+ lateInit();
+
+ if (parsetree && online_analyze_enable)
+ {
+ if (IsA(parsetree, CreateTableAsStmt) &&
+ ((CreateTableAsStmt*)parsetree)->into)
+ {
+ tblnames =
+ list_make1((RangeVar*)copyObject(((CreateTableAsStmt*)parsetree)->into->rel));
+ op = CK_CREATE;
+ }
+ else if (IsA(parsetree, TruncateStmt))
+ {
+ tblnames = list_copy(((TruncateStmt*)parsetree)->relations);
+ op = CK_TRUNCATE;
+ }
+ else if (IsA(parsetree, DropStmt) &&
+ ((DropStmt*)parsetree)->removeType == OBJECT_TABLE)
+ {
+ ListCell *cell;
+
+ foreach(cell, ((DropStmt*)parsetree)->objects)
+ {
+ List *relname = (List *) lfirst(cell);
+ RangeVar *rel = makeRangeVarFromNameList(relname);
+ Oid relOid = RangeVarGetRelid(rel, NoLock, true);
+
+ if (OidIsValid(relOid))
+ {
+ MemoryContext ctx;
+
+ ctx = MemoryContextSwitchTo(TopTransactionContext);
+ toremove = lappend_oid(toremove, relOid);
+ MemoryContextSwitchTo(ctx);
+ }
+ }
+ }
+ else if (IsA(parsetree, VacuumStmt))
+ {
+ VacuumStmt *vac = (VacuumStmt*)parsetree;
+ int options =
+#if PG_VERSION_NUM >= 120000
+ parse_vacuum_opt(vac)
+#else
+ vac->options
+#endif
+ ;
+
+
+#if PG_VERSION_NUM >= 110000
+ tblnames = vac->rels;
+#else
+ if (vac->relation)
+ tblnames = list_make1(vac->relation);
+#endif
+
+ if (options & (VACOPT_VACUUM | VACOPT_FULL | VACOPT_FREEZE))
+ {
+ /* optionally with analyze */
+ op = CK_VACUUM;
- if (IsA(parsetree, CreateTableAsStmt) && ((CreateTableAsStmt*)parsetree)->into)
- tblname = (RangeVar*)copyObject(((CreateTableAsStmt*)parsetree)->into->rel);
+ /* drop all collected stat */
+ if (tblnames == NIL)
+ relstatsInit();
+ }
+ else if (options & VACOPT_ANALYZE)
+ {
+ op = CK_ANALYZE;
+
+ /* should reset all counters */
+ if (tblnames == NIL)
+ {
+ HASH_SEQ_STATUS hs;
+ OnlineAnalyzeTableStat *rstat;
+ TimestampTz now = GetCurrentTimestamp();
+
+ hash_seq_init(&hs, relstats);
+
+ while((rstat = hash_seq_search(&hs)) != NULL)
+ {
+ rstat->mod_since_analyze = 0;
+ rstat->last_analyze_time = now;
+ }
+ }
+ }
+ else
+ tblnames = NIL;
+ }
+ }
+
+#if PG_VERSION_NUM >= 100000
+#define parsetree pstmt
+#endif
if (oldProcessUtilityHook)
oldProcessUtilityHook(parsetree, queryString,
+#if PG_VERSION_NUM >= 140000
+ readOnlyTree,
+#endif
#if PG_VERSION_NUM >= 90300
context, params,
+#if PG_VERSION_NUM >= 100000
+ queryEnv,
+#endif
#else
params, isTopLevel,
#endif
dest, completionTag);
else
standard_ProcessUtility(parsetree, queryString,
+#if PG_VERSION_NUM >= 140000
+ readOnlyTree,
+#endif
#if PG_VERSION_NUM >= 90300
context, params,
+#if PG_VERSION_NUM >= 100000
+ queryEnv,
+#endif
#else
params, isTopLevel,
#endif
dest, completionTag);
- if (tblname) {
- Oid tblOid = RangeVarGetRelid(tblname, NoLock, true);
+#if PG_VERSION_NUM >= 100000
+#undef parsetree
+#endif
+
+ if (tblnames) {
+ ListCell *l;
+
+ foreach(l, tblnames)
+ {
+ RangeVar *tblname =
+#if PG_VERSION_NUM >= 110000
+ (IsA(lfirst(l), VacuumRelation)) ?
+ ((VacuumRelation*)lfirst(l))->relation :
+#endif
+ (RangeVar*)lfirst(l);
+ Oid tblOid;
+
+ Assert(IsA(tblname, RangeVar));
- makeAnalyze(tblOid, CMD_INSERT, -1);
+ tblOid = RangeVarGetRelid(tblname, NoLock, true);
+ makeAnalyze(tblOid, op, -1);
+ }
}
}
#endif
+
static void
relstatsInit(void)
{
else
{
Assert(relstats == NULL);
+
+#if PG_VERSION_NUM < 90600
onlineAnalyzeMemoryContext =
AllocSetContextCreate(CacheMemoryContext,
- "online_analyze storage context",
-#if PG_VERSION_NUM < 90600
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE
+ "online_analyze storage context",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE
+ );
#else
- ALLOCSET_DEFAULT_SIZES
+ onlineAnalyzeMemoryContext =
+ AllocSetContextCreate(CacheMemoryContext,
+ "online_analyze storage context", ALLOCSET_DEFAULT_SIZES);
#endif
- );
}
hash_ctl.hcxt = onlineAnalyzeMemoryContext;
NULL
);
+ DefineCustomBoolVariable(
+ "online_analyze.local_tracking",
+ "Per backend tracking",
+ "Per backend tracking for temp tables (do not use system statistic)",
+ &online_analyze_local_tracking,
+#if PG_VERSION_NUM >= 80400
+ online_analyze_local_tracking,
+#endif
+ PGC_USERSET,
+#if PG_VERSION_NUM >= 80400
+ GUC_NOT_IN_SAMPLE,
+#if PG_VERSION_NUM >= 90100
+ NULL,
+#endif
+#endif
+ NULL,
+ NULL
+ );
+
DefineCustomBoolVariable(
"online_analyze.verbose",
"Verbosity of on-line analyze",
NULL
);
-}
-
-void _PG_fini(void);
-void
-_PG_fini(void)
-{
- ExecutorEnd_hook = oldExecutorEndHook;
-#if PG_VERSION_NUM >= 90200
- ProcessUtility_hook = oldProcessUtilityHook;
-#endif
-
- if (excludeTables.tables)
- free(excludeTables.tables);
- if (includeTables.tables)
- free(includeTables.tables);
-
- excludeTables.tables = includeTables.tables = NULL;
- excludeTables.nTables = includeTables.nTables = 0;
+ RegisterXactCallback(removeTable, NULL);
}