#include "postgres.h"
#include "pgstat.h"
+#include "access/transam.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"
+#endif
#endif
#endif
static int online_analyze_threshold = 50;
static int online_analyze_capacity_threshold = 100000;
static double online_analyze_min_interval = 10000;
+static int online_analyze_lower_limit = 0;
static ExecutorEnd_hook_type oldExecutorEndHook = NULL;
#if PG_VERSION_NUM >= 90200
static ProcessUtility_hook_type oldProcessUtilityHook = NULL;
#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
+} CmdKind;
+
+
typedef enum
{
OATT_ALL = 0x03,
static MemoryContext onlineAnalyzeMemoryContext = NULL;
static HTAB *relstats = NULL;
-static void relstatsInit();
+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)
#endif
static void
-makeAnalyze(Oid relOid, CmdType operation, int32 naffected)
+makeAnalyze(Oid relOid, CmdKind operation, int64 naffected)
{
TimestampTz now = GetCurrentTimestamp();
Relation rel;
if (relOid == InvalidOid)
return;
+ elog(NOTICE,"makeAnalyze operation: %d naffected: %d",
+ operation, (int32)naffected);
+
if (naffected == 0)
/* return if there is no changes */
return;
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);
+ 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))
+ {
+ rstat->rereadStat = false;
tabentry = pgstat_fetch_stat_tabentry(relOid);
rstat->autovac_analyze_timestamp =
tabentry->autovac_analyze_timestamp;
rstat->analyze_timestamp = tabentry->analyze_timestamp;
- rstat->rereadStat = false;
- }
- else
- {
- newTable = true;
- rstat->rereadStat = true;
}
}
- 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) &&
+ /* do not analyze too small tables */
+ rstat->n_tuples + rstat->changes_since_analyze + naffected > online_analyze_lower_limit &&
/* be in sync with relation_needs_vacanalyze */
((double)(rstat->changes_since_analyze + naffected)) >=
online_analyze_scale_factor * ((double)rstat->n_tuples) +
rstat->autovac_analyze_timestamp = now;
rstat->changes_since_analyze = 0;
- rstat->rereadStat = true;
+
+ switch(operation)
+ {
+ case CK_CREATE:
+ case CK_INSERT:
+ case CK_UPDATE:
+ rstat->n_tuples += naffected;
+ case CK_DELETE:
+ rstat->rereadStat = (reltype == OATT_PERSISTENT);
+ break;
+ case CK_TRUNCATE:
+ case CK_FASTTRUNCATE:
+ rstat->n_tuples = 0;
+ break;
+ default:
+ break;
+ }
/* update last analyze timestamp in local memory of backend */
if (tabentry)
if (tabentry)
tabentry->changes_since_analyze += naffected;
#endif
- rstat->changes_since_analyze += naffected;
+ switch(operation)
+ {
+ case CK_CREATE:
+ case CK_INSERT:
+ rstat->changes_since_analyze += naffected;
+ rstat->n_tuples += naffected;
+ break;
+ case CK_UPDATE:
+ rstat->changes_since_analyze += 2 * naffected;
+ rstat->n_tuples += naffected;
+ case CK_DELETE:
+ rstat->changes_since_analyze += naffected;
+ break;
+ case CK_TRUNCATE:
+ case CK_FASTTRUNCATE:
+ rstat->changes_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 &&
+ IsA(linitial(queryDesc->plannedstmt->planTree->targetlist), TargetEntry)
+ ))
+ 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 &&
+ IsA(linitial(fe->args), Const)
+ ))
+ 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)
{
- uint32 naffected = -1;
+ int64 naffected = -1;
+ Const *constval;
if (queryDesc->estate)
naffected = queryDesc->estate->es_processed;
+#if PG_VERSION_NUM >= 90200
+ if (online_analyze_enable &&
+ (constval = isFastTruncateCall(queryDesc)) != NULL)
+ {
+ Datum tblnamed = constval->constvalue;
+ char *tblname = text_to_cstring(DatumGetTextP(tblnamed));
+ RangeVar *tblvar =
+ makeRangeVarFromNameList(stringToQualifiedNameList(tblname));
+
+ 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);
}
}
}
#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 >= 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;
+ List *tblnames = NIL;
+ CmdKind op = CK_INSERT;
+#if PG_VERSION_NUM >= 100000
+ Node *parsetree = NULL;
+
+ if (pstmt->commandType == CMD_UTILITY)
+ parsetree = pstmt->utilityStmt;
+#endif
- if (IsA(parsetree, CreateTableAsStmt) && ((CreateTableAsStmt*)parsetree)->into)
- tblname = (RangeVar*)copyObject(((CreateTableAsStmt*)parsetree)->into->rel);
+ 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;
+ }
+ }
+
+#if PG_VERSION_NUM >= 100000
+#define parsetree pstmt
+#endif
if (oldProcessUtilityHook)
oldProcessUtilityHook(parsetree, queryString,
#if PG_VERSION_NUM >= 90300
context, params,
+#if PG_VERSION_NUM >= 100000
+ queryEnv,
+#endif
#else
params, isTopLevel,
#endif
standard_ProcessUtility(parsetree, queryString,
#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 = (RangeVar*)lfirst(l);
+ Oid tblOid = RangeVarGetRelid(tblname, NoLock, true);
- makeAnalyze(tblOid, CMD_INSERT, -1);
+ makeAnalyze(tblOid, op, -1);
+ }
}
}
#endif
static void
-relstatsInit()
+relstatsInit(void)
{
HASHCTL hash_ctl;
int flags = 0;
onlineAnalyzeMemoryContext =
AllocSetContextCreate(CacheMemoryContext,
"online_analyze storage context",
- ALLOCSET_DEFAULT_SIZES);
+#if PG_VERSION_NUM < 90600
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE
+#else
+ ALLOCSET_DEFAULT_SIZES
+#endif
+ );
}
- hash_ctl.hcxt = AllocSetContextCreate(CacheMemoryContext,
- "online_analyze storage context",
- ALLOCSET_DEFAULT_SIZES);
+ hash_ctl.hcxt = onlineAnalyzeMemoryContext;
flags |= HASH_CONTEXT;
hash_ctl.keysize = sizeof(Oid);
#endif
includeTablesShow
);
+
+ DefineCustomIntVariable(
+ "online_analyze.lower_limit",
+ "min number of rows in table to analyze",
+ "min number of rows in table to analyze",
+ &online_analyze_lower_limit,
+#if PG_VERSION_NUM >= 80400
+ online_analyze_lower_limit,
+#endif
+ 0,
+ 0x7fffffff,
+ PGC_USERSET,
+#if PG_VERSION_NUM >= 80400
+ GUC_NOT_IN_SAMPLE,
+#if PG_VERSION_NUM >= 90100
+ NULL,
+#endif
+#endif
+ NULL,
+ NULL
+ );
+
}
void _PG_fini(void);