4e0f0c598b2abca7b9b933bd9427b528affb3f77
[plantuner.git] / plantuner.c
1 /*
2  * Copyright (c) 2009 Teodor Sigaev <teodor@sigaev.ru>
3  */
4
5 #include <postgres.h>
6
7 #include <fmgr.h>
8 #include <catalog/namespace.h>
9 #include <catalog/pg_class.h>
10 #include <nodes/pg_list.h>
11 #include <optimizer/plancat.h>
12 #include <utils/builtins.h>
13 #include <utils/guc.h>
14 #include <utils/lsyscache.h>
15
16 PG_MODULE_MAGIC;
17
18 static int      nIndexesOut = 0;
19 static Oid      *indexesOut = NULL;
20 get_relation_info_hook_type     prevHook = NULL;
21
22 static char *indexesOutStr = "";
23
24 static const char *
25 indexesOutAssign(const char * newval, bool doit, GucSource source) 
26 {
27         char       *rawname;
28         List       *namelist;
29         ListCell   *l;
30         Oid                     *newOids = NULL;
31         int                     nOids = 0,
32                                 i = 0;
33
34         rawname = pstrdup(newval);
35
36         if (!SplitIdentifierString(rawname, ',', &namelist))
37                 goto cleanup;
38
39         if (doit) 
40         {
41                 nOids = list_length(namelist);
42                 newOids = malloc(sizeof(Oid) * (nOids+1));
43                 if (!newOids)
44                         elog(ERROR,"could not allocate %d bytes", sizeof(Oid) * (nOids+1));
45         }
46
47         foreach(l, namelist)
48         {
49                 char            *curname = (char *) lfirst(l);
50                 Oid                     indexOid = RangeVarGetRelid(makeRangeVarFromNameList(stringToQualifiedNameList(curname)), true);
51
52                 if (indexOid == InvalidOid)
53                 {
54                         elog(WARNING,"'%s' does not exist", curname);
55                         continue;
56                 }
57                 else if ( get_rel_relkind(indexOid) != RELKIND_INDEX )
58                 {
59                         elog(WARNING,"'%s' is not an index", curname);
60                         continue;
61                 }
62                 else if (doit)
63                 {
64                         newOids[i++] = indexOid;
65                 }
66         }
67
68         if (doit) 
69         {
70                 nIndexesOut = nOids;
71                 indexesOut = newOids;
72         }
73
74         pfree(rawname);
75         list_free(namelist);
76
77         return newval;
78
79 cleanup:
80         if (newOids)
81                 free(newOids);
82         pfree(rawname);
83         list_free(namelist);
84         return NULL;
85 }
86
87
88 static void
89 indexFilter(PlannerInfo *root, Oid relationObjectId, bool inhparent, RelOptInfo *rel) {
90         int i;
91
92         for(i=0;i<nIndexesOut;i++)
93         {
94                 ListCell   *l;
95
96                 foreach(l, rel->indexlist)
97                 {
98                         IndexOptInfo    *info = (IndexOptInfo*)lfirst(l);
99
100                         if (indexesOut[i] == info->indexoid)
101                         {
102                                 rel->indexlist = list_delete_ptr(rel->indexlist, info);
103                                 break;
104                         }
105                 }
106         }
107
108         /*
109          * Call next hook if it exists 
110          */
111         if (prevHook)
112                 prevHook(root, relationObjectId, inhparent, rel);
113 }
114
115 static const char*
116 IndexFilterShow(void) 
117 {
118         char    *val, *ptr;
119         int     i,
120                         len;
121
122         len = 1 /* \0 */ + nIndexesOut * (2 * NAMEDATALEN + 2 /* ', ' */ + 1 /* . */);
123         ptr = val = palloc(len);
124
125         *ptr ='\0';
126         for(i=0; i<nIndexesOut; i++)
127         {
128                 char    *relname = get_rel_name(indexesOut[i]);
129                 Oid     nspOid = get_rel_namespace(indexesOut[i]);
130                 char    *nspname = get_namespace_name(nspOid); 
131
132                 if ( relname == NULL || nspOid == InvalidOid || nspname == NULL )
133                         continue;
134
135                 ptr += snprintf(ptr, len - (ptr - val), "%s%s.%s",
136                                                                                                 (i==0) ? "" : ", ",
137                                                                                                 nspname,
138                                                                                                 relname);
139         }
140
141         return val;
142 }
143
144 void _PG_init(void);
145 void
146 _PG_init(void) 
147 {
148     DefineCustomStringVariable(
149                 "plantuner.forbid_index",
150                 "List of forbidden indexes",
151                 "Listed indexes will not be used in queries",
152                 &indexesOutStr,
153                 "",
154                 PGC_USERSET,
155                 0,
156                 indexesOutAssign,
157                 IndexFilterShow
158         );
159
160         if (get_relation_info_hook != indexFilter )
161         {
162                 prevHook = get_relation_info_hook;
163                 get_relation_info_hook = indexFilter;
164         }
165 }