Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * indexcmds.c
4 : : * POSTGRES define and remove index code.
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/commands/indexcmds.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : :
16 : : #include "postgres.h"
17 : :
18 : : #include "access/amapi.h"
19 : : #include "access/attmap.h"
20 : : #include "access/gist.h"
21 : : #include "access/heapam.h"
22 : : #include "access/htup_details.h"
23 : : #include "access/reloptions.h"
24 : : #include "access/sysattr.h"
25 : : #include "access/tableam.h"
26 : : #include "access/xact.h"
27 : : #include "catalog/catalog.h"
28 : : #include "catalog/index.h"
29 : : #include "catalog/indexing.h"
30 : : #include "catalog/namespace.h"
31 : : #include "catalog/pg_am.h"
32 : : #include "catalog/pg_authid.h"
33 : : #include "catalog/pg_collation.h"
34 : : #include "catalog/pg_constraint.h"
35 : : #include "catalog/pg_database.h"
36 : : #include "catalog/pg_inherits.h"
37 : : #include "catalog/pg_namespace.h"
38 : : #include "catalog/pg_opclass.h"
39 : : #include "catalog/pg_tablespace.h"
40 : : #include "catalog/pg_type.h"
41 : : #include "commands/comment.h"
42 : : #include "commands/defrem.h"
43 : : #include "commands/event_trigger.h"
44 : : #include "commands/progress.h"
45 : : #include "commands/tablecmds.h"
46 : : #include "commands/tablespace.h"
47 : : #include "mb/pg_wchar.h"
48 : : #include "miscadmin.h"
49 : : #include "nodes/makefuncs.h"
50 : : #include "nodes/nodeFuncs.h"
51 : : #include "optimizer/optimizer.h"
52 : : #include "parser/parse_coerce.h"
53 : : #include "parser/parse_oper.h"
54 : : #include "parser/parse_utilcmd.h"
55 : : #include "partitioning/partdesc.h"
56 : : #include "pgstat.h"
57 : : #include "rewrite/rewriteManip.h"
58 : : #include "storage/lmgr.h"
59 : : #include "storage/proc.h"
60 : : #include "storage/procarray.h"
61 : : #include "utils/acl.h"
62 : : #include "utils/builtins.h"
63 : : #include "utils/fmgroids.h"
64 : : #include "utils/guc.h"
65 : : #include "utils/injection_point.h"
66 : : #include "utils/inval.h"
67 : : #include "utils/lsyscache.h"
68 : : #include "utils/memutils.h"
69 : : #include "utils/partcache.h"
70 : : #include "utils/pg_rusage.h"
71 : : #include "utils/regproc.h"
72 : : #include "utils/snapmgr.h"
73 : : #include "utils/syscache.h"
74 : :
75 : :
76 : : /* context for ChooseIndexExpressionName_walker */
77 : : typedef struct CIEN_context
78 : : {
79 : : Relation rel; /* index's parent relation */
80 : : StringInfo buf; /* output string */
81 : : } CIEN_context;
82 : :
83 : : /* non-export function prototypes */
84 : : static bool CompareOpclassOptions(const Datum *opts1, const Datum *opts2, int natts);
85 : : static void CheckPredicate(Expr *predicate);
86 : : static void ComputeIndexAttrs(ParseState *pstate,
87 : : IndexInfo *indexInfo,
88 : : Oid *typeOids,
89 : : Oid *collationOids,
90 : : Oid *opclassOids,
91 : : Datum *opclassOptions,
92 : : int16 *colOptions,
93 : : const List *attList,
94 : : const List *exclusionOpNames,
95 : : Oid relId,
96 : : const char *accessMethodName,
97 : : Oid accessMethodId,
98 : : bool amcanorder,
99 : : bool isconstraint,
100 : : bool iswithoutoverlaps,
101 : : Oid ddl_userid,
102 : : int ddl_sec_context,
103 : : int *ddl_save_nestlevel);
104 : : static char *ChooseIndexName(const char *tabname, Oid namespaceId,
105 : : const List *colnames, const List *exclusionOpNames,
106 : : bool primary, bool isconstraint);
107 : : static char *ChooseIndexNameAddition(const List *colnames);
108 : : static List *ChooseIndexColumnNames(Relation rel, const List *indexElems);
109 : : static char *ChooseIndexExpressionName(Relation rel, Node *indexExpr);
110 : : static bool ChooseIndexExpressionName_walker(Node *node,
111 : : CIEN_context *context);
112 : : static void ReindexIndex(const ReindexStmt *stmt, const ReindexParams *params,
113 : : bool isTopLevel);
114 : : static void RangeVarCallbackForReindexIndex(const RangeVar *relation,
115 : : Oid relId, Oid oldRelId, void *arg);
116 : : static Oid ReindexTable(const ReindexStmt *stmt, const ReindexParams *params,
117 : : bool isTopLevel);
118 : : static void ReindexMultipleTables(const ReindexStmt *stmt,
119 : : const ReindexParams *params);
120 : : static void reindex_error_callback(void *arg);
121 : : static void ReindexPartitions(const ReindexStmt *stmt, Oid relid,
122 : : const ReindexParams *params, bool isTopLevel);
123 : : static void ReindexMultipleInternal(const ReindexStmt *stmt, const List *relids,
124 : : const ReindexParams *params);
125 : : static bool ReindexRelationConcurrently(const ReindexStmt *stmt,
126 : : Oid relationOid,
127 : : const ReindexParams *params);
128 : : static void update_relispartition(Oid relationId, bool newval);
129 : : static inline void set_indexsafe_procflags(void);
130 : :
131 : : /*
132 : : * callback argument type for RangeVarCallbackForReindexIndex()
133 : : */
134 : : struct ReindexIndexCallbackState
135 : : {
136 : : ReindexParams params; /* options from statement */
137 : : Oid locked_table_oid; /* tracks previously locked table */
138 : : };
139 : :
140 : : /*
141 : : * callback arguments for reindex_error_callback()
142 : : */
143 : : typedef struct ReindexErrorInfo
144 : : {
145 : : char *relname;
146 : : char *relnamespace;
147 : : char relkind;
148 : : } ReindexErrorInfo;
149 : :
150 : : /*
151 : : * CheckIndexCompatible
152 : : * Determine whether an existing index definition is compatible with a
153 : : * prospective index definition, such that the existing index storage
154 : : * could become the storage of the new index, avoiding a rebuild.
155 : : *
156 : : * 'oldId': the OID of the existing index
157 : : * 'accessMethodName': name of the AM to use.
158 : : * 'attributeList': a list of IndexElem specifying columns and expressions
159 : : * to index on.
160 : : * 'exclusionOpNames': list of names of exclusion-constraint operators,
161 : : * or NIL if not an exclusion constraint.
162 : : * 'isWithoutOverlaps': true iff this index has a WITHOUT OVERLAPS clause.
163 : : *
164 : : * This is tailored to the needs of ALTER TABLE ALTER TYPE, which recreates
165 : : * any indexes that depended on a changing column from their pg_get_indexdef
166 : : * or pg_get_constraintdef definitions. We omit some of the sanity checks of
167 : : * DefineIndex. We assume that the old and new indexes have the same number
168 : : * of columns and that if one has an expression column or predicate, both do.
169 : : * Errors arising from the attribute list still apply.
170 : : *
171 : : * Most column type changes that can skip a table rewrite do not invalidate
172 : : * indexes. We acknowledge this when all operator classes, collations and
173 : : * exclusion operators match. Though we could further permit intra-opfamily
174 : : * changes for btree and hash indexes, that adds subtle complexity with no
175 : : * concrete benefit for core types. Note, that INCLUDE columns aren't
176 : : * checked by this function, for them it's enough that table rewrite is
177 : : * skipped.
178 : : *
179 : : * When a comparison or exclusion operator has a polymorphic input type, the
180 : : * actual input types must also match. This defends against the possibility
181 : : * that operators could vary behavior in response to get_fn_expr_argtype().
182 : : * At present, this hazard is theoretical: check_exclusion_constraint() and
183 : : * all core index access methods decline to set fn_expr for such calls.
184 : : *
185 : : * We do not yet implement a test to verify compatibility of expression
186 : : * columns or predicates, so assume any such index is incompatible.
187 : : */
188 : : bool
189 : 73 : CheckIndexCompatible(Oid oldId,
190 : : const char *accessMethodName,
191 : : const List *attributeList,
192 : : const List *exclusionOpNames,
193 : : bool isWithoutOverlaps)
194 : : {
195 : : bool isconstraint;
196 : : Oid *typeIds;
197 : : Oid *collationIds;
198 : : Oid *opclassIds;
199 : : Datum *opclassOptions;
200 : : Oid accessMethodId;
201 : : Oid relationId;
202 : : HeapTuple tuple;
203 : : Form_pg_index indexForm;
204 : : Form_pg_am accessMethodForm;
205 : : const IndexAmRoutine *amRoutine;
206 : : bool amcanorder;
207 : : bool amsummarizing;
208 : : int16 *coloptions;
209 : : IndexInfo *indexInfo;
210 : : int numberOfAttributes;
211 : : int old_natts;
212 : 73 : bool ret = true;
213 : : oidvector *old_indclass;
214 : : oidvector *old_indcollation;
215 : : Relation irel;
216 : : int i;
217 : : Datum d;
218 : :
219 : : /* Caller should already have the relation locked in some way. */
220 : 73 : relationId = IndexGetRelation(oldId, false);
221 : :
222 : : /*
223 : : * We can pretend isconstraint = false unconditionally. It only serves to
224 : : * decide the text of an error message that should never happen for us.
225 : : */
226 : 73 : isconstraint = false;
227 : :
228 : 73 : numberOfAttributes = list_length(attributeList);
229 : : Assert(numberOfAttributes > 0);
230 : : Assert(numberOfAttributes <= INDEX_MAX_KEYS);
231 : :
232 : : /* look up the access method */
233 : 73 : tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
234 [ - + ]: 73 : if (!HeapTupleIsValid(tuple))
235 [ # # ]: 0 : ereport(ERROR,
236 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
237 : : errmsg("access method \"%s\" does not exist",
238 : : accessMethodName)));
239 : 73 : accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
240 : 73 : accessMethodId = accessMethodForm->oid;
241 : 73 : amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);
242 : 73 : ReleaseSysCache(tuple);
243 : :
244 : 73 : amcanorder = amRoutine->amcanorder;
245 : 73 : amsummarizing = amRoutine->amsummarizing;
246 : :
247 : : /*
248 : : * Compute the operator classes, collations, and exclusion operators for
249 : : * the new index, so we can test whether it's compatible with the existing
250 : : * one. Note that ComputeIndexAttrs might fail here, but that's OK:
251 : : * DefineIndex would have failed later. Our attributeList contains only
252 : : * key attributes, thus we're filling ii_NumIndexAttrs and
253 : : * ii_NumIndexKeyAttrs with same value.
254 : : */
255 : 73 : indexInfo = makeIndexInfo(numberOfAttributes, numberOfAttributes,
256 : : accessMethodId, NIL, NIL, false, false,
257 : : false, false, amsummarizing, isWithoutOverlaps);
258 : 73 : typeIds = palloc_array(Oid, numberOfAttributes);
259 : 73 : collationIds = palloc_array(Oid, numberOfAttributes);
260 : 73 : opclassIds = palloc_array(Oid, numberOfAttributes);
261 : 73 : opclassOptions = palloc_array(Datum, numberOfAttributes);
262 : 73 : coloptions = palloc_array(int16, numberOfAttributes);
263 : 73 : ComputeIndexAttrs(NULL, indexInfo,
264 : : typeIds, collationIds, opclassIds, opclassOptions,
265 : : coloptions, attributeList,
266 : : exclusionOpNames, relationId,
267 : : accessMethodName, accessMethodId,
268 : : amcanorder, isconstraint, isWithoutOverlaps, InvalidOid,
269 : : 0, NULL);
270 : :
271 : : /* Get the soon-obsolete pg_index tuple. */
272 : 73 : tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oldId));
273 [ - + ]: 73 : if (!HeapTupleIsValid(tuple))
274 [ # # ]: 0 : elog(ERROR, "cache lookup failed for index %u", oldId);
275 : 73 : indexForm = (Form_pg_index) GETSTRUCT(tuple);
276 : :
277 : : /*
278 : : * We don't assess expressions or predicates; assume incompatibility.
279 : : * Also, if the index is invalid for any reason, treat it as incompatible.
280 : : */
281 [ + - + + ]: 146 : if (!(heap_attisnull(tuple, Anum_pg_index_indpred, NULL) &&
282 : 73 : heap_attisnull(tuple, Anum_pg_index_indexprs, NULL) &&
283 [ - + ]: 69 : indexForm->indisvalid))
284 : : {
285 : 4 : ReleaseSysCache(tuple);
286 : 4 : return false;
287 : : }
288 : :
289 : : /* Any change in operator class or collation breaks compatibility. */
290 : 69 : old_natts = indexForm->indnkeyatts;
291 : : Assert(old_natts == numberOfAttributes);
292 : :
293 : 69 : d = SysCacheGetAttrNotNull(INDEXRELID, tuple, Anum_pg_index_indcollation);
294 : 69 : old_indcollation = (oidvector *) DatumGetPointer(d);
295 : :
296 : 69 : d = SysCacheGetAttrNotNull(INDEXRELID, tuple, Anum_pg_index_indclass);
297 : 69 : old_indclass = (oidvector *) DatumGetPointer(d);
298 : :
299 [ + - ]: 138 : ret = (memcmp(old_indclass->values, opclassIds, old_natts * sizeof(Oid)) == 0 &&
300 [ + - ]: 69 : memcmp(old_indcollation->values, collationIds, old_natts * sizeof(Oid)) == 0);
301 : :
302 : 69 : ReleaseSysCache(tuple);
303 : :
304 [ - + ]: 69 : if (!ret)
305 : 0 : return false;
306 : :
307 : : /* For polymorphic opcintype, column type changes break compatibility. */
308 : 69 : irel = index_open(oldId, AccessShareLock); /* caller probably has a lock */
309 [ + + ]: 142 : for (i = 0; i < old_natts; i++)
310 : : {
311 [ + - + - : 73 : if (IsPolymorphicType(get_opclass_input_type(opclassIds[i])) &&
+ - + - +
- + - + -
+ - + - +
- - + ]
312 [ # # ]: 0 : TupleDescAttr(irel->rd_att, i)->atttypid != typeIds[i])
313 : : {
314 : 0 : ret = false;
315 : 0 : break;
316 : : }
317 : : }
318 : :
319 : : /* Any change in opclass options break compatibility. */
320 [ + - ]: 69 : if (ret)
321 : : {
322 : 69 : Datum *oldOpclassOptions = palloc_array(Datum, old_natts);
323 : :
324 [ + + ]: 142 : for (i = 0; i < old_natts; i++)
325 : 73 : oldOpclassOptions[i] = get_attoptions(oldId, i + 1);
326 : :
327 : 69 : ret = CompareOpclassOptions(oldOpclassOptions, opclassOptions, old_natts);
328 : :
329 : 69 : pfree(oldOpclassOptions);
330 : : }
331 : :
332 : : /* Any change in exclusion operator selections breaks compatibility. */
333 [ + - - + ]: 69 : if (ret && indexInfo->ii_ExclusionOps != NULL)
334 : : {
335 : : Oid *old_operators,
336 : : *old_procs;
337 : : uint16 *old_strats;
338 : :
339 : 0 : RelationGetExclusionInfo(irel, &old_operators, &old_procs, &old_strats);
340 : 0 : ret = memcmp(old_operators, indexInfo->ii_ExclusionOps,
341 : : old_natts * sizeof(Oid)) == 0;
342 : :
343 : : /* Require an exact input type match for polymorphic operators. */
344 [ # # ]: 0 : if (ret)
345 : : {
346 [ # # # # ]: 0 : for (i = 0; i < old_natts && ret; i++)
347 : : {
348 : : Oid left,
349 : : right;
350 : :
351 : 0 : op_input_types(indexInfo->ii_ExclusionOps[i], &left, &right);
352 [ # # # # : 0 : if ((IsPolymorphicType(left) || IsPolymorphicType(right)) &&
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
353 [ # # ]: 0 : TupleDescAttr(irel->rd_att, i)->atttypid != typeIds[i])
354 : : {
355 : 0 : ret = false;
356 : 0 : break;
357 : : }
358 : : }
359 : : }
360 : : }
361 : :
362 : 69 : index_close(irel, NoLock);
363 : 69 : return ret;
364 : : }
365 : :
366 : : /*
367 : : * CompareOpclassOptions
368 : : *
369 : : * Compare per-column opclass options which are represented by arrays of text[]
370 : : * datums. Both elements of arrays and array themselves can be NULL.
371 : : */
372 : : static bool
373 : 69 : CompareOpclassOptions(const Datum *opts1, const Datum *opts2, int natts)
374 : : {
375 : : int i;
376 : : FmgrInfo fm;
377 : :
378 [ - + - - ]: 69 : if (!opts1 && !opts2)
379 : 0 : return true;
380 : :
381 : 69 : fmgr_info(F_ARRAY_EQ, &fm);
382 [ + + ]: 142 : for (i = 0; i < natts; i++)
383 : : {
384 [ + - ]: 73 : Datum opt1 = opts1 ? opts1[i] : (Datum) 0;
385 [ + - ]: 73 : Datum opt2 = opts2 ? opts2[i] : (Datum) 0;
386 : :
387 [ + + ]: 73 : if (opt1 == (Datum) 0)
388 : : {
389 [ + - ]: 72 : if (opt2 == (Datum) 0)
390 : 72 : continue;
391 : : else
392 : 0 : return false;
393 : : }
394 [ - + ]: 1 : else if (opt2 == (Datum) 0)
395 : 0 : return false;
396 : :
397 : : /*
398 : : * Compare non-NULL text[] datums. Use C collation to enforce binary
399 : : * equivalence of texts, because we don't know anything about the
400 : : * semantics of opclass options.
401 : : */
402 [ - + ]: 1 : if (!DatumGetBool(FunctionCall2Coll(&fm, C_COLLATION_OID, opt1, opt2)))
403 : 0 : return false;
404 : : }
405 : :
406 : 69 : return true;
407 : : }
408 : :
409 : : /*
410 : : * WaitForOlderSnapshots
411 : : *
412 : : * Wait for transactions that might have an older snapshot than the given xmin
413 : : * limit, because it might not contain tuples deleted just before it has
414 : : * been taken. Obtain a list of VXIDs of such transactions, and wait for them
415 : : * individually. This is used when building an index concurrently.
416 : : *
417 : : * We can exclude any running transactions that have xmin > the xmin given;
418 : : * their oldest snapshot must be newer than our xmin limit.
419 : : * We can also exclude any transactions that have xmin = zero, since they
420 : : * evidently have no live snapshot at all (and any one they might be in
421 : : * process of taking is certainly newer than ours). Transactions in other
422 : : * DBs can be ignored too, since they'll never even be able to see the
423 : : * index being worked on.
424 : : *
425 : : * We can also exclude autovacuum processes and processes running manual
426 : : * lazy VACUUMs, because they won't be fazed by missing index entries
427 : : * either. (Manual ANALYZEs, however, can't be excluded because they
428 : : * might be within transactions that are going to do arbitrary operations
429 : : * later.) Processes running CREATE INDEX CONCURRENTLY or REINDEX CONCURRENTLY
430 : : * on indexes that are neither expressional nor partial are also safe to
431 : : * ignore, since we know that those processes won't examine any data
432 : : * outside the table they're indexing.
433 : : *
434 : : * Also, GetCurrentVirtualXIDs never reports our own vxid, so we need not
435 : : * check for that.
436 : : *
437 : : * If a process goes idle-in-transaction with xmin zero, we do not need to
438 : : * wait for it anymore, per the above argument. We do not have the
439 : : * infrastructure right now to stop waiting if that happens, but we can at
440 : : * least avoid the folly of waiting when it is idle at the time we would
441 : : * begin to wait. We do this by repeatedly rechecking the output of
442 : : * GetCurrentVirtualXIDs. If, during any iteration, a particular vxid
443 : : * doesn't show up in the output, we know we can forget about it.
444 : : */
445 : : void
446 : 427 : WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
447 : : {
448 : : int n_old_snapshots;
449 : : int i;
450 : : VirtualTransactionId *old_snapshots;
451 : :
452 : 427 : old_snapshots = GetCurrentVirtualXIDs(limitXmin, true, false,
453 : : PROC_IS_AUTOVACUUM | PROC_IN_VACUUM
454 : : | PROC_IN_SAFE_IC,
455 : : &n_old_snapshots);
456 [ + + ]: 427 : if (progress)
457 : 420 : pgstat_progress_update_param(PROGRESS_WAITFOR_TOTAL, n_old_snapshots);
458 : :
459 [ + + ]: 634 : for (i = 0; i < n_old_snapshots; i++)
460 : : {
461 [ + + ]: 207 : if (!VirtualTransactionIdIsValid(old_snapshots[i]))
462 : 57 : continue; /* found uninteresting in previous cycle */
463 : :
464 [ + + ]: 150 : if (i > 0)
465 : : {
466 : : /* see if anything's changed ... */
467 : : VirtualTransactionId *newer_snapshots;
468 : : int n_newer_snapshots;
469 : : int j;
470 : : int k;
471 : :
472 : 73 : newer_snapshots = GetCurrentVirtualXIDs(limitXmin,
473 : : true, false,
474 : : PROC_IS_AUTOVACUUM | PROC_IN_VACUUM
475 : : | PROC_IN_SAFE_IC,
476 : : &n_newer_snapshots);
477 [ + + ]: 285 : for (j = i; j < n_old_snapshots; j++)
478 : : {
479 [ + + ]: 212 : if (!VirtualTransactionIdIsValid(old_snapshots[j]))
480 : 25 : continue; /* found uninteresting in previous cycle */
481 [ + + ]: 644 : for (k = 0; k < n_newer_snapshots; k++)
482 : : {
483 [ + + + + ]: 552 : if (VirtualTransactionIdEquals(old_snapshots[j],
484 : : newer_snapshots[k]))
485 : 95 : break;
486 : : }
487 [ + + ]: 187 : if (k >= n_newer_snapshots) /* not there anymore */
488 : 92 : SetInvalidVirtualTransactionId(old_snapshots[j]);
489 : : }
490 : 73 : pfree(newer_snapshots);
491 : : }
492 : :
493 [ + + ]: 150 : if (VirtualTransactionIdIsValid(old_snapshots[i]))
494 : : {
495 : : /* If requested, publish who we're going to wait for. */
496 [ + - ]: 115 : if (progress)
497 : : {
498 : 115 : PGPROC *holder = ProcNumberGetProc(old_snapshots[i].procNumber);
499 : :
500 [ + - ]: 115 : if (holder)
501 : 115 : pgstat_progress_update_param(PROGRESS_WAITFOR_CURRENT_PID,
502 : 115 : holder->pid);
503 : : }
504 : 115 : VirtualXactLock(old_snapshots[i], true);
505 : : }
506 : :
507 [ + - ]: 150 : if (progress)
508 : 150 : pgstat_progress_update_param(PROGRESS_WAITFOR_DONE, i + 1);
509 : : }
510 : 427 : }
511 : :
512 : :
513 : : /*
514 : : * DefineIndex
515 : : * Creates a new index.
516 : : *
517 : : * This function manages the current userid according to the needs of pg_dump.
518 : : * Recreating old-database catalog entries in new-database is fine, regardless
519 : : * of which users would have permission to recreate those entries now. That's
520 : : * just preservation of state. Running opaque expressions, like calling a
521 : : * function named in a catalog entry or evaluating a pg_node_tree in a catalog
522 : : * entry, as anyone other than the object owner, is not fine. To adhere to
523 : : * those principles and to remain fail-safe, use the table owner userid for
524 : : * most ACL checks. Use the original userid for ACL checks reached without
525 : : * traversing opaque expressions. (pg_dump can predict such ACL checks from
526 : : * catalogs.) Overall, this is a mess. Future DDL development should
527 : : * consider offering one DDL command for catalog setup and a separate DDL
528 : : * command for steps that run opaque expressions.
529 : : *
530 : : * 'pstate': ParseState struct (used only for error reports; pass NULL if
531 : : * not available)
532 : : * 'tableId': the OID of the table relation on which the index is to be
533 : : * created
534 : : * 'stmt': IndexStmt describing the properties of the new index.
535 : : * 'indexRelationId': normally InvalidOid, but during bootstrap can be
536 : : * nonzero to specify a preselected OID for the index.
537 : : * 'parentIndexId': the OID of the parent index; InvalidOid if not the child
538 : : * of a partitioned index.
539 : : * 'parentConstraintId': the OID of the parent constraint; InvalidOid if not
540 : : * the child of a constraint (only used when recursing)
541 : : * 'total_parts': total number of direct and indirect partitions of relation;
542 : : * pass -1 if not known or rel is not partitioned.
543 : : * 'is_alter_table': this is due to an ALTER rather than a CREATE operation.
544 : : * 'check_rights': check for CREATE rights in namespace and tablespace. (This
545 : : * should be true except when ALTER is deleting/recreating an index.)
546 : : * 'check_not_in_use': check for table not already in use in current session.
547 : : * This should be true unless caller is holding the table open, in which
548 : : * case the caller had better have checked it earlier.
549 : : * 'skip_build': make the catalog entries but don't create the index files
550 : : * 'quiet': suppress the NOTICE chatter ordinarily provided for constraints.
551 : : *
552 : : * Returns the object address of the created index.
553 : : */
554 : : ObjectAddress
555 : 20059 : DefineIndex(ParseState *pstate,
556 : : Oid tableId,
557 : : const IndexStmt *stmt,
558 : : Oid indexRelationId,
559 : : Oid parentIndexId,
560 : : Oid parentConstraintId,
561 : : int total_parts,
562 : : bool is_alter_table,
563 : : bool check_rights,
564 : : bool check_not_in_use,
565 : : bool skip_build,
566 : : bool quiet)
567 : : {
568 : : bool concurrent;
569 : : char *indexRelationName;
570 : : char *accessMethodName;
571 : : Oid *typeIds;
572 : : Oid *collationIds;
573 : : Oid *opclassIds;
574 : : Datum *opclassOptions;
575 : : Oid accessMethodId;
576 : : Oid namespaceId;
577 : : Oid tablespaceId;
578 : 20059 : Oid createdConstraintId = InvalidOid;
579 : : List *indexColNames;
580 : : List *allIndexParams;
581 : : Relation rel;
582 : : HeapTuple tuple;
583 : : Form_pg_am accessMethodForm;
584 : : const IndexAmRoutine *amRoutine;
585 : : bool amcanorder;
586 : : bool amissummarizing;
587 : : amoptions_function amoptions;
588 : : bool exclusion;
589 : : bool partitioned;
590 : : bool safe_index;
591 : : Datum reloptions;
592 : : int16 *coloptions;
593 : : IndexInfo *indexInfo;
594 : : uint16 flags;
595 : : uint16 constr_flags;
596 : : int numberOfAttributes;
597 : : int numberOfKeyAttributes;
598 : : TransactionId limitXmin;
599 : : ObjectAddress address;
600 : : LockRelId heaprelid;
601 : : LOCKTAG heaplocktag;
602 : : LOCKMODE lockmode;
603 : : Snapshot snapshot;
604 : : Oid root_save_userid;
605 : : int root_save_sec_context;
606 : : int root_save_nestlevel;
607 : :
608 : 20059 : root_save_nestlevel = NewGUCNestLevel();
609 : :
610 : 20059 : RestrictSearchPath();
611 : :
612 : : /*
613 : : * Some callers need us to run with an empty default_tablespace; this is a
614 : : * necessary hack to be able to reproduce catalog state accurately when
615 : : * recreating indexes after table-rewriting ALTER TABLE.
616 : : */
617 [ + + ]: 20059 : if (stmt->reset_default_tblspc)
618 : 306 : (void) set_config_option("default_tablespace", "",
619 : : PGC_USERSET, PGC_S_SESSION,
620 : : GUC_ACTION_SAVE, true, 0, false);
621 : :
622 : : /*
623 : : * Force non-concurrent build on temporary relations, even if CONCURRENTLY
624 : : * was requested. Other backends can't access a temporary relation, so
625 : : * there's no harm in grabbing a stronger lock, and a non-concurrent DROP
626 : : * is more efficient. Do this before any use of the concurrent option is
627 : : * done.
628 : : */
629 [ + + + + ]: 20059 : if (stmt->concurrent && get_rel_persistence(tableId) != RELPERSISTENCE_TEMP)
630 : 135 : concurrent = true;
631 : : else
632 : 19924 : concurrent = false;
633 : :
634 : : /*
635 : : * Start progress report. If we're building a partition, this was already
636 : : * done.
637 : : */
638 [ + + ]: 20059 : if (!OidIsValid(parentIndexId))
639 : : {
640 : 17968 : pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, tableId);
641 [ + + ]: 17968 : pgstat_progress_update_param(PROGRESS_CREATEIDX_COMMAND,
642 : : concurrent ?
643 : : PROGRESS_CREATEIDX_COMMAND_CREATE_CONCURRENTLY :
644 : : PROGRESS_CREATEIDX_COMMAND_CREATE);
645 : : }
646 : :
647 : : /*
648 : : * No index OID to report yet
649 : : */
650 : 20059 : pgstat_progress_update_param(PROGRESS_CREATEIDX_INDEX_OID,
651 : : InvalidOid);
652 : :
653 : : /*
654 : : * count key attributes in index
655 : : */
656 : 20059 : numberOfKeyAttributes = list_length(stmt->indexParams);
657 : :
658 : : /*
659 : : * Calculate the new list of index columns including both key columns and
660 : : * INCLUDE columns. Later we can determine which of these are key
661 : : * columns, and which are just part of the INCLUDE list by checking the
662 : : * list position. A list item in a position less than ii_NumIndexKeyAttrs
663 : : * is part of the key columns, and anything equal to and over is part of
664 : : * the INCLUDE columns.
665 : : */
666 : 20059 : allIndexParams = list_concat_copy(stmt->indexParams,
667 : 20059 : stmt->indexIncludingParams);
668 : 20059 : numberOfAttributes = list_length(allIndexParams);
669 : :
670 [ - + ]: 20059 : if (numberOfKeyAttributes <= 0)
671 [ # # ]: 0 : ereport(ERROR,
672 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
673 : : errmsg("must specify at least one column")));
674 [ - + ]: 20059 : if (numberOfAttributes > INDEX_MAX_KEYS)
675 [ # # ]: 0 : ereport(ERROR,
676 : : (errcode(ERRCODE_TOO_MANY_COLUMNS),
677 : : errmsg("cannot use more than %d columns in an index",
678 : : INDEX_MAX_KEYS)));
679 : :
680 : : /*
681 : : * Only SELECT ... FOR UPDATE/SHARE are allowed while doing a standard
682 : : * index build; but for concurrent builds we allow INSERT/UPDATE/DELETE
683 : : * (but not VACUUM).
684 : : *
685 : : * NB: Caller is responsible for making sure that tableId refers to the
686 : : * relation on which the index should be built; except in bootstrap mode,
687 : : * this will typically require the caller to have already locked the
688 : : * relation. To avoid lock upgrade hazards, that lock should be at least
689 : : * as strong as the one we take here.
690 : : *
691 : : * NB: If the lock strength here ever changes, code that is run by
692 : : * parallel workers under the control of certain particular ambuild
693 : : * functions will need to be updated, too.
694 : : */
695 [ + + ]: 20059 : lockmode = concurrent ? ShareUpdateExclusiveLock : ShareLock;
696 : 20059 : rel = table_open(tableId, lockmode);
697 : :
698 : : /*
699 : : * Switch to the table owner's userid, so that any index functions are run
700 : : * as that user. Also lock down security-restricted operations. We
701 : : * already arranged to make GUC variable changes local to this command.
702 : : */
703 : 20059 : GetUserIdAndSecContext(&root_save_userid, &root_save_sec_context);
704 : 20059 : SetUserIdAndSecContext(rel->rd_rel->relowner,
705 : : root_save_sec_context | SECURITY_RESTRICTED_OPERATION);
706 : :
707 : 20059 : namespaceId = RelationGetNamespace(rel);
708 : :
709 : : /*
710 : : * It has exclusion constraint behavior if it's an EXCLUDE constraint or a
711 : : * temporal PRIMARY KEY/UNIQUE constraint
712 : : */
713 [ + + + + ]: 20059 : exclusion = stmt->excludeOpNames || stmt->iswithoutoverlaps;
714 : :
715 : : /* Ensure that it makes sense to index this kind of relation */
716 [ + + ]: 20059 : switch (rel->rd_rel->relkind)
717 : : {
718 : 20055 : case RELKIND_RELATION:
719 : : case RELKIND_MATVIEW:
720 : : case RELKIND_PARTITIONED_TABLE:
721 : : /* OK */
722 : 20055 : break;
723 : 4 : default:
724 [ + - ]: 4 : ereport(ERROR,
725 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
726 : : errmsg("cannot create index on relation \"%s\"",
727 : : RelationGetRelationName(rel)),
728 : : errdetail_relkind_not_supported(rel->rd_rel->relkind)));
729 : : break;
730 : : }
731 : :
732 : : /*
733 : : * Establish behavior for partitioned tables, and verify sanity of
734 : : * parameters.
735 : : *
736 : : * We do not build an actual index in this case; we only create a few
737 : : * catalog entries. The actual indexes are built by recursing for each
738 : : * partition.
739 : : */
740 : 20055 : partitioned = rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
741 [ + + ]: 20055 : if (partitioned)
742 : : {
743 : : /*
744 : : * Note: we check 'stmt->concurrent' rather than 'concurrent', so that
745 : : * the error is thrown also for temporary tables. Seems better to be
746 : : * consistent, even though we could do it on temporary table because
747 : : * we're not actually doing it concurrently.
748 : : */
749 [ + + ]: 1488 : if (stmt->concurrent)
750 [ + - ]: 4 : ereport(ERROR,
751 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
752 : : errmsg("cannot create index on partitioned table \"%s\" concurrently",
753 : : RelationGetRelationName(rel))));
754 : : }
755 : :
756 : : /*
757 : : * Don't try to CREATE INDEX on temp tables of other backends.
758 : : */
759 [ + + - + ]: 20051 : if (RELATION_IS_OTHER_TEMP(rel))
760 [ # # ]: 0 : ereport(ERROR,
761 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
762 : : errmsg("cannot create indexes on temporary tables of other sessions")));
763 : :
764 : : /*
765 : : * Unless our caller vouches for having checked this already, insist that
766 : : * the table not be in use by our own session, either. Otherwise we might
767 : : * fail to make entries in the new index (for instance, if an INSERT or
768 : : * UPDATE is in progress and has already made its list of target indexes).
769 : : */
770 [ + + ]: 20051 : if (check_not_in_use)
771 : 9749 : CheckTableNotInUse(rel, "CREATE INDEX");
772 : :
773 : : /*
774 : : * Verify we (still) have CREATE rights in the rel's namespace.
775 : : * (Presumably we did when the rel was created, but maybe not anymore.)
776 : : * Skip check if caller doesn't want it. Also skip check if
777 : : * bootstrapping, since permissions machinery may not be working yet.
778 : : */
779 [ + + + - ]: 20047 : if (check_rights && !IsBootstrapProcessingMode())
780 : : {
781 : : AclResult aclresult;
782 : :
783 : 10533 : aclresult = object_aclcheck(NamespaceRelationId, namespaceId, root_save_userid,
784 : : ACL_CREATE);
785 [ - + ]: 10533 : if (aclresult != ACLCHECK_OK)
786 : 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
787 : 0 : get_namespace_name(namespaceId));
788 : : }
789 : :
790 : : /*
791 : : * Select tablespace to use. If not specified, use default tablespace
792 : : * (which may in turn default to database's default).
793 : : */
794 [ + + ]: 20047 : if (stmt->tableSpace)
795 : : {
796 : 160 : tablespaceId = get_tablespace_oid(stmt->tableSpace, false);
797 [ + + + + ]: 160 : if (partitioned && tablespaceId == MyDatabaseTableSpace)
798 [ + - ]: 4 : ereport(ERROR,
799 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
800 : : errmsg("cannot specify default tablespace for partitioned relations")));
801 : : }
802 : : else
803 : : {
804 : 19887 : tablespaceId = GetDefaultTablespace(rel->rd_rel->relpersistence,
805 : : partitioned);
806 : : /* note InvalidOid is OK in this case */
807 : : }
808 : :
809 : : /* Check tablespace permissions */
810 [ + + + + ]: 20039 : if (check_rights &&
811 [ + - ]: 73 : OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
812 : : {
813 : : AclResult aclresult;
814 : :
815 : 73 : aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, root_save_userid,
816 : : ACL_CREATE);
817 [ - + ]: 73 : if (aclresult != ACLCHECK_OK)
818 : 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
819 : 0 : get_tablespace_name(tablespaceId));
820 : : }
821 : :
822 : : /*
823 : : * Force shared indexes into the pg_global tablespace. This is a bit of a
824 : : * hack but seems simpler than marking them in the BKI commands. On the
825 : : * other hand, if it's not shared, don't allow it to be placed there.
826 : : */
827 [ + + ]: 20039 : if (rel->rd_rel->relisshared)
828 : 1197 : tablespaceId = GLOBALTABLESPACE_OID;
829 [ - + ]: 18842 : else if (tablespaceId == GLOBALTABLESPACE_OID)
830 [ # # ]: 0 : ereport(ERROR,
831 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
832 : : errmsg("only shared relations can be placed in pg_global tablespace")));
833 : :
834 : : /*
835 : : * Choose the index column names.
836 : : */
837 : 20039 : indexColNames = ChooseIndexColumnNames(rel, allIndexParams);
838 : :
839 : : /*
840 : : * Select name for index if caller didn't specify
841 : : */
842 : 20039 : indexRelationName = stmt->idxname;
843 [ + + ]: 20039 : if (indexRelationName == NULL)
844 : 8120 : indexRelationName = ChooseIndexName(RelationGetRelationName(rel),
845 : : namespaceId,
846 : : indexColNames,
847 : 8120 : stmt->excludeOpNames,
848 : 8120 : stmt->primary,
849 : 8120 : stmt->isconstraint);
850 : :
851 : : /*
852 : : * look up the access method, verify it can handle the requested features
853 : : */
854 : 20039 : accessMethodName = stmt->accessMethod;
855 : 20039 : tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
856 [ + + ]: 20039 : if (!HeapTupleIsValid(tuple))
857 : : {
858 : : /*
859 : : * Hack to provide more-or-less-transparent updating of old RTREE
860 : : * indexes to GiST: if RTREE is requested and not found, use GIST.
861 : : */
862 [ + - ]: 4 : if (strcmp(accessMethodName, "rtree") == 0)
863 : : {
864 [ + - ]: 4 : ereport(NOTICE,
865 : : (errmsg("substituting access method \"gist\" for obsolete method \"rtree\"")));
866 : 4 : accessMethodName = "gist";
867 : 4 : tuple = SearchSysCache1(AMNAME, PointerGetDatum(accessMethodName));
868 : : }
869 : :
870 [ - + ]: 4 : if (!HeapTupleIsValid(tuple))
871 [ # # ]: 0 : ereport(ERROR,
872 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
873 : : errmsg("access method \"%s\" does not exist",
874 : : accessMethodName)));
875 : : }
876 : 20039 : accessMethodForm = (Form_pg_am) GETSTRUCT(tuple);
877 : 20039 : accessMethodId = accessMethodForm->oid;
878 : 20039 : amRoutine = GetIndexAmRoutine(accessMethodForm->amhandler);
879 : :
880 : 20039 : pgstat_progress_update_param(PROGRESS_CREATEIDX_ACCESS_METHOD_OID,
881 : : accessMethodId);
882 : :
883 [ + + + + : 20039 : if (stmt->unique && !stmt->iswithoutoverlaps && !amRoutine->amcanunique)
- + ]
884 [ # # ]: 0 : ereport(ERROR,
885 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
886 : : errmsg("access method \"%s\" does not support unique indexes",
887 : : accessMethodName)));
888 [ + + + + ]: 20039 : if (stmt->indexIncludingParams != NIL && !amRoutine->amcaninclude)
889 [ + - ]: 12 : ereport(ERROR,
890 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
891 : : errmsg("access method \"%s\" does not support included columns",
892 : : accessMethodName)));
893 [ + + - + ]: 20027 : if (numberOfKeyAttributes > 1 && !amRoutine->amcanmulticol)
894 [ # # ]: 0 : ereport(ERROR,
895 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
896 : : errmsg("access method \"%s\" does not support multicolumn indexes",
897 : : accessMethodName)));
898 [ + + - + ]: 20027 : if (exclusion && amRoutine->amgettuple == NULL)
899 [ # # ]: 0 : ereport(ERROR,
900 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
901 : : errmsg("access method \"%s\" does not support exclusion constraints",
902 : : accessMethodName)));
903 [ + + - + ]: 20027 : if (stmt->iswithoutoverlaps && strcmp(accessMethodName, "gist") != 0)
904 [ # # ]: 0 : ereport(ERROR,
905 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
906 : : errmsg("access method \"%s\" does not support WITHOUT OVERLAPS constraints",
907 : : accessMethodName)));
908 : :
909 : 20027 : amcanorder = amRoutine->amcanorder;
910 : 20027 : amoptions = amRoutine->amoptions;
911 : 20027 : amissummarizing = amRoutine->amsummarizing;
912 : :
913 : 20027 : ReleaseSysCache(tuple);
914 : :
915 : : /*
916 : : * Validate predicate, if given
917 : : */
918 [ + + ]: 20027 : if (stmt->whereClause)
919 : 285 : CheckPredicate((Expr *) stmt->whereClause);
920 : :
921 : : /*
922 : : * Parse AM-specific options, convert to text array form, validate.
923 : : */
924 : 20027 : reloptions = transformRelOptions((Datum) 0, stmt->options,
925 : : NULL, NULL, false, false);
926 : :
927 : 20023 : (void) index_reloptions(amoptions, reloptions, true);
928 : :
929 : : /*
930 : : * Prepare arguments for index_create, primarily an IndexInfo structure.
931 : : * Note that predicates must be in implicit-AND format. In a concurrent
932 : : * build, mark it not-ready-for-inserts.
933 : : */
934 : 19981 : indexInfo = makeIndexInfo(numberOfAttributes,
935 : : numberOfKeyAttributes,
936 : : accessMethodId,
937 : : NIL, /* expressions, NIL for now */
938 : 19981 : make_ands_implicit((Expr *) stmt->whereClause),
939 : 19981 : stmt->unique,
940 : 19981 : stmt->nulls_not_distinct,
941 : : !concurrent,
942 : : concurrent,
943 : : amissummarizing,
944 : 19981 : stmt->iswithoutoverlaps);
945 : :
946 : 19981 : typeIds = palloc_array(Oid, numberOfAttributes);
947 : 19981 : collationIds = palloc_array(Oid, numberOfAttributes);
948 : 19981 : opclassIds = palloc_array(Oid, numberOfAttributes);
949 : 19981 : opclassOptions = palloc_array(Datum, numberOfAttributes);
950 : 19981 : coloptions = palloc_array(int16, numberOfAttributes);
951 : 19981 : ComputeIndexAttrs(pstate,
952 : : indexInfo,
953 : : typeIds, collationIds, opclassIds, opclassOptions,
954 : : coloptions, allIndexParams,
955 : 19981 : stmt->excludeOpNames, tableId,
956 : : accessMethodName, accessMethodId,
957 : 19981 : amcanorder, stmt->isconstraint, stmt->iswithoutoverlaps,
958 : : root_save_userid, root_save_sec_context,
959 : : &root_save_nestlevel);
960 : :
961 : : /*
962 : : * Extra checks when creating a PRIMARY KEY index.
963 : : */
964 [ + + ]: 19764 : if (stmt->primary)
965 : 5978 : index_check_primary_key(rel, indexInfo, is_alter_table, stmt);
966 : :
967 : : /*
968 : : * If this table is partitioned and we're creating a unique index, primary
969 : : * key, or exclusion constraint, make sure that the partition key is a
970 : : * subset of the index's columns. Otherwise it would be possible to
971 : : * violate uniqueness by putting values that ought to be unique in
972 : : * different partitions.
973 : : *
974 : : * We could lift this limitation if we had global indexes, but those have
975 : : * their own problems, so this is a useful feature combination.
976 : : */
977 [ + + + + : 19740 : if (partitioned && (stmt->unique || exclusion))
+ + ]
978 : : {
979 : 860 : PartitionKey key = RelationGetPartitionKey(rel);
980 : : const char *constraint_type;
981 : : int i;
982 : :
983 [ + + ]: 860 : if (stmt->primary)
984 : 628 : constraint_type = "PRIMARY KEY";
985 [ + + ]: 232 : else if (stmt->unique)
986 : 166 : constraint_type = "UNIQUE";
987 [ + - ]: 66 : else if (stmt->excludeOpNames)
988 : 66 : constraint_type = "EXCLUDE";
989 : : else
990 : : {
991 [ # # ]: 0 : elog(ERROR, "unknown constraint type");
992 : : constraint_type = NULL; /* keep compiler quiet */
993 : : }
994 : :
995 : : /*
996 : : * Verify that all the columns in the partition key appear in the
997 : : * unique key definition, with the same notion of equality.
998 : : */
999 [ + + ]: 1731 : for (i = 0; i < key->partnatts; i++)
1000 : : {
1001 : 940 : bool found = false;
1002 : : int eq_strategy;
1003 : : Oid ptkey_eqop;
1004 : : int j;
1005 : :
1006 : : /*
1007 : : * Identify the equality operator associated with this partkey
1008 : : * column. For list and range partitioning, partkeys use btree
1009 : : * operator classes; hash partitioning uses hash operator classes.
1010 : : * (Keep this in sync with ComputePartitionAttrs!)
1011 : : */
1012 [ + + ]: 940 : if (key->strategy == PARTITION_STRATEGY_HASH)
1013 : 42 : eq_strategy = HTEqualStrategyNumber;
1014 : : else
1015 : 898 : eq_strategy = BTEqualStrategyNumber;
1016 : :
1017 : 940 : ptkey_eqop = get_opfamily_member(key->partopfamily[i],
1018 : 940 : key->partopcintype[i],
1019 : 940 : key->partopcintype[i],
1020 : : eq_strategy);
1021 [ - + ]: 940 : if (!OidIsValid(ptkey_eqop))
1022 [ # # ]: 0 : elog(ERROR, "missing operator %d(%u,%u) in partition opfamily %u",
1023 : : eq_strategy, key->partopcintype[i], key->partopcintype[i],
1024 : : key->partopfamily[i]);
1025 : :
1026 : : /*
1027 : : * It may be possible to support UNIQUE constraints when partition
1028 : : * keys are expressions, but is it worth it? Give up for now.
1029 : : */
1030 [ + + ]: 940 : if (key->partattrs[i] == 0)
1031 [ + - ]: 8 : ereport(ERROR,
1032 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1033 : : errmsg("unsupported %s constraint with partition key definition",
1034 : : constraint_type),
1035 : : errdetail("%s constraints cannot be used when partition keys include expressions.",
1036 : : constraint_type)));
1037 : :
1038 : : /* Search the index column(s) for a match */
1039 [ + + ]: 1065 : for (j = 0; j < indexInfo->ii_NumIndexKeyAttrs; j++)
1040 : : {
1041 [ + + ]: 1013 : if (key->partattrs[i] == indexInfo->ii_IndexAttrNumbers[j])
1042 : : {
1043 : : /*
1044 : : * Matched the column, now what about the collation and
1045 : : * equality op?
1046 : : */
1047 : : Oid idx_opfamily;
1048 : : Oid idx_opcintype;
1049 : :
1050 [ - + ]: 880 : if (key->partcollation[i] != collationIds[j])
1051 : 0 : continue;
1052 : :
1053 [ + - ]: 880 : if (get_opclass_opfamily_and_input_type(opclassIds[j],
1054 : : &idx_opfamily,
1055 : : &idx_opcintype))
1056 : : {
1057 : 880 : Oid idx_eqop = InvalidOid;
1058 : :
1059 [ + + + + ]: 880 : if (stmt->unique && !stmt->iswithoutoverlaps)
1060 : 778 : idx_eqop = get_opfamily_member_for_cmptype(idx_opfamily,
1061 : : idx_opcintype,
1062 : : idx_opcintype,
1063 : : COMPARE_EQ);
1064 [ + - ]: 102 : else if (exclusion)
1065 : 102 : idx_eqop = indexInfo->ii_ExclusionOps[j];
1066 : :
1067 [ - + ]: 880 : if (!idx_eqop)
1068 [ # # ]: 0 : ereport(ERROR,
1069 : : errcode(ERRCODE_UNDEFINED_OBJECT),
1070 : : errmsg("could not identify an equality operator for type %s", format_type_be(idx_opcintype)),
1071 : : errdetail("There is no suitable operator in operator family \"%s\" for access method \"%s\".",
1072 : : get_opfamily_name(idx_opfamily, false), get_am_name(get_opfamily_method(idx_opfamily))));
1073 : :
1074 [ + + ]: 880 : if (ptkey_eqop == idx_eqop)
1075 : : {
1076 : 871 : found = true;
1077 : 871 : break;
1078 : : }
1079 [ + - ]: 9 : else if (exclusion)
1080 : : {
1081 : : /*
1082 : : * We found a match, but it's not an equality
1083 : : * operator. Instead of failing below with an
1084 : : * error message about a missing column, fail now
1085 : : * and explain that the operator is wrong.
1086 : : */
1087 : 9 : Form_pg_attribute att = TupleDescAttr(RelationGetDescr(rel), key->partattrs[i] - 1);
1088 : :
1089 [ + - ]: 9 : ereport(ERROR,
1090 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1091 : : errmsg("cannot match partition key to index on column \"%s\" using non-equal operator \"%s\"",
1092 : : NameStr(att->attname),
1093 : : get_opname(indexInfo->ii_ExclusionOps[j]))));
1094 : : }
1095 : : }
1096 : : }
1097 : : }
1098 : :
1099 [ + + ]: 923 : if (!found)
1100 : : {
1101 : : Form_pg_attribute att;
1102 : :
1103 : 52 : att = TupleDescAttr(RelationGetDescr(rel),
1104 : 52 : key->partattrs[i] - 1);
1105 [ + - ]: 52 : ereport(ERROR,
1106 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1107 : : /* translator: %s is UNIQUE, PRIMARY KEY, etc */
1108 : : errmsg("%s constraint on partitioned table must include all partitioning columns",
1109 : : constraint_type),
1110 : : /* translator: first %s is UNIQUE, PRIMARY KEY, etc */
1111 : : errdetail("%s constraint on table \"%s\" lacks column \"%s\" which is part of the partition key.",
1112 : : constraint_type, RelationGetRelationName(rel),
1113 : : NameStr(att->attname))));
1114 : : }
1115 : : }
1116 : : }
1117 : :
1118 : :
1119 : : /*
1120 : : * We disallow indexes on system columns. They would not necessarily get
1121 : : * updated correctly, and they don't seem useful anyway.
1122 : : *
1123 : : * Also disallow virtual generated columns in indexes (use expression
1124 : : * index instead).
1125 : : */
1126 [ + + ]: 47279 : for (int i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
1127 : : {
1128 : 27624 : AttrNumber attno = indexInfo->ii_IndexAttrNumbers[i];
1129 : :
1130 [ + + ]: 27624 : if (attno < 0)
1131 [ + - ]: 4 : ereport(ERROR,
1132 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1133 : : errmsg("index creation on system columns is not supported")));
1134 : :
1135 : :
1136 [ + + ]: 27620 : if (attno > 0 &&
1137 [ + + ]: 26901 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
1138 [ + - + + : 12 : ereport(ERROR,
+ - ]
1139 : : errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1140 : : stmt->primary ?
1141 : : errmsg("primary keys on virtual generated columns are not supported") :
1142 : : stmt->isconstraint ?
1143 : : errmsg("unique constraints on virtual generated columns are not supported") :
1144 : : errmsg("indexes on virtual generated columns are not supported"));
1145 : : }
1146 : :
1147 : : /*
1148 : : * Also check for system and generated columns used in expressions or
1149 : : * predicates.
1150 : : */
1151 [ + + + + ]: 19655 : if (indexInfo->ii_Expressions || indexInfo->ii_Predicate)
1152 : : {
1153 : 922 : Bitmapset *indexattrs = NULL;
1154 : : int j;
1155 : :
1156 : 922 : pull_varattnos((Node *) indexInfo->ii_Expressions, 1, &indexattrs);
1157 : 922 : pull_varattnos((Node *) indexInfo->ii_Predicate, 1, &indexattrs);
1158 : :
1159 [ + + ]: 6446 : for (int i = FirstLowInvalidHeapAttributeNumber + 1; i < 0; i++)
1160 : : {
1161 [ + + ]: 5532 : if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
1162 : : indexattrs))
1163 [ + - ]: 8 : ereport(ERROR,
1164 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1165 : : errmsg("index creation on system columns is not supported")));
1166 : : }
1167 : :
1168 : : /*
1169 : : * XXX Virtual generated columns in index expressions or predicates
1170 : : * could be supported, but it needs support in
1171 : : * RelationGetIndexExpressions() and RelationGetIndexPredicate().
1172 : : */
1173 : 914 : j = -1;
1174 [ + + ]: 1996 : while ((j = bms_next_member(indexattrs, j)) >= 0)
1175 : : {
1176 : 1082 : AttrNumber attno = j + FirstLowInvalidHeapAttributeNumber;
1177 : :
1178 [ + + ]: 1082 : if (attno > 0 &&
1179 [ - + ]: 1078 : TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
1180 [ # # # # ]: 0 : ereport(ERROR,
1181 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1182 : : stmt->isconstraint ?
1183 : : errmsg("unique constraints on virtual generated columns are not supported") :
1184 : : errmsg("indexes on virtual generated columns are not supported")));
1185 : : }
1186 : : }
1187 : :
1188 : : /* Is index safe for others to ignore? See set_indexsafe_procflags() */
1189 [ + + ]: 38595 : safe_index = indexInfo->ii_Expressions == NIL &&
1190 [ + + ]: 18948 : indexInfo->ii_Predicate == NIL;
1191 : :
1192 : : /*
1193 : : * Report index creation if appropriate (delay this till after most of the
1194 : : * error checks)
1195 : : */
1196 [ + + + + ]: 19647 : if (stmt->isconstraint && !quiet)
1197 : : {
1198 : : const char *constraint_type;
1199 : :
1200 [ + + ]: 6602 : if (stmt->primary)
1201 : 5842 : constraint_type = "PRIMARY KEY";
1202 [ + + ]: 760 : else if (stmt->unique)
1203 : 644 : constraint_type = "UNIQUE";
1204 [ + - ]: 116 : else if (stmt->excludeOpNames)
1205 : 116 : constraint_type = "EXCLUDE";
1206 : : else
1207 : : {
1208 [ # # ]: 0 : elog(ERROR, "unknown constraint type");
1209 : : constraint_type = NULL; /* keep compiler quiet */
1210 : : }
1211 : :
1212 [ + + - + ]: 6602 : ereport(DEBUG1,
1213 : : (errmsg_internal("%s %s will create implicit index \"%s\" for table \"%s\"",
1214 : : is_alter_table ? "ALTER TABLE / ADD" : "CREATE TABLE /",
1215 : : constraint_type,
1216 : : indexRelationName, RelationGetRelationName(rel))));
1217 : : }
1218 : :
1219 : : /*
1220 : : * A valid stmt->oldNumber implies that we already have a built form of
1221 : : * the index. The caller should also decline any index build.
1222 : : */
1223 : : Assert(!RelFileNumberIsValid(stmt->oldNumber) || (skip_build && !concurrent));
1224 : :
1225 : : /*
1226 : : * Make the catalog entries for the index, including constraints. This
1227 : : * step also actually builds the index, except if caller requested not to
1228 : : * or in concurrent mode, in which case it'll be done later, or doing a
1229 : : * partitioned index (because those don't have storage).
1230 : : */
1231 : 19647 : flags = constr_flags = 0;
1232 [ + + ]: 19647 : if (stmt->isconstraint)
1233 : 6762 : flags |= INDEX_CREATE_ADD_CONSTRAINT;
1234 [ + + + + : 19647 : if (skip_build || concurrent || partitioned)
+ + ]
1235 : 9522 : flags |= INDEX_CREATE_SKIP_BUILD;
1236 [ + + ]: 19647 : if (stmt->if_not_exists)
1237 : 12 : flags |= INDEX_CREATE_IF_NOT_EXISTS;
1238 [ + + ]: 19647 : if (concurrent)
1239 : 131 : flags |= INDEX_CREATE_CONCURRENT;
1240 [ + + ]: 19647 : if (partitioned)
1241 : 1407 : flags |= INDEX_CREATE_PARTITIONED;
1242 [ + + ]: 19647 : if (stmt->primary)
1243 : 5926 : flags |= INDEX_CREATE_IS_PRIMARY;
1244 : :
1245 : : /*
1246 : : * If the table is partitioned, and recursion was declined but partitions
1247 : : * exist, mark the index as invalid.
1248 : : */
1249 [ + + + + : 19647 : if (partitioned && stmt->relation && !stmt->relation->inh)
+ + ]
1250 : : {
1251 : 175 : PartitionDesc pd = RelationGetPartitionDesc(rel, true);
1252 : :
1253 [ + + ]: 175 : if (pd->nparts != 0)
1254 : 158 : flags |= INDEX_CREATE_INVALID;
1255 : : }
1256 : :
1257 [ + + ]: 19647 : if (stmt->deferrable)
1258 : 91 : constr_flags |= INDEX_CONSTR_CREATE_DEFERRABLE;
1259 [ + + ]: 19647 : if (stmt->initdeferred)
1260 : 28 : constr_flags |= INDEX_CONSTR_CREATE_INIT_DEFERRED;
1261 [ + + ]: 19647 : if (stmt->iswithoutoverlaps)
1262 : 589 : constr_flags |= INDEX_CONSTR_CREATE_WITHOUT_OVERLAPS;
1263 : :
1264 : : indexRelationId =
1265 : 19647 : index_create(rel, indexRelationName, indexRelationId, parentIndexId,
1266 : : parentConstraintId,
1267 : 19647 : stmt->oldNumber, indexInfo, indexColNames,
1268 : : accessMethodId, tablespaceId,
1269 : : collationIds, opclassIds, opclassOptions,
1270 : : coloptions, NULL, reloptions,
1271 : : flags, constr_flags,
1272 : : allowSystemTableMods, !check_rights,
1273 : 19647 : &createdConstraintId);
1274 : :
1275 : 19497 : ObjectAddressSet(address, RelationRelationId, indexRelationId);
1276 : :
1277 [ + + ]: 19497 : if (!OidIsValid(indexRelationId))
1278 : : {
1279 : : /*
1280 : : * Roll back any GUC changes executed by index functions. Also revert
1281 : : * to original default_tablespace if we changed it above.
1282 : : */
1283 : 12 : AtEOXact_GUC(false, root_save_nestlevel);
1284 : :
1285 : : /* Restore userid and security context */
1286 : 12 : SetUserIdAndSecContext(root_save_userid, root_save_sec_context);
1287 : :
1288 : 12 : table_close(rel, NoLock);
1289 : :
1290 : : /* If this is the top-level index, we're done */
1291 [ + - ]: 12 : if (!OidIsValid(parentIndexId))
1292 : 12 : pgstat_progress_end_command();
1293 : :
1294 : 12 : return address;
1295 : : }
1296 : :
1297 : : /*
1298 : : * Roll back any GUC changes executed by index functions, and keep
1299 : : * subsequent changes local to this command. This is essential if some
1300 : : * index function changed a behavior-affecting GUC, e.g. search_path.
1301 : : */
1302 : 19485 : AtEOXact_GUC(false, root_save_nestlevel);
1303 : 19485 : root_save_nestlevel = NewGUCNestLevel();
1304 : 19485 : RestrictSearchPath();
1305 : :
1306 : : /* Add any requested comment */
1307 [ + + ]: 19485 : if (stmt->idxcomment != NULL)
1308 : 52 : CreateComments(indexRelationId, RelationRelationId, 0,
1309 : 52 : stmt->idxcomment);
1310 : :
1311 [ + + ]: 19485 : if (partitioned)
1312 : : {
1313 : : PartitionDesc partdesc;
1314 : :
1315 : : /*
1316 : : * Unless caller specified to skip this step (via ONLY), process each
1317 : : * partition to make sure they all contain a corresponding index.
1318 : : *
1319 : : * If we're called internally (no stmt->relation), recurse always.
1320 : : */
1321 : 1407 : partdesc = RelationGetPartitionDesc(rel, true);
1322 [ + + + + : 1407 : if ((!stmt->relation || stmt->relation->inh) && partdesc->nparts > 0)
+ + ]
1323 : : {
1324 : 412 : int nparts = partdesc->nparts;
1325 : 412 : Oid *part_oids = palloc_array(Oid, nparts);
1326 : 412 : bool invalidate_parent = false;
1327 : : Relation parentIndex;
1328 : : TupleDesc parentDesc;
1329 : :
1330 : : /*
1331 : : * Report the total number of partitions at the start of the
1332 : : * command; don't update it when being called recursively.
1333 : : */
1334 [ + + ]: 412 : if (!OidIsValid(parentIndexId))
1335 : : {
1336 : : /*
1337 : : * When called by ProcessUtilitySlow, the number of partitions
1338 : : * is passed in as an optimization; but other callers pass -1
1339 : : * since they don't have the value handy. This should count
1340 : : * partitions the same way, ie one less than the number of
1341 : : * relations find_all_inheritors reports.
1342 : : *
1343 : : * We assume we needn't ask find_all_inheritors to take locks,
1344 : : * because that should have happened already for all callers.
1345 : : * Even if it did not, this is safe as long as we don't try to
1346 : : * touch the partitions here; the worst consequence would be a
1347 : : * bogus progress-reporting total.
1348 : : */
1349 [ + + ]: 338 : if (total_parts < 0)
1350 : : {
1351 : 84 : List *children = find_all_inheritors(tableId, NoLock, NULL);
1352 : :
1353 : 84 : total_parts = list_length(children) - 1;
1354 : 84 : list_free(children);
1355 : : }
1356 : :
1357 : 338 : pgstat_progress_update_param(PROGRESS_CREATEIDX_PARTITIONS_TOTAL,
1358 : : total_parts);
1359 : : }
1360 : :
1361 : : /* Make a local copy of partdesc->oids[], just for safety */
1362 : 412 : memcpy(part_oids, partdesc->oids, sizeof(Oid) * nparts);
1363 : :
1364 : : /*
1365 : : * We'll need an IndexInfo describing the parent index. The one
1366 : : * built above is almost good enough, but not quite, because (for
1367 : : * example) its predicate expression if any hasn't been through
1368 : : * expression preprocessing. The most reliable way to get an
1369 : : * IndexInfo that will match those for child indexes is to build
1370 : : * it the same way, using BuildIndexInfo().
1371 : : */
1372 : 412 : parentIndex = index_open(indexRelationId, lockmode);
1373 : 412 : indexInfo = BuildIndexInfo(parentIndex);
1374 : :
1375 : 412 : parentDesc = RelationGetDescr(rel);
1376 : :
1377 : : /*
1378 : : * For each partition, scan all existing indexes; if one matches
1379 : : * our index definition and is not already attached to some other
1380 : : * parent index, attach it to the one we just created.
1381 : : *
1382 : : * If none matches, build a new index by calling ourselves
1383 : : * recursively with the same options (except for the index name).
1384 : : */
1385 [ + + ]: 1134 : for (int i = 0; i < nparts; i++)
1386 : : {
1387 : 738 : Oid childRelid = part_oids[i];
1388 : : Relation childrel;
1389 : : Oid child_save_userid;
1390 : : int child_save_sec_context;
1391 : : int child_save_nestlevel;
1392 : : List *childidxs;
1393 : : ListCell *cell;
1394 : : AttrMap *attmap;
1395 : 738 : bool found = false;
1396 : :
1397 : 738 : childrel = table_open(childRelid, lockmode);
1398 : :
1399 : 738 : GetUserIdAndSecContext(&child_save_userid,
1400 : : &child_save_sec_context);
1401 : 738 : SetUserIdAndSecContext(childrel->rd_rel->relowner,
1402 : : child_save_sec_context | SECURITY_RESTRICTED_OPERATION);
1403 : 738 : child_save_nestlevel = NewGUCNestLevel();
1404 : 738 : RestrictSearchPath();
1405 : :
1406 : : /*
1407 : : * Don't try to create indexes on foreign tables, though. Skip
1408 : : * those if a regular index, or fail if trying to create a
1409 : : * constraint index.
1410 : : */
1411 [ + + ]: 738 : if (childrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1412 : : {
1413 [ + + - + ]: 12 : if (stmt->unique || stmt->primary)
1414 [ + - ]: 8 : ereport(ERROR,
1415 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1416 : : errmsg("cannot create unique index on partitioned table \"%s\"",
1417 : : RelationGetRelationName(rel)),
1418 : : errdetail("Table \"%s\" contains partitions that are foreign tables.",
1419 : : RelationGetRelationName(rel))));
1420 : :
1421 : 4 : AtEOXact_GUC(false, child_save_nestlevel);
1422 : 4 : SetUserIdAndSecContext(child_save_userid,
1423 : : child_save_sec_context);
1424 : 4 : table_close(childrel, lockmode);
1425 : 4 : continue;
1426 : : }
1427 : :
1428 : 726 : childidxs = RelationGetIndexList(childrel);
1429 : : attmap =
1430 : 726 : build_attrmap_by_name(RelationGetDescr(childrel),
1431 : : parentDesc,
1432 : : false);
1433 : :
1434 [ + + + + : 961 : foreach(cell, childidxs)
+ + ]
1435 : : {
1436 : 287 : Oid cldidxid = lfirst_oid(cell);
1437 : : Relation cldidx;
1438 : : IndexInfo *cldIdxInfo;
1439 : :
1440 : : /* this index is already partition of another one */
1441 [ + + ]: 287 : if (has_superclass(cldidxid))
1442 : 219 : continue;
1443 : :
1444 : 68 : cldidx = index_open(cldidxid, lockmode);
1445 : 68 : cldIdxInfo = BuildIndexInfo(cldidx);
1446 [ + + ]: 68 : if (CompareIndexInfo(cldIdxInfo, indexInfo,
1447 : 68 : cldidx->rd_indcollation,
1448 : 68 : parentIndex->rd_indcollation,
1449 : 68 : cldidx->rd_opfamily,
1450 : 68 : parentIndex->rd_opfamily,
1451 : : attmap))
1452 : : {
1453 : 52 : Oid cldConstrOid = InvalidOid;
1454 : :
1455 : : /*
1456 : : * Found a match.
1457 : : *
1458 : : * If this index is being created in the parent
1459 : : * because of a constraint, then the child needs to
1460 : : * have a constraint also, so look for one. If there
1461 : : * is no such constraint, this index is no good, so
1462 : : * keep looking.
1463 : : */
1464 [ + + ]: 52 : if (createdConstraintId != InvalidOid)
1465 : : {
1466 : : cldConstrOid =
1467 : 8 : get_relation_idx_constraint_oid(childRelid,
1468 : : cldidxid);
1469 [ - + ]: 8 : if (cldConstrOid == InvalidOid)
1470 : : {
1471 : 0 : index_close(cldidx, lockmode);
1472 : 0 : continue;
1473 : : }
1474 : : }
1475 : :
1476 : : /* Attach index to parent and we're done. */
1477 : 52 : IndexSetParentIndex(cldidx, indexRelationId);
1478 [ + + ]: 52 : if (createdConstraintId != InvalidOid)
1479 : 8 : ConstraintSetParentConstraint(cldConstrOid,
1480 : : createdConstraintId,
1481 : : childRelid);
1482 : :
1483 [ + + ]: 52 : if (!cldidx->rd_index->indisvalid)
1484 : 12 : invalidate_parent = true;
1485 : :
1486 : 52 : found = true;
1487 : :
1488 : : /*
1489 : : * Report this partition as processed. Note that if
1490 : : * the partition has children itself, we'd ideally
1491 : : * count the children and update the progress report
1492 : : * for all of them; but that seems unduly expensive.
1493 : : * Instead, the progress report will act like all such
1494 : : * indirect children were processed in zero time at
1495 : : * the end of the command.
1496 : : */
1497 : 52 : pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1);
1498 : :
1499 : : /* keep lock till commit */
1500 : 52 : index_close(cldidx, NoLock);
1501 : 52 : break;
1502 : : }
1503 : :
1504 : 16 : index_close(cldidx, lockmode);
1505 : : }
1506 : :
1507 : 726 : list_free(childidxs);
1508 : 726 : AtEOXact_GUC(false, child_save_nestlevel);
1509 : 726 : SetUserIdAndSecContext(child_save_userid,
1510 : : child_save_sec_context);
1511 : 726 : table_close(childrel, NoLock);
1512 : :
1513 : : /*
1514 : : * If no matching index was found, create our own.
1515 : : */
1516 [ + + ]: 726 : if (!found)
1517 : : {
1518 : : IndexStmt *childStmt;
1519 : : ObjectAddress childAddr;
1520 : :
1521 : : /*
1522 : : * Build an IndexStmt describing the desired child index
1523 : : * in the same way that we do during ATTACH PARTITION.
1524 : : * Notably, we rely on generateClonedIndexStmt to produce
1525 : : * a search-path-independent representation, which the
1526 : : * original IndexStmt might not be.
1527 : : */
1528 : 674 : childStmt = generateClonedIndexStmt(NULL,
1529 : : parentIndex,
1530 : : attmap,
1531 : : NULL);
1532 : :
1533 : : /*
1534 : : * Recurse as the starting user ID. Callee will use that
1535 : : * for permission checks, then switch again.
1536 : : */
1537 : : Assert(GetUserId() == child_save_userid);
1538 : 674 : SetUserIdAndSecContext(root_save_userid,
1539 : : root_save_sec_context);
1540 : : childAddr =
1541 : 674 : DefineIndex(NULL, /* original pstate not applicable */
1542 : : childRelid, childStmt,
1543 : : InvalidOid, /* no predefined OID */
1544 : : indexRelationId, /* this is our child */
1545 : : createdConstraintId,
1546 : : -1,
1547 : : is_alter_table, check_rights,
1548 : : check_not_in_use,
1549 : : skip_build, quiet);
1550 : 666 : SetUserIdAndSecContext(child_save_userid,
1551 : : child_save_sec_context);
1552 : :
1553 : : /*
1554 : : * Check if the index just created is valid or not, as it
1555 : : * could be possible that it has been switched as invalid
1556 : : * when recursing across multiple partition levels.
1557 : : */
1558 [ + + ]: 666 : if (!get_index_isvalid(childAddr.objectId))
1559 : 4 : invalidate_parent = true;
1560 : : }
1561 : :
1562 : 718 : free_attrmap(attmap);
1563 : : }
1564 : :
1565 : 396 : index_close(parentIndex, lockmode);
1566 : :
1567 : : /*
1568 : : * The pg_index row we inserted for this index was marked
1569 : : * indisvalid=true. But if we attached an existing index that is
1570 : : * invalid, this is incorrect, so update our row to invalid too.
1571 : : */
1572 [ + + ]: 396 : if (invalidate_parent)
1573 : : {
1574 : 16 : Relation pg_index = table_open(IndexRelationId, RowExclusiveLock);
1575 : : HeapTuple tup,
1576 : : newtup;
1577 : :
1578 : 16 : tup = SearchSysCache1(INDEXRELID,
1579 : : ObjectIdGetDatum(indexRelationId));
1580 [ - + ]: 16 : if (!HeapTupleIsValid(tup))
1581 [ # # ]: 0 : elog(ERROR, "cache lookup failed for index %u",
1582 : : indexRelationId);
1583 : 16 : newtup = heap_copytuple(tup);
1584 : 16 : ((Form_pg_index) GETSTRUCT(newtup))->indisvalid = false;
1585 : 16 : CatalogTupleUpdate(pg_index, &tup->t_self, newtup);
1586 : 16 : ReleaseSysCache(tup);
1587 : 16 : table_close(pg_index, RowExclusiveLock);
1588 : 16 : heap_freetuple(newtup);
1589 : :
1590 : : /*
1591 : : * CCI here to make this update visible, in case this recurses
1592 : : * across multiple partition levels.
1593 : : */
1594 : 16 : CommandCounterIncrement();
1595 : : }
1596 : : }
1597 : :
1598 : : /*
1599 : : * Indexes on partitioned tables are not themselves built, so we're
1600 : : * done here.
1601 : : */
1602 : 1391 : AtEOXact_GUC(false, root_save_nestlevel);
1603 : 1391 : SetUserIdAndSecContext(root_save_userid, root_save_sec_context);
1604 : 1391 : table_close(rel, NoLock);
1605 [ + + ]: 1391 : if (!OidIsValid(parentIndexId))
1606 : 1192 : pgstat_progress_end_command();
1607 : : else
1608 : : {
1609 : : /* Update progress for an intermediate partitioned index itself */
1610 : 199 : pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1);
1611 : : }
1612 : :
1613 : 1391 : return address;
1614 : : }
1615 : :
1616 : 18078 : AtEOXact_GUC(false, root_save_nestlevel);
1617 : 18078 : SetUserIdAndSecContext(root_save_userid, root_save_sec_context);
1618 : :
1619 [ + + ]: 18078 : if (!concurrent)
1620 : : {
1621 : : /* Close the heap and we're done, in the non-concurrent case */
1622 : 17955 : table_close(rel, NoLock);
1623 : :
1624 : : /*
1625 : : * If this is the top-level index, the command is done overall;
1626 : : * otherwise, increment progress to report one child index is done.
1627 : : */
1628 [ + + ]: 17955 : if (!OidIsValid(parentIndexId))
1629 : 16087 : pgstat_progress_end_command();
1630 : : else
1631 : 1868 : pgstat_progress_incr_param(PROGRESS_CREATEIDX_PARTITIONS_DONE, 1);
1632 : :
1633 : 17955 : return address;
1634 : : }
1635 : :
1636 : : /* save lockrelid and locktag for below, then close rel */
1637 : 123 : heaprelid = rel->rd_lockInfo.lockRelId;
1638 : 123 : SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
1639 : 123 : table_close(rel, NoLock);
1640 : :
1641 : : /*
1642 : : * For a concurrent build, it's important to make the catalog entries
1643 : : * visible to other transactions before we start to build the index. That
1644 : : * will prevent them from making incompatible HOT updates. The new index
1645 : : * will be marked not indisready and not indisvalid, so that no one else
1646 : : * tries to either insert into it or use it for queries.
1647 : : *
1648 : : * We must commit our current transaction so that the index becomes
1649 : : * visible; then start another. Note that all the data structures we just
1650 : : * built are lost in the commit. The only data we keep past here are the
1651 : : * relation IDs.
1652 : : *
1653 : : * Before committing, get a session-level lock on the table, to ensure
1654 : : * that neither it nor the index can be dropped before we finish. This
1655 : : * cannot block, even if someone else is waiting for access, because we
1656 : : * already have the same lock within our transaction.
1657 : : *
1658 : : * Note: we don't currently bother with a session lock on the index,
1659 : : * because there are no operations that could change its state while we
1660 : : * hold lock on the parent table. This might need to change later.
1661 : : */
1662 : 123 : LockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock);
1663 : :
1664 : 123 : PopActiveSnapshot();
1665 : 123 : CommitTransactionCommand();
1666 : 123 : StartTransactionCommand();
1667 : :
1668 : : /* Tell concurrent index builds to ignore us, if index qualifies */
1669 [ + + ]: 123 : if (safe_index)
1670 : 81 : set_indexsafe_procflags();
1671 : :
1672 : : /*
1673 : : * The index is now visible, so we can report the OID. While on it,
1674 : : * include the report for the beginning of phase 2.
1675 : : */
1676 : : {
1677 : 123 : const int progress_cols[] = {
1678 : : PROGRESS_CREATEIDX_INDEX_OID,
1679 : : PROGRESS_CREATEIDX_PHASE
1680 : : };
1681 : 123 : const int64 progress_vals[] = {
1682 : : indexRelationId,
1683 : : PROGRESS_CREATEIDX_PHASE_WAIT_1
1684 : : };
1685 : :
1686 : 123 : pgstat_progress_update_multi_param(2, progress_cols, progress_vals);
1687 : : }
1688 : :
1689 : : /*
1690 : : * Phase 2 of concurrent index build (see comments for validate_index()
1691 : : * for an overview of how this works)
1692 : : *
1693 : : * Now we must wait until no running transaction could have the table open
1694 : : * with the old list of indexes. Use ShareLock to consider running
1695 : : * transactions that hold locks that permit writing to the table. Note we
1696 : : * do not need to worry about xacts that open the table for writing after
1697 : : * this point; they will see the new index when they open it.
1698 : : *
1699 : : * Note: the reason we use actual lock acquisition here, rather than just
1700 : : * checking the ProcArray and sleeping, is that deadlock is possible if
1701 : : * one of the transactions in question is blocked trying to acquire an
1702 : : * exclusive lock on our table. The lock code will detect deadlock and
1703 : : * error out properly.
1704 : : */
1705 : 123 : WaitForLockers(heaplocktag, ShareLock, true);
1706 : :
1707 : : /*
1708 : : * At this moment we are sure that there are no transactions with the
1709 : : * table open for write that don't have this new index in their list of
1710 : : * indexes. We have waited out all the existing transactions and any new
1711 : : * transaction will have the new index in its list, but the index is still
1712 : : * marked as "not-ready-for-inserts". The index is consulted while
1713 : : * deciding HOT-safety though. This arrangement ensures that no new HOT
1714 : : * chains can be created where the new tuple and the old tuple in the
1715 : : * chain have different index keys.
1716 : : *
1717 : : * We now take a new snapshot, and build the index using all tuples that
1718 : : * are visible in this snapshot. We can be sure that any HOT updates to
1719 : : * these tuples will be compatible with the index, since any updates made
1720 : : * by transactions that didn't know about the index are now committed or
1721 : : * rolled back. Thus, each visible tuple is either the end of its
1722 : : * HOT-chain or the extension of the chain is HOT-safe for this index.
1723 : : */
1724 : :
1725 : : /* Set ActiveSnapshot since functions in the indexes may need it */
1726 : 123 : PushActiveSnapshot(GetTransactionSnapshot());
1727 : :
1728 : : /* Perform concurrent build of index */
1729 : 123 : index_concurrently_build(tableId, indexRelationId);
1730 : :
1731 : : /* we can do away with our snapshot */
1732 : 99 : PopActiveSnapshot();
1733 : :
1734 : : /*
1735 : : * Commit this transaction to make the indisready update visible.
1736 : : */
1737 : 99 : CommitTransactionCommand();
1738 : 99 : StartTransactionCommand();
1739 : :
1740 : : /* Tell concurrent index builds to ignore us, if index qualifies */
1741 [ + + ]: 99 : if (safe_index)
1742 : 73 : set_indexsafe_procflags();
1743 : :
1744 : : /*
1745 : : * Phase 3 of concurrent index build
1746 : : *
1747 : : * We once again wait until no transaction can have the table open with
1748 : : * the index marked as read-only for updates.
1749 : : */
1750 : 99 : pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
1751 : : PROGRESS_CREATEIDX_PHASE_WAIT_2);
1752 : 99 : WaitForLockers(heaplocktag, ShareLock, true);
1753 : :
1754 : : /*
1755 : : * Now take the "reference snapshot" that will be used by validate_index()
1756 : : * to filter candidate tuples. Beware! There might still be snapshots in
1757 : : * use that treat some transaction as in-progress that our reference
1758 : : * snapshot treats as committed. If such a recently-committed transaction
1759 : : * deleted tuples in the table, we will not include them in the index; yet
1760 : : * those transactions which see the deleting one as still-in-progress will
1761 : : * expect such tuples to be there once we mark the index as valid.
1762 : : *
1763 : : * We solve this by waiting for all endangered transactions to exit before
1764 : : * we mark the index as valid.
1765 : : *
1766 : : * We also set ActiveSnapshot to this snap, since functions in indexes may
1767 : : * need a snapshot.
1768 : : */
1769 : 99 : snapshot = RegisterSnapshot(GetTransactionSnapshot());
1770 : 99 : PushActiveSnapshot(snapshot);
1771 : :
1772 : : /*
1773 : : * Scan the index and the heap, insert any missing index entries.
1774 : : */
1775 : 99 : validate_index(tableId, indexRelationId, snapshot);
1776 : :
1777 : : /*
1778 : : * Drop the reference snapshot. We must do this before waiting out other
1779 : : * snapshot holders, else we will deadlock against other processes also
1780 : : * doing CREATE INDEX CONCURRENTLY, which would see our snapshot as one
1781 : : * they must wait for. But first, save the snapshot's xmin to use as
1782 : : * limitXmin for GetCurrentVirtualXIDs().
1783 : : */
1784 : 99 : limitXmin = snapshot->xmin;
1785 : :
1786 : 99 : PopActiveSnapshot();
1787 : 99 : UnregisterSnapshot(snapshot);
1788 : :
1789 : : /*
1790 : : * The snapshot subsystem could still contain registered snapshots that
1791 : : * are holding back our process's advertised xmin; in particular, if
1792 : : * default_transaction_isolation = serializable, there is a transaction
1793 : : * snapshot that is still active. The CatalogSnapshot is likewise a
1794 : : * hazard. To ensure no deadlocks, we must commit and start yet another
1795 : : * transaction, and do our wait before any snapshot has been taken in it.
1796 : : */
1797 : 99 : CommitTransactionCommand();
1798 : 99 : StartTransactionCommand();
1799 : :
1800 : : /* Tell concurrent index builds to ignore us, if index qualifies */
1801 [ + + ]: 99 : if (safe_index)
1802 : 73 : set_indexsafe_procflags();
1803 : :
1804 : : /* We should now definitely not be advertising any xmin. */
1805 : : Assert(MyProc->xmin == InvalidTransactionId);
1806 : :
1807 : : /*
1808 : : * The index is now valid in the sense that it contains all currently
1809 : : * interesting tuples. But since it might not contain tuples deleted just
1810 : : * before the reference snap was taken, we have to wait out any
1811 : : * transactions that might have older snapshots.
1812 : : */
1813 : 99 : INJECTION_POINT("define-index-before-set-valid", NULL);
1814 : 99 : pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
1815 : : PROGRESS_CREATEIDX_PHASE_WAIT_3);
1816 : 99 : WaitForOlderSnapshots(limitXmin, true);
1817 : :
1818 : : /*
1819 : : * Updating pg_index might involve TOAST table access, so ensure we have a
1820 : : * valid snapshot.
1821 : : */
1822 : 99 : PushActiveSnapshot(GetTransactionSnapshot());
1823 : :
1824 : : /*
1825 : : * Index can now be marked valid -- update its pg_index entry
1826 : : */
1827 : 99 : index_set_state_flags(indexRelationId, INDEX_CREATE_SET_VALID);
1828 : :
1829 : 99 : PopActiveSnapshot();
1830 : :
1831 : : /*
1832 : : * The pg_index update will cause backends (including this one) to update
1833 : : * relcache entries for the index itself, but we should also send a
1834 : : * relcache inval on the parent table to force replanning of cached plans.
1835 : : * Otherwise existing sessions might fail to use the new index where it
1836 : : * would be useful. (Note that our earlier commits did not create reasons
1837 : : * to replan; so relcache flush on the index itself was sufficient.)
1838 : : */
1839 : 99 : CacheInvalidateRelcacheByRelid(heaprelid.relId);
1840 : :
1841 : : /*
1842 : : * Last thing to do is release the session-level lock on the parent table.
1843 : : */
1844 : 99 : UnlockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock);
1845 : :
1846 : 99 : pgstat_progress_end_command();
1847 : :
1848 : 99 : return address;
1849 : : }
1850 : :
1851 : :
1852 : : /*
1853 : : * CheckPredicate
1854 : : * Checks that the given partial-index predicate is valid.
1855 : : *
1856 : : * This used to also constrain the form of the predicate to forms that
1857 : : * indxpath.c could do something with. However, that seems overly
1858 : : * restrictive. One useful application of partial indexes is to apply
1859 : : * a UNIQUE constraint across a subset of a table, and in that scenario
1860 : : * any evaluable predicate will work. So accept any predicate here
1861 : : * (except ones requiring a plan), and let indxpath.c fend for itself.
1862 : : */
1863 : : static void
1864 : 285 : CheckPredicate(Expr *predicate)
1865 : : {
1866 : : /*
1867 : : * transformExpr() should have already rejected subqueries, aggregates,
1868 : : * and window functions, based on the EXPR_KIND_ for a predicate.
1869 : : */
1870 : :
1871 : : /*
1872 : : * A predicate using mutable functions is probably wrong, for the same
1873 : : * reasons that we don't allow an index expression to use one.
1874 : : */
1875 [ - + ]: 285 : if (contain_mutable_functions_after_planning(predicate))
1876 [ # # ]: 0 : ereport(ERROR,
1877 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1878 : : errmsg("functions in index predicate must be marked IMMUTABLE")));
1879 : 285 : }
1880 : :
1881 : : /*
1882 : : * Compute per-index-column information, including indexed column numbers
1883 : : * or index expressions, opclasses and their options. Note, all output vectors
1884 : : * should be allocated for all columns, including "including" ones.
1885 : : *
1886 : : * If the caller switched to the table owner, ddl_userid is the role for ACL
1887 : : * checks reached without traversing opaque expressions. Otherwise, it's
1888 : : * InvalidOid, and other ddl_* arguments are undefined.
1889 : : */
1890 : : static void
1891 : 20054 : ComputeIndexAttrs(ParseState *pstate,
1892 : : IndexInfo *indexInfo,
1893 : : Oid *typeOids,
1894 : : Oid *collationOids,
1895 : : Oid *opclassOids,
1896 : : Datum *opclassOptions,
1897 : : int16 *colOptions,
1898 : : const List *attList, /* list of IndexElem's */
1899 : : const List *exclusionOpNames,
1900 : : Oid relId,
1901 : : const char *accessMethodName,
1902 : : Oid accessMethodId,
1903 : : bool amcanorder,
1904 : : bool isconstraint,
1905 : : bool iswithoutoverlaps,
1906 : : Oid ddl_userid,
1907 : : int ddl_sec_context,
1908 : : int *ddl_save_nestlevel)
1909 : : {
1910 : : ListCell *nextExclOp;
1911 : : ListCell *lc;
1912 : : int attn;
1913 : 20054 : int nkeycols = indexInfo->ii_NumIndexKeyAttrs;
1914 : : Oid save_userid;
1915 : : int save_sec_context;
1916 : :
1917 : : /* Allocate space for exclusion operator info, if needed */
1918 [ + + ]: 20054 : if (exclusionOpNames)
1919 : : {
1920 : : Assert(list_length(exclusionOpNames) == nkeycols);
1921 : 230 : indexInfo->ii_ExclusionOps = palloc_array(Oid, nkeycols);
1922 : 230 : indexInfo->ii_ExclusionProcs = palloc_array(Oid, nkeycols);
1923 : 230 : indexInfo->ii_ExclusionStrats = palloc_array(uint16, nkeycols);
1924 : 230 : nextExclOp = list_head(exclusionOpNames);
1925 : : }
1926 : : else
1927 : 19824 : nextExclOp = NULL;
1928 : :
1929 : : /*
1930 : : * If this is a WITHOUT OVERLAPS constraint, we need space for exclusion
1931 : : * ops, but we don't need to parse anything, so we can let nextExclOp be
1932 : : * NULL. Note that for partitions/inheriting/LIKE, exclusionOpNames will
1933 : : * be set, so we already allocated above.
1934 : : */
1935 [ + + ]: 20054 : if (iswithoutoverlaps)
1936 : : {
1937 [ + + ]: 589 : if (exclusionOpNames == NIL)
1938 : : {
1939 : 517 : indexInfo->ii_ExclusionOps = palloc_array(Oid, nkeycols);
1940 : 517 : indexInfo->ii_ExclusionProcs = palloc_array(Oid, nkeycols);
1941 : 517 : indexInfo->ii_ExclusionStrats = palloc_array(uint16, nkeycols);
1942 : : }
1943 : 589 : nextExclOp = NULL;
1944 : : }
1945 : :
1946 [ + + ]: 20054 : if (OidIsValid(ddl_userid))
1947 : 19981 : GetUserIdAndSecContext(&save_userid, &save_sec_context);
1948 : :
1949 : : /*
1950 : : * process attributeList
1951 : : */
1952 : 20054 : attn = 0;
1953 [ + - + + : 47861 : foreach(lc, attList)
+ + ]
1954 : : {
1955 : 28024 : IndexElem *attribute = (IndexElem *) lfirst(lc);
1956 : : Oid atttype;
1957 : : Oid attcollation;
1958 : :
1959 : : /*
1960 : : * Process the column-or-expression to be indexed.
1961 : : */
1962 [ + + ]: 28024 : if (attribute->name != NULL)
1963 : : {
1964 : : /* Simple index attribute */
1965 : : HeapTuple atttuple;
1966 : : Form_pg_attribute attform;
1967 : :
1968 : : Assert(attribute->expr == NULL);
1969 : 27108 : atttuple = SearchSysCacheAttName(relId, attribute->name);
1970 [ + + ]: 27108 : if (!HeapTupleIsValid(atttuple))
1971 : : {
1972 : : /* difference in error message spellings is historical */
1973 [ + + ]: 20 : if (isconstraint)
1974 [ + - ]: 12 : ereport(ERROR,
1975 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
1976 : : errmsg("column \"%s\" named in key does not exist",
1977 : : attribute->name),
1978 : : parser_errposition(pstate, attribute->location)));
1979 : : else
1980 [ + - ]: 8 : ereport(ERROR,
1981 : : (errcode(ERRCODE_UNDEFINED_COLUMN),
1982 : : errmsg("column \"%s\" does not exist",
1983 : : attribute->name),
1984 : : parser_errposition(pstate, attribute->location)));
1985 : : }
1986 : 27088 : attform = (Form_pg_attribute) GETSTRUCT(atttuple);
1987 : 27088 : indexInfo->ii_IndexAttrNumbers[attn] = attform->attnum;
1988 : 27088 : atttype = attform->atttypid;
1989 : 27088 : attcollation = attform->attcollation;
1990 : 27088 : ReleaseSysCache(atttuple);
1991 : : }
1992 : : else
1993 : : {
1994 : : /* Index expression */
1995 : 916 : Node *expr = attribute->expr;
1996 : :
1997 : : Assert(expr != NULL);
1998 : :
1999 [ - + ]: 916 : if (attn >= nkeycols)
2000 [ # # ]: 0 : ereport(ERROR,
2001 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2002 : : errmsg("expressions are not supported in included columns"),
2003 : : parser_errposition(pstate, attribute->location)));
2004 : 916 : atttype = exprType(expr);
2005 : 916 : attcollation = exprCollation(expr);
2006 : :
2007 : : /*
2008 : : * Strip any top-level COLLATE clause. This ensures that we treat
2009 : : * "x COLLATE y" and "(x COLLATE y)" alike.
2010 : : */
2011 [ + + ]: 936 : while (IsA(expr, CollateExpr))
2012 : 20 : expr = (Node *) ((CollateExpr *) expr)->arg;
2013 : :
2014 [ + + ]: 916 : if (IsA(expr, Var) &&
2015 [ + - ]: 8 : ((Var *) expr)->varattno != InvalidAttrNumber)
2016 : : {
2017 : : /*
2018 : : * User wrote "(column)" or "(column COLLATE something)".
2019 : : * Treat it like simple attribute anyway.
2020 : : */
2021 : 8 : indexInfo->ii_IndexAttrNumbers[attn] = ((Var *) expr)->varattno;
2022 : : }
2023 : : else
2024 : : {
2025 : 908 : indexInfo->ii_IndexAttrNumbers[attn] = 0; /* marks expression */
2026 : 908 : indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
2027 : : expr);
2028 : :
2029 : : /*
2030 : : * transformExpr() should have already rejected subqueries,
2031 : : * aggregates, and window functions, based on the EXPR_KIND_
2032 : : * for an index expression.
2033 : : */
2034 : :
2035 : : /*
2036 : : * An expression using mutable functions is probably wrong,
2037 : : * since if you aren't going to get the same result for the
2038 : : * same data every time, it's not clear what the index entries
2039 : : * mean at all.
2040 : : */
2041 [ + + ]: 908 : if (contain_mutable_functions_after_planning((Expr *) expr))
2042 [ + - ]: 184 : ereport(ERROR,
2043 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2044 : : errmsg("functions in index expression must be marked IMMUTABLE"),
2045 : : parser_errposition(pstate, attribute->location)));
2046 : : }
2047 : : }
2048 : :
2049 : 27820 : typeOids[attn] = atttype;
2050 : :
2051 : : /*
2052 : : * Included columns have no collation, no opclass and no ordering
2053 : : * options.
2054 : : */
2055 [ + + ]: 27820 : if (attn >= nkeycols)
2056 : : {
2057 [ - + ]: 410 : if (attribute->collation)
2058 [ # # ]: 0 : ereport(ERROR,
2059 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2060 : : errmsg("including column does not support a collation"),
2061 : : parser_errposition(pstate, attribute->location)));
2062 [ - + ]: 410 : if (attribute->opclass)
2063 [ # # ]: 0 : ereport(ERROR,
2064 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2065 : : errmsg("including column does not support an operator class"),
2066 : : parser_errposition(pstate, attribute->location)));
2067 [ - + ]: 410 : if (attribute->ordering != SORTBY_DEFAULT)
2068 [ # # ]: 0 : ereport(ERROR,
2069 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2070 : : errmsg("including column does not support ASC/DESC options"),
2071 : : parser_errposition(pstate, attribute->location)));
2072 [ - + ]: 410 : if (attribute->nulls_ordering != SORTBY_NULLS_DEFAULT)
2073 [ # # ]: 0 : ereport(ERROR,
2074 : : (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2075 : : errmsg("including column does not support NULLS FIRST/LAST options"),
2076 : : parser_errposition(pstate, attribute->location)));
2077 : :
2078 : 410 : opclassOids[attn] = InvalidOid;
2079 : 410 : opclassOptions[attn] = (Datum) 0;
2080 : 410 : colOptions[attn] = 0;
2081 : 410 : collationOids[attn] = InvalidOid;
2082 : 410 : attn++;
2083 : :
2084 : 410 : continue;
2085 : : }
2086 : :
2087 : : /*
2088 : : * Apply collation override if any. Use of ddl_userid is necessary
2089 : : * due to ACL checks therein, and it's safe because collations don't
2090 : : * contain opaque expressions (or non-opaque expressions).
2091 : : */
2092 [ + + ]: 27410 : if (attribute->collation)
2093 : : {
2094 [ + - ]: 80 : if (OidIsValid(ddl_userid))
2095 : : {
2096 : 80 : AtEOXact_GUC(false, *ddl_save_nestlevel);
2097 : 80 : SetUserIdAndSecContext(ddl_userid, ddl_sec_context);
2098 : : }
2099 : 80 : attcollation = get_collation_oid(attribute->collation, false);
2100 [ + - ]: 79 : if (OidIsValid(ddl_userid))
2101 : : {
2102 : 79 : SetUserIdAndSecContext(save_userid, save_sec_context);
2103 : 79 : *ddl_save_nestlevel = NewGUCNestLevel();
2104 : 79 : RestrictSearchPath();
2105 : : }
2106 : : }
2107 : :
2108 : : /*
2109 : : * Check we have a collation iff it's a collatable type. The only
2110 : : * expected failures here are (1) COLLATE applied to a noncollatable
2111 : : * type, or (2) index expression had an unresolved collation. But we
2112 : : * might as well code this to be a complete consistency check.
2113 : : */
2114 [ + + ]: 27409 : if (type_is_collatable(atttype))
2115 : : {
2116 [ - + ]: 4077 : if (!OidIsValid(attcollation))
2117 [ # # ]: 0 : ereport(ERROR,
2118 : : (errcode(ERRCODE_INDETERMINATE_COLLATION),
2119 : : errmsg("could not determine which collation to use for index expression"),
2120 : : errhint("Use the COLLATE clause to set the collation explicitly."),
2121 : : parser_errposition(pstate, attribute->location)));
2122 : : }
2123 : : else
2124 : : {
2125 [ + + ]: 23332 : if (OidIsValid(attcollation))
2126 [ + - ]: 8 : ereport(ERROR,
2127 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
2128 : : errmsg("collations are not supported by type %s",
2129 : : format_type_be(atttype)),
2130 : : parser_errposition(pstate, attribute->location)));
2131 : : }
2132 : :
2133 : 27401 : collationOids[attn] = attcollation;
2134 : :
2135 : : /*
2136 : : * Identify the opclass to use. Use of ddl_userid is necessary due to
2137 : : * ACL checks therein. This is safe despite opclasses containing
2138 : : * opaque expressions (specifically, functions), because only
2139 : : * superusers can define opclasses.
2140 : : */
2141 [ + + ]: 27401 : if (OidIsValid(ddl_userid))
2142 : : {
2143 : 27324 : AtEOXact_GUC(false, *ddl_save_nestlevel);
2144 : 27324 : SetUserIdAndSecContext(ddl_userid, ddl_sec_context);
2145 : : }
2146 : 27401 : opclassOids[attn] = ResolveOpClass(attribute->opclass,
2147 : : atttype,
2148 : : accessMethodName,
2149 : : accessMethodId);
2150 [ + + ]: 27397 : if (OidIsValid(ddl_userid))
2151 : : {
2152 : 27320 : SetUserIdAndSecContext(save_userid, save_sec_context);
2153 : 27320 : *ddl_save_nestlevel = NewGUCNestLevel();
2154 : 27320 : RestrictSearchPath();
2155 : : }
2156 : :
2157 : : /*
2158 : : * Identify the exclusion operator, if any.
2159 : : */
2160 [ + + ]: 27397 : if (nextExclOp)
2161 : : {
2162 : 228 : List *opname = (List *) lfirst(nextExclOp);
2163 : : Oid opid;
2164 : : Oid opfamily;
2165 : : int strat;
2166 : :
2167 : : /*
2168 : : * Find the operator --- it must accept the column datatype
2169 : : * without runtime coercion (but binary compatibility is OK).
2170 : : * Operators contain opaque expressions (specifically, functions).
2171 : : * compatible_oper_opid() boils down to oper() and
2172 : : * IsBinaryCoercible(). PostgreSQL would have security problems
2173 : : * elsewhere if oper() started calling opaque expressions.
2174 : : */
2175 [ + - ]: 228 : if (OidIsValid(ddl_userid))
2176 : : {
2177 : 228 : AtEOXact_GUC(false, *ddl_save_nestlevel);
2178 : 228 : SetUserIdAndSecContext(ddl_userid, ddl_sec_context);
2179 : : }
2180 : 228 : opid = compatible_oper_opid(opname, atttype, atttype, false);
2181 [ + - ]: 228 : if (OidIsValid(ddl_userid))
2182 : : {
2183 : 228 : SetUserIdAndSecContext(save_userid, save_sec_context);
2184 : 228 : *ddl_save_nestlevel = NewGUCNestLevel();
2185 : 228 : RestrictSearchPath();
2186 : : }
2187 : :
2188 : : /*
2189 : : * Only allow commutative operators to be used in exclusion
2190 : : * constraints. If X conflicts with Y, but Y does not conflict
2191 : : * with X, bad things will happen.
2192 : : */
2193 [ - + ]: 228 : if (get_commutator(opid) != opid)
2194 [ # # ]: 0 : ereport(ERROR,
2195 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2196 : : errmsg("operator %s is not commutative",
2197 : : format_operator(opid)),
2198 : : errdetail("Only commutative operators can be used in exclusion constraints."),
2199 : : parser_errposition(pstate, attribute->location)));
2200 : :
2201 : : /*
2202 : : * Operator must be a member of the right opfamily, too
2203 : : */
2204 : 228 : opfamily = get_opclass_family(opclassOids[attn]);
2205 : 228 : strat = get_op_opfamily_strategy(opid, opfamily);
2206 [ - + ]: 228 : if (strat == 0)
2207 [ # # ]: 0 : ereport(ERROR,
2208 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2209 : : errmsg("operator %s is not a member of operator family \"%s\"",
2210 : : format_operator(opid),
2211 : : get_opfamily_name(opfamily, false)),
2212 : : errdetail("The exclusion operator must be related to the index operator class for the constraint."),
2213 : : parser_errposition(pstate, attribute->location)));
2214 : :
2215 : 228 : indexInfo->ii_ExclusionOps[attn] = opid;
2216 : 228 : indexInfo->ii_ExclusionProcs[attn] = get_opcode(opid);
2217 : 228 : indexInfo->ii_ExclusionStrats[attn] = strat;
2218 : 228 : nextExclOp = lnext(exclusionOpNames, nextExclOp);
2219 : : }
2220 [ + + ]: 27169 : else if (iswithoutoverlaps)
2221 : : {
2222 : : CompareType cmptype;
2223 : : StrategyNumber strat;
2224 : : Oid opid;
2225 : :
2226 [ + + ]: 1200 : if (attn == nkeycols - 1)
2227 : 589 : cmptype = COMPARE_OVERLAP;
2228 : : else
2229 : 611 : cmptype = COMPARE_EQ;
2230 : 1200 : GetOperatorFromCompareType(opclassOids[attn], InvalidOid, cmptype, &opid, &strat);
2231 : 1200 : indexInfo->ii_ExclusionOps[attn] = opid;
2232 : 1200 : indexInfo->ii_ExclusionProcs[attn] = get_opcode(opid);
2233 : 1200 : indexInfo->ii_ExclusionStrats[attn] = strat;
2234 : : }
2235 : :
2236 : : /*
2237 : : * Set up the per-column options (indoption field). For now, this is
2238 : : * zero for any un-ordered index, while ordered indexes have DESC and
2239 : : * NULLS FIRST/LAST options.
2240 : : */
2241 : 27397 : colOptions[attn] = 0;
2242 [ + + ]: 27397 : if (amcanorder)
2243 : : {
2244 : : /* default ordering is ASC */
2245 [ + + ]: 24439 : if (attribute->ordering == SORTBY_DESC)
2246 : 28 : colOptions[attn] |= INDOPTION_DESC;
2247 : : /* default null ordering is LAST for ASC, FIRST for DESC */
2248 [ + + ]: 24439 : if (attribute->nulls_ordering == SORTBY_NULLS_DEFAULT)
2249 : : {
2250 [ + + ]: 24419 : if (attribute->ordering == SORTBY_DESC)
2251 : 20 : colOptions[attn] |= INDOPTION_NULLS_FIRST;
2252 : : }
2253 [ + + ]: 20 : else if (attribute->nulls_ordering == SORTBY_NULLS_FIRST)
2254 : 8 : colOptions[attn] |= INDOPTION_NULLS_FIRST;
2255 : : }
2256 : : else
2257 : : {
2258 : : /* index AM does not support ordering */
2259 [ - + ]: 2958 : if (attribute->ordering != SORTBY_DEFAULT)
2260 [ # # ]: 0 : ereport(ERROR,
2261 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2262 : : errmsg("access method \"%s\" does not support ASC/DESC options",
2263 : : accessMethodName),
2264 : : parser_errposition(pstate, attribute->location)));
2265 [ - + ]: 2958 : if (attribute->nulls_ordering != SORTBY_NULLS_DEFAULT)
2266 [ # # ]: 0 : ereport(ERROR,
2267 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2268 : : errmsg("access method \"%s\" does not support NULLS FIRST/LAST options",
2269 : : accessMethodName),
2270 : : parser_errposition(pstate, attribute->location)));
2271 : : }
2272 : :
2273 : : /* Set up the per-column opclass options (attoptions field). */
2274 [ + + ]: 27397 : if (attribute->opclassopts)
2275 : : {
2276 : : Assert(attn < nkeycols);
2277 : :
2278 : 88 : opclassOptions[attn] =
2279 : 88 : transformRelOptions((Datum) 0, attribute->opclassopts,
2280 : : NULL, NULL, false, false);
2281 : : }
2282 : : else
2283 : 27309 : opclassOptions[attn] = (Datum) 0;
2284 : :
2285 : 27397 : attn++;
2286 : : }
2287 : 19837 : }
2288 : :
2289 : : /*
2290 : : * Resolve possibly-defaulted operator class specification
2291 : : *
2292 : : * Note: This is used to resolve operator class specifications in index and
2293 : : * partition key definitions.
2294 : : */
2295 : : Oid
2296 : 27493 : ResolveOpClass(const List *opclass, Oid attrType,
2297 : : const char *accessMethodName, Oid accessMethodId)
2298 : : {
2299 : : char *schemaname;
2300 : : char *opcname;
2301 : : HeapTuple tuple;
2302 : : Form_pg_opclass opform;
2303 : : Oid opClassId,
2304 : : opInputType;
2305 : :
2306 [ + + ]: 27493 : if (opclass == NIL)
2307 : : {
2308 : : /* no operator class specified, so find the default */
2309 : 14072 : opClassId = GetDefaultOpClass(attrType, accessMethodId);
2310 [ + + ]: 14072 : if (!OidIsValid(opClassId))
2311 [ + - ]: 4 : ereport(ERROR,
2312 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
2313 : : errmsg("data type %s has no default operator class for access method \"%s\"",
2314 : : format_type_be(attrType), accessMethodName),
2315 : : errhint("You must specify an operator class for the index or define a default operator class for the data type.")));
2316 : 14068 : return opClassId;
2317 : : }
2318 : :
2319 : : /*
2320 : : * Specific opclass name given, so look up the opclass.
2321 : : */
2322 : :
2323 : : /* deconstruct the name list */
2324 : 13421 : DeconstructQualifiedName(opclass, &schemaname, &opcname);
2325 : :
2326 [ + + ]: 13421 : if (schemaname)
2327 : : {
2328 : : /* Look in specific schema only */
2329 : : Oid namespaceId;
2330 : :
2331 : 18 : namespaceId = LookupExplicitNamespace(schemaname, false);
2332 : 18 : tuple = SearchSysCache3(CLAAMNAMENSP,
2333 : : ObjectIdGetDatum(accessMethodId),
2334 : : PointerGetDatum(opcname),
2335 : : ObjectIdGetDatum(namespaceId));
2336 : : }
2337 : : else
2338 : : {
2339 : : /* Unqualified opclass name, so search the search path */
2340 : 13403 : opClassId = OpclassnameGetOpcid(accessMethodId, opcname);
2341 [ + + ]: 13403 : if (!OidIsValid(opClassId))
2342 [ + - ]: 8 : ereport(ERROR,
2343 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
2344 : : errmsg("operator class \"%s\" does not exist for access method \"%s\"",
2345 : : opcname, accessMethodName)));
2346 : 13395 : tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opClassId));
2347 : : }
2348 : :
2349 [ - + ]: 13413 : if (!HeapTupleIsValid(tuple))
2350 [ # # ]: 0 : ereport(ERROR,
2351 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
2352 : : errmsg("operator class \"%s\" does not exist for access method \"%s\"",
2353 : : NameListToString(opclass), accessMethodName)));
2354 : :
2355 : : /*
2356 : : * Verify that the index operator class accepts this datatype. Note we
2357 : : * will accept binary compatibility.
2358 : : */
2359 : 13413 : opform = (Form_pg_opclass) GETSTRUCT(tuple);
2360 : 13413 : opClassId = opform->oid;
2361 : 13413 : opInputType = opform->opcintype;
2362 : :
2363 [ - + ]: 13413 : if (!IsBinaryCoercible(attrType, opInputType))
2364 [ # # ]: 0 : ereport(ERROR,
2365 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
2366 : : errmsg("operator class \"%s\" does not accept data type %s",
2367 : : NameListToString(opclass), format_type_be(attrType))));
2368 : :
2369 : 13413 : ReleaseSysCache(tuple);
2370 : :
2371 : 13413 : return opClassId;
2372 : : }
2373 : :
2374 : : /*
2375 : : * GetDefaultOpClass
2376 : : *
2377 : : * Given the OIDs of a datatype and an access method, find the default
2378 : : * operator class, if any. Returns InvalidOid if there is none.
2379 : : */
2380 : : Oid
2381 : 70870 : GetDefaultOpClass(Oid type_id, Oid am_id)
2382 : : {
2383 : 70870 : Oid result = InvalidOid;
2384 : 70870 : int nexact = 0;
2385 : 70870 : int ncompatible = 0;
2386 : 70870 : int ncompatiblepreferred = 0;
2387 : : Relation rel;
2388 : : ScanKeyData skey[1];
2389 : : SysScanDesc scan;
2390 : : HeapTuple tup;
2391 : : TYPCATEGORY tcategory;
2392 : :
2393 : : /* If it's a domain, look at the base type instead */
2394 : 70870 : type_id = getBaseType(type_id);
2395 : :
2396 : 70870 : tcategory = TypeCategory(type_id);
2397 : :
2398 : : /*
2399 : : * We scan through all the opclasses available for the access method,
2400 : : * looking for one that is marked default and matches the target type
2401 : : * (either exactly or binary-compatibly, but prefer an exact match).
2402 : : *
2403 : : * We could find more than one binary-compatible match. If just one is
2404 : : * for a preferred type, use that one; otherwise we fail, forcing the user
2405 : : * to specify which one he wants. (The preferred-type special case is a
2406 : : * kluge for varchar: it's binary-compatible to both text and bpchar, so
2407 : : * we need a tiebreaker.) If we find more than one exact match, then
2408 : : * someone put bogus entries in pg_opclass.
2409 : : */
2410 : 70870 : rel = table_open(OperatorClassRelationId, AccessShareLock);
2411 : :
2412 : 70870 : ScanKeyInit(&skey[0],
2413 : : Anum_pg_opclass_opcmethod,
2414 : : BTEqualStrategyNumber, F_OIDEQ,
2415 : : ObjectIdGetDatum(am_id));
2416 : :
2417 : 70870 : scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
2418 : : NULL, 1, skey);
2419 : :
2420 [ + + ]: 3125270 : while (HeapTupleIsValid(tup = systable_getnext(scan)))
2421 : : {
2422 : 3054400 : Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
2423 : :
2424 : : /* ignore altogether if not a default opclass */
2425 [ + + ]: 3054400 : if (!opclass->opcdefault)
2426 : 437521 : continue;
2427 [ + + ]: 2616879 : if (opclass->opcintype == type_id)
2428 : : {
2429 : 61406 : nexact++;
2430 : 61406 : result = opclass->oid;
2431 : : }
2432 [ + + + + ]: 3831046 : else if (nexact == 0 &&
2433 : 1275573 : IsBinaryCoercible(type_id, opclass->opcintype))
2434 : : {
2435 [ + + ]: 16328 : if (IsPreferredType(tcategory, opclass->opcintype))
2436 : : {
2437 : 1313 : ncompatiblepreferred++;
2438 : 1313 : result = opclass->oid;
2439 : : }
2440 [ + - ]: 15015 : else if (ncompatiblepreferred == 0)
2441 : : {
2442 : 15015 : ncompatible++;
2443 : 15015 : result = opclass->oid;
2444 : : }
2445 : : }
2446 : : }
2447 : :
2448 : 70870 : systable_endscan(scan);
2449 : :
2450 : 70870 : table_close(rel, AccessShareLock);
2451 : :
2452 : : /* raise error if pg_opclass contains inconsistent data */
2453 [ - + ]: 70870 : if (nexact > 1)
2454 [ # # ]: 0 : ereport(ERROR,
2455 : : (errcode(ERRCODE_DUPLICATE_OBJECT),
2456 : : errmsg("there are multiple default operator classes for data type %s",
2457 : : format_type_be(type_id))));
2458 : :
2459 [ + + + + ]: 70870 : if (nexact == 1 ||
2460 [ + - ]: 8152 : ncompatiblepreferred == 1 ||
2461 [ + + ]: 8152 : (ncompatiblepreferred == 0 && ncompatible == 1))
2462 : 70140 : return result;
2463 : :
2464 : 730 : return InvalidOid;
2465 : : }
2466 : :
2467 : : /*
2468 : : * GetOperatorFromCompareType
2469 : : *
2470 : : * opclass - the opclass to use
2471 : : * rhstype - the type for the right-hand side, or InvalidOid to use the type of the given opclass.
2472 : : * cmptype - kind of operator to find
2473 : : * opid - holds the operator we found
2474 : : * strat - holds the output strategy number
2475 : : *
2476 : : * Finds an operator from a CompareType. This is used for temporal index
2477 : : * constraints (and other temporal features) to look up equality and overlaps
2478 : : * operators. We ask an opclass support function to translate from the
2479 : : * compare type to the internal strategy numbers. Raises ERROR on search
2480 : : * failure.
2481 : : */
2482 : : void
2483 : 2647 : GetOperatorFromCompareType(Oid opclass, Oid rhstype, CompareType cmptype,
2484 : : Oid *opid, StrategyNumber *strat)
2485 : : {
2486 : : Oid amid;
2487 : : Oid opfamily;
2488 : : Oid opcintype;
2489 : :
2490 : : Assert(cmptype == COMPARE_EQ || cmptype == COMPARE_OVERLAP || cmptype == COMPARE_CONTAINED_BY);
2491 : :
2492 : : /*
2493 : : * Use the opclass to get the opfamily, opcintype, and access method. If
2494 : : * any of this fails, quit early.
2495 : : */
2496 [ - + ]: 2647 : if (!get_opclass_opfamily_and_input_type(opclass, &opfamily, &opcintype))
2497 [ # # ]: 0 : elog(ERROR, "cache lookup failed for opclass %u", opclass);
2498 : :
2499 : 2647 : amid = get_opclass_method(opclass);
2500 : :
2501 : : /*
2502 : : * Ask the index AM to translate to its internal stratnum
2503 : : */
2504 : 2647 : *strat = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
2505 [ - + ]: 2647 : if (*strat == InvalidStrategy)
2506 [ # # # # : 0 : ereport(ERROR,
# # # # ]
2507 : : errcode(ERRCODE_UNDEFINED_OBJECT),
2508 : : cmptype == COMPARE_EQ ? errmsg("could not identify an equality operator for type %s", format_type_be(opcintype)) :
2509 : : cmptype == COMPARE_OVERLAP ? errmsg("could not identify an overlaps operator for type %s", format_type_be(opcintype)) :
2510 : : cmptype == COMPARE_CONTAINED_BY ? errmsg("could not identify a contained-by operator for type %s", format_type_be(opcintype)) : 0,
2511 : : errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
2512 : : cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
2513 : :
2514 : : /*
2515 : : * We parameterize rhstype so foreign keys can ask for a <@ operator whose
2516 : : * rhs matches the aggregate function. For example range_agg returns
2517 : : * anymultirange.
2518 : : */
2519 [ + + ]: 2647 : if (!OidIsValid(rhstype))
2520 : 2388 : rhstype = opcintype;
2521 : 2647 : *opid = get_opfamily_member(opfamily, opcintype, rhstype, *strat);
2522 : :
2523 [ - + ]: 2647 : if (!OidIsValid(*opid))
2524 [ # # # # : 0 : ereport(ERROR,
# # # # ]
2525 : : errcode(ERRCODE_UNDEFINED_OBJECT),
2526 : : cmptype == COMPARE_EQ ? errmsg("could not identify an equality operator for type %s", format_type_be(opcintype)) :
2527 : : cmptype == COMPARE_OVERLAP ? errmsg("could not identify an overlaps operator for type %s", format_type_be(opcintype)) :
2528 : : cmptype == COMPARE_CONTAINED_BY ? errmsg("could not identify a contained-by operator for type %s", format_type_be(opcintype)) : 0,
2529 : : errdetail("There is no suitable operator in operator family \"%s\" for access method \"%s\".",
2530 : : get_opfamily_name(opfamily, false), get_am_name(amid)));
2531 : 2647 : }
2532 : :
2533 : : /*
2534 : : * makeObjectName()
2535 : : *
2536 : : * Create a name for an implicitly created index, sequence, constraint,
2537 : : * extended statistics, etc.
2538 : : *
2539 : : * The parameters are typically: the original table name, the original field
2540 : : * name, and a "type" string (such as "seq" or "pkey"). The field name
2541 : : * and/or type can be NULL if not relevant.
2542 : : *
2543 : : * The result is a palloc'd string.
2544 : : *
2545 : : * The basic result we want is "name1_name2_label", omitting "_name2" or
2546 : : * "_label" when those parameters are NULL. However, we must generate
2547 : : * a name with less than NAMEDATALEN characters! So, we truncate one or
2548 : : * both names if necessary to make a short-enough string. The label part
2549 : : * is never truncated (so it had better be reasonably short).
2550 : : *
2551 : : * The caller is responsible for checking uniqueness of the generated
2552 : : * name and retrying as needed; retrying will be done by altering the
2553 : : * "label" string (which is why we never truncate that part).
2554 : : */
2555 : : char *
2556 : 76606 : makeObjectName(const char *name1, const char *name2, const char *label)
2557 : : {
2558 : : char *name;
2559 : 76606 : int overhead = 0; /* chars needed for label and underscores */
2560 : : int availchars; /* chars available for name(s) */
2561 : : int name1chars; /* chars allocated to name1 */
2562 : : int name2chars; /* chars allocated to name2 */
2563 : : int ndx;
2564 : :
2565 : 76606 : name1chars = strlen(name1);
2566 [ + + ]: 76606 : if (name2)
2567 : : {
2568 : 67325 : name2chars = strlen(name2);
2569 : 67325 : overhead++; /* allow for separating underscore */
2570 : : }
2571 : : else
2572 : 9281 : name2chars = 0;
2573 [ + + ]: 76606 : if (label)
2574 : 28636 : overhead += strlen(label) + 1;
2575 : :
2576 : 76606 : availchars = NAMEDATALEN - 1 - overhead;
2577 : : Assert(availchars > 0); /* else caller chose a bad label */
2578 : :
2579 : : /*
2580 : : * If we must truncate, preferentially truncate the longer name. This
2581 : : * logic could be expressed without a loop, but it's simple and obvious as
2582 : : * a loop.
2583 : : */
2584 [ + + ]: 76770 : while (name1chars + name2chars > availchars)
2585 : : {
2586 [ - + ]: 164 : if (name1chars > name2chars)
2587 : 0 : name1chars--;
2588 : : else
2589 : 164 : name2chars--;
2590 : : }
2591 : :
2592 : 76606 : name1chars = pg_mbcliplen(name1, name1chars, name1chars);
2593 [ + + ]: 76606 : if (name2)
2594 : 67325 : name2chars = pg_mbcliplen(name2, name2chars, name2chars);
2595 : :
2596 : : /* Now construct the string using the chosen lengths */
2597 : 76606 : name = palloc(name1chars + name2chars + overhead + 1);
2598 : 76606 : memcpy(name, name1, name1chars);
2599 : 76606 : ndx = name1chars;
2600 [ + + ]: 76606 : if (name2)
2601 : : {
2602 : 67325 : name[ndx++] = '_';
2603 : 67325 : memcpy(name + ndx, name2, name2chars);
2604 : 67325 : ndx += name2chars;
2605 : : }
2606 [ + + ]: 76606 : if (label)
2607 : : {
2608 : 28636 : name[ndx++] = '_';
2609 : 28636 : strcpy(name + ndx, label);
2610 : : }
2611 : : else
2612 : 47970 : name[ndx] = '\0';
2613 : :
2614 : 76606 : return name;
2615 : : }
2616 : :
2617 : : /*
2618 : : * Select a nonconflicting name for a new relation. This is ordinarily
2619 : : * used to choose index names (which is why it's here) but it can also
2620 : : * be used for sequences, or any autogenerated relation kind.
2621 : : *
2622 : : * name1, name2, and label are used the same way as for makeObjectName(),
2623 : : * except that the label can't be NULL; digits will be appended to the label
2624 : : * if needed to create a name that is unique within the specified namespace.
2625 : : *
2626 : : * If isconstraint is true, we also avoid choosing a name matching any
2627 : : * existing constraint in the same namespace. (This is stricter than what
2628 : : * Postgres itself requires, but the SQL standard says that constraint names
2629 : : * should be unique within schemas, so we follow that for autogenerated
2630 : : * constraint names.)
2631 : : *
2632 : : * Note: it is theoretically possible to get a collision anyway, if someone
2633 : : * else chooses the same name concurrently. We shorten the race condition
2634 : : * window by checking for conflicting relations using SnapshotDirty, but
2635 : : * that doesn't close the window entirely. This is fairly unlikely to be
2636 : : * a problem in practice, especially if one is holding an exclusive lock on
2637 : : * the relation identified by name1. However, if choosing multiple names
2638 : : * within a single command, you'd better create the new object and do
2639 : : * CommandCounterIncrement before choosing the next one!
2640 : : *
2641 : : * Returns a palloc'd string.
2642 : : */
2643 : : char *
2644 : 9635 : ChooseRelationName(const char *name1, const char *name2,
2645 : : const char *label, Oid namespaceid,
2646 : : bool isconstraint)
2647 : : {
2648 : 9635 : int pass = 0;
2649 : 9635 : char *relname = NULL;
2650 : : char modlabel[NAMEDATALEN];
2651 : : SnapshotData SnapshotDirty;
2652 : : Relation pgclassrel;
2653 : :
2654 : : /* prepare to search pg_class with a dirty snapshot */
2655 : 9635 : InitDirtySnapshot(SnapshotDirty);
2656 : 9635 : pgclassrel = table_open(RelationRelationId, AccessShareLock);
2657 : :
2658 : : /* try the unmodified label first */
2659 : 9635 : strlcpy(modlabel, label, sizeof(modlabel));
2660 : :
2661 : : for (;;)
2662 : 143 : {
2663 : : ScanKeyData key[2];
2664 : : SysScanDesc scan;
2665 : : bool collides;
2666 : :
2667 : 9778 : relname = makeObjectName(name1, name2, modlabel);
2668 : :
2669 : : /* is there any conflicting relation name? */
2670 : 9778 : ScanKeyInit(&key[0],
2671 : : Anum_pg_class_relname,
2672 : : BTEqualStrategyNumber, F_NAMEEQ,
2673 : : CStringGetDatum(relname));
2674 : 9778 : ScanKeyInit(&key[1],
2675 : : Anum_pg_class_relnamespace,
2676 : : BTEqualStrategyNumber, F_OIDEQ,
2677 : : ObjectIdGetDatum(namespaceid));
2678 : :
2679 : 9778 : scan = systable_beginscan(pgclassrel, ClassNameNspIndexId,
2680 : : true /* indexOK */ ,
2681 : : &SnapshotDirty,
2682 : : 2, key);
2683 : :
2684 : 9778 : collides = HeapTupleIsValid(systable_getnext(scan));
2685 : :
2686 : 9778 : systable_endscan(scan);
2687 : :
2688 : : /* break out of loop if no conflict */
2689 [ + + ]: 9778 : if (!collides)
2690 : : {
2691 [ + + ]: 9639 : if (!isconstraint ||
2692 [ + + ]: 5955 : !ConstraintNameExists(relname, namespaceid))
2693 : : break;
2694 : : }
2695 : :
2696 : : /* found a conflict, so try a new name component */
2697 : 143 : pfree(relname);
2698 : 143 : snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
2699 : : }
2700 : :
2701 : 9635 : table_close(pgclassrel, AccessShareLock);
2702 : :
2703 : 9635 : return relname;
2704 : : }
2705 : :
2706 : : /*
2707 : : * Select the name to be used for an index.
2708 : : *
2709 : : * The argument list is pretty ad-hoc :-(
2710 : : */
2711 : : static char *
2712 : 8120 : ChooseIndexName(const char *tabname, Oid namespaceId,
2713 : : const List *colnames, const List *exclusionOpNames,
2714 : : bool primary, bool isconstraint)
2715 : : {
2716 : : char *indexname;
2717 : :
2718 [ + + ]: 8120 : if (primary)
2719 : : {
2720 : : /* the primary key's name does not depend on the specific column(s) */
2721 : 5295 : indexname = ChooseRelationName(tabname,
2722 : : NULL,
2723 : : "pkey",
2724 : : namespaceId,
2725 : : true);
2726 : : }
2727 [ + + ]: 2825 : else if (exclusionOpNames != NIL)
2728 : : {
2729 : 157 : indexname = ChooseRelationName(tabname,
2730 : 157 : ChooseIndexNameAddition(colnames),
2731 : : "excl",
2732 : : namespaceId,
2733 : : true);
2734 : : }
2735 [ + + ]: 2668 : else if (isconstraint)
2736 : : {
2737 : 499 : indexname = ChooseRelationName(tabname,
2738 : 499 : ChooseIndexNameAddition(colnames),
2739 : : "key",
2740 : : namespaceId,
2741 : : true);
2742 : : }
2743 : : else
2744 : : {
2745 : 2169 : indexname = ChooseRelationName(tabname,
2746 : 2169 : ChooseIndexNameAddition(colnames),
2747 : : "idx",
2748 : : namespaceId,
2749 : : false);
2750 : : }
2751 : :
2752 : 8120 : return indexname;
2753 : : }
2754 : :
2755 : : /*
2756 : : * Generate "name2" for a new index given the list of column names for it
2757 : : * (as produced by ChooseIndexColumnNames). This will be passed to
2758 : : * ChooseRelationName along with the parent table name and a suitable label.
2759 : : *
2760 : : * We know that less than NAMEDATALEN characters will actually be used,
2761 : : * so we can truncate the result once we've generated that many.
2762 : : *
2763 : : * XXX See also ChooseForeignKeyConstraintNameAddition and
2764 : : * ChooseExtendedStatisticNameAddition.
2765 : : */
2766 : : static char *
2767 : 2825 : ChooseIndexNameAddition(const List *colnames)
2768 : : {
2769 : : char buf[NAMEDATALEN * 2];
2770 : 2825 : int buflen = 0;
2771 : : ListCell *lc;
2772 : :
2773 : 2825 : buf[0] = '\0';
2774 [ + - + + : 6368 : foreach(lc, colnames)
+ + ]
2775 : : {
2776 : 3543 : const char *name = (const char *) lfirst(lc);
2777 : :
2778 [ + + ]: 3543 : if (buflen > 0)
2779 : 718 : buf[buflen++] = '_'; /* insert _ between names */
2780 : :
2781 : : /*
2782 : : * At this point we have buflen <= NAMEDATALEN. name should be less
2783 : : * than NAMEDATALEN already, but use strlcpy for paranoia.
2784 : : */
2785 : 3543 : strlcpy(buf + buflen, name, NAMEDATALEN);
2786 : 3543 : buflen += strlen(buf + buflen);
2787 [ - + ]: 3543 : if (buflen >= NAMEDATALEN)
2788 : 0 : break;
2789 : : }
2790 : 2825 : return pstrdup(buf);
2791 : : }
2792 : :
2793 : : /*
2794 : : * Select the actual names to be used for the columns of an index, given the
2795 : : * parent Relation and the list of IndexElems for the columns. The logic in
2796 : : * this function is mostly about ensuring the names are unique so we don't
2797 : : * get a conflicting-attribute-names error.
2798 : : *
2799 : : * Returns a List of plain strings (char *, not String nodes).
2800 : : */
2801 : : static List *
2802 : 20039 : ChooseIndexColumnNames(Relation rel, const List *indexElems)
2803 : : {
2804 : 20039 : List *result = NIL;
2805 : : ListCell *lc;
2806 : :
2807 [ + - + + : 48082 : foreach(lc, indexElems)
+ + ]
2808 : : {
2809 : 28043 : IndexElem *ielem = (IndexElem *) lfirst(lc);
2810 : : const char *origname;
2811 : : const char *curname;
2812 : : int i;
2813 : : char buf[NAMEDATALEN];
2814 : :
2815 : : /* Get the preliminary name from the IndexElem */
2816 [ + + ]: 28043 : if (ielem->indexcolname)
2817 : 2564 : origname = ielem->indexcolname; /* caller-specified name */
2818 [ + + ]: 25479 : else if (ielem->name)
2819 : 24690 : origname = ielem->name; /* simple column reference */
2820 : : else
2821 : 789 : origname = ChooseIndexExpressionName(rel, ielem->expr);
2822 : :
2823 : : /* If it conflicts with any previous column, tweak it */
2824 : 28043 : curname = origname;
2825 : 28043 : for (i = 1;; i++)
2826 : 36 : {
2827 : : ListCell *lc2;
2828 : : char nbuf[32];
2829 : : int nlen;
2830 : :
2831 [ + + + + : 43568 : foreach(lc2, result)
+ + ]
2832 : : {
2833 [ + + ]: 15525 : if (strcmp(curname, (char *) lfirst(lc2)) == 0)
2834 : 36 : break;
2835 : : }
2836 [ + + ]: 28079 : if (lc2 == NULL)
2837 : 28043 : break; /* found nonconflicting name */
2838 : :
2839 : 36 : sprintf(nbuf, "%d", i);
2840 : :
2841 : : /* Ensure generated names are shorter than NAMEDATALEN */
2842 : 36 : nlen = pg_mbcliplen(origname, strlen(origname),
2843 : 36 : NAMEDATALEN - 1 - strlen(nbuf));
2844 : 36 : memcpy(buf, origname, nlen);
2845 : 36 : strcpy(buf + nlen, nbuf);
2846 : 36 : curname = buf;
2847 : : }
2848 : :
2849 : : /* And attach to the result list */
2850 : 28043 : result = lappend(result, pstrdup(curname));
2851 : : }
2852 : 20039 : return result;
2853 : : }
2854 : :
2855 : : /*
2856 : : * Generate a suitable index-column name for an index expression.
2857 : : *
2858 : : * Our strategy is to collect the Var names, Const values, and function names
2859 : : * appearing in the expression, and print them separated by underscores.
2860 : : * We could expend a lot more effort to handle additional expression node
2861 : : * types, but this seems sufficient to usually produce a column name distinct
2862 : : * from other index expressions.
2863 : : */
2864 : : static char *
2865 : 789 : ChooseIndexExpressionName(Relation rel, Node *indexExpr)
2866 : : {
2867 : : StringInfoData buf;
2868 : : CIEN_context context;
2869 : : int nlen;
2870 : :
2871 : : /* Prepare ... */
2872 : 789 : initStringInfo(&buf);
2873 : 789 : context.rel = rel;
2874 : 789 : context.buf = &buf;
2875 : : /* Walk the tree, stopping when we have enough text */
2876 : 789 : (void) ChooseIndexExpressionName_walker(indexExpr, &context);
2877 : : /* Ensure generated names are shorter than NAMEDATALEN */
2878 : 789 : nlen = pg_mbcliplen(buf.data, buf.len, NAMEDATALEN - 1);
2879 : 789 : buf.data[nlen] = '\0';
2880 : 789 : return buf.data;
2881 : : }
2882 : :
2883 : : /* Recursive guts of ChooseIndexExpressionName */
2884 : : static bool
2885 : 3573 : ChooseIndexExpressionName_walker(Node *node,
2886 : : CIEN_context *context)
2887 : : {
2888 [ + + ]: 3573 : if (node == NULL)
2889 : 336 : return false;
2890 [ + + ]: 3237 : if (IsA(node, Var))
2891 : : {
2892 : 916 : Var *var = (Var *) node;
2893 : 916 : TupleDesc tupdesc = RelationGetDescr(context->rel);
2894 : : Form_pg_attribute att;
2895 : :
2896 : : /* Paranoia: ignore the Var if it looks fishy */
2897 [ + - + - ]: 916 : if (var->varno != 1 || var->varlevelsup != 0 ||
2898 [ + + - + ]: 916 : var->varattno <= 0 || var->varattno > tupdesc->natts)
2899 : 8 : return false;
2900 : 908 : att = TupleDescAttr(tupdesc, var->varattno - 1);
2901 [ - + ]: 908 : if (att->attisdropped)
2902 : 0 : return false; /* even more paranoia; shouldn't happen */
2903 : :
2904 [ + + ]: 908 : if (context->buf->len > 0)
2905 : 386 : appendStringInfoChar(context->buf, '_');
2906 : 908 : appendStringInfoString(context->buf, NameStr(att->attname));
2907 : :
2908 : : /* Done if we've already reached NAMEDATALEN */
2909 : 908 : return (context->buf->len >= NAMEDATALEN);
2910 : : }
2911 [ + + ]: 2321 : else if (IsA(node, Const))
2912 : : {
2913 : 874 : Const *constval = (Const *) node;
2914 : :
2915 [ + + ]: 874 : if (context->buf->len > 0)
2916 : 785 : appendStringInfoChar(context->buf, '_');
2917 [ + + ]: 874 : if (constval->constisnull)
2918 : 372 : appendStringInfoString(context->buf, "NULL");
2919 : : else
2920 : : {
2921 : : Oid typoutput;
2922 : : bool typIsVarlena;
2923 : : char *extval;
2924 : :
2925 : 502 : getTypeOutputInfo(constval->consttype,
2926 : : &typoutput, &typIsVarlena);
2927 : 502 : extval = OidOutputFunctionCall(typoutput, constval->constvalue);
2928 : :
2929 : : /*
2930 : : * We sanitize constant values by dropping non-alphanumeric ASCII
2931 : : * characters. This is probably not really necessary, but it
2932 : : * reduces the odds of needing to double-quote the generated name.
2933 : : */
2934 [ + + ]: 397691 : for (const char *ptr = extval; *ptr; ptr++)
2935 : : {
2936 [ + - ]: 397189 : if (IS_HIGHBIT_SET(*ptr) ||
2937 : 397189 : strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2938 : : "abcdefghijklmnopqrstuvwxyz"
2939 [ + + ]: 397189 : "0123456789", *ptr) != NULL)
2940 : 314427 : appendStringInfoChar(context->buf, *ptr);
2941 : : }
2942 : : }
2943 : :
2944 : : /* Done if we've already reached NAMEDATALEN */
2945 : 874 : return (context->buf->len >= NAMEDATALEN);
2946 : : }
2947 [ + + ]: 1447 : else if (IsA(node, FuncExpr))
2948 : : {
2949 : 226 : FuncExpr *funcexpr = (FuncExpr *) node;
2950 : 226 : char *fname = get_func_name(funcexpr->funcid);
2951 : :
2952 [ + - ]: 226 : if (fname)
2953 : : {
2954 [ + + ]: 226 : if (context->buf->len > 0)
2955 : 48 : appendStringInfoChar(context->buf, '_');
2956 : 226 : appendStringInfoString(context->buf, fname);
2957 : : }
2958 : : /* fall through to examine arguments */
2959 : : }
2960 : :
2961 : : /* Abandon recursion once we reach NAMEDATALEN */
2962 [ - + ]: 1447 : if (context->buf->len >= NAMEDATALEN)
2963 : 0 : return true;
2964 : :
2965 : 1447 : return expression_tree_walker(node, ChooseIndexExpressionName_walker,
2966 : : context);
2967 : : }
2968 : :
2969 : : /*
2970 : : * ExecReindex
2971 : : *
2972 : : * Primary entry point for manual REINDEX commands. This is mainly a
2973 : : * preparation wrapper for the real operations that will happen in
2974 : : * each subroutine of REINDEX.
2975 : : */
2976 : : void
2977 : 700 : ExecReindex(ParseState *pstate, const ReindexStmt *stmt, bool isTopLevel)
2978 : : {
2979 : 700 : ReindexParams params = {0};
2980 : : ListCell *lc;
2981 : 700 : bool concurrently = false;
2982 : 700 : bool verbose = false;
2983 : 700 : char *tablespacename = NULL;
2984 : :
2985 : : /* Parse option list */
2986 [ + + + + : 1179 : foreach(lc, stmt->params)
+ + ]
2987 : : {
2988 : 479 : DefElem *opt = (DefElem *) lfirst(lc);
2989 : :
2990 [ + + ]: 479 : if (strcmp(opt->defname, "verbose") == 0)
2991 : 8 : verbose = defGetBoolean(opt);
2992 [ + + ]: 471 : else if (strcmp(opt->defname, "concurrently") == 0)
2993 : 390 : concurrently = defGetBoolean(opt);
2994 [ + - ]: 81 : else if (strcmp(opt->defname, "tablespace") == 0)
2995 : 81 : tablespacename = defGetString(opt);
2996 : : else
2997 [ # # ]: 0 : ereport(ERROR,
2998 : : (errcode(ERRCODE_SYNTAX_ERROR),
2999 : : errmsg("unrecognized %s option \"%s\"",
3000 : : "REINDEX", opt->defname),
3001 : : parser_errposition(pstate, opt->location)));
3002 : : }
3003 : :
3004 [ + + ]: 700 : if (concurrently)
3005 : 390 : PreventInTransactionBlock(isTopLevel,
3006 : : "REINDEX CONCURRENTLY");
3007 : :
3008 : 683 : params.options =
3009 : 1366 : (verbose ? REINDEXOPT_VERBOSE : 0) |
3010 [ + + ]: 683 : (concurrently ? REINDEXOPT_CONCURRENTLY : 0);
3011 : :
3012 : : /*
3013 : : * Assign the tablespace OID to move indexes to, with InvalidOid to do
3014 : : * nothing.
3015 : : */
3016 [ + + ]: 683 : if (tablespacename != NULL)
3017 : : {
3018 : 81 : params.tablespaceOid = get_tablespace_oid(tablespacename, false);
3019 : :
3020 : : /* Check permissions except when moving to database's default */
3021 [ + - ]: 81 : if (OidIsValid(params.tablespaceOid) &&
3022 [ + - ]: 81 : params.tablespaceOid != MyDatabaseTableSpace)
3023 : : {
3024 : : AclResult aclresult;
3025 : :
3026 : 81 : aclresult = object_aclcheck(TableSpaceRelationId, params.tablespaceOid,
3027 : : GetUserId(), ACL_CREATE);
3028 [ + + ]: 81 : if (aclresult != ACLCHECK_OK)
3029 : 6 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
3030 : 6 : get_tablespace_name(params.tablespaceOid));
3031 : : }
3032 : : }
3033 : : else
3034 : 602 : params.tablespaceOid = InvalidOid;
3035 : :
3036 [ + + + - ]: 677 : switch (stmt->kind)
3037 : : {
3038 : 258 : case REINDEX_OBJECT_INDEX:
3039 : 258 : ReindexIndex(stmt, ¶ms, isTopLevel);
3040 : 189 : break;
3041 : 310 : case REINDEX_OBJECT_TABLE:
3042 : 310 : ReindexTable(stmt, ¶ms, isTopLevel);
3043 : 231 : break;
3044 : 109 : case REINDEX_OBJECT_SCHEMA:
3045 : : case REINDEX_OBJECT_SYSTEM:
3046 : : case REINDEX_OBJECT_DATABASE:
3047 : :
3048 : : /*
3049 : : * This cannot run inside a user transaction block; if we were
3050 : : * inside a transaction, then its commit- and
3051 : : * start-transaction-command calls would not have the intended
3052 : : * effect!
3053 : : */
3054 : 109 : PreventInTransactionBlock(isTopLevel,
3055 [ + + ]: 144 : (stmt->kind == REINDEX_OBJECT_SCHEMA) ? "REINDEX SCHEMA" :
3056 [ + + ]: 35 : (stmt->kind == REINDEX_OBJECT_SYSTEM) ? "REINDEX SYSTEM" :
3057 : : "REINDEX DATABASE");
3058 : 105 : ReindexMultipleTables(stmt, ¶ms);
3059 : 72 : break;
3060 : 0 : default:
3061 [ # # ]: 0 : elog(ERROR, "unrecognized object type: %d",
3062 : : (int) stmt->kind);
3063 : : break;
3064 : : }
3065 : 492 : }
3066 : :
3067 : : /*
3068 : : * ReindexIndex
3069 : : * Recreate a specific index.
3070 : : */
3071 : : static void
3072 : 258 : ReindexIndex(const ReindexStmt *stmt, const ReindexParams *params, bool isTopLevel)
3073 : : {
3074 : 258 : const RangeVar *indexRelation = stmt->relation;
3075 : : struct ReindexIndexCallbackState state;
3076 : : Oid indOid;
3077 : : char persistence;
3078 : : char relkind;
3079 : :
3080 : : /*
3081 : : * Find and lock index, and check permissions on table; use callback to
3082 : : * obtain lock on table first, to avoid deadlock hazard. The lock level
3083 : : * used here must match the index lock obtained in reindex_index().
3084 : : *
3085 : : * If it's a temporary index, we will perform a non-concurrent reindex,
3086 : : * even if CONCURRENTLY was requested. In that case, reindex_index() will
3087 : : * upgrade the lock, but that's OK, because other sessions can't hold
3088 : : * locks on our temporary table.
3089 : : */
3090 : 258 : state.params = *params;
3091 : 258 : state.locked_table_oid = InvalidOid;
3092 : 258 : indOid = RangeVarGetRelidExtended(indexRelation,
3093 [ + + ]: 258 : (params->options & REINDEXOPT_CONCURRENTLY) != 0 ?
3094 : : ShareUpdateExclusiveLock : AccessExclusiveLock,
3095 : : 0,
3096 : : RangeVarCallbackForReindexIndex,
3097 : : &state);
3098 : :
3099 : : /*
3100 : : * Obtain the current persistence and kind of the existing index. We
3101 : : * already hold a lock on the index.
3102 : : */
3103 : 227 : persistence = get_rel_persistence(indOid);
3104 : 227 : relkind = get_rel_relkind(indOid);
3105 : :
3106 [ + + ]: 227 : if (relkind == RELKIND_PARTITIONED_INDEX)
3107 : 24 : ReindexPartitions(stmt, indOid, params, isTopLevel);
3108 [ + + + + ]: 203 : else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
3109 : : persistence != RELPERSISTENCE_TEMP)
3110 : 120 : ReindexRelationConcurrently(stmt, indOid, params);
3111 : : else
3112 : : {
3113 : 83 : ReindexParams newparams = *params;
3114 : :
3115 : 83 : newparams.options |= REINDEXOPT_REPORT_PROGRESS;
3116 : 83 : reindex_index(stmt, indOid, false, persistence, &newparams);
3117 : : }
3118 : 189 : }
3119 : :
3120 : : /*
3121 : : * Check permissions on table before acquiring relation lock; also lock
3122 : : * the heap before the RangeVarGetRelidExtended takes the index lock, to avoid
3123 : : * deadlocks.
3124 : : */
3125 : : static void
3126 : 266 : RangeVarCallbackForReindexIndex(const RangeVar *relation,
3127 : : Oid relId, Oid oldRelId, void *arg)
3128 : : {
3129 : : char relkind;
3130 : 266 : struct ReindexIndexCallbackState *state = arg;
3131 : : LOCKMODE table_lockmode;
3132 : : Oid table_oid;
3133 : : AclResult aclresult;
3134 : :
3135 : : /*
3136 : : * Lock level here should match table lock in reindex_index() for
3137 : : * non-concurrent case and table locks used by index_concurrently_*() for
3138 : : * concurrent case.
3139 : : */
3140 : 532 : table_lockmode = (state->params.options & REINDEXOPT_CONCURRENTLY) != 0 ?
3141 [ + + ]: 266 : ShareUpdateExclusiveLock : ShareLock;
3142 : :
3143 : : /*
3144 : : * If we previously locked some other index's heap, and the name we're
3145 : : * looking up no longer refers to that relation, release the now-useless
3146 : : * lock.
3147 : : */
3148 [ + + + + ]: 266 : if (relId != oldRelId && OidIsValid(oldRelId))
3149 : : {
3150 : 3 : UnlockRelationOid(state->locked_table_oid, table_lockmode);
3151 : 3 : state->locked_table_oid = InvalidOid;
3152 : : }
3153 : :
3154 : : /* If the relation does not exist, there's nothing more to do. */
3155 [ + + ]: 266 : if (!OidIsValid(relId))
3156 : 7 : return;
3157 : :
3158 : : /* If the relation does exist, check whether it's an index. */
3159 : 259 : relkind = get_rel_relkind(relId);
3160 [ + + + + ]: 259 : if (relkind != RELKIND_INDEX &&
3161 : : relkind != RELKIND_PARTITIONED_INDEX)
3162 [ + - ]: 16 : ereport(ERROR,
3163 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3164 : : errmsg("\"%s\" is not an index", relation->relname)));
3165 : :
3166 : : /* Look up the index's table. */
3167 : 243 : table_oid = IndexGetRelation(relId, false);
3168 : :
3169 : : /*
3170 : : * In the unlikely event that, upon retry, we get the same index OID with
3171 : : * a different table OID, fail. RangeVarGetRelidExtended() will have
3172 : : * already locked the index in this case, and it won't retry again, so we
3173 : : * can't lock the newly discovered table OID without risking deadlock.
3174 : : * Also, while this corner case is indeed possible, it is extremely
3175 : : * unlikely to happen in practice, so it's probably not worth any more
3176 : : * effort than this.
3177 : : */
3178 [ + + - + ]: 243 : if (relId == oldRelId && table_oid != state->locked_table_oid)
3179 [ # # ]: 0 : ereport(ERROR,
3180 : : (errcode(ERRCODE_UNDEFINED_OBJECT),
3181 : : errmsg("index \"%s\" was concurrently dropped",
3182 : : relation->relname)));
3183 : :
3184 : : /* Check permissions. */
3185 : 243 : aclresult = pg_class_aclcheck(table_oid, GetUserId(), ACL_MAINTAIN);
3186 [ + + ]: 243 : if (aclresult != ACLCHECK_OK)
3187 : 8 : aclcheck_error(aclresult, OBJECT_INDEX, relation->relname);
3188 : :
3189 : : /* Lock heap before index to avoid deadlock. */
3190 [ + + ]: 235 : if (relId != oldRelId)
3191 : : {
3192 : 230 : LockRelationOid(table_oid, table_lockmode);
3193 : 230 : state->locked_table_oid = table_oid;
3194 : : }
3195 : : }
3196 : :
3197 : : /*
3198 : : * ReindexTable
3199 : : * Recreate all indexes of a table (and of its toast table, if any)
3200 : : */
3201 : : static Oid
3202 : 310 : ReindexTable(const ReindexStmt *stmt, const ReindexParams *params, bool isTopLevel)
3203 : : {
3204 : : Oid heapOid;
3205 : : bool result;
3206 : 310 : const RangeVar *relation = stmt->relation;
3207 : :
3208 : : /*
3209 : : * The lock level used here should match reindex_relation().
3210 : : *
3211 : : * If it's a temporary table, we will perform a non-concurrent reindex,
3212 : : * even if CONCURRENTLY was requested. In that case, reindex_relation()
3213 : : * will upgrade the lock, but that's OK, because other sessions can't hold
3214 : : * locks on our temporary table.
3215 : : */
3216 : 310 : heapOid = RangeVarGetRelidExtended(relation,
3217 [ + + ]: 310 : (params->options & REINDEXOPT_CONCURRENTLY) != 0 ?
3218 : : ShareUpdateExclusiveLock : ShareLock,
3219 : : 0,
3220 : : RangeVarCallbackMaintainsTable, NULL);
3221 : :
3222 [ + + ]: 281 : if (get_rel_relkind(heapOid) == RELKIND_PARTITIONED_TABLE)
3223 : 47 : ReindexPartitions(stmt, heapOid, params, isTopLevel);
3224 [ + + + + ]: 379 : else if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
3225 : 145 : get_rel_persistence(heapOid) != RELPERSISTENCE_TEMP)
3226 : : {
3227 : 137 : result = ReindexRelationConcurrently(stmt, heapOid, params);
3228 : :
3229 [ + + ]: 112 : if (!result)
3230 [ + - ]: 12 : ereport(NOTICE,
3231 : : (errmsg("table \"%s\" has no indexes that can be reindexed concurrently",
3232 : : relation->relname)));
3233 : : }
3234 : : else
3235 : : {
3236 : 97 : ReindexParams newparams = *params;
3237 : :
3238 : 97 : newparams.options |= REINDEXOPT_REPORT_PROGRESS;
3239 : 97 : result = reindex_relation(stmt, heapOid,
3240 : : REINDEX_REL_PROCESS_TOAST |
3241 : : REINDEX_REL_CHECK_CONSTRAINTS,
3242 : : &newparams);
3243 [ + + ]: 76 : if (!result)
3244 [ + - ]: 8 : ereport(NOTICE,
3245 : : (errmsg("table \"%s\" has no indexes to reindex",
3246 : : relation->relname)));
3247 : : }
3248 : :
3249 : 231 : return heapOid;
3250 : : }
3251 : :
3252 : : /*
3253 : : * ReindexMultipleTables
3254 : : * Recreate indexes of tables selected by objectName/objectKind.
3255 : : *
3256 : : * To reduce the probability of deadlocks, each table is reindexed in a
3257 : : * separate transaction, so we can release the lock on it right away.
3258 : : * That means this must not be called within a user transaction block!
3259 : : */
3260 : : static void
3261 : 105 : ReindexMultipleTables(const ReindexStmt *stmt, const ReindexParams *params)
3262 : : {
3263 : :
3264 : : Oid objectOid;
3265 : : Relation relationRelation;
3266 : : TableScanDesc scan;
3267 : : ScanKeyData scan_keys[1];
3268 : : HeapTuple tuple;
3269 : : MemoryContext private_context;
3270 : : MemoryContext old;
3271 : 105 : List *relids = NIL;
3272 : : int num_keys;
3273 : 105 : bool concurrent_warning = false;
3274 : 105 : bool tablespace_warning = false;
3275 : 105 : const char *objectName = stmt->name;
3276 : 105 : const ReindexObjectType objectKind = stmt->kind;
3277 : :
3278 : : Assert(objectKind == REINDEX_OBJECT_SCHEMA ||
3279 : : objectKind == REINDEX_OBJECT_SYSTEM ||
3280 : : objectKind == REINDEX_OBJECT_DATABASE);
3281 : :
3282 : : /*
3283 : : * This matches the options enforced by the grammar, where the object name
3284 : : * is optional for DATABASE and SYSTEM.
3285 : : */
3286 : : Assert(objectName || objectKind != REINDEX_OBJECT_SCHEMA);
3287 : :
3288 [ + + ]: 105 : if (objectKind == REINDEX_OBJECT_SYSTEM &&
3289 [ + + ]: 20 : (params->options & REINDEXOPT_CONCURRENTLY) != 0)
3290 [ + - ]: 13 : ereport(ERROR,
3291 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3292 : : errmsg("cannot reindex system catalogs concurrently")));
3293 : :
3294 : : /*
3295 : : * Get OID of object to reindex, being the database currently being used
3296 : : * by session for a database or for system catalogs, or the schema defined
3297 : : * by caller. At the same time do permission checks that need different
3298 : : * processing depending on the object type.
3299 : : */
3300 [ + + ]: 92 : if (objectKind == REINDEX_OBJECT_SCHEMA)
3301 : : {
3302 : 70 : objectOid = get_namespace_oid(objectName, false);
3303 : :
3304 [ + + ]: 66 : if (!object_ownercheck(NamespaceRelationId, objectOid, GetUserId()) &&
3305 [ + + ]: 16 : !has_privs_of_role(GetUserId(), ROLE_PG_MAINTAIN))
3306 : 12 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SCHEMA,
3307 : : objectName);
3308 : : }
3309 : : else
3310 : : {
3311 : 22 : objectOid = MyDatabaseId;
3312 : :
3313 [ + - + + ]: 22 : if (objectName && strcmp(objectName, get_database_name(objectOid)) != 0)
3314 [ + - ]: 4 : ereport(ERROR,
3315 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3316 : : errmsg("can only reindex the currently open database")));
3317 [ - + ]: 18 : if (!object_ownercheck(DatabaseRelationId, objectOid, GetUserId()) &&
3318 [ # # ]: 0 : !has_privs_of_role(GetUserId(), ROLE_PG_MAINTAIN))
3319 : 0 : aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_DATABASE,
3320 : 0 : get_database_name(objectOid));
3321 : : }
3322 : :
3323 : : /*
3324 : : * Create a memory context that will survive forced transaction commits we
3325 : : * do below. Since it is a child of PortalContext, it will go away
3326 : : * eventually even if we suffer an error; there's no need for special
3327 : : * abort cleanup logic.
3328 : : */
3329 : 72 : private_context = AllocSetContextCreate(PortalContext,
3330 : : "ReindexMultipleTables",
3331 : : ALLOCSET_SMALL_SIZES);
3332 : :
3333 : : /*
3334 : : * Define the search keys to find the objects to reindex. For a schema, we
3335 : : * select target relations using relnamespace, something not necessary for
3336 : : * a database-wide operation.
3337 : : */
3338 [ + + ]: 72 : if (objectKind == REINDEX_OBJECT_SCHEMA)
3339 : : {
3340 : 54 : num_keys = 1;
3341 : 54 : ScanKeyInit(&scan_keys[0],
3342 : : Anum_pg_class_relnamespace,
3343 : : BTEqualStrategyNumber, F_OIDEQ,
3344 : : ObjectIdGetDatum(objectOid));
3345 : : }
3346 : : else
3347 : 18 : num_keys = 0;
3348 : :
3349 : : /*
3350 : : * Scan pg_class to build a list of the relations we need to reindex.
3351 : : *
3352 : : * We only consider plain relations and materialized views here (toast
3353 : : * rels will be processed indirectly by reindex_relation).
3354 : : */
3355 : 72 : relationRelation = table_open(RelationRelationId, AccessShareLock);
3356 : 72 : scan = table_beginscan_catalog(relationRelation, num_keys, scan_keys);
3357 [ + + ]: 9981 : while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
3358 : : {
3359 : 9909 : Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
3360 : 9909 : Oid relid = classtuple->oid;
3361 : :
3362 : : /*
3363 : : * Only regular tables and matviews can have indexes, so ignore any
3364 : : * other kind of relation.
3365 : : *
3366 : : * Partitioned tables/indexes are skipped but matching leaf partitions
3367 : : * are processed.
3368 : : */
3369 [ + + ]: 9909 : if (classtuple->relkind != RELKIND_RELATION &&
3370 [ + + ]: 8136 : classtuple->relkind != RELKIND_MATVIEW)
3371 : 8124 : continue;
3372 : :
3373 : : /* Skip temp tables of other backends; we can't reindex them at all */
3374 [ + + ]: 1785 : if (classtuple->relpersistence == RELPERSISTENCE_TEMP &&
3375 [ - + ]: 24 : !isTempNamespace(classtuple->relnamespace))
3376 : 0 : continue;
3377 : :
3378 : : /*
3379 : : * Check user/system classification. SYSTEM processes all the
3380 : : * catalogs, and DATABASE processes everything that's not a catalog.
3381 : : */
3382 [ + + ]: 1785 : if (objectKind == REINDEX_OBJECT_SYSTEM &&
3383 [ + + ]: 527 : !IsCatalogRelationOid(relid))
3384 : 44 : continue;
3385 [ + + + + ]: 2550 : else if (objectKind == REINDEX_OBJECT_DATABASE &&
3386 : 809 : IsCatalogRelationOid(relid))
3387 : 759 : continue;
3388 : :
3389 : : /*
3390 : : * We already checked privileges on the database or schema, but we
3391 : : * further restrict reindexing shared catalogs to roles with the
3392 : : * MAINTAIN privilege on the relation.
3393 : : */
3394 [ + + - + ]: 1114 : if (classtuple->relisshared &&
3395 : 132 : pg_class_aclcheck(relid, GetUserId(), ACL_MAINTAIN) != ACLCHECK_OK)
3396 : 0 : continue;
3397 : :
3398 : : /*
3399 : : * Skip system tables, since index_create() would reject indexing them
3400 : : * concurrently (and it would likely fail if we tried).
3401 : : */
3402 [ + + + + ]: 1320 : if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
3403 : 338 : IsCatalogRelationOid(relid))
3404 : : {
3405 [ + + ]: 276 : if (!concurrent_warning)
3406 [ + - ]: 4 : ereport(WARNING,
3407 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3408 : : errmsg("cannot reindex system catalogs concurrently, skipping all")));
3409 : 276 : concurrent_warning = true;
3410 : 276 : continue;
3411 : : }
3412 : :
3413 : : /*
3414 : : * If a new tablespace is set, check if this relation has to be
3415 : : * skipped.
3416 : : */
3417 [ - + ]: 706 : if (OidIsValid(params->tablespaceOid))
3418 : : {
3419 : 0 : bool skip_rel = false;
3420 : :
3421 : : /*
3422 : : * Mapped relations cannot be moved to different tablespaces (in
3423 : : * particular this eliminates all shared catalogs.).
3424 : : */
3425 [ # # # # : 0 : if (RELKIND_HAS_STORAGE(classtuple->relkind) &&
# # # # #
# ]
3426 [ # # ]: 0 : !RelFileNumberIsValid(classtuple->relfilenode))
3427 : 0 : skip_rel = true;
3428 : :
3429 : : /*
3430 : : * A system relation is always skipped, even with
3431 : : * allow_system_table_mods enabled.
3432 : : */
3433 [ # # ]: 0 : if (IsSystemClass(relid, classtuple))
3434 : 0 : skip_rel = true;
3435 : :
3436 [ # # ]: 0 : if (skip_rel)
3437 : : {
3438 [ # # ]: 0 : if (!tablespace_warning)
3439 [ # # ]: 0 : ereport(WARNING,
3440 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3441 : : errmsg("cannot move system relations, skipping all")));
3442 : 0 : tablespace_warning = true;
3443 : 0 : continue;
3444 : : }
3445 : : }
3446 : :
3447 : : /* Save the list of relation OIDs in private context */
3448 : 706 : old = MemoryContextSwitchTo(private_context);
3449 : :
3450 : : /*
3451 : : * We always want to reindex pg_class first if it's selected to be
3452 : : * reindexed. This ensures that if there is any corruption in
3453 : : * pg_class' indexes, they will be fixed before we process any other
3454 : : * tables. This is critical because reindexing itself will try to
3455 : : * update pg_class.
3456 : : */
3457 [ + + ]: 706 : if (relid == RelationRelationId)
3458 : 8 : relids = lcons_oid(relid, relids);
3459 : : else
3460 : 698 : relids = lappend_oid(relids, relid);
3461 : :
3462 : 706 : MemoryContextSwitchTo(old);
3463 : : }
3464 : 72 : table_endscan(scan);
3465 : 72 : table_close(relationRelation, AccessShareLock);
3466 : :
3467 : : /*
3468 : : * Process each relation listed in a separate transaction. Note that this
3469 : : * commits and then starts a new transaction immediately.
3470 : : */
3471 : 72 : ReindexMultipleInternal(stmt, relids, params);
3472 : :
3473 : 72 : MemoryContextDelete(private_context);
3474 : 72 : }
3475 : :
3476 : : /*
3477 : : * Error callback specific to ReindexPartitions().
3478 : : */
3479 : : static void
3480 : 8 : reindex_error_callback(void *arg)
3481 : : {
3482 : 8 : ReindexErrorInfo *errinfo = (ReindexErrorInfo *) arg;
3483 : :
3484 : : Assert(RELKIND_HAS_PARTITIONS(errinfo->relkind));
3485 : :
3486 [ + + ]: 8 : if (errinfo->relkind == RELKIND_PARTITIONED_TABLE)
3487 : 4 : errcontext("while reindexing partitioned table \"%s.%s\"",
3488 : : errinfo->relnamespace, errinfo->relname);
3489 [ + - ]: 4 : else if (errinfo->relkind == RELKIND_PARTITIONED_INDEX)
3490 : 4 : errcontext("while reindexing partitioned index \"%s.%s\"",
3491 : : errinfo->relnamespace, errinfo->relname);
3492 : 8 : }
3493 : :
3494 : : /*
3495 : : * ReindexPartitions
3496 : : *
3497 : : * Reindex a set of partitions, per the partitioned index or table given
3498 : : * by the caller.
3499 : : */
3500 : : static void
3501 : 71 : ReindexPartitions(const ReindexStmt *stmt, Oid relid, const ReindexParams *params, bool isTopLevel)
3502 : : {
3503 : 71 : List *partitions = NIL;
3504 : 71 : char relkind = get_rel_relkind(relid);
3505 : 71 : char *relname = get_rel_name(relid);
3506 : 71 : char *relnamespace = get_namespace_name(get_rel_namespace(relid));
3507 : : MemoryContext reindex_context;
3508 : : List *inhoids;
3509 : : ListCell *lc;
3510 : : ErrorContextCallback errcallback;
3511 : : ReindexErrorInfo errinfo;
3512 : :
3513 : : Assert(RELKIND_HAS_PARTITIONS(relkind));
3514 : :
3515 : : /*
3516 : : * Check if this runs in a transaction block, with an error callback to
3517 : : * provide more context under which a problem happens.
3518 : : */
3519 : 71 : errinfo.relname = pstrdup(relname);
3520 : 71 : errinfo.relnamespace = pstrdup(relnamespace);
3521 : 71 : errinfo.relkind = relkind;
3522 : 71 : errcallback.callback = reindex_error_callback;
3523 : 71 : errcallback.arg = &errinfo;
3524 : 71 : errcallback.previous = error_context_stack;
3525 : 71 : error_context_stack = &errcallback;
3526 : :
3527 [ + + ]: 71 : PreventInTransactionBlock(isTopLevel,
3528 : : relkind == RELKIND_PARTITIONED_TABLE ?
3529 : : "REINDEX TABLE" : "REINDEX INDEX");
3530 : :
3531 : : /* Pop the error context stack */
3532 : 63 : error_context_stack = errcallback.previous;
3533 : :
3534 : : /*
3535 : : * Create special memory context for cross-transaction storage.
3536 : : *
3537 : : * Since it is a child of PortalContext, it will go away eventually even
3538 : : * if we suffer an error so there is no need for special abort cleanup
3539 : : * logic.
3540 : : */
3541 : 63 : reindex_context = AllocSetContextCreate(PortalContext, "Reindex",
3542 : : ALLOCSET_DEFAULT_SIZES);
3543 : :
3544 : : /* ShareLock is enough to prevent schema modifications */
3545 : 63 : inhoids = find_all_inheritors(relid, ShareLock, NULL);
3546 : :
3547 : : /*
3548 : : * The list of relations to reindex are the physical partitions of the
3549 : : * tree so discard any partitioned table or index.
3550 : : */
3551 [ + - + + : 245 : foreach(lc, inhoids)
+ + ]
3552 : : {
3553 : 182 : Oid partoid = lfirst_oid(lc);
3554 : 182 : char partkind = get_rel_relkind(partoid);
3555 : : MemoryContext old_context;
3556 : :
3557 : : /*
3558 : : * This discards partitioned tables, partitioned indexes and foreign
3559 : : * tables.
3560 : : */
3561 [ + + + + : 182 : if (!RELKIND_HAS_STORAGE(partkind))
+ - + - +
- ]
3562 : 105 : continue;
3563 : :
3564 : : Assert(partkind == RELKIND_INDEX ||
3565 : : partkind == RELKIND_RELATION);
3566 : :
3567 : : /* Save partition OID */
3568 : 77 : old_context = MemoryContextSwitchTo(reindex_context);
3569 : 77 : partitions = lappend_oid(partitions, partoid);
3570 : 77 : MemoryContextSwitchTo(old_context);
3571 : : }
3572 : :
3573 : : /*
3574 : : * Process each partition listed in a separate transaction. Note that
3575 : : * this commits and then starts a new transaction immediately.
3576 : : */
3577 : 63 : ReindexMultipleInternal(stmt, partitions, params);
3578 : :
3579 : : /*
3580 : : * Clean up working storage --- note we must do this after
3581 : : * StartTransactionCommand, else we might be trying to delete the active
3582 : : * context!
3583 : : */
3584 : 63 : MemoryContextDelete(reindex_context);
3585 : 63 : }
3586 : :
3587 : : /*
3588 : : * ReindexMultipleInternal
3589 : : *
3590 : : * Reindex a list of relations, each one being processed in its own
3591 : : * transaction. This commits the existing transaction immediately,
3592 : : * and starts a new transaction when finished.
3593 : : */
3594 : : static void
3595 : 135 : ReindexMultipleInternal(const ReindexStmt *stmt, const List *relids, const ReindexParams *params)
3596 : : {
3597 : : ListCell *l;
3598 : :
3599 : 135 : PopActiveSnapshot();
3600 : 135 : CommitTransactionCommand();
3601 : :
3602 [ + + + + : 918 : foreach(l, relids)
+ + ]
3603 : : {
3604 : 783 : Oid relid = lfirst_oid(l);
3605 : : char relkind;
3606 : : char relpersistence;
3607 : :
3608 : 783 : StartTransactionCommand();
3609 : :
3610 : : /* functions in indexes may want a snapshot set */
3611 : 783 : PushActiveSnapshot(GetTransactionSnapshot());
3612 : :
3613 : : /* check if the relation still exists */
3614 [ + + ]: 783 : if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(relid)))
3615 : : {
3616 : 2 : PopActiveSnapshot();
3617 : 2 : CommitTransactionCommand();
3618 : 2 : continue;
3619 : : }
3620 : :
3621 : : /*
3622 : : * Check permissions except when moving to database's default if a new
3623 : : * tablespace is chosen. Note that this check also happens in
3624 : : * ExecReindex(), but we do an extra check here as this runs across
3625 : : * multiple transactions.
3626 : : */
3627 [ + + ]: 781 : if (OidIsValid(params->tablespaceOid) &&
3628 [ + - ]: 8 : params->tablespaceOid != MyDatabaseTableSpace)
3629 : : {
3630 : : AclResult aclresult;
3631 : :
3632 : 8 : aclresult = object_aclcheck(TableSpaceRelationId, params->tablespaceOid,
3633 : : GetUserId(), ACL_CREATE);
3634 [ - + ]: 8 : if (aclresult != ACLCHECK_OK)
3635 : 0 : aclcheck_error(aclresult, OBJECT_TABLESPACE,
3636 : 0 : get_tablespace_name(params->tablespaceOid));
3637 : : }
3638 : :
3639 : 781 : relkind = get_rel_relkind(relid);
3640 : 781 : relpersistence = get_rel_persistence(relid);
3641 : :
3642 : : /*
3643 : : * Partitioned tables and indexes can never be processed directly, and
3644 : : * a list of their leaves should be built first.
3645 : : */
3646 : : Assert(!RELKIND_HAS_PARTITIONS(relkind));
3647 : :
3648 [ + + + + ]: 781 : if ((params->options & REINDEXOPT_CONCURRENTLY) != 0 &&
3649 : : relpersistence != RELPERSISTENCE_TEMP)
3650 : 81 : {
3651 : 81 : ReindexParams newparams = *params;
3652 : :
3653 : 81 : newparams.options |= REINDEXOPT_MISSING_OK;
3654 : 81 : (void) ReindexRelationConcurrently(stmt, relid, &newparams);
3655 [ + + ]: 81 : if (ActiveSnapshotSet())
3656 : 17 : PopActiveSnapshot();
3657 : : /* ReindexRelationConcurrently() does the verbose output */
3658 : : }
3659 [ + + ]: 700 : else if (relkind == RELKIND_INDEX)
3660 : : {
3661 : 12 : ReindexParams newparams = *params;
3662 : :
3663 : 12 : newparams.options |=
3664 : : REINDEXOPT_REPORT_PROGRESS | REINDEXOPT_MISSING_OK;
3665 : 12 : reindex_index(stmt, relid, false, relpersistence, &newparams);
3666 : 12 : PopActiveSnapshot();
3667 : : /* reindex_index() does the verbose output */
3668 : : }
3669 : : else
3670 : : {
3671 : : bool result;
3672 : 688 : ReindexParams newparams = *params;
3673 : :
3674 : 688 : newparams.options |=
3675 : : REINDEXOPT_REPORT_PROGRESS | REINDEXOPT_MISSING_OK;
3676 : 688 : result = reindex_relation(stmt, relid,
3677 : : REINDEX_REL_PROCESS_TOAST |
3678 : : REINDEX_REL_CHECK_CONSTRAINTS,
3679 : : &newparams);
3680 : :
3681 [ + + - + ]: 688 : if (result && (params->options & REINDEXOPT_VERBOSE) != 0)
3682 [ # # ]: 0 : ereport(INFO,
3683 : : (errmsg("table \"%s.%s\" was reindexed",
3684 : : get_namespace_name(get_rel_namespace(relid)),
3685 : : get_rel_name(relid))));
3686 : :
3687 : 688 : PopActiveSnapshot();
3688 : : }
3689 : :
3690 : 781 : CommitTransactionCommand();
3691 : : }
3692 : :
3693 : 135 : StartTransactionCommand();
3694 : 135 : }
3695 : :
3696 : :
3697 : : /*
3698 : : * ReindexRelationConcurrently - process REINDEX CONCURRENTLY for given
3699 : : * relation OID
3700 : : *
3701 : : * 'relationOid' can either belong to an index, a table or a materialized
3702 : : * view. For tables and materialized views, all its indexes will be rebuilt,
3703 : : * excluding invalid indexes and any indexes used in exclusion constraints,
3704 : : * but including its associated toast table indexes. For indexes, the index
3705 : : * itself will be rebuilt.
3706 : : *
3707 : : * The locks taken on parent tables and involved indexes are kept until the
3708 : : * transaction is committed, at which point a session lock is taken on each
3709 : : * relation. Both of these protect against concurrent schema changes.
3710 : : *
3711 : : * Returns true if any indexes have been rebuilt (including toast table's
3712 : : * indexes, when relevant), otherwise returns false.
3713 : : *
3714 : : * NOTE: This cannot be used on temporary relations. A concurrent build would
3715 : : * cause issues with ON COMMIT actions triggered by the transactions of the
3716 : : * concurrent build. Temporary relations are not subject to concurrent
3717 : : * concerns, so there's no need for the more complicated concurrent build,
3718 : : * anyway, and a non-concurrent reindex is more efficient.
3719 : : */
3720 : : static bool
3721 : 338 : ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const ReindexParams *params)
3722 : : {
3723 : : typedef struct ReindexIndexInfo
3724 : : {
3725 : : Oid indexId;
3726 : : Oid tableId;
3727 : : Oid amId;
3728 : : bool safe; /* for set_indexsafe_procflags */
3729 : : } ReindexIndexInfo;
3730 : 338 : List *heapRelationIds = NIL;
3731 : 338 : List *indexIds = NIL;
3732 : 338 : List *newIndexIds = NIL;
3733 : 338 : List *relationLocks = NIL;
3734 : 338 : List *lockTags = NIL;
3735 : : ListCell *lc,
3736 : : *lc2;
3737 : : MemoryContext private_context;
3738 : : MemoryContext oldcontext;
3739 : : char relkind;
3740 : 338 : char *relationName = NULL;
3741 : 338 : char *relationNamespace = NULL;
3742 : : PGRUsage ru0;
3743 : 338 : const int progress_index[] = {
3744 : : PROGRESS_CREATEIDX_COMMAND,
3745 : : PROGRESS_CREATEIDX_PHASE,
3746 : : PROGRESS_CREATEIDX_INDEX_OID,
3747 : : PROGRESS_CREATEIDX_ACCESS_METHOD_OID
3748 : : };
3749 : : int64 progress_vals[4];
3750 : :
3751 : : /*
3752 : : * Create a memory context that will survive forced transaction commits we
3753 : : * do below. Since it is a child of PortalContext, it will go away
3754 : : * eventually even if we suffer an error; there's no need for special
3755 : : * abort cleanup logic.
3756 : : */
3757 : 338 : private_context = AllocSetContextCreate(PortalContext,
3758 : : "ReindexConcurrent",
3759 : : ALLOCSET_SMALL_SIZES);
3760 : :
3761 [ + + ]: 338 : if ((params->options & REINDEXOPT_VERBOSE) != 0)
3762 : : {
3763 : : /* Save data needed by REINDEX VERBOSE in private context */
3764 : 2 : oldcontext = MemoryContextSwitchTo(private_context);
3765 : :
3766 : 2 : relationName = get_rel_name(relationOid);
3767 : 2 : relationNamespace = get_namespace_name(get_rel_namespace(relationOid));
3768 : :
3769 : 2 : pg_rusage_init(&ru0);
3770 : :
3771 : 2 : MemoryContextSwitchTo(oldcontext);
3772 : : }
3773 : :
3774 : 338 : relkind = get_rel_relkind(relationOid);
3775 : :
3776 : : /*
3777 : : * Extract the list of indexes that are going to be rebuilt based on the
3778 : : * relation Oid given by caller.
3779 : : */
3780 [ + + - ]: 338 : switch (relkind)
3781 : : {
3782 : 202 : case RELKIND_RELATION:
3783 : : case RELKIND_MATVIEW:
3784 : : case RELKIND_TOASTVALUE:
3785 : : {
3786 : : /*
3787 : : * In the case of a relation, find all its indexes including
3788 : : * toast indexes.
3789 : : */
3790 : : Relation heapRelation;
3791 : :
3792 : : /* Save the list of relation OIDs in private context */
3793 : 202 : oldcontext = MemoryContextSwitchTo(private_context);
3794 : :
3795 : : /* Track this relation for session locks */
3796 : 202 : heapRelationIds = lappend_oid(heapRelationIds, relationOid);
3797 : :
3798 : 202 : MemoryContextSwitchTo(oldcontext);
3799 : :
3800 [ + + ]: 202 : if (IsCatalogRelationOid(relationOid))
3801 [ + - ]: 24 : ereport(ERROR,
3802 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3803 : : errmsg("cannot reindex system catalogs concurrently")));
3804 : :
3805 : : /* Open relation to get its indexes */
3806 [ + + ]: 178 : if ((params->options & REINDEXOPT_MISSING_OK) != 0)
3807 : : {
3808 : 65 : heapRelation = try_table_open(relationOid,
3809 : : ShareUpdateExclusiveLock);
3810 : : /* leave if relation does not exist */
3811 [ - + ]: 65 : if (!heapRelation)
3812 : 0 : break;
3813 : : }
3814 : : else
3815 : 113 : heapRelation = table_open(relationOid,
3816 : : ShareUpdateExclusiveLock);
3817 : :
3818 [ + + + + ]: 192 : if (OidIsValid(params->tablespaceOid) &&
3819 : 14 : IsSystemRelation(heapRelation))
3820 [ + - ]: 1 : ereport(ERROR,
3821 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3822 : : errmsg("cannot move system relation \"%s\"",
3823 : : RelationGetRelationName(heapRelation))));
3824 : :
3825 : : /* Add all the valid indexes of relation to list */
3826 [ + + + + : 344 : foreach(lc, RelationGetIndexList(heapRelation))
+ + ]
3827 : : {
3828 : 167 : Oid cellOid = lfirst_oid(lc);
3829 : 167 : Relation indexRelation = index_open(cellOid,
3830 : : ShareUpdateExclusiveLock);
3831 : :
3832 [ + + ]: 167 : if (!indexRelation->rd_index->indisvalid)
3833 [ + - ]: 4 : ereport(WARNING,
3834 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3835 : : errmsg("skipping reindex of invalid index \"%s.%s\"",
3836 : : get_namespace_name(get_rel_namespace(cellOid)),
3837 : : get_rel_name(cellOid)),
3838 : : errhint("Use DROP INDEX or REINDEX INDEX.")));
3839 [ + + ]: 163 : else if (indexRelation->rd_index->indisexclusion)
3840 [ + - ]: 4 : ereport(WARNING,
3841 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3842 : : errmsg("cannot reindex exclusion constraint index \"%s.%s\" concurrently, skipping",
3843 : : get_namespace_name(get_rel_namespace(cellOid)),
3844 : : get_rel_name(cellOid))));
3845 : : else
3846 : : {
3847 : : ReindexIndexInfo *idx;
3848 : :
3849 : : /* Save the list of relation OIDs in private context */
3850 : 159 : oldcontext = MemoryContextSwitchTo(private_context);
3851 : :
3852 : 159 : idx = palloc_object(ReindexIndexInfo);
3853 : 159 : idx->indexId = cellOid;
3854 : : /* other fields set later */
3855 : :
3856 : 159 : indexIds = lappend(indexIds, idx);
3857 : :
3858 : 159 : MemoryContextSwitchTo(oldcontext);
3859 : : }
3860 : :
3861 : 167 : index_close(indexRelation, NoLock);
3862 : : }
3863 : :
3864 : : /* Also add the toast indexes */
3865 [ + + ]: 177 : if (OidIsValid(heapRelation->rd_rel->reltoastrelid))
3866 : : {
3867 : 51 : Oid toastOid = heapRelation->rd_rel->reltoastrelid;
3868 : 51 : Relation toastRelation = table_open(toastOid,
3869 : : ShareUpdateExclusiveLock);
3870 : :
3871 : : /* Save the list of relation OIDs in private context */
3872 : 51 : oldcontext = MemoryContextSwitchTo(private_context);
3873 : :
3874 : : /* Track this relation for session locks */
3875 : 51 : heapRelationIds = lappend_oid(heapRelationIds, toastOid);
3876 : :
3877 : 51 : MemoryContextSwitchTo(oldcontext);
3878 : :
3879 [ + - + + : 102 : foreach(lc2, RelationGetIndexList(toastRelation))
+ + ]
3880 : : {
3881 : 51 : Oid cellOid = lfirst_oid(lc2);
3882 : 51 : Relation indexRelation = index_open(cellOid,
3883 : : ShareUpdateExclusiveLock);
3884 : :
3885 [ - + ]: 51 : if (!indexRelation->rd_index->indisvalid)
3886 [ # # ]: 0 : ereport(WARNING,
3887 : : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3888 : : errmsg("skipping reindex of invalid index \"%s.%s\"",
3889 : : get_namespace_name(get_rel_namespace(cellOid)),
3890 : : get_rel_name(cellOid)),
3891 : : errhint("Use DROP INDEX or REINDEX INDEX.")));
3892 : : else
3893 : : {
3894 : : ReindexIndexInfo *idx;
3895 : :
3896 : : /*
3897 : : * Save the list of relation OIDs in private
3898 : : * context
3899 : : */
3900 : 51 : oldcontext = MemoryContextSwitchTo(private_context);
3901 : :
3902 : 51 : idx = palloc_object(ReindexIndexInfo);
3903 : 51 : idx->indexId = cellOid;
3904 : 51 : indexIds = lappend(indexIds, idx);
3905 : : /* other fields set later */
3906 : :
3907 : 51 : MemoryContextSwitchTo(oldcontext);
3908 : : }
3909 : :
3910 : 51 : index_close(indexRelation, NoLock);
3911 : : }
3912 : :
3913 : 51 : table_close(toastRelation, NoLock);
3914 : : }
3915 : :
3916 : 177 : table_close(heapRelation, NoLock);
3917 : 177 : break;
3918 : : }
3919 : 136 : case RELKIND_INDEX:
3920 : : {
3921 : 136 : Oid heapId = IndexGetRelation(relationOid,
3922 : 136 : (params->options & REINDEXOPT_MISSING_OK) != 0);
3923 : : Relation heapRelation;
3924 : : ReindexIndexInfo *idx;
3925 : :
3926 : : /* if relation is missing, leave */
3927 [ - + ]: 136 : if (!OidIsValid(heapId))
3928 : 0 : break;
3929 : :
3930 [ + + ]: 136 : if (IsCatalogRelationOid(heapId))
3931 [ + - ]: 12 : ereport(ERROR,
3932 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3933 : : errmsg("cannot reindex system catalogs concurrently")));
3934 : :
3935 : : /*
3936 : : * Don't allow reindex for an invalid index on TOAST table, as
3937 : : * if rebuilt it would not be possible to drop it. Match
3938 : : * error message in reindex_index().
3939 : : */
3940 [ + + ]: 124 : if (IsToastNamespace(get_rel_namespace(relationOid)) &&
3941 [ - + ]: 28 : !get_index_isvalid(relationOid))
3942 [ # # ]: 0 : ereport(ERROR,
3943 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3944 : : errmsg("cannot reindex invalid index on TOAST table")));
3945 : :
3946 : : /*
3947 : : * Check if parent relation can be locked and if it exists,
3948 : : * this needs to be done at this stage as the list of indexes
3949 : : * to rebuild is not complete yet, and REINDEXOPT_MISSING_OK
3950 : : * should not be used once all the session locks are taken.
3951 : : */
3952 [ + + ]: 124 : if ((params->options & REINDEXOPT_MISSING_OK) != 0)
3953 : : {
3954 : 16 : heapRelation = try_table_open(heapId,
3955 : : ShareUpdateExclusiveLock);
3956 : : /* leave if relation does not exist */
3957 [ - + ]: 16 : if (!heapRelation)
3958 : 0 : break;
3959 : : }
3960 : : else
3961 : 108 : heapRelation = table_open(heapId,
3962 : : ShareUpdateExclusiveLock);
3963 : :
3964 [ + + + + ]: 129 : if (OidIsValid(params->tablespaceOid) &&
3965 : 5 : IsSystemRelation(heapRelation))
3966 [ + - ]: 1 : ereport(ERROR,
3967 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3968 : : errmsg("cannot move system relation \"%s\"",
3969 : : get_rel_name(relationOid))));
3970 : :
3971 : 123 : table_close(heapRelation, NoLock);
3972 : :
3973 : : /* Save the list of relation OIDs in private context */
3974 : 123 : oldcontext = MemoryContextSwitchTo(private_context);
3975 : :
3976 : : /* Track the heap relation of this index for session locks */
3977 : 123 : heapRelationIds = list_make1_oid(heapId);
3978 : :
3979 : : /*
3980 : : * Save the list of relation OIDs in private context. Note
3981 : : * that invalid indexes are allowed here.
3982 : : */
3983 : 123 : idx = palloc_object(ReindexIndexInfo);
3984 : 123 : idx->indexId = relationOid;
3985 : 123 : indexIds = lappend(indexIds, idx);
3986 : : /* other fields set later */
3987 : :
3988 : 123 : MemoryContextSwitchTo(oldcontext);
3989 : 123 : break;
3990 : : }
3991 : :
3992 : 0 : case RELKIND_PARTITIONED_TABLE:
3993 : : case RELKIND_PARTITIONED_INDEX:
3994 : : default:
3995 : : /* Return error if type of relation is not supported */
3996 [ # # ]: 0 : ereport(ERROR,
3997 : : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3998 : : errmsg("cannot reindex this type of relation concurrently")));
3999 : : break;
4000 : : }
4001 : :
4002 : : /*
4003 : : * Definitely no indexes, so leave. Any checks based on
4004 : : * REINDEXOPT_MISSING_OK should be done only while the list of indexes to
4005 : : * work on is built as the session locks taken before this transaction
4006 : : * commits will make sure that they cannot be dropped by a concurrent
4007 : : * session until this operation completes.
4008 : : */
4009 [ + + ]: 300 : if (indexIds == NIL)
4010 : 29 : return false;
4011 : :
4012 : : /* It's not a shared catalog, so refuse to move it to shared tablespace */
4013 [ + + ]: 271 : if (params->tablespaceOid == GLOBALTABLESPACE_OID)
4014 [ + - ]: 4 : ereport(ERROR,
4015 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4016 : : errmsg("cannot move non-shared relation to tablespace \"%s\"",
4017 : : get_tablespace_name(params->tablespaceOid))));
4018 : :
4019 : : Assert(heapRelationIds != NIL);
4020 : :
4021 : : /*-----
4022 : : * Now we have all the indexes we want to process in indexIds.
4023 : : *
4024 : : * The phases now are:
4025 : : *
4026 : : * 1. create new indexes in the catalog
4027 : : * 2. build new indexes
4028 : : * 3. let new indexes catch up with tuples inserted in the meantime
4029 : : * 4. swap index names
4030 : : * 5. mark old indexes as dead
4031 : : * 6. drop old indexes
4032 : : *
4033 : : * We process each phase for all indexes before moving to the next phase,
4034 : : * for efficiency.
4035 : : */
4036 : :
4037 : : /*
4038 : : * Phase 1 of REINDEX CONCURRENTLY
4039 : : *
4040 : : * Create a new index with the same properties as the old one, but it is
4041 : : * only registered in catalogs and will be built later. Then get session
4042 : : * locks on all involved tables. See analogous code in DefineIndex() for
4043 : : * more detailed comments.
4044 : : */
4045 : :
4046 [ + - + + : 592 : foreach(lc, indexIds)
+ + ]
4047 : : {
4048 : : char *concurrentName;
4049 : 329 : ReindexIndexInfo *idx = lfirst(lc);
4050 : : ReindexIndexInfo *newidx;
4051 : : Oid newIndexId;
4052 : : Relation indexRel;
4053 : : Relation heapRel;
4054 : : Oid save_userid;
4055 : : int save_sec_context;
4056 : : int save_nestlevel;
4057 : : Relation newIndexRel;
4058 : : LockRelId *lockrelid;
4059 : : Oid tablespaceid;
4060 : :
4061 : 329 : indexRel = index_open(idx->indexId, ShareUpdateExclusiveLock);
4062 : 329 : heapRel = table_open(indexRel->rd_index->indrelid,
4063 : : ShareUpdateExclusiveLock);
4064 : :
4065 : : /*
4066 : : * Switch to the table owner's userid, so that any index functions are
4067 : : * run as that user. Also lock down security-restricted operations
4068 : : * and arrange to make GUC variable changes local to this command.
4069 : : */
4070 : 329 : GetUserIdAndSecContext(&save_userid, &save_sec_context);
4071 : 329 : SetUserIdAndSecContext(heapRel->rd_rel->relowner,
4072 : : save_sec_context | SECURITY_RESTRICTED_OPERATION);
4073 : 329 : save_nestlevel = NewGUCNestLevel();
4074 : 329 : RestrictSearchPath();
4075 : :
4076 : : /* determine safety of this index for set_indexsafe_procflags */
4077 [ + + + + ]: 624 : idx->safe = (RelationGetIndexExpressions(indexRel) == NIL &&
4078 : 295 : RelationGetIndexPredicate(indexRel) == NIL);
4079 : :
4080 : : #ifdef USE_INJECTION_POINTS
4081 [ + + ]: 329 : if (idx->safe)
4082 : 290 : INJECTION_POINT("reindex-conc-index-safe", NULL);
4083 : : else
4084 : 39 : INJECTION_POINT("reindex-conc-index-not-safe", NULL);
4085 : : #endif
4086 : :
4087 : 329 : idx->tableId = RelationGetRelid(heapRel);
4088 : 329 : idx->amId = indexRel->rd_rel->relam;
4089 : :
4090 : : /* This function shouldn't be called for temporary relations. */
4091 [ - + ]: 329 : if (indexRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
4092 [ # # ]: 0 : elog(ERROR, "cannot reindex a temporary table concurrently");
4093 : :
4094 : 329 : pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, idx->tableId);
4095 : :
4096 : 329 : progress_vals[0] = PROGRESS_CREATEIDX_COMMAND_REINDEX_CONCURRENTLY;
4097 : 329 : progress_vals[1] = 0; /* initializing */
4098 : 329 : progress_vals[2] = idx->indexId;
4099 : 329 : progress_vals[3] = idx->amId;
4100 : 329 : pgstat_progress_update_multi_param(4, progress_index, progress_vals);
4101 : :
4102 : : /* Choose a temporary relation name for the new index */
4103 : 329 : concurrentName = ChooseRelationName(get_rel_name(idx->indexId),
4104 : : NULL,
4105 : : "ccnew",
4106 : 329 : get_rel_namespace(indexRel->rd_index->indrelid),
4107 : : false);
4108 : :
4109 : : /* Choose the new tablespace, indexes of toast tables are not moved */
4110 [ + + ]: 329 : if (OidIsValid(params->tablespaceOid) &&
4111 [ + + ]: 18 : heapRel->rd_rel->relkind != RELKIND_TOASTVALUE)
4112 : 13 : tablespaceid = params->tablespaceOid;
4113 : : else
4114 : 316 : tablespaceid = indexRel->rd_rel->reltablespace;
4115 : :
4116 : : /* Create new index definition based on given index */
4117 : 329 : newIndexId = index_create_copy(heapRel,
4118 : : INDEX_CREATE_CONCURRENT |
4119 : : INDEX_CREATE_SKIP_BUILD |
4120 : : INDEX_CREATE_SUPPRESS_PROGRESS,
4121 : : idx->indexId,
4122 : : tablespaceid,
4123 : : concurrentName);
4124 : :
4125 : : /*
4126 : : * Now open the relation of the new index, a session-level lock is
4127 : : * also needed on it.
4128 : : */
4129 : 325 : newIndexRel = index_open(newIndexId, ShareUpdateExclusiveLock);
4130 : :
4131 : : /*
4132 : : * Save the list of OIDs and locks in private context
4133 : : */
4134 : 325 : oldcontext = MemoryContextSwitchTo(private_context);
4135 : :
4136 : 325 : newidx = palloc_object(ReindexIndexInfo);
4137 : 325 : newidx->indexId = newIndexId;
4138 : 325 : newidx->safe = idx->safe;
4139 : 325 : newidx->tableId = idx->tableId;
4140 : 325 : newidx->amId = idx->amId;
4141 : :
4142 : 325 : newIndexIds = lappend(newIndexIds, newidx);
4143 : :
4144 : : /*
4145 : : * Save lockrelid to protect each relation from drop then close
4146 : : * relations. The lockrelid on parent relation is not taken here to
4147 : : * avoid multiple locks taken on the same relation, instead we rely on
4148 : : * parentRelationIds built earlier.
4149 : : */
4150 : 325 : lockrelid = palloc_object(LockRelId);
4151 : 325 : *lockrelid = indexRel->rd_lockInfo.lockRelId;
4152 : 325 : relationLocks = lappend(relationLocks, lockrelid);
4153 : 325 : lockrelid = palloc_object(LockRelId);
4154 : 325 : *lockrelid = newIndexRel->rd_lockInfo.lockRelId;
4155 : 325 : relationLocks = lappend(relationLocks, lockrelid);
4156 : :
4157 : 325 : MemoryContextSwitchTo(oldcontext);
4158 : :
4159 : 325 : index_close(indexRel, NoLock);
4160 : 325 : index_close(newIndexRel, NoLock);
4161 : :
4162 : : /* Roll back any GUC changes executed by index functions */
4163 : 325 : AtEOXact_GUC(false, save_nestlevel);
4164 : :
4165 : : /* Restore userid and security context */
4166 : 325 : SetUserIdAndSecContext(save_userid, save_sec_context);
4167 : :
4168 : 325 : table_close(heapRel, NoLock);
4169 : :
4170 : : /*
4171 : : * If a statement is available, telling that this comes from a REINDEX
4172 : : * command, collect the new index for event triggers.
4173 : : */
4174 [ + - ]: 325 : if (stmt)
4175 : : {
4176 : : ObjectAddress address;
4177 : :
4178 : 325 : ObjectAddressSet(address, RelationRelationId, newIndexId);
4179 : 325 : EventTriggerCollectSimpleCommand(address,
4180 : : InvalidObjectAddress,
4181 : : (const Node *) stmt);
4182 : : }
4183 : : }
4184 : :
4185 : : /*
4186 : : * Save the heap lock for following visibility checks with other backends
4187 : : * might conflict with this session.
4188 : : */
4189 [ + - + + : 577 : foreach(lc, heapRelationIds)
+ + ]
4190 : : {
4191 : 314 : Relation heapRelation = table_open(lfirst_oid(lc), ShareUpdateExclusiveLock);
4192 : : LockRelId *lockrelid;
4193 : : LOCKTAG *heaplocktag;
4194 : :
4195 : : /* Save the list of locks in private context */
4196 : 314 : oldcontext = MemoryContextSwitchTo(private_context);
4197 : :
4198 : : /* Add lockrelid of heap relation to the list of locked relations */
4199 : 314 : lockrelid = palloc_object(LockRelId);
4200 : 314 : *lockrelid = heapRelation->rd_lockInfo.lockRelId;
4201 : 314 : relationLocks = lappend(relationLocks, lockrelid);
4202 : :
4203 : 314 : heaplocktag = palloc_object(LOCKTAG);
4204 : :
4205 : : /* Save the LOCKTAG for this parent relation for the wait phase */
4206 : 314 : SET_LOCKTAG_RELATION(*heaplocktag, lockrelid->dbId, lockrelid->relId);
4207 : 314 : lockTags = lappend(lockTags, heaplocktag);
4208 : :
4209 : 314 : MemoryContextSwitchTo(oldcontext);
4210 : :
4211 : : /* Close heap relation */
4212 : 314 : table_close(heapRelation, NoLock);
4213 : : }
4214 : :
4215 : : /* Get a session-level lock on each table. */
4216 [ + - + + : 1227 : foreach(lc, relationLocks)
+ + ]
4217 : : {
4218 : 964 : LockRelId *lockrelid = (LockRelId *) lfirst(lc);
4219 : :
4220 : 964 : LockRelationIdForSession(lockrelid, ShareUpdateExclusiveLock);
4221 : : }
4222 : :
4223 : 263 : PopActiveSnapshot();
4224 : 263 : CommitTransactionCommand();
4225 : 263 : StartTransactionCommand();
4226 : :
4227 : : /*
4228 : : * Because we don't take a snapshot in this transaction, there's no need
4229 : : * to set the PROC_IN_SAFE_IC flag here.
4230 : : */
4231 : :
4232 : : /*
4233 : : * Phase 2 of REINDEX CONCURRENTLY
4234 : : *
4235 : : * Build the new indexes in a separate transaction for each index to avoid
4236 : : * having open transactions for an unnecessary long time. But before
4237 : : * doing that, wait until no running transactions could have the table of
4238 : : * the index open with the old list of indexes. See "phase 2" in
4239 : : * DefineIndex() for more details.
4240 : : */
4241 : :
4242 : 263 : pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
4243 : : PROGRESS_CREATEIDX_PHASE_WAIT_1);
4244 : 263 : WaitForLockersMultiple(lockTags, ShareLock, true);
4245 : 263 : CommitTransactionCommand();
4246 : :
4247 [ + - + + : 584 : foreach(lc, newIndexIds)
+ + ]
4248 : : {
4249 : 325 : ReindexIndexInfo *newidx = lfirst(lc);
4250 : :
4251 : : /* Start new transaction for this index's concurrent build */
4252 : 325 : StartTransactionCommand();
4253 : :
4254 : : /*
4255 : : * Check for user-requested abort. This is inside a transaction so as
4256 : : * xact.c does not issue a useless WARNING, and ensures that
4257 : : * session-level locks are cleaned up on abort.
4258 : : */
4259 [ - + ]: 325 : CHECK_FOR_INTERRUPTS();
4260 : :
4261 : : /* Tell concurrent indexing to ignore us, if index qualifies */
4262 [ + + ]: 325 : if (newidx->safe)
4263 : 286 : set_indexsafe_procflags();
4264 : :
4265 : : /* Set ActiveSnapshot since functions in the indexes may need it */
4266 : 325 : PushActiveSnapshot(GetTransactionSnapshot());
4267 : :
4268 : : /*
4269 : : * Update progress for the index to build, with the correct parent
4270 : : * table involved.
4271 : : */
4272 : 325 : pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, newidx->tableId);
4273 : 325 : progress_vals[0] = PROGRESS_CREATEIDX_COMMAND_REINDEX_CONCURRENTLY;
4274 : 325 : progress_vals[1] = PROGRESS_CREATEIDX_PHASE_BUILD;
4275 : 325 : progress_vals[2] = newidx->indexId;
4276 : 325 : progress_vals[3] = newidx->amId;
4277 : 325 : pgstat_progress_update_multi_param(4, progress_index, progress_vals);
4278 : :
4279 : : /* Perform concurrent build of new index */
4280 : 325 : index_concurrently_build(newidx->tableId, newidx->indexId);
4281 : :
4282 : 321 : PopActiveSnapshot();
4283 : 321 : CommitTransactionCommand();
4284 : : }
4285 : :
4286 : 259 : StartTransactionCommand();
4287 : :
4288 : : /*
4289 : : * Because we don't take a snapshot or Xid in this transaction, there's no
4290 : : * need to set the PROC_IN_SAFE_IC flag here.
4291 : : */
4292 : :
4293 : : /*
4294 : : * Phase 3 of REINDEX CONCURRENTLY
4295 : : *
4296 : : * During this phase the old indexes catch up with any new tuples that
4297 : : * were created during the previous phase. See "phase 3" in DefineIndex()
4298 : : * for more details.
4299 : : */
4300 : :
4301 : 259 : pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
4302 : : PROGRESS_CREATEIDX_PHASE_WAIT_2);
4303 : 259 : WaitForLockersMultiple(lockTags, ShareLock, true);
4304 : 259 : CommitTransactionCommand();
4305 : :
4306 [ + - + + : 580 : foreach(lc, newIndexIds)
+ + ]
4307 : : {
4308 : 321 : ReindexIndexInfo *newidx = lfirst(lc);
4309 : : TransactionId limitXmin;
4310 : : Snapshot snapshot;
4311 : :
4312 : 321 : StartTransactionCommand();
4313 : :
4314 : : /*
4315 : : * Check for user-requested abort. This is inside a transaction so as
4316 : : * xact.c does not issue a useless WARNING, and ensures that
4317 : : * session-level locks are cleaned up on abort.
4318 : : */
4319 [ - + ]: 321 : CHECK_FOR_INTERRUPTS();
4320 : :
4321 : : /* Tell concurrent indexing to ignore us, if index qualifies */
4322 [ + + ]: 321 : if (newidx->safe)
4323 : 282 : set_indexsafe_procflags();
4324 : :
4325 : : /*
4326 : : * Take the "reference snapshot" that will be used by validate_index()
4327 : : * to filter candidate tuples.
4328 : : */
4329 : 321 : snapshot = RegisterSnapshot(GetTransactionSnapshot());
4330 : 321 : PushActiveSnapshot(snapshot);
4331 : :
4332 : : /*
4333 : : * Update progress for the index to build, with the correct parent
4334 : : * table involved.
4335 : : */
4336 : 321 : pgstat_progress_start_command(PROGRESS_COMMAND_CREATE_INDEX, newidx->tableId);
4337 : 321 : progress_vals[0] = PROGRESS_CREATEIDX_COMMAND_REINDEX_CONCURRENTLY;
4338 : 321 : progress_vals[1] = PROGRESS_CREATEIDX_PHASE_VALIDATE_IDXSCAN;
4339 : 321 : progress_vals[2] = newidx->indexId;
4340 : 321 : progress_vals[3] = newidx->amId;
4341 : 321 : pgstat_progress_update_multi_param(4, progress_index, progress_vals);
4342 : :
4343 : 321 : validate_index(newidx->tableId, newidx->indexId, snapshot);
4344 : :
4345 : : /*
4346 : : * We can now do away with our active snapshot, we still need to save
4347 : : * the xmin limit to wait for older snapshots.
4348 : : */
4349 : 321 : limitXmin = snapshot->xmin;
4350 : :
4351 : 321 : PopActiveSnapshot();
4352 : 321 : UnregisterSnapshot(snapshot);
4353 : :
4354 : : /*
4355 : : * To ensure no deadlocks, we must commit and start yet another
4356 : : * transaction, and do our wait before any snapshot has been taken in
4357 : : * it.
4358 : : */
4359 : 321 : CommitTransactionCommand();
4360 : 321 : StartTransactionCommand();
4361 : :
4362 : : /*
4363 : : * The index is now valid in the sense that it contains all currently
4364 : : * interesting tuples. But since it might not contain tuples deleted
4365 : : * just before the reference snap was taken, we have to wait out any
4366 : : * transactions that might have older snapshots.
4367 : : *
4368 : : * Because we don't take a snapshot or Xid in this transaction,
4369 : : * there's no need to set the PROC_IN_SAFE_IC flag here.
4370 : : */
4371 : 321 : pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
4372 : : PROGRESS_CREATEIDX_PHASE_WAIT_3);
4373 : 321 : WaitForOlderSnapshots(limitXmin, true);
4374 : :
4375 : 321 : CommitTransactionCommand();
4376 : : }
4377 : :
4378 : : /*
4379 : : * Phase 4 of REINDEX CONCURRENTLY
4380 : : *
4381 : : * Now that the new indexes have been validated, swap each new index with
4382 : : * its corresponding old index.
4383 : : *
4384 : : * We mark the new indexes as valid and the old indexes as not valid at
4385 : : * the same time to make sure we only get constraint violations from the
4386 : : * indexes with the correct names.
4387 : : */
4388 : :
4389 : 259 : INJECTION_POINT("reindex-relation-concurrently-before-swap", NULL);
4390 : 259 : StartTransactionCommand();
4391 : :
4392 : : /*
4393 : : * Because this transaction only does catalog manipulations and doesn't do
4394 : : * any index operations, we can set the PROC_IN_SAFE_IC flag here
4395 : : * unconditionally.
4396 : : */
4397 : 259 : set_indexsafe_procflags();
4398 : :
4399 [ + - + + : 580 : forboth(lc, indexIds, lc2, newIndexIds)
+ - + + +
+ + - +
+ ]
4400 : : {
4401 : 321 : ReindexIndexInfo *oldidx = lfirst(lc);
4402 : 321 : ReindexIndexInfo *newidx = lfirst(lc2);
4403 : : char *oldName;
4404 : :
4405 : : /*
4406 : : * Check for user-requested abort. This is inside a transaction so as
4407 : : * xact.c does not issue a useless WARNING, and ensures that
4408 : : * session-level locks are cleaned up on abort.
4409 : : */
4410 [ - + ]: 321 : CHECK_FOR_INTERRUPTS();
4411 : :
4412 : : /* Choose a relation name for old index */
4413 : 321 : oldName = ChooseRelationName(get_rel_name(oldidx->indexId),
4414 : : NULL,
4415 : : "ccold",
4416 : : get_rel_namespace(oldidx->tableId),
4417 : : false);
4418 : :
4419 : : /*
4420 : : * Swapping the indexes might involve TOAST table access, so ensure we
4421 : : * have a valid snapshot.
4422 : : */
4423 : 321 : PushActiveSnapshot(GetTransactionSnapshot());
4424 : :
4425 : : /*
4426 : : * Swap old index with the new one. This also marks the new one as
4427 : : * valid and the old one as not valid.
4428 : : */
4429 : 321 : index_concurrently_swap(newidx->indexId, oldidx->indexId, oldName);
4430 : :
4431 : 321 : PopActiveSnapshot();
4432 : :
4433 : : /*
4434 : : * Invalidate the relcache for the table, so that after this commit
4435 : : * all sessions will refresh any cached plans that might reference the
4436 : : * index.
4437 : : */
4438 : 321 : CacheInvalidateRelcacheByRelid(oldidx->tableId);
4439 : :
4440 : : /*
4441 : : * CCI here so that subsequent iterations see the oldName in the
4442 : : * catalog and can choose a nonconflicting name for their oldName.
4443 : : * Otherwise, this could lead to conflicts if a table has two indexes
4444 : : * whose names are equal for the first NAMEDATALEN-minus-a-few
4445 : : * characters.
4446 : : */
4447 : 321 : CommandCounterIncrement();
4448 : : }
4449 : :
4450 : : /* Commit this transaction and make index swaps visible */
4451 : 259 : CommitTransactionCommand();
4452 : 259 : StartTransactionCommand();
4453 : :
4454 : : /*
4455 : : * While we could set PROC_IN_SAFE_IC if all indexes qualified, there's no
4456 : : * real need for that, because we only acquire an Xid after the wait is
4457 : : * done, and that lasts for a very short period.
4458 : : */
4459 : :
4460 : : /*
4461 : : * Phase 5 of REINDEX CONCURRENTLY
4462 : : *
4463 : : * Mark the old indexes as dead. First we must wait until no running
4464 : : * transaction could be using the index for a query. See also
4465 : : * index_drop() for more details.
4466 : : */
4467 : :
4468 : 259 : INJECTION_POINT("reindex-relation-concurrently-before-set-dead", NULL);
4469 : 259 : pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
4470 : : PROGRESS_CREATEIDX_PHASE_WAIT_4);
4471 : 259 : WaitForLockersMultiple(lockTags, AccessExclusiveLock, true);
4472 : :
4473 [ + - + + : 580 : foreach(lc, indexIds)
+ + ]
4474 : : {
4475 : 321 : ReindexIndexInfo *oldidx = lfirst(lc);
4476 : :
4477 : : /*
4478 : : * Check for user-requested abort. This is inside a transaction so as
4479 : : * xact.c does not issue a useless WARNING, and ensures that
4480 : : * session-level locks are cleaned up on abort.
4481 : : */
4482 [ - + ]: 321 : CHECK_FOR_INTERRUPTS();
4483 : :
4484 : : /*
4485 : : * Updating pg_index might involve TOAST table access, so ensure we
4486 : : * have a valid snapshot.
4487 : : */
4488 : 321 : PushActiveSnapshot(GetTransactionSnapshot());
4489 : :
4490 : 321 : index_concurrently_set_dead(oldidx->tableId, oldidx->indexId);
4491 : :
4492 : 321 : PopActiveSnapshot();
4493 : : }
4494 : :
4495 : : /* Commit this transaction to make the updates visible. */
4496 : 259 : CommitTransactionCommand();
4497 : 259 : StartTransactionCommand();
4498 : :
4499 : : /*
4500 : : * While we could set PROC_IN_SAFE_IC if all indexes qualified, there's no
4501 : : * real need for that, because we only acquire an Xid after the wait is
4502 : : * done, and that lasts for a very short period.
4503 : : */
4504 : :
4505 : : /*
4506 : : * Phase 6 of REINDEX CONCURRENTLY
4507 : : *
4508 : : * Drop the old indexes.
4509 : : */
4510 : :
4511 : 259 : pgstat_progress_update_param(PROGRESS_CREATEIDX_PHASE,
4512 : : PROGRESS_CREATEIDX_PHASE_WAIT_5);
4513 : 259 : WaitForLockersMultiple(lockTags, AccessExclusiveLock, true);
4514 : :
4515 : 259 : PushActiveSnapshot(GetTransactionSnapshot());
4516 : :
4517 : : {
4518 : 259 : ObjectAddresses *objects = new_object_addresses();
4519 : :
4520 [ + - + + : 580 : foreach(lc, indexIds)
+ + ]
4521 : : {
4522 : 321 : ReindexIndexInfo *idx = lfirst(lc);
4523 : : ObjectAddress object;
4524 : :
4525 : 321 : object.classId = RelationRelationId;
4526 : 321 : object.objectId = idx->indexId;
4527 : 321 : object.objectSubId = 0;
4528 : :
4529 : 321 : add_exact_object_address(&object, objects);
4530 : : }
4531 : :
4532 : : /*
4533 : : * Use PERFORM_DELETION_CONCURRENT_LOCK so that index_drop() uses the
4534 : : * right lock level.
4535 : : */
4536 : 259 : performMultipleDeletions(objects, DROP_RESTRICT,
4537 : : PERFORM_DELETION_CONCURRENT_LOCK | PERFORM_DELETION_INTERNAL);
4538 : : }
4539 : :
4540 : 259 : PopActiveSnapshot();
4541 : 259 : CommitTransactionCommand();
4542 : :
4543 : : /*
4544 : : * Finally, release the session-level lock on the table.
4545 : : */
4546 [ + - + + : 1211 : foreach(lc, relationLocks)
+ + ]
4547 : : {
4548 : 952 : LockRelId *lockrelid = (LockRelId *) lfirst(lc);
4549 : :
4550 : 952 : UnlockRelationIdForSession(lockrelid, ShareUpdateExclusiveLock);
4551 : : }
4552 : :
4553 : : /* Start a new transaction to finish process properly */
4554 : 259 : StartTransactionCommand();
4555 : :
4556 : : /* Log what we did */
4557 [ + + ]: 259 : if ((params->options & REINDEXOPT_VERBOSE) != 0)
4558 : : {
4559 [ - + ]: 2 : if (relkind == RELKIND_INDEX)
4560 [ # # ]: 0 : ereport(INFO,
4561 : : (errmsg("index \"%s.%s\" was reindexed",
4562 : : relationNamespace, relationName),
4563 : : errdetail("%s.",
4564 : : pg_rusage_show(&ru0))));
4565 : : else
4566 : : {
4567 [ + - + + : 6 : foreach(lc, newIndexIds)
+ + ]
4568 : : {
4569 : 4 : ReindexIndexInfo *idx = lfirst(lc);
4570 : 4 : Oid indOid = idx->indexId;
4571 : :
4572 [ + - ]: 4 : ereport(INFO,
4573 : : (errmsg("index \"%s.%s\" was reindexed",
4574 : : get_namespace_name(get_rel_namespace(indOid)),
4575 : : get_rel_name(indOid))));
4576 : : /* Don't show rusage here, since it's not per index. */
4577 : : }
4578 : :
4579 [ + - ]: 2 : ereport(INFO,
4580 : : (errmsg("table \"%s.%s\" was reindexed",
4581 : : relationNamespace, relationName),
4582 : : errdetail("%s.",
4583 : : pg_rusage_show(&ru0))));
4584 : : }
4585 : : }
4586 : :
4587 : 259 : MemoryContextDelete(private_context);
4588 : :
4589 : 259 : pgstat_progress_end_command();
4590 : :
4591 : 259 : return true;
4592 : : }
4593 : :
4594 : : /*
4595 : : * Insert or delete an appropriate pg_inherits tuple to make the given index
4596 : : * be a partition of the indicated parent index.
4597 : : *
4598 : : * This also corrects the pg_depend information for the affected index.
4599 : : */
4600 : : void
4601 : 704 : IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
4602 : : {
4603 : : Relation pg_inherits;
4604 : : ScanKeyData key[2];
4605 : : SysScanDesc scan;
4606 : 704 : Oid partRelid = RelationGetRelid(partitionIdx);
4607 : : HeapTuple tuple;
4608 : : bool fix_dependencies;
4609 : :
4610 : : /* Make sure this is an index */
4611 : : Assert(partitionIdx->rd_rel->relkind == RELKIND_INDEX ||
4612 : : partitionIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
4613 : :
4614 : : /*
4615 : : * Scan pg_inherits for rows linking our index to some parent.
4616 : : */
4617 : 704 : pg_inherits = relation_open(InheritsRelationId, RowExclusiveLock);
4618 : 704 : ScanKeyInit(&key[0],
4619 : : Anum_pg_inherits_inhrelid,
4620 : : BTEqualStrategyNumber, F_OIDEQ,
4621 : : ObjectIdGetDatum(partRelid));
4622 : 704 : ScanKeyInit(&key[1],
4623 : : Anum_pg_inherits_inhseqno,
4624 : : BTEqualStrategyNumber, F_INT4EQ,
4625 : : Int32GetDatum(1));
4626 : 704 : scan = systable_beginscan(pg_inherits, InheritsRelidSeqnoIndexId, true,
4627 : : NULL, 2, key);
4628 : 704 : tuple = systable_getnext(scan);
4629 : :
4630 [ + + ]: 704 : if (!HeapTupleIsValid(tuple))
4631 : : {
4632 [ - + ]: 406 : if (parentOid == InvalidOid)
4633 : : {
4634 : : /*
4635 : : * No pg_inherits row, and no parent wanted: nothing to do in this
4636 : : * case.
4637 : : */
4638 : 0 : fix_dependencies = false;
4639 : : }
4640 : : else
4641 : : {
4642 : 406 : StoreSingleInheritance(partRelid, parentOid, 1);
4643 : 406 : fix_dependencies = true;
4644 : : }
4645 : : }
4646 : : else
4647 : : {
4648 : 298 : Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(tuple);
4649 : :
4650 [ + - ]: 298 : if (parentOid == InvalidOid)
4651 : : {
4652 : : /*
4653 : : * There exists a pg_inherits row, which we want to clear; do so.
4654 : : */
4655 : 298 : CatalogTupleDelete(pg_inherits, &tuple->t_self);
4656 : 298 : fix_dependencies = true;
4657 : : }
4658 : : else
4659 : : {
4660 : : /*
4661 : : * A pg_inherits row exists. If it's the same we want, then we're
4662 : : * good; if it differs, that amounts to a corrupt catalog and
4663 : : * should not happen.
4664 : : */
4665 [ # # ]: 0 : if (inhForm->inhparent != parentOid)
4666 : : {
4667 : : /* unexpected: we should not get called in this case */
4668 [ # # ]: 0 : elog(ERROR, "bogus pg_inherit row: inhrelid %u inhparent %u",
4669 : : inhForm->inhrelid, inhForm->inhparent);
4670 : : }
4671 : :
4672 : : /* already in the right state */
4673 : 0 : fix_dependencies = false;
4674 : : }
4675 : : }
4676 : :
4677 : : /* done with pg_inherits */
4678 : 704 : systable_endscan(scan);
4679 : 704 : relation_close(pg_inherits, RowExclusiveLock);
4680 : :
4681 : : /* set relhassubclass if an index partition has been added to the parent */
4682 [ + + ]: 704 : if (OidIsValid(parentOid))
4683 : : {
4684 : 406 : LockRelationOid(parentOid, ShareUpdateExclusiveLock);
4685 : 406 : SetRelationHasSubclass(parentOid, true);
4686 : : }
4687 : :
4688 : : /* set relispartition correctly on the partition */
4689 : 704 : update_relispartition(partRelid, OidIsValid(parentOid));
4690 : :
4691 [ + - ]: 704 : if (fix_dependencies)
4692 : : {
4693 : : /*
4694 : : * Insert/delete pg_depend rows. If setting a parent, add PARTITION
4695 : : * dependencies on the parent index and the table; if removing a
4696 : : * parent, delete PARTITION dependencies.
4697 : : */
4698 [ + + ]: 704 : if (OidIsValid(parentOid))
4699 : : {
4700 : : ObjectAddress partIdx;
4701 : : ObjectAddress parentIdx;
4702 : : ObjectAddress partitionTbl;
4703 : :
4704 : 406 : ObjectAddressSet(partIdx, RelationRelationId, partRelid);
4705 : 406 : ObjectAddressSet(parentIdx, RelationRelationId, parentOid);
4706 : 406 : ObjectAddressSet(partitionTbl, RelationRelationId,
4707 : : partitionIdx->rd_index->indrelid);
4708 : 406 : recordDependencyOn(&partIdx, &parentIdx,
4709 : : DEPENDENCY_PARTITION_PRI);
4710 : 406 : recordDependencyOn(&partIdx, &partitionTbl,
4711 : : DEPENDENCY_PARTITION_SEC);
4712 : : }
4713 : : else
4714 : : {
4715 : 298 : deleteDependencyRecordsForClass(RelationRelationId, partRelid,
4716 : : RelationRelationId,
4717 : : DEPENDENCY_PARTITION_PRI);
4718 : 298 : deleteDependencyRecordsForClass(RelationRelationId, partRelid,
4719 : : RelationRelationId,
4720 : : DEPENDENCY_PARTITION_SEC);
4721 : : }
4722 : :
4723 : : /* make our updates visible */
4724 : 704 : CommandCounterIncrement();
4725 : : }
4726 : 704 : }
4727 : :
4728 : : /*
4729 : : * Subroutine of IndexSetParentIndex to update the relispartition flag of the
4730 : : * given index to the given value.
4731 : : */
4732 : : static void
4733 : 704 : update_relispartition(Oid relationId, bool newval)
4734 : : {
4735 : : HeapTuple tup;
4736 : : Relation classRel;
4737 : : ItemPointerData otid;
4738 : :
4739 : 704 : classRel = table_open(RelationRelationId, RowExclusiveLock);
4740 : 704 : tup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relationId));
4741 [ - + ]: 704 : if (!HeapTupleIsValid(tup))
4742 [ # # ]: 0 : elog(ERROR, "cache lookup failed for relation %u", relationId);
4743 : 704 : otid = tup->t_self;
4744 : : Assert(((Form_pg_class) GETSTRUCT(tup))->relispartition != newval);
4745 : 704 : ((Form_pg_class) GETSTRUCT(tup))->relispartition = newval;
4746 : 704 : CatalogTupleUpdate(classRel, &otid, tup);
4747 : 704 : UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
4748 : 704 : heap_freetuple(tup);
4749 : 704 : table_close(classRel, RowExclusiveLock);
4750 : 704 : }
4751 : :
4752 : : /*
4753 : : * Set the PROC_IN_SAFE_IC flag in MyProc->statusFlags.
4754 : : *
4755 : : * When doing concurrent index builds, we can set this flag
4756 : : * to tell other processes concurrently running CREATE
4757 : : * INDEX CONCURRENTLY or REINDEX CONCURRENTLY to ignore us when
4758 : : * doing their waits for concurrent snapshots. On one hand it
4759 : : * avoids pointlessly waiting for a process that's not interesting
4760 : : * anyway; but more importantly it avoids deadlocks in some cases.
4761 : : *
4762 : : * This can be done safely only for indexes that don't execute any
4763 : : * expressions that could access other tables, so index must not be
4764 : : * expressional nor partial. Caller is responsible for only calling
4765 : : * this routine when that assumption holds true.
4766 : : *
4767 : : * (The flag is reset automatically at transaction end, so it must be
4768 : : * set for each transaction.)
4769 : : */
4770 : : static inline void
4771 : 1054 : set_indexsafe_procflags(void)
4772 : : {
4773 : : /*
4774 : : * This should only be called before installing xid or xmin in MyProc;
4775 : : * otherwise, concurrent processes could see an Xmin that moves backwards.
4776 : : */
4777 : : Assert(MyProc->xid == InvalidTransactionId &&
4778 : : MyProc->xmin == InvalidTransactionId);
4779 : :
4780 : 1054 : LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
4781 : 1054 : MyProc->statusFlags |= PROC_IN_SAFE_IC;
4782 : 1054 : ProcGlobal->statusFlags[MyProc->pgxactoff] = MyProc->statusFlags;
4783 : 1054 : LWLockRelease(ProcArrayLock);
4784 : 1054 : }
|