add pgproee support
[online_analyze.git] / online_analyze.c
index 22c43e6..6ffcc30 100644 (file)
@@ -30,7 +30,9 @@
 #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"
@@ -54,6 +56,9 @@
 #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
@@ -63,6 +68,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;
@@ -75,6 +81,10 @@ static ExecutorEnd_hook_type oldExecutorEndHook = NULL;
 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,
@@ -83,7 +93,9 @@ typedef enum CmdKind
        CK_DELETE = CMD_DELETE,
        CK_TRUNCATE,
        CK_FASTTRUNCATE,
-       CK_CREATE
+       CK_CREATE,
+       CK_ANALYZE,
+       CK_VACUUM
 } CmdKind;
 
 
@@ -110,10 +122,11 @@ typedef struct TableList {
        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;
@@ -154,6 +167,18 @@ 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 (MyBackendId == InvalidBackendId || !IsUnderPostmaster ||
+               !IsTransactionState())
+       {
+               includeTables.inited = false;
+               excludeTables.inited = false;
+               return newval;
+       }
+
        if (doit)
        {
                nOids = list_length(namelist);
@@ -261,7 +286,7 @@ includeTablesCheck(char **newval, void **extra, GucSource source)
 static void
 includeTablesAssign(const char *newval, void *extra)
 {
-       tableListAssign(newval, true, &excludeTables);
+       tableListAssign(newval, true, &includeTables);
 }
 
 #else /* PG_VERSION_NUM < 90100 */
@@ -280,6 +305,26 @@ includeTablesAssign(const char * newval, bool doit, GucSource source)
 
 #endif
 
+static void
+lateInit()
+{
+       TableList       *tl[] = {&includeTables, &excludeTables};
+       int i;
+
+       if (MyBackendId == InvalidBackendId || !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)
 {
@@ -287,6 +332,8 @@ tableListShow(TableList *tbl)
        int             i,
                        len;
 
+       lateInit();
+
        len = 1 /* \0 */ + tbl->nTables * (2 * NAMEDATALEN + 2 /* ', ' */ + 1 /* . */);
        ptr = val = palloc(len);
        *ptr ='\0';
@@ -370,9 +417,6 @@ makeAnalyze(Oid relOid, CmdKind operation, int64 naffected)
        if (relOid == InvalidOid)
                return;
 
-       elog(NOTICE,"makeAnalyze operation: %d  naffected: %d",
-                operation, (int32)naffected);
-
        if (naffected == 0)
                /* return if there is no changes */
                return;
@@ -446,11 +490,32 @@ makeAnalyze(Oid relOid, CmdKind operation, int64 naffected)
                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;
 
@@ -491,7 +556,13 @@ makeAnalyze(Oid relOid, CmdKind operation, int64 naffected)
                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 */
 
@@ -519,9 +590,16 @@ makeAnalyze(Oid relOid, CmdKind operation, int64 naffected)
                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
@@ -534,11 +612,16 @@ makeAnalyze(Oid relOid, CmdKind operation, int64 naffected)
 #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;
@@ -560,11 +643,13 @@ makeAnalyze(Oid relOid, CmdKind operation, int64 naffected)
                        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:
@@ -599,6 +684,7 @@ makeAnalyze(Oid relOid, CmdKind operation, int64 naffected)
                        case CK_UPDATE:
                                rstat->changes_since_analyze += 2 * naffected;
                                rstat->n_tuples += naffected;
+                               break;
                        case CK_DELETE:
                                rstat->changes_since_analyze += naffected;
                                break;
@@ -629,8 +715,7 @@ isFastTruncateCall(QueryDesc *queryDesc)
                  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;
 
@@ -647,8 +732,7 @@ isFastTruncateCall(QueryDesc *queryDesc)
                  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;
 
@@ -665,7 +749,6 @@ isFastTruncateCall(QueryDesc *queryDesc)
 }
 
 
-
 extern PGDLLIMPORT void onlineAnalyzeHooker(QueryDesc *queryDesc);
 void
 onlineAnalyzeHooker(QueryDesc *queryDesc)
@@ -676,6 +759,8 @@ onlineAnalyzeHooker(QueryDesc *queryDesc)
        if (queryDesc->estate)
                naffected = queryDesc->estate->es_processed;
 
+       lateInit();
+
 #if PG_VERSION_NUM >= 90200
        if (online_analyze_enable &&
                (constval = isFastTruncateCall(queryDesc)) != NULL)
@@ -733,6 +818,67 @@ 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 >= 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(
@@ -742,6 +888,9 @@ onlineAnalyzeHookerUtility(
                                                   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
@@ -750,7 +899,13 @@ onlineAnalyzeHookerUtility(
 #else
                                                        ParamListInfo params, bool isTopLevel,
 #endif
-                                                       DestReceiver *dest, char *completionTag) {
+                                                       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
@@ -760,6 +915,8 @@ onlineAnalyzeHookerUtility(
                parsetree = pstmt->utilityStmt;
 #endif
 
+       lateInit();
+
        if (parsetree && online_analyze_enable)
        {
                if (IsA(parsetree, CreateTableAsStmt) &&
@@ -774,6 +931,78 @@ 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;
+                       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;
+
+                               /* 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->changes_since_analyze = 0;
+                                               rstat->analyze_timestamp = now;
+                                       }
+                               }
+                       }
+                       else
+                               tblnames = NIL;
+               }
        }
 
 #if PG_VERSION_NUM >= 100000
@@ -782,6 +1011,9 @@ onlineAnalyzeHookerUtility(
 
        if (oldProcessUtilityHook)
                oldProcessUtilityHook(parsetree, queryString,
+#if PG_VERSION_NUM >= 140000
+                                                         readOnlyTree,
+#endif
 #if PG_VERSION_NUM >= 90300
                                                          context, params,
 #if PG_VERSION_NUM >= 100000
@@ -793,6 +1025,9 @@ onlineAnalyzeHookerUtility(
                                                          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
@@ -812,15 +1047,24 @@ onlineAnalyzeHookerUtility(
 
                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);
                }
        }
 }
 #endif
 
+
 static void
 relstatsInit(void)
 {
@@ -840,17 +1084,20 @@ 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;
@@ -900,6 +1147,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",
@@ -1082,6 +1348,7 @@ _PG_init(void)
                NULL
        );
 
+       RegisterXactCallback(removeTable, NULL);
 }
 
 void _PG_fini(void);