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