forked from HypoPG/hypopg
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hypopg_import_index.c
305 lines (278 loc) · 8.78 KB
/
hypopg_import_index.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
/*-------------------------------------------------------------------------
*
* hypopg_import_index.c: Import of some PostgreSQL private fuctions, used for
* hypothetical index.
*
* This program is open source, licensed under the PostgreSQL license.
* For license terms, see the LICENSE file.
*
* Copyright (c) 2008-2023, PostgreSQL Global Development Group
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#if PG_VERSION_NUM >= 90300
#include "access/htup_details.h"
#endif
#include "catalog/heap.h"
#include "catalog/namespace.h"
#include "catalog/pg_opclass.h"
#include "commands/defrem.h"
#include "commands/vacuum.h"
#include "nodes/makefuncs.h"
#include "nodes/pg_list.h"
#include "optimizer/clauses.h"
#if PG_VERSION_NUM >= 120000
#include "optimizer/optimizer.h"
#endif
#include "optimizer/planner.h"
#include "optimizer/pathnode.h"
#if PG_VERSION_NUM >= 110000
#include "partitioning/partbounds.h"
#endif
#include "parser/parse_coerce.h"
#include "utils/builtins.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "include/hypopg.h"
/* Copied from src/backend/optimizer/util/plancat.c, not exported.
*
* Build a targetlist representing the columns of the specified index.
* Each column is represented by a Var for the corresponding base-relation
* column, or an expression in base-relation Vars, as appropriate.
*
* There are never any dropped columns in indexes, so unlike
* build_physical_tlist, we need no failure case.
*/
List *
build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
Relation heapRelation)
{
List *tlist = NIL;
Index varno = index->rel->relid;
ListCell *indexpr_item;
int i;
indexpr_item = list_head(index->indexprs);
for (i = 0; i < index->ncolumns; i++)
{
int indexkey = index->indexkeys[i];
Expr *indexvar;
if (indexkey != 0)
{
/* simple column */
const FormData_pg_attribute *att_tup;
if (indexkey < 0)
att_tup = SystemAttributeDefinition(indexkey
#if PG_VERSION_NUM < 120000
, heapRelation->rd_rel->relhasoids
#endif
);
else
#if PG_VERSION_NUM >= 110000
att_tup = TupleDescAttr(heapRelation->rd_att, indexkey - 1);
#else
att_tup = heapRelation->rd_att->attrs[indexkey - 1];
#endif
indexvar = (Expr *) makeVar(varno,
indexkey,
att_tup->atttypid,
att_tup->atttypmod,
att_tup->attcollation,
0);
}
else
{
/* expression column */
if (indexpr_item == NULL)
elog(ERROR, "wrong number of index expressions");
indexvar = (Expr *) lfirst(indexpr_item);
indexpr_item = lnext(index->indexprs, indexpr_item);
}
tlist = lappend(tlist,
makeTargetEntry(indexvar,
i + 1,
NULL,
false));
}
if (indexpr_item != NULL)
elog(ERROR, "wrong number of index expressions");
return tlist;
}
#if PG_VERSION_NUM < 100000
/*
* Copied from src/backend/commands/indexcmds.c, not exported.
* Resolve possibly-defaulted operator class specification
*/
Oid
GetIndexOpClass(List *opclass, Oid attrType,
char *accessMethodName, Oid accessMethodId)
{
char *schemaname;
char *opcname;
HeapTuple tuple;
Oid opClassId,
opInputType;
/*
* Release 7.0 removed network_ops, timespan_ops, and datetime_ops, so we
* ignore those opclass names so the default *_ops is used. This can be
* removed in some later release. bjm 2000/02/07
*
* Release 7.1 removes lztext_ops, so suppress that too for a while. tgl
* 2000/07/30
*
* Release 7.2 renames timestamp_ops to timestamptz_ops, so suppress that
* too for awhile. I'm starting to think we need a better approach. tgl
* 2000/10/01
*
* Release 8.0 removes bigbox_ops (which was dead code for a long while
* anyway). tgl 2003/11/11
*/
if (list_length(opclass) == 1)
{
char *claname = strVal(linitial(opclass));
if (strcmp(claname, "network_ops") == 0 ||
strcmp(claname, "timespan_ops") == 0 ||
strcmp(claname, "datetime_ops") == 0 ||
strcmp(claname, "lztext_ops") == 0 ||
strcmp(claname, "timestamp_ops") == 0 ||
strcmp(claname, "bigbox_ops") == 0)
opclass = NIL;
}
if (opclass == NIL)
{
/* no operator class specified, so find the default */
opClassId = GetDefaultOpClass(attrType, accessMethodId);
if (!OidIsValid(opClassId))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("data type %s has no default operator class for access method \"%s\"",
format_type_be(attrType), accessMethodName),
errhint("You must specify an operator class for the index or define a default operator class for the data type.")));
return opClassId;
}
/*
* Specific opclass name given, so look up the opclass.
*/
/* deconstruct the name list */
DeconstructQualifiedName(opclass, &schemaname, &opcname);
if (schemaname)
{
/* Look in specific schema only */
Oid namespaceId;
#if PG_VERSION_NUM >= 90300
namespaceId = LookupExplicitNamespace(schemaname, false);
#else
namespaceId = LookupExplicitNamespace(schemaname);
#endif
tuple = SearchSysCache3(CLAAMNAMENSP,
ObjectIdGetDatum(accessMethodId),
PointerGetDatum(opcname),
ObjectIdGetDatum(namespaceId));
}
else
{
/* Unqualified opclass name, so search the search path */
opClassId = OpclassnameGetOpcid(accessMethodId, opcname);
if (!OidIsValid(opClassId))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("operator class \"%s\" does not exist for access method \"%s\"",
opcname, accessMethodName)));
tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opClassId));
}
if (!HeapTupleIsValid(tuple))
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("operator class \"%s\" does not exist for access method \"%s\"",
NameListToString(opclass), accessMethodName)));
}
/*
* Verify that the index operator class accepts this datatype. Note we
* will accept binary compatibility.
*/
opClassId = HeapTupleGetOid(tuple);
opInputType = ((Form_pg_opclass) GETSTRUCT(tuple))->opcintype;
if (!IsBinaryCoercible(attrType, opInputType))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("operator class \"%s\" does not accept data type %s",
NameListToString(opclass), format_type_be(attrType))));
ReleaseSysCache(tuple);
return opClassId;
}
#endif
/*
* Copied from src/backend/commands/indexcmds.c, not exported.
* CheckPredicate
* Checks that the given partial-index predicate is valid.
*
* This used to also constrain the form of the predicate to forms that
* indxpath.c could do something with. However, that seems overly
* restrictive. One useful application of partial indexes is to apply
* a UNIQUE constraint across a subset of a table, and in that scenario
* any evaluatable predicate will work. So accept any predicate here
* (except ones requiring a plan), and let indxpath.c fend for itself.
*/
void
CheckPredicate(Expr *predicate)
{
/*
* transformExpr() should have already rejected subqueries, aggregates,
* and window functions, based on the EXPR_KIND_ for a predicate.
*/
/*
* A predicate using mutable functions is probably wrong, for the same
* reasons that we don't allow an index expression to use one.
*/
if (CheckMutability(predicate))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("functions in index predicate must be marked IMMUTABLE")));
}
/*
* Copied from src/backend/commands/indexcmds.c, not exported.
* CheckMutability
* Test whether given expression is mutable
*/
bool
CheckMutability(Expr *expr)
{
/*
* First run the expression through the planner. This has a couple of
* important consequences. First, function default arguments will get
* inserted, which may affect volatility (consider "default now()").
* Second, inline-able functions will get inlined, which may allow us to
* conclude that the function is really less volatile than it's marked. As
* an example, polymorphic functions must be marked with the most volatile
* behavior that they have for any input type, but once we inline the
* function we may be able to conclude that it's not so volatile for the
* particular input type we're dealing with.
*
* We assume here that expression_planner() won't scribble on its input.
*/
expr = expression_planner(expr);
/* Now we can search for non-immutable functions */
return contain_mutable_functions((Node *) expr);
}
#if PG_VERSION_NUM < 90500
/*
* Copied from src/backend/commands/amcmds.c
*
* get_am_name - given an access method OID name and type, look up its name.
*/
char *
get_am_name(Oid amOid)
{
HeapTuple tup;
char *result = NULL;
tup = SearchSysCache1(AMOID, ObjectIdGetDatum(amOid));
if (HeapTupleIsValid(tup))
{
Form_pg_am amform = (Form_pg_am) GETSTRUCT(tup);
result = pstrdup(NameStr(amform->amname));
ReleaseSysCache(tup);
}
return result;
}
#endif