#include "pgstat.h"
#include "access/transam.h"
+#include "access/xact.h"
#include "catalog/namespace.h"
#include "commands/vacuum.h"
#include "executor/executor.h"
#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;
CK_DELETE = CMD_DELETE,
CK_TRUNCATE,
CK_FASTTRUNCATE,
- CK_CREATE
+ CK_CREATE,
+ CK_ANALYZE,
+ CK_VACUUM
} CmdKind;
if (relOid == InvalidOid)
return;
- elog(NOTICE,"makeAnalyze operation: %d naffected: %d",
- operation, (int32)naffected);
-
if (naffected == 0)
/* return if there is no changes */
return;
newTable = true;
}
+ if (operation == CK_VACUUM)
+ {
+ /* force reread because vacuum could change n_tuples */
+ rstat->rereadStat = true;
+ return;
+ }
+ else if (operation == CK_ANALYZE)
+ {
+ /* only analyze */
+ rstat->changes_since_analyze = 0;
+ rstat->analyze_timestamp = now;
+ if (newTable)
+ rstat->rereadStat = true;
+ return;
+ }
+
Assert(rstat->tableid == relOid);
- /* do not rered data if it was a truncation */
- if (operation != CK_TRUNCATE && operation != CK_FASTTRUNCATE &&
- (newTable == true || rstat->rereadStat == true))
+ 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;
#endif
);
+ /* Make changes visible to subsequent calls */
+ CommandCounterIncrement();
+
if (online_analyze_verbose)
{
long secs;
break;
case CK_TRUNCATE:
case CK_FASTTRUNCATE:
+ rstat->rereadStat = false;
rstat->n_tuples = 0;
break;
default:
queryDesc->operation == CMD_SELECT &&
queryDesc->plannedstmt->planTree &&
queryDesc->plannedstmt->planTree->targetlist &&
- list_length(queryDesc->plannedstmt->planTree->targetlist) == 1 &&
- IsA(linitial(queryDesc->plannedstmt->planTree->targetlist), TargetEntry)
+ list_length(queryDesc->plannedstmt->planTree->targetlist) == 1
))
return NULL;
fe->funcretset == false &&
fe->funcresulttype == VOIDOID &&
fe->funcvariadic == false &&
- list_length(fe->args) == 1 &&
- IsA(linitial(fe->args), Const)
+ list_length(fe->args) == 1
))
return NULL;
}
-
extern PGDLLIMPORT void onlineAnalyzeHooker(QueryDesc *queryDesc);
void
onlineAnalyzeHooker(QueryDesc *queryDesc)
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 >= 90200
static void
onlineAnalyzeHookerUtility(
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;
+
+#if PG_VERSION_NUM >= 110000
+ tblnames = vac->rels;
+#else
+ if (vac->relation)
+ tblnames = list_make1(vac->relation);
+#endif
+
+ if (vac->options & (VACOPT_VACUUM | VACOPT_FULL | VACOPT_FREEZE))
+ {
+ /* optionally with analyze */
+ op = CK_VACUUM;
+
+ /* drop all collected stat */
+ if (tblnames == NIL)
+ relstatsInit();
+ }
+ else if (vac->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->changes_since_analyze = 0;
+ rstat->analyze_timestamp = now;
+ }
+ }
+ }
+ else
+ tblnames = NIL;
+ }
}
#if PG_VERSION_NUM >= 100000
foreach(l, tblnames)
{
- RangeVar *tblname = (RangeVar*)lfirst(l);
- Oid tblOid = RangeVarGetRelid(tblname, NoLock, true);
+ 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));
+
+ tblOid = RangeVarGetRelid(tblname, NoLock, true);
makeAnalyze(tblOid, op, -1);
}
}
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
);
+ RegisterXactCallback(removeTable, NULL);
}
void _PG_fini(void);