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