X-Git-Url: http://sigaev.ru/git/gitweb.cgi?a=blobdiff_plain;f=online_analyze.c;h=2ef4193306edc650716ce35d13b447793666475e;hb=a99e7faa88748694900e81d84508cbe2d54e9689;hp=b1a3578d983e191759dce87dfa14dda4e85b1d8c;hpb=1376db930b4cc3ba46ee7f83a1baed16e5f4b69b;p=online_analyze.git diff --git a/online_analyze.c b/online_analyze.c index b1a3578..2ef4193 100644 --- a/online_analyze.c +++ b/online_analyze.c @@ -30,6 +30,7 @@ #include "postgres.h" #include "pgstat.h" +#include "access/transam.h" #include "catalog/namespace.h" #include "commands/vacuum.h" #include "executor/executor.h" @@ -50,6 +51,10 @@ #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 @@ -63,12 +68,23 @@ static double online_analyze_scale_factor = 0.1; 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_CREATE +} CmdKind; + typedef enum { OATT_ALL = 0x03, @@ -111,6 +127,7 @@ static HTAB *relstats = NULL; static void relstatsInit(void); +#if PG_VERSION_NUM < 100000 static int oid_cmp(const void *a, const void *b) { @@ -118,6 +135,7 @@ 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) @@ -336,7 +354,7 @@ makeRangeVarFromOid(Oid relOid) #endif static void -makeAnalyze(Oid relOid, CmdType operation, int32 naffected) +makeAnalyze(Oid relOid, CmdKind operation, int64 naffected) { TimestampTz now = GetCurrentTimestamp(); Relation rel; @@ -416,15 +434,22 @@ makeAnalyze(Oid relOid, CmdType operation, int32 naffected) 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); + + elog(NOTICE,"makeAnalyze op:%d %u", operation, naffected); + + if (operation != CK_TRUNCATE && + (found == false || rstat->rereadStat == true)) + { + + rstat->rereadStat = false; tabentry = pgstat_fetch_stat_tabentry(relOid); @@ -441,19 +466,17 @@ makeAnalyze(Oid relOid, CmdType operation, int32 naffected) 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 from after truncate */ + 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) + @@ -527,7 +550,27 @@ makeAnalyze(Oid relOid, CmdType operation, int32 naffected) rstat->autovac_analyze_timestamp = now; rstat->changes_since_analyze = 0; - rstat->rereadStat = true; + + switch(operation) + { + case CK_INSERT: + rstat->n_tuples += naffected; + rstat->rereadStat = false; + break; + case CK_UPDATE: + rstat->n_tuples += naffected; + rstat->rereadStat = true; + break; + case CK_DELETE: + rstat->rereadStat = true; + break; + case CK_TRUNCATE: + rstat->n_tuples = 0; + rstat->rereadStat = false; + break; + default: + break; + } /* update last analyze timestamp in local memory of backend */ if (tabentry) @@ -547,7 +590,25 @@ makeAnalyze(Oid relOid, CmdType operation, int32 naffected) if (tabentry) tabentry->changes_since_analyze += naffected; #endif - rstat->changes_since_analyze += naffected; + switch(operation) + { + 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: + rstat->changes_since_analyze = 0; + rstat->n_tuples = 0; + break; + default: + break; + } } /* Reset local cache if we are over limit */ @@ -555,15 +616,81 @@ makeAnalyze(Oid relOid, CmdType operation, int32 naffected) 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 (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, +#if PG_VERSION_NUM >= 90200 + NoLock, +#endif + false), + CK_TRUNCATE, -1); + } + if (online_analyze_enable && queryDesc->plannedstmt && (queryDesc->operation == CMD_INSERT || queryDesc->operation == CMD_UPDATE || @@ -594,7 +721,7 @@ onlineAnalyzeHooker(QueryDesc *queryDesc) 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); } } } @@ -607,22 +734,58 @@ onlineAnalyzeHooker(QueryDesc *queryDesc) #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 (IsA(parsetree, CreateTableAsStmt) && ((CreateTableAsStmt*)parsetree)->into) - tblname = (RangeVar*)copyObject(((CreateTableAsStmt*)parsetree)->into->rel); + if (pstmt->commandType == CMD_UTILITY) + parsetree = pstmt->utilityStmt; +#endif + + 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 @@ -631,15 +794,28 @@ onlineAnalyzeHookerUtility(Node *parsetree, const char *queryString, 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; - makeAnalyze(tblOid, CMD_INSERT, -1); + foreach(l, tblnames) + { + RangeVar *tblname = (RangeVar*)lfirst(l); + Oid tblOid = RangeVarGetRelid(tblname, NoLock, true); + + makeAnalyze(tblOid, op, -1); + } } } #endif @@ -883,6 +1059,28 @@ _PG_init(void) #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);