X-Git-Url: http://sigaev.ru/git/gitweb.cgi?a=blobdiff_plain;f=online_analyze.c;h=53ffd201f8adc46b8f7d64214606f816cb37abe5;hb=31e2328fdeefe0e5710680e704a3c78a9cadffa4;hp=b2f8199769e11b45a95b32476c4261fe5f043880;hpb=3848a6cb4ae35252902d38ad1759696f66340968;p=online_analyze.git diff --git a/online_analyze.c b/online_analyze.c index b2f8199..53ffd20 100644 --- a/online_analyze.c +++ b/online_analyze.c @@ -30,6 +30,8 @@ #include "postgres.h" #include "pgstat.h" +#include "access/transam.h" +#include "access/xact.h" #include "catalog/namespace.h" #include "commands/vacuum.h" #include "executor/executor.h" @@ -62,6 +64,7 @@ PG_MODULE_MAGIC; #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; @@ -81,9 +84,13 @@ typedef enum CmdKind CK_INSERT = CMD_INSERT, CK_DELETE = CMD_DELETE, CK_TRUNCATE, - CK_CREATE + CK_FASTTRUNCATE, + CK_CREATE, + CK_ANALYZE, + CK_VACUUM } CmdKind; + typedef enum { OATT_ALL = 0x03, @@ -439,13 +446,31 @@ makeAnalyze(Oid relOid, CmdKind operation, int64 naffected) rstat->tableid = relOid; newTable = true; } + else if (operation == CK_VACUUM) + { + /* force reread becouse 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; + return; + } Assert(rstat->tableid == relOid); - if (operation != CK_TRUNCATE && - (found == false || 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; tabentry = pgstat_fetch_stat_tabentry(relOid); @@ -466,11 +491,8 @@ makeAnalyze(Oid relOid, CmdKind operation, int64 naffected) } } - if (naffected == 0) - rstat->rereadStat = true; - if (newTable || - /* force analyze from after truncate */ + /* 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) && @@ -553,17 +575,16 @@ makeAnalyze(Oid relOid, CmdKind operation, int64 naffected) switch(operation) { + case CK_CREATE: case CK_INSERT: - rstat->n_tuples += naffected; - break; case CK_UPDATE: rstat->n_tuples += naffected; - rstat->rereadStat = true; - break; case CK_DELETE: - rstat->rereadStat = true; + rstat->rereadStat = (reltype == OATT_PERSISTENT); break; case CK_TRUNCATE: + case CK_FASTTRUNCATE: + rstat->rereadStat = false; rstat->n_tuples = 0; break; default: @@ -590,6 +611,7 @@ makeAnalyze(Oid relOid, CmdKind operation, int64 naffected) #endif switch(operation) { + case CK_CREATE: case CK_INSERT: rstat->changes_since_analyze += naffected; rstat->n_tuples += naffected; @@ -601,7 +623,8 @@ makeAnalyze(Oid relOid, CmdKind operation, int64 naffected) rstat->changes_since_analyze += naffected; break; case CK_TRUNCATE: - rstat->changes_since_analyze = rstat->n_tuples; + case CK_FASTTRUNCATE: + rstat->changes_since_analyze = 0; rstat->n_tuples = 0; break; default: @@ -614,15 +637,78 @@ makeAnalyze(Oid relOid, CmdKind operation, int64 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 + )) + 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; +#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 || @@ -664,6 +750,37 @@ 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( @@ -705,6 +822,41 @@ 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; + + tblnames = list_make1(vac->relation); + + if (vac->options & (VACOPT_VACUUM | VACOPT_FULL | VACOPT_FREEZE)) + /* optionally with analyze */ + op = CK_VACUUM; + else if (vac->options & VACOPT_ANALYZE) + op = CK_ANALYZE; + else + tblnames = NIL; + } } #if PG_VERSION_NUM >= 100000 @@ -831,6 +983,25 @@ _PG_init(void) 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", @@ -1013,6 +1184,7 @@ _PG_init(void) NULL ); + RegisterXactCallback(removeTable, NULL); } void _PG_fini(void);