add online_analyze.lower_limit, use int64 to count changes rows
[online_analyze.git] / online_analyze.c
1 /*
2  * Copyright (c) 2011 Teodor Sigaev <teodor@sigaev.ru>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *              notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *              notice, this list of conditions and the following disclaimer in the
12  *              documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the author nor the names of any co-contributors
14  *              may be used to endorse or promote products derived from this software
15  *              without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "postgres.h"
31
32 #include "pgstat.h"
33 #include "catalog/namespace.h"
34 #include "commands/vacuum.h"
35 #include "executor/executor.h"
36 #include "nodes/nodes.h"
37 #include "nodes/parsenodes.h"
38 #include "storage/bufmgr.h"
39 #include "utils/builtins.h"
40 #include "utils/hsearch.h"
41 #include "utils/memutils.h"
42 #include "utils/lsyscache.h"
43 #include "utils/guc.h"
44 #if PG_VERSION_NUM >= 90200
45 #include "catalog/pg_class.h"
46 #include "nodes/primnodes.h"
47 #include "tcop/utility.h"
48 #include "utils/rel.h"
49 #include "utils/relcache.h"
50 #include "utils/timestamp.h"
51 #if PG_VERSION_NUM >= 90500
52 #include "nodes/makefuncs.h"
53 #endif
54 #endif
55
56 #ifdef PG_MODULE_MAGIC
57 PG_MODULE_MAGIC;
58 #endif
59
60 static bool online_analyze_enable = true;
61 static bool online_analyze_verbose = true;
62 static double online_analyze_scale_factor = 0.1;
63 static int online_analyze_threshold = 50;
64 static int online_analyze_capacity_threshold = 100000;
65 static double online_analyze_min_interval = 10000;
66 static int online_analyze_lower_limit = 0;
67
68 static ExecutorEnd_hook_type oldExecutorEndHook = NULL;
69 #if PG_VERSION_NUM >= 90200
70 static ProcessUtility_hook_type oldProcessUtilityHook = NULL;
71 #endif
72
73 typedef enum
74 {
75         OATT_ALL                = 0x03,
76         OATT_PERSISTENT = 0x01,
77         OATT_TEMPORARY  = 0x02,
78         OATT_NONE               = 0x00
79 } OnlineAnalyzeTableType;
80
81 static const struct config_enum_entry online_analyze_table_type_options[] =
82 {
83         {"all", OATT_ALL, false},
84         {"persistent", OATT_PERSISTENT, false},
85         {"temporary", OATT_TEMPORARY, false},
86         {"none", OATT_NONE, false},
87         {NULL, 0, false},
88 };
89
90 static int online_analyze_table_type = (int)OATT_ALL;
91
92 typedef struct TableList {
93         int             nTables;
94         Oid             *tables;
95         char    *tableStr;
96 } TableList;
97
98 static TableList excludeTables = {0, NULL, NULL};
99 static TableList includeTables = {0, NULL, NULL};
100
101 typedef struct OnlineAnalyzeTableStat {
102         Oid                             tableid;
103         bool                    rereadStat;
104         PgStat_Counter  n_tuples;
105         PgStat_Counter  changes_since_analyze;
106         TimestampTz             autovac_analyze_timestamp;
107         TimestampTz             analyze_timestamp;
108 } OnlineAnalyzeTableStat;
109
110 static  MemoryContext   onlineAnalyzeMemoryContext = NULL;
111 static  HTAB    *relstats = NULL;
112
113 static void relstatsInit(void);
114
115 static int
116 oid_cmp(const void *a, const void *b)
117 {
118         if (*(Oid*)a == *(Oid*)b)
119                 return 0;
120         return (*(Oid*)a > *(Oid*)b) ? 1 : -1;
121 }
122
123 static const char *
124 tableListAssign(const char * newval, bool doit, TableList *tbl)
125 {
126         char            *rawname;
127         List            *namelist;
128         ListCell        *l;
129         Oid                     *newOids = NULL;
130         int                     nOids = 0,
131                                 i = 0;
132
133         rawname = pstrdup(newval);
134
135         if (!SplitIdentifierString(rawname, ',', &namelist))
136                 goto cleanup;
137
138         if (doit)
139         {
140                 nOids = list_length(namelist);
141                 newOids = malloc(sizeof(Oid) * (nOids+1));
142                 if (!newOids)
143                         elog(ERROR,"could not allocate %d bytes",
144                                  (int)(sizeof(Oid) * (nOids+1)));
145         }
146
147         foreach(l, namelist)
148         {
149                 char    *curname = (char *) lfirst(l);
150 #if PG_VERSION_NUM >= 90200
151                 Oid             relOid = RangeVarGetRelid(makeRangeVarFromNameList(
152                                                         stringToQualifiedNameList(curname)), NoLock, true);
153 #else
154                 Oid             relOid = RangeVarGetRelid(makeRangeVarFromNameList(
155                                                         stringToQualifiedNameList(curname)), true);
156 #endif
157
158                 if (relOid == InvalidOid)
159                 {
160 #if PG_VERSION_NUM >= 90100
161                         if (doit == false)
162 #endif
163                         elog(WARNING,"'%s' does not exist", curname);
164                         continue;
165                 }
166                 else if ( get_rel_relkind(relOid) != RELKIND_RELATION )
167                 {
168 #if PG_VERSION_NUM >= 90100
169                         if (doit == false)
170 #endif
171                                 elog(WARNING,"'%s' is not an table", curname);
172                         continue;
173                 }
174                 else if (doit)
175                 {
176                         newOids[i++] = relOid;
177                 }
178         }
179
180         if (doit)
181         {
182                 tbl->nTables = i;
183                 if (tbl->tables)
184                         free(tbl->tables);
185                 tbl->tables = newOids;
186                 if (tbl->nTables > 1)
187                         qsort(tbl->tables, tbl->nTables, sizeof(tbl->tables[0]), oid_cmp);
188         }
189
190         pfree(rawname);
191         list_free(namelist);
192
193         return newval;
194
195 cleanup:
196         if (newOids)
197                 free(newOids);
198         pfree(rawname);
199         list_free(namelist);
200         return NULL;
201 }
202
203 #if PG_VERSION_NUM >= 90100
204 static bool
205 excludeTablesCheck(char **newval, void **extra, GucSource source)
206 {
207         char *val;
208
209         val = (char*)tableListAssign(*newval, false, &excludeTables);
210
211         if (val)
212         {
213                 *newval = val;
214                 return true;
215         }
216
217         return false;
218 }
219
220 static void
221 excludeTablesAssign(const char *newval, void *extra)
222 {
223         tableListAssign(newval, true, &excludeTables);
224 }
225
226 static bool
227 includeTablesCheck(char **newval, void **extra, GucSource source)
228 {
229         char *val;
230
231         val = (char*)tableListAssign(*newval, false, &includeTables);
232
233         if (val)
234         {
235                 *newval = val;
236                 return true;
237         }
238
239         return false;
240 }
241
242 static void
243 includeTablesAssign(const char *newval, void *extra)
244 {
245         tableListAssign(newval, true, &excludeTables);
246 }
247
248 #else /* PG_VERSION_NUM < 90100 */
249
250 static const char *
251 excludeTablesAssign(const char * newval, bool doit, GucSource source)
252 {
253         return tableListAssign(newval, doit, &excludeTables);
254 }
255
256 static const char *
257 includeTablesAssign(const char * newval, bool doit, GucSource source)
258 {
259         return tableListAssign(newval, doit, &includeTables);
260 }
261
262 #endif
263
264 static const char*
265 tableListShow(TableList *tbl)
266 {
267         char    *val, *ptr;
268         int             i,
269                         len;
270
271         len = 1 /* \0 */ + tbl->nTables * (2 * NAMEDATALEN + 2 /* ', ' */ + 1 /* . */);
272         ptr = val = palloc(len);
273         *ptr ='\0';
274         for(i=0; i<tbl->nTables; i++)
275         {
276                 char    *relname = get_rel_name(tbl->tables[i]);
277                 Oid             nspOid = get_rel_namespace(tbl->tables[i]);
278                 char    *nspname = get_namespace_name(nspOid);
279
280                 if ( relname == NULL || nspOid == InvalidOid || nspname == NULL )
281                         continue;
282
283                 ptr += snprintf(ptr, len - (ptr - val), "%s%s.%s",
284                                                                                                         (i==0) ? "" : ", ",
285                                                                                                         nspname, relname);
286         }
287
288         return val;
289 }
290
291 static const char*
292 excludeTablesShow(void)
293 {
294         return tableListShow(&excludeTables);
295 }
296
297 static const char*
298 includeTablesShow(void)
299 {
300         return tableListShow(&includeTables);
301 }
302
303 static bool
304 matchOid(TableList *tbl, Oid oid)
305 {
306         Oid     *StopLow = tbl->tables,
307                 *StopHigh = tbl->tables + tbl->nTables,
308                 *StopMiddle;
309
310         /* Loop invariant: StopLow <= val < StopHigh */
311         while (StopLow < StopHigh)
312         {
313                 StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
314
315                 if (*StopMiddle == oid)
316                         return true;
317                 else  if (*StopMiddle < oid)
318                         StopLow = StopMiddle + 1;
319                 else
320                         StopHigh = StopMiddle;
321         }
322
323         return false;
324 }
325
326 #if PG_VERSION_NUM >= 90500
327 static RangeVar*
328 makeRangeVarFromOid(Oid relOid)
329 {
330         return makeRangeVar(
331                                 get_namespace_name(get_rel_namespace(relOid)),
332                                 get_rel_name(relOid),
333                                 -1
334                         );
335
336 }
337 #endif
338
339 static void
340 makeAnalyze(Oid relOid, CmdType operation, int64 naffected)
341 {
342         TimestampTz                             now = GetCurrentTimestamp();
343         Relation                                rel;
344         OnlineAnalyzeTableType  reltype;
345         bool                                    found = false,
346                                                         newTable = false;
347         OnlineAnalyzeTableStat  *rstat,
348                                                         dummyrstat;
349         PgStat_StatTabEntry             *tabentry = NULL;
350
351         if (relOid == InvalidOid)
352                 return;
353
354         if (naffected == 0)
355                 /* return if there is no changes */
356                 return;
357         else if (naffected < 0)
358                 /* number if affected rows is unknown */
359                 naffected = 0;
360
361         rel = RelationIdGetRelation(relOid);
362         if (rel->rd_rel->relkind != RELKIND_RELATION)
363         {
364                 RelationClose(rel);
365                 return;
366         }
367
368         reltype =
369 #if PG_VERSION_NUM >= 90100
370                 (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
371 #else
372                 (rel->rd_istemp || rel->rd_islocaltemp)
373 #endif
374                         ? OATT_TEMPORARY : OATT_PERSISTENT;
375
376         RelationClose(rel);
377
378         /*
379          * includeTables overwrites excludeTables
380          */
381         switch(online_analyze_table_type)
382         {
383                 case OATT_ALL:
384                         if (get_rel_relkind(relOid) != RELKIND_RELATION ||
385                                 (matchOid(&excludeTables, relOid) == true &&
386                                 matchOid(&includeTables, relOid) == false))
387                                 return;
388                         break;
389                 case OATT_NONE:
390                         if (get_rel_relkind(relOid) != RELKIND_RELATION ||
391                                 matchOid(&includeTables, relOid) == false)
392                                 return;
393                         break;
394                 case OATT_TEMPORARY:
395                 case OATT_PERSISTENT:
396                 default:
397                         /*
398                          * skip analyze if relation's type doesn't not match
399                          * online_analyze_table_type
400                          */
401                         if ((online_analyze_table_type & reltype) == 0 ||
402                                 matchOid(&excludeTables, relOid) == true)
403                         {
404                                 if (matchOid(&includeTables, relOid) == false)
405                                         return;
406                         }
407                         break;
408         }
409
410         /*
411          * Do not store data about persistent table in local memory because we
412          * could not track changes of them: they could be changed by another
413          * backends. So always get a pgstat table entry.
414          */
415         if (reltype == OATT_TEMPORARY)
416                 rstat = hash_search(relstats, &relOid, HASH_ENTER, &found);
417         else
418                 rstat = &dummyrstat; /* found == false for following if */
419
420         if (found == false || rstat->rereadStat == true || naffected == 0)
421         {
422
423                 if (!found)
424                 {
425                         MemSet(rstat, 0, sizeof(*rstat));
426                         rstat->tableid = relOid;
427                 }
428                 Assert(rstat->tableid == relOid);
429
430                 tabentry = pgstat_fetch_stat_tabentry(relOid);
431
432                 if (tabentry)
433                 {
434                         rstat->n_tuples = tabentry->n_dead_tuples + tabentry->n_live_tuples;
435                         rstat->changes_since_analyze =
436 #if PG_VERSION_NUM >= 90000
437                                 tabentry->changes_since_analyze;
438 #else
439                                 tabentry->n_live_tuples + tabentry->n_dead_tuples -
440                                         tabentry->last_anl_tuples;
441 #endif
442                         rstat->autovac_analyze_timestamp =
443                                 tabentry->autovac_analyze_timestamp;
444                         rstat->analyze_timestamp = tabentry->analyze_timestamp;
445                         rstat->rereadStat = false;
446                 }
447                 else
448                 {
449                         newTable = true;
450                         rstat->rereadStat = true;
451                 }
452         }
453
454         if (newTable || (
455                 /* do not analyze too often, if both stamps are exceeded the go */
456                 TimestampDifferenceExceeds(rstat->analyze_timestamp, now, online_analyze_min_interval) &&
457                 TimestampDifferenceExceeds(rstat->autovac_analyze_timestamp, now, online_analyze_min_interval) &&
458                 /* do not analyze too small tables */
459                 rstat->n_tuples + rstat->changes_since_analyze + naffected > online_analyze_lower_limit &&
460                 /* be in sync with relation_needs_vacanalyze */
461                 ((double)(rstat->changes_since_analyze + naffected)) >=
462                          online_analyze_scale_factor * ((double)rstat->n_tuples) +
463                          (double)online_analyze_threshold))
464         {
465 #if PG_VERSION_NUM < 90500
466                 VacuumStmt                              vacstmt;
467 #else
468                 VacuumParams                    vacstmt;
469 #endif
470                 TimestampTz                             startStamp, endStamp;
471
472
473                 memset(&startStamp, 0, sizeof(startStamp)); /* keep compiler quiet */
474
475                 memset(&vacstmt, 0, sizeof(vacstmt));
476
477                 vacstmt.freeze_min_age = -1;
478                 vacstmt.freeze_table_age = -1; /* ??? */
479
480 #if PG_VERSION_NUM < 90500
481                 vacstmt.type = T_VacuumStmt;
482                 vacstmt.relation = NULL;
483                 vacstmt.va_cols = NIL;
484 #if PG_VERSION_NUM >= 90000
485                 vacstmt.options = VACOPT_ANALYZE;
486                 if (online_analyze_verbose)
487                         vacstmt.options |= VACOPT_VERBOSE;
488 #else
489                 vacstmt.vacuum = vacstmt.full = false;
490                 vacstmt.analyze = true;
491                 vacstmt.verbose = online_analyze_verbose;
492 #endif
493 #else
494                 vacstmt.multixact_freeze_min_age = -1;
495                 vacstmt.multixact_freeze_table_age = -1;
496                 vacstmt.log_min_duration = -1;
497 #endif
498
499                 if (online_analyze_verbose)
500                         startStamp = GetCurrentTimestamp();
501
502                 analyze_rel(relOid,
503 #if PG_VERSION_NUM < 90500
504                         &vacstmt
505 #if PG_VERSION_NUM >= 90018
506                         , true
507 #endif
508                         , GetAccessStrategy(BAS_VACUUM)
509 #if (PG_VERSION_NUM >= 90000) && (PG_VERSION_NUM < 90004)
510                         , true
511 #endif
512 #else
513                         makeRangeVarFromOid(relOid),
514                         VACOPT_ANALYZE | ((online_analyze_verbose) ? VACOPT_VERBOSE : 0),
515                         &vacstmt, NULL, true, GetAccessStrategy(BAS_VACUUM)
516 #endif
517                 );
518
519                 if (online_analyze_verbose)
520                 {
521                         long    secs;
522                         int             microsecs;
523
524                         endStamp = GetCurrentTimestamp();
525                         TimestampDifference(startStamp, endStamp, &secs, &microsecs);
526                         elog(INFO, "analyze \"%s\" took %.02f seconds",
527                                 get_rel_name(relOid),
528                                 ((double)secs) + ((double)microsecs)/1.0e6);
529                 }
530
531                 rstat->autovac_analyze_timestamp = now;
532                 rstat->changes_since_analyze = 0;
533                 rstat->rereadStat = true;
534
535                 /* update last analyze timestamp in local memory of backend */
536                 if (tabentry)
537                 {
538                         tabentry->analyze_timestamp = now;
539                         tabentry->changes_since_analyze = 0;
540                 }
541 #if 0
542                 /* force reload stat for new table */
543                 if (newTable)
544                         pgstat_clear_snapshot();
545 #endif
546         }
547         else
548         {
549 #if PG_VERSION_NUM >= 90000
550                 if (tabentry)
551                         tabentry->changes_since_analyze += naffected;
552 #endif
553                 rstat->changes_since_analyze += naffected;
554         }
555
556         /* Reset local cache if we are over limit */
557         if (hash_get_num_entries(relstats) > online_analyze_capacity_threshold)
558                 relstatsInit();
559 }
560
561 extern PGDLLIMPORT void onlineAnalyzeHooker(QueryDesc *queryDesc);
562 void
563 onlineAnalyzeHooker(QueryDesc *queryDesc)
564 {
565         int64   naffected = -1;
566
567         if (queryDesc->estate)
568                 naffected = queryDesc->estate->es_processed;
569
570         if (online_analyze_enable && queryDesc->plannedstmt &&
571                         (queryDesc->operation == CMD_INSERT ||
572                          queryDesc->operation == CMD_UPDATE ||
573                          queryDesc->operation == CMD_DELETE
574 #if PG_VERSION_NUM < 90200
575                          || (queryDesc->operation == CMD_SELECT &&
576                                  queryDesc->plannedstmt->intoClause)
577 #endif
578                          ))
579         {
580 #if PG_VERSION_NUM < 90200
581                 if (queryDesc->operation == CMD_SELECT)
582                 {
583                         Oid     relOid = RangeVarGetRelid(queryDesc->plannedstmt->intoClause->rel, true);
584
585                         makeAnalyze(relOid, queryDesc->operation, naffected);
586                 }
587                 else
588 #endif
589                 if (queryDesc->plannedstmt->resultRelations &&
590                                  queryDesc->plannedstmt->rtable)
591                 {
592                         ListCell        *l;
593
594                         foreach(l, queryDesc->plannedstmt->resultRelations)
595                         {
596                                 int                             n = lfirst_int(l);
597                                 RangeTblEntry   *rte = list_nth(queryDesc->plannedstmt->rtable, n-1);
598
599                                 if (rte->rtekind == RTE_RELATION)
600                                         makeAnalyze(rte->relid, queryDesc->operation, naffected);
601                         }
602                 }
603         }
604
605         if (oldExecutorEndHook)
606                 oldExecutorEndHook(queryDesc);
607         else
608                 standard_ExecutorEnd(queryDesc);
609 }
610
611 #if PG_VERSION_NUM >= 90200
612 static void
613 onlineAnalyzeHookerUtility(Node *parsetree, const char *queryString,
614 #if PG_VERSION_NUM >= 90300
615                                                         ProcessUtilityContext context, ParamListInfo params,
616 #else
617                                                         ParamListInfo params, bool isTopLevel,
618 #endif
619                                                         DestReceiver *dest, char *completionTag) {
620         RangeVar        *tblname = NULL;
621
622         if (IsA(parsetree, CreateTableAsStmt) && ((CreateTableAsStmt*)parsetree)->into)
623                 tblname = (RangeVar*)copyObject(((CreateTableAsStmt*)parsetree)->into->rel);
624
625         if (oldProcessUtilityHook)
626                 oldProcessUtilityHook(parsetree, queryString,
627 #if PG_VERSION_NUM >= 90300
628                                                           context, params,
629 #else
630                                                           params, isTopLevel,
631 #endif
632                                                           dest, completionTag);
633         else
634                 standard_ProcessUtility(parsetree, queryString,
635 #if PG_VERSION_NUM >= 90300
636                                                                 context, params,
637 #else
638                                                                 params, isTopLevel,
639 #endif
640                                                                 dest, completionTag);
641
642         if (tblname) {
643                 Oid     tblOid = RangeVarGetRelid(tblname, NoLock, true);
644
645                 makeAnalyze(tblOid, CMD_INSERT, -1);
646         }
647 }
648 #endif
649
650 static void
651 relstatsInit(void)
652 {
653         HASHCTL hash_ctl;
654         int             flags = 0;
655
656         MemSet(&hash_ctl, 0, sizeof(hash_ctl));
657
658         hash_ctl.hash = oid_hash;
659         flags |= HASH_FUNCTION;
660
661         if (onlineAnalyzeMemoryContext)
662         {
663                 Assert(relstats != NULL);
664                 MemoryContextReset(onlineAnalyzeMemoryContext);
665         }
666         else
667         {
668                 Assert(relstats == NULL);
669                 onlineAnalyzeMemoryContext =
670                         AllocSetContextCreate(CacheMemoryContext,
671                                                                   "online_analyze storage context",
672 #if PG_VERSION_NUM < 90600
673                                                                   ALLOCSET_DEFAULT_MINSIZE,
674                                                                   ALLOCSET_DEFAULT_INITSIZE,
675                                                                   ALLOCSET_DEFAULT_MAXSIZE
676 #else
677                                                                   ALLOCSET_DEFAULT_SIZES
678 #endif
679                                                                  );
680         }
681
682         hash_ctl.hcxt = onlineAnalyzeMemoryContext;
683         flags |= HASH_CONTEXT;
684
685         hash_ctl.keysize = sizeof(Oid);
686
687         hash_ctl.entrysize = sizeof(OnlineAnalyzeTableStat);
688         flags |= HASH_ELEM;
689
690         relstats = hash_create("online_analyze storage", 1024, &hash_ctl, flags);
691 }
692
693 void _PG_init(void);
694 void
695 _PG_init(void)
696 {
697         relstatsInit();
698
699         oldExecutorEndHook = ExecutorEnd_hook;
700
701         ExecutorEnd_hook = onlineAnalyzeHooker;
702
703 #if PG_VERSION_NUM >= 90200
704         oldProcessUtilityHook = ProcessUtility_hook;
705
706         ProcessUtility_hook = onlineAnalyzeHookerUtility;
707 #endif
708
709
710         DefineCustomBoolVariable(
711                 "online_analyze.enable",
712                 "Enable on-line analyze",
713                 "Enables analyze of table directly after insert/update/delete/select into",
714                 &online_analyze_enable,
715 #if PG_VERSION_NUM >= 80400
716                 online_analyze_enable,
717 #endif
718                 PGC_USERSET,
719 #if PG_VERSION_NUM >= 80400
720                 GUC_NOT_IN_SAMPLE,
721 #if PG_VERSION_NUM >= 90100
722                 NULL,
723 #endif
724 #endif
725                 NULL,
726                 NULL
727         );
728
729         DefineCustomBoolVariable(
730                 "online_analyze.verbose",
731                 "Verbosity of on-line analyze",
732                 "Make ANALYZE VERBOSE after table's changes",
733                 &online_analyze_verbose,
734 #if PG_VERSION_NUM >= 80400
735                 online_analyze_verbose,
736 #endif
737                 PGC_USERSET,
738 #if PG_VERSION_NUM >= 80400
739                 GUC_NOT_IN_SAMPLE,
740 #if PG_VERSION_NUM >= 90100
741                 NULL,
742 #endif
743 #endif
744                 NULL,
745                 NULL
746         );
747
748         DefineCustomRealVariable(
749                 "online_analyze.scale_factor",
750                 "fraction of table size to start on-line analyze",
751                 "fraction of table size to start on-line analyze",
752                 &online_analyze_scale_factor,
753 #if PG_VERSION_NUM >= 80400
754                 online_analyze_scale_factor,
755 #endif
756                 0.0,
757                 1.0,
758                 PGC_USERSET,
759 #if PG_VERSION_NUM >= 80400
760                 GUC_NOT_IN_SAMPLE,
761 #if PG_VERSION_NUM >= 90100
762                 NULL,
763 #endif
764 #endif
765                 NULL,
766                 NULL
767         );
768
769         DefineCustomIntVariable(
770                 "online_analyze.threshold",
771                 "min number of row updates before on-line analyze",
772                 "min number of row updates before on-line analyze",
773                 &online_analyze_threshold,
774 #if PG_VERSION_NUM >= 80400
775                 online_analyze_threshold,
776 #endif
777                 0,
778                 0x7fffffff,
779                 PGC_USERSET,
780 #if PG_VERSION_NUM >= 80400
781                 GUC_NOT_IN_SAMPLE,
782 #if PG_VERSION_NUM >= 90100
783                 NULL,
784 #endif
785 #endif
786                 NULL,
787                 NULL
788         );
789
790         DefineCustomIntVariable(
791                 "online_analyze.capacity_threshold",
792                 "Max local cache table capacity",
793                 "Max local cache table capacity",
794                 &online_analyze_capacity_threshold,
795 #if PG_VERSION_NUM >= 80400
796                 online_analyze_capacity_threshold,
797 #endif
798                 0,
799                 0x7fffffff,
800                 PGC_USERSET,
801 #if PG_VERSION_NUM >= 80400
802                 GUC_NOT_IN_SAMPLE,
803 #if PG_VERSION_NUM >= 90100
804                 NULL,
805 #endif
806 #endif
807                 NULL,
808                 NULL
809         );
810
811         DefineCustomRealVariable(
812                 "online_analyze.min_interval",
813                 "minimum time interval between analyze call (in milliseconds)",
814                 "minimum time interval between analyze call (in milliseconds)",
815                 &online_analyze_min_interval,
816 #if PG_VERSION_NUM >= 80400
817                 online_analyze_min_interval,
818 #endif
819                 0.0,
820                 1e30,
821                 PGC_USERSET,
822 #if PG_VERSION_NUM >= 80400
823                 GUC_NOT_IN_SAMPLE,
824 #if PG_VERSION_NUM >= 90100
825                 NULL,
826 #endif
827 #endif
828                 NULL,
829                 NULL
830         );
831
832         DefineCustomEnumVariable(
833                 "online_analyze.table_type",
834                 "Type(s) of table for online analyze: all(default), persistent, temporary, none",
835                 NULL,
836                 &online_analyze_table_type,
837 #if PG_VERSION_NUM >= 80400
838                 online_analyze_table_type,
839 #endif
840                 online_analyze_table_type_options,
841                 PGC_USERSET,
842 #if PG_VERSION_NUM >= 80400
843                 GUC_NOT_IN_SAMPLE,
844 #if PG_VERSION_NUM >= 90100
845                 NULL,
846 #endif
847 #endif
848                 NULL,
849                 NULL
850         );
851
852         DefineCustomStringVariable(
853                 "online_analyze.exclude_tables",
854                 "List of tables which will not online analyze",
855                 NULL,
856                 &excludeTables.tableStr,
857 #if PG_VERSION_NUM >= 80400
858                 "",
859 #endif
860                 PGC_USERSET,
861                 0,
862 #if PG_VERSION_NUM >= 90100
863                 excludeTablesCheck,
864                 excludeTablesAssign,
865 #else
866                 excludeTablesAssign,
867 #endif
868                 excludeTablesShow
869         );
870
871         DefineCustomStringVariable(
872                 "online_analyze.include_tables",
873                 "List of tables which will online analyze",
874                 NULL,
875                 &includeTables.tableStr,
876 #if PG_VERSION_NUM >= 80400
877                 "",
878 #endif
879                 PGC_USERSET,
880                 0,
881 #if PG_VERSION_NUM >= 90100
882                 includeTablesCheck,
883                 includeTablesAssign,
884 #else
885                 includeTablesAssign,
886 #endif
887                 includeTablesShow
888         );
889
890         DefineCustomIntVariable(
891                 "online_analyze.lower_limit",
892                 "min number of rows in table to analyze",
893                 "min number of rows in table to analyze",
894                 &online_analyze_lower_limit,
895 #if PG_VERSION_NUM >= 80400
896                 online_analyze_lower_limit,
897 #endif
898                 0,
899                 0x7fffffff,
900                 PGC_USERSET,
901 #if PG_VERSION_NUM >= 80400
902                 GUC_NOT_IN_SAMPLE,
903 #if PG_VERSION_NUM >= 90100
904                 NULL,
905 #endif
906 #endif
907                 NULL,
908                 NULL
909         );
910
911 }
912
913 void _PG_fini(void);
914 void
915 _PG_fini(void)
916 {
917         ExecutorEnd_hook = oldExecutorEndHook;
918 #if PG_VERSION_NUM >= 90200
919         ProcessUtility_hook = oldProcessUtilityHook;
920 #endif
921
922         if (excludeTables.tables)
923                 free(excludeTables.tables);
924         if (includeTables.tables)
925                 free(includeTables.tables);
926
927         excludeTables.tables = includeTables.tables = NULL;
928         excludeTables.nTables = includeTables.nTables = 0;
929 }