Try to reduce CPU consumption
[online_analyze.git] / online_analyze.c
index 78077e8..0d85df6 100644 (file)
@@ -6,13 +6,13 @@
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
- *        notice, this list of conditions and the following disclaimer.
+ *             notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
- *        notice, this list of conditions and the following disclaimer in the
- *        documentation and/or other materials provided with the distribution.
+ *             notice, this list of conditions and the following disclaimer in the
+ *             documentation and/or other materials provided with the distribution.
  * 3. Neither the name of the author nor the names of any co-contributors
- *        may be used to endorse or promote products derived from this software
- *        without specific prior written permission.
+ *             may be used to endorse or promote products derived from this software
+ *             without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/guc.h"
+#if PG_VERSION_NUM >= 90200
+#include "catalog/pg_class.h"
+#include "nodes/primnodes.h"
+#include "tcop/utility.h"
+#include "utils/rel.h"
+#include "utils/relcache.h"
+#include "utils/timestamp.h"
+#if PG_VERSION_NUM >= 90500
+#include "nodes/makefuncs.h"
+#endif
+#endif
 
 #ifdef PG_MODULE_MAGIC
 PG_MODULE_MAGIC;
@@ -50,15 +61,20 @@ static double online_analyze_scale_factor = 0.1;
 static int online_analyze_threshold = 50;
 static double online_analyze_min_interval = 10000;
 
-typedef enum 
+static ExecutorEnd_hook_type oldExecutorEndHook = NULL;
+#if PG_VERSION_NUM >= 90200
+static ProcessUtility_hook_type        oldProcessUtilityHook = NULL;
+#endif
+
+typedef enum
 {
        OATT_ALL                = 0x03,
        OATT_PERSISTENT = 0x01,
        OATT_TEMPORARY  = 0x02,
        OATT_NONE               = 0x00
-} OnlyneAnalyzeTableType;
+} OnlineAnalyzeTableType;
 
-static const struct config_enum_entry online_analyze_table_type_options[] = 
+static const struct config_enum_entry online_analyze_table_type_options[] =
 {
        {"all", OATT_ALL, false},
        {"persistent", OATT_PERSISTENT, false},
@@ -89,11 +105,11 @@ oid_cmp(const void *a, const void *b)
 static const char *
 tableListAssign(const char * newval, bool doit, TableList *tbl)
 {
-       char       *rawname;
-       List       *namelist;
-       ListCell   *l;
-       Oid         *newOids = NULL;
-       int         nOids = 0,
+       char            *rawname;
+       List            *namelist;
+       ListCell        *l;
+       Oid                     *newOids = NULL;
+       int                     nOids = 0,
                                i = 0;
 
        rawname = pstrdup(newval);
@@ -106,13 +122,20 @@ tableListAssign(const char * newval, bool doit, TableList *tbl)
                nOids = list_length(namelist);
                newOids = malloc(sizeof(Oid) * (nOids+1));
                if (!newOids)
-                       elog(ERROR,"could not allocate %d bytes", (int)(sizeof(Oid) * (nOids+1)));
+                       elog(ERROR,"could not allocate %d bytes",
+                                (int)(sizeof(Oid) * (nOids+1)));
        }
 
        foreach(l, namelist)
        {
-               char        *curname = (char *) lfirst(l);
-               Oid         relOid = RangeVarGetRelid(makeRangeVarFromNameList(stringToQualifiedNameList(curname)), true);
+               char    *curname = (char *) lfirst(l);
+#if PG_VERSION_NUM >= 90200
+               Oid             relOid = RangeVarGetRelid(makeRangeVarFromNameList(
+                                                       stringToQualifiedNameList(curname)), NoLock, true);
+#else
+               Oid             relOid = RangeVarGetRelid(makeRangeVarFromNameList(
+                                                       stringToQualifiedNameList(curname)), true);
+#endif
 
                if (relOid == InvalidOid)
                {
@@ -204,7 +227,7 @@ includeTablesAssign(const char *newval, void *extra)
        tableListAssign(newval, true, &excludeTables);
 }
 
-#else /* PG_VERSION_NUM < 90100 */ 
+#else /* PG_VERSION_NUM < 90100 */
 
 static const char *
 excludeTablesAssign(const char * newval, bool doit, GucSource source)
@@ -223,8 +246,8 @@ includeTablesAssign(const char * newval, bool doit, GucSource source)
 static const char*
 tableListShow(TableList *tbl)
 {
-       char    *val, *ptr;
-       int     i,
+       char    *val, *ptr;
+       int             i,
                        len;
 
        len = 1 /* \0 */ + tbl->nTables * (2 * NAMEDATALEN + 2 /* ', ' */ + 1 /* . */);
@@ -232,9 +255,9 @@ tableListShow(TableList *tbl)
        *ptr ='\0';
        for(i=0; i<tbl->nTables; i++)
        {
-               char    *relname = get_rel_name(tbl->tables[i]);
-               Oid     nspOid = get_rel_namespace(tbl->tables[i]);
-               char    *nspname = get_namespace_name(nspOid);
+               char    *relname = get_rel_name(tbl->tables[i]);
+               Oid             nspOid = get_rel_namespace(tbl->tables[i]);
+               char    *nspname = get_namespace_name(nspOid);
 
                if ( relname == NULL || nspOid == InvalidOid || nspname == NULL )
                        continue;
@@ -282,17 +305,89 @@ matchOid(TableList *tbl, Oid oid)
        return false;
 }
 
-static ExecutorEnd_hook_type oldhook = NULL;
+#if PG_VERSION_NUM >= 90500
+static RangeVar*
+makeRangeVarFromOid(Oid relOid)
+{
+       return makeRangeVar(
+                               get_namespace_name(get_rel_namespace(relOid)),
+                               get_rel_name(relOid),
+                               -1
+                       );
+
+}
+#endif
 
 static void
-makeAnalyze(Oid relOid, CmdType operation, uint32 naffected)
+makeAnalyze(Oid relOid, CmdType operation, int32 naffected)
 {
        PgStat_StatTabEntry             *tabentry;
-       TimestampTz                     now = GetCurrentTimestamp();
+       TimestampTz                             now = GetCurrentTimestamp();
 
        if (relOid == InvalidOid)
                return;
 
+       if (naffected == 0)
+               /* return if there is not changes */
+               return;
+       else if (naffected < 0)
+               /* number if affected rows is unknown */
+               naffected = 0;
+
+       /*
+        * includeTables overwrites excludeTables
+        */
+       switch(online_analyze_table_type)
+       {
+               case OATT_ALL:
+                       if (get_rel_relkind(relOid) != RELKIND_RELATION ||
+                               (matchOid(&excludeTables, relOid) == true &&
+                               matchOid(&includeTables, relOid) == false))
+                               return;
+                       break;
+               case OATT_NONE:
+                       if (get_rel_relkind(relOid) != RELKIND_RELATION ||
+                               matchOid(&includeTables, relOid) == false)
+                               return;
+                       break;
+               case OATT_TEMPORARY:
+               case OATT_PERSISTENT:
+               default:
+                       {
+                               Relation                                rel;
+                               OnlineAnalyzeTableType  reltype;
+
+                               rel = RelationIdGetRelation(relOid);
+
+                               if (rel->rd_rel->relkind != RELKIND_RELATION)
+                               {
+                                       RelationClose(rel);
+                                       return;
+                               }
+
+                               reltype =
+#if PG_VERSION_NUM >= 90100
+                                       (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
+#else
+                                       (rel->rd_istemp || rel->rd_islocaltemp)
+#endif
+                                               ? OATT_TEMPORARY : OATT_PERSISTENT;
+                               RelationClose(rel);
+
+                               /*
+                                * skip analyze if relation's type doesn't not match
+                                * online_analyze_table_type
+                                */
+                               if ((online_analyze_table_type & reltype) == 0 ||
+                                       matchOid(&excludeTables, relOid) == true)
+                               {
+                                       if (matchOid(&includeTables, relOid) == false)
+                                               return;
+                               }
+                               break;
+                       }
+       }
+
        tabentry = pgstat_fetch_stat_tabentry(relOid);
 
 #if PG_VERSION_NUM >= 90000
@@ -301,64 +396,37 @@ makeAnalyze(Oid relOid, CmdType operation, uint32 naffected)
 #define changes_since_analyze(t)       ((t)->n_live_tuples + (t)->n_dead_tuples - (t)->last_anl_tuples)
 #endif
 
-       if (    
+       if (
                tabentry == NULL /* a new table */ ||
                (
                        /* do not analyze too often, if both stamps are exceeded the go */
-                       TimestampDifferenceExceeds(tabentry->analyze_timestamp, now, online_analyze_min_interval) && 
+                       TimestampDifferenceExceeds(tabentry->analyze_timestamp, now, online_analyze_min_interval) &&
                        TimestampDifferenceExceeds(tabentry->autovac_analyze_timestamp, now, online_analyze_min_interval) &&
                        /* be in sync with relation_needs_vacanalyze */
                        ((double)(changes_since_analyze(tabentry) + naffected)) >=
-                               online_analyze_scale_factor * ((double)(tabentry->n_dead_tuples + tabentry->n_live_tuples)) + 
+                               online_analyze_scale_factor * ((double)(tabentry->n_dead_tuples + tabentry->n_live_tuples)) +
                                        (double)online_analyze_threshold
                )
        )
        {
+#if PG_VERSION_NUM < 90500
                VacuumStmt                              vacstmt;
+#else
+               VacuumParams                    vacstmt;
+#endif
                TimestampTz                             startStamp, endStamp;
 
-               /*
-                * includeTables overwrites excludeTables
-                */
-               switch(online_analyze_table_type)
-               {
-                       case OATT_ALL:
-                               if (matchOid(&excludeTables, relOid) == true && matchOid(&includeTables, relOid) == false)
-                                       return;
-                               break;
-                       case OATT_NONE:
-                               if (matchOid(&includeTables, relOid) == false)
-                                       return;
-                               break;
-                       case OATT_TEMPORARY:
-                       case OATT_PERSISTENT:
-                       default:
-                               {
-                                       Relation                                rel;
-                                       OnlyneAnalyzeTableType  reltype;
-
-                                       rel = RelationIdGetRelation(relOid);
-                                       reltype = (rel->rd_istemp || rel->rd_islocaltemp) ? OATT_TEMPORARY : OATT_PERSISTENT;
-                                       RelationClose(rel);
+               memset(&startStamp, 0, sizeof(startStamp)); /* keep compiler quiet */
 
-                                       /*
-                                        * skip analyze if relation's type doesn't not match online_analyze_table_type
-                                        */
-                                       if ((online_analyze_table_type & reltype) == 0 || matchOid(&excludeTables, relOid) == true)
-                                       {
-                                               if (matchOid(&includeTables, relOid) == false)
-                                                       return;
-                                       }
-                               }
-                               break;
-               }
+               memset(&vacstmt, 0, sizeof(vacstmt));
 
-               vacstmt.type = T_VacuumStmt;
                vacstmt.freeze_min_age = -1;
                vacstmt.freeze_table_age = -1; /* ??? */
+
+#if PG_VERSION_NUM < 90500
+               vacstmt.type = T_VacuumStmt;
                vacstmt.relation = NULL;
                vacstmt.va_cols = NIL;
-
 #if PG_VERSION_NUM >= 90000
                vacstmt.options = VACOPT_ANALYZE;
                if (online_analyze_verbose)
@@ -368,13 +436,29 @@ makeAnalyze(Oid relOid, CmdType operation, uint32 naffected)
                vacstmt.analyze = true;
                vacstmt.verbose = online_analyze_verbose;
 #endif
+#else
+               vacstmt.multixact_freeze_min_age = -1;
+               vacstmt.multixact_freeze_table_age = -1;
+               vacstmt.log_min_duration = -1;
+#endif
 
                if (online_analyze_verbose)
                        startStamp = GetCurrentTimestamp();
 
-               analyze_rel(relOid, &vacstmt, GetAccessStrategy(BAS_VACUUM)
-#if (PG_VERSION_NUM < 90004) && (PG_VERSION_NUM >= 90000)
+               analyze_rel(relOid,
+#if PG_VERSION_NUM < 90500
+                       &vacstmt
+#if PG_VERSION_NUM >= 90018
+                       , true
+#endif
+                       , GetAccessStrategy(BAS_VACUUM)
+#if (PG_VERSION_NUM >= 90000) && (PG_VERSION_NUM < 90004)
                        , true
+#endif
+#else
+                       makeRangeVarFromOid(relOid),
+                       VACOPT_ANALYZE | ((online_analyze_verbose) ? VACOPT_VERBOSE : 0),
+                       &vacstmt, NULL, true, GetAccessStrategy(BAS_VACUUM)
 #endif
                );
 
@@ -385,11 +469,11 @@ makeAnalyze(Oid relOid, CmdType operation, uint32 naffected)
 
                        endStamp = GetCurrentTimestamp();
                        TimestampDifference(startStamp, endStamp, &secs, &microsecs);
-                       elog(INFO, "analyze \"%s\" took %.02f seconds", 
-                               get_rel_name(relOid), ((double)secs) + ((double)microsecs)/1.0e6);
+                       elog(INFO, "analyze \"%s\" took %.02f seconds",
+                               get_rel_name(relOid),
+                               ((double)secs) + ((double)microsecs)/1.0e6);
                }
 
-
                if (tabentry == NULL)
                {
                        /* new table */
@@ -411,55 +495,108 @@ makeAnalyze(Oid relOid, CmdType operation, uint32 naffected)
 
 extern PGDLLIMPORT void onlineAnalyzeHooker(QueryDesc *queryDesc);
 void
-onlineAnalyzeHooker(QueryDesc *queryDesc) 
+onlineAnalyzeHooker(QueryDesc *queryDesc)
 {
-       uint32  naffected = 0;
+       uint32  naffected = -1;
 
        if (queryDesc->estate)
-               naffected = queryDesc->estate->es_processed;    
+               naffected = queryDesc->estate->es_processed;
 
        if (online_analyze_enable && queryDesc->plannedstmt &&
-                       (queryDesc->operation == CMD_INSERT || 
+                       (queryDesc->operation == CMD_INSERT ||
                         queryDesc->operation == CMD_UPDATE ||
-                        queryDesc->operation == CMD_DELETE ||
-                        (queryDesc->operation == CMD_SELECT && queryDesc->plannedstmt->intoClause)))
+                        queryDesc->operation == CMD_DELETE
+#if PG_VERSION_NUM < 90200
+                        || (queryDesc->operation == CMD_SELECT &&
+                                queryDesc->plannedstmt->intoClause)
+#endif
+                        ))
        {
-               if (queryDesc->plannedstmt->intoClause)
+#if PG_VERSION_NUM < 90200
+               if (queryDesc->operation == CMD_SELECT)
                {
                        Oid     relOid = RangeVarGetRelid(queryDesc->plannedstmt->intoClause->rel, true);
 
                        makeAnalyze(relOid, queryDesc->operation, naffected);
                }
-               else if (queryDesc->plannedstmt->resultRelations &&
+               else
+#endif
+               if (queryDesc->plannedstmt->resultRelations &&
                                 queryDesc->plannedstmt->rtable)
                {
                        ListCell        *l;
 
                        foreach(l, queryDesc->plannedstmt->resultRelations)
                        {
-                               int                     n = lfirst_int(l);
+                               int                             n = lfirst_int(l);
                                RangeTblEntry   *rte = list_nth(queryDesc->plannedstmt->rtable, n-1);
-               
+
                                if (rte->rtekind == RTE_RELATION)
                                        makeAnalyze(rte->relid, queryDesc->operation, naffected);
                        }
                }
        }
 
-       if (oldhook)
-               (*oldhook)(queryDesc);
+       if (oldExecutorEndHook)
+               oldExecutorEndHook(queryDesc);
        else
                standard_ExecutorEnd(queryDesc);
 }
 
+#if PG_VERSION_NUM >= 90200
+static void
+onlineAnalyzeHookerUtility(Node *parsetree, const char *queryString,
+#if PG_VERSION_NUM >= 90300
+                                                       ProcessUtilityContext context, ParamListInfo params,
+#else
+                                                       ParamListInfo params, bool isTopLevel,
+#endif
+                                                       DestReceiver *dest, char *completionTag) {
+       RangeVar        *tblname = NULL;
+
+       if (IsA(parsetree, CreateTableAsStmt) && ((CreateTableAsStmt*)parsetree)->into)
+               tblname = (RangeVar*)copyObject(((CreateTableAsStmt*)parsetree)->into->rel);
+
+       if (oldProcessUtilityHook)
+               oldProcessUtilityHook(parsetree, queryString,
+#if PG_VERSION_NUM >= 90300
+                                                         context, params,
+#else
+                                                         params, isTopLevel,
+#endif
+                                                         dest, completionTag);
+       else
+               standard_ProcessUtility(parsetree, queryString,
+#if PG_VERSION_NUM >= 90300
+                                                               context, params,
+#else
+                                                               params, isTopLevel,
+#endif
+                                                               dest, completionTag);
+
+       if (tblname) {
+               Oid     tblOid = RangeVarGetRelid(tblname, NoLock, true);
+
+               makeAnalyze(tblOid, CMD_INSERT, -1);
+       }
+}
+#endif
+
 void _PG_init(void);
 void
 _PG_init(void)
 {
-       oldhook = ExecutorEnd_hook;
+       oldExecutorEndHook = ExecutorEnd_hook;
 
        ExecutorEnd_hook = onlineAnalyzeHooker;
 
+#if PG_VERSION_NUM >= 90200
+       oldProcessUtilityHook = ProcessUtility_hook;
+
+       ProcessUtility_hook = onlineAnalyzeHookerUtility;
+#endif
+
+
        DefineCustomBoolVariable(
                "online_analyze.enable",
                "Enable on-line analyze",
@@ -498,7 +635,7 @@ _PG_init(void)
                NULL
        );
 
-    DefineCustomRealVariable(
+       DefineCustomRealVariable(
                "online_analyze.scale_factor",
                "fraction of table size to start on-line analyze",
                "fraction of table size to start on-line analyze",
@@ -519,7 +656,7 @@ _PG_init(void)
                NULL
        );
 
-    DefineCustomIntVariable(
+       DefineCustomIntVariable(
                "online_analyze.threshold",
                "min number of row updates before on-line analyze",
                "min number of row updates before on-line analyze",
@@ -540,11 +677,11 @@ _PG_init(void)
                NULL
        );
 
-    DefineCustomRealVariable(
+       DefineCustomRealVariable(
                "online_analyze.min_interval",
                "minimum time interval between analyze call (in milliseconds)",
                "minimum time interval between analyze call (in milliseconds)",
-               &online_analyze_scale_factor,
+               &online_analyze_min_interval,
 #if PG_VERSION_NUM >= 80400
                online_analyze_min_interval,
 #endif
@@ -561,9 +698,9 @@ _PG_init(void)
                NULL
        );
 
-DefineCustomEnumVariable(
+       DefineCustomEnumVariable(
                "online_analyze.table_type",
-               "Type(s) of table for onlyne analyze: all(default), persistent, temporary, none",
+               "Type(s) of table for online analyze: all(default), persistent, temporary, none",
                NULL,
                &online_analyze_table_type,
 #if PG_VERSION_NUM >= 80400
@@ -572,7 +709,7 @@ DefineCustomEnumVariable(
                online_analyze_table_type_options,
                PGC_USERSET,
 #if PG_VERSION_NUM >= 80400
-        GUC_NOT_IN_SAMPLE,
+               GUC_NOT_IN_SAMPLE,
 #if PG_VERSION_NUM >= 90100
                NULL,
 #endif
@@ -581,7 +718,7 @@ DefineCustomEnumVariable(
                NULL
        );
 
-    DefineCustomStringVariable(
+       DefineCustomStringVariable(
                "online_analyze.exclude_tables",
                "List of tables which will not online analyze",
                NULL,
@@ -600,7 +737,7 @@ DefineCustomEnumVariable(
                excludeTablesShow
        );
 
-    DefineCustomStringVariable(
+       DefineCustomStringVariable(
                "online_analyze.include_tables",
                "List of tables which will online analyze",
                NULL,
@@ -624,7 +761,10 @@ void _PG_fini(void);
 void
 _PG_fini(void)
 {
-       ExecutorEnd_hook = oldhook;
+       ExecutorEnd_hook = oldExecutorEndHook;
+#if PG_VERSION_NUM >= 90200
+       ProcessUtility_hook = oldProcessUtilityHook;
+#endif
 
        if (excludeTables.tables)
                free(excludeTables.tables);