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