diff options
Diffstat (limited to 'src/backend/utils')
| -rw-r--r-- | src/backend/utils/adt/ruleutils.c | 87 | ||||
| -rw-r--r-- | src/backend/utils/cache/relcache.c | 138 |
2 files changed, 213 insertions, 12 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index fc5885e68e..71e614d700 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.315 2009/11/20 20:38:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.316 2009/12/07 05:22:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -144,6 +144,7 @@ static void decompile_column_index_array(Datum column_index_array, Oid relId, StringInfo buf); static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags); static char *pg_get_indexdef_worker(Oid indexrelid, int colno, + const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags); static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, @@ -705,6 +706,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS) Oid indexrelid = PG_GETARG_OID(0); PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, 0, + NULL, false, false, 0))); } @@ -718,6 +720,7 @@ pg_get_indexdef_ext(PG_FUNCTION_ARGS) prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0; PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, colno, + NULL, colno != 0, false, prettyFlags))); @@ -727,7 +730,7 @@ pg_get_indexdef_ext(PG_FUNCTION_ARGS) char * pg_get_indexdef_string(Oid indexrelid) { - return pg_get_indexdef_worker(indexrelid, 0, false, true, 0); + return pg_get_indexdef_worker(indexrelid, 0, NULL, false, true, 0); } /* Internal version that just reports the column definitions */ @@ -737,14 +740,23 @@ pg_get_indexdef_columns(Oid indexrelid, bool pretty) int prettyFlags; prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0; - return pg_get_indexdef_worker(indexrelid, 0, true, false, prettyFlags); + return pg_get_indexdef_worker(indexrelid, 0, NULL, true, false, prettyFlags); } +/* + * Internal workhorse to decompile an index definition. + * + * This is now used for exclusion constraints as well: if excludeOps is not + * NULL then it points to an array of exclusion operator OIDs. + */ static char * pg_get_indexdef_worker(Oid indexrelid, int colno, + const Oid *excludeOps, bool attrsOnly, bool showTblSpc, int prettyFlags) { + /* might want a separate isConstraint parameter later */ + bool isConstraint = (excludeOps != NULL); HeapTuple ht_idx; HeapTuple ht_idxrel; HeapTuple ht_am; @@ -842,11 +854,17 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, initStringInfo(&buf); if (!attrsOnly) - appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (", - idxrec->indisunique ? "UNIQUE " : "", - quote_identifier(NameStr(idxrelrec->relname)), - generate_relation_name(indrelid, NIL), - quote_identifier(NameStr(amrec->amname))); + { + if (!isConstraint) + appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (", + idxrec->indisunique ? "UNIQUE " : "", + quote_identifier(NameStr(idxrelrec->relname)), + generate_relation_name(indrelid, NIL), + quote_identifier(NameStr(amrec->amname))); + else /* currently, must be EXCLUDE constraint */ + appendStringInfo(&buf, "EXCLUDE USING %s (", + quote_identifier(NameStr(amrec->amname))); + } /* * Report the indexed attributes @@ -917,6 +935,13 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, appendStringInfo(&buf, " NULLS FIRST"); } } + + /* Add the exclusion operator if relevant */ + if (excludeOps != NULL) + appendStringInfo(&buf, " WITH %s", + generate_operator_name(excludeOps[keyno], + keycoltype, + keycoltype)); } } @@ -943,8 +968,12 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, tblspc = get_rel_tablespace(indexrelid); if (OidIsValid(tblspc)) + { + if (isConstraint) + appendStringInfoString(&buf, " USING INDEX"); appendStringInfo(&buf, " TABLESPACE %s", quote_identifier(get_tablespace_name(tblspc))); + } } /* @@ -968,7 +997,10 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, /* Deparse */ str = deparse_expression_pretty(node, context, false, false, prettyFlags, 0); - appendStringInfo(&buf, " WHERE %s", str); + if (isConstraint) + appendStringInfo(&buf, " WHERE (%s)", str); + else + appendStringInfo(&buf, " WHERE %s", str); } } @@ -1244,6 +1276,43 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand, break; } + case CONSTRAINT_EXCLUSION: + { + Oid indexOid = conForm->conindid; + Datum val; + bool isnull; + Datum *elems; + int nElems; + int i; + Oid *operators; + + /* Extract operator OIDs from the pg_constraint tuple */ + val = SysCacheGetAttr(CONSTROID, tup, + Anum_pg_constraint_conexclop, + &isnull); + if (isnull) + elog(ERROR, "null conexclop for constraint %u", + constraintId); + + deconstruct_array(DatumGetArrayTypeP(val), + OIDOID, sizeof(Oid), true, 'i', + &elems, NULL, &nElems); + + operators = (Oid *) palloc(nElems * sizeof(Oid)); + for (i = 0; i < nElems; i++) + operators[i] = DatumGetObjectId(elems[i]); + + /* pg_get_indexdef_worker does the rest */ + /* suppress tablespace because pg_dump wants it that way */ + appendStringInfoString(&buf, + pg_get_indexdef_worker(indexOid, + 0, + operators, + false, + false, + prettyFlags)); + break; + } default: elog(ERROR, "invalid constraint type \"%c\"", conForm->contype); break; diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 3a66e2aa8e..82c4bbc01a 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.292 2009/09/26 23:08:22 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.293 2009/12/07 05:22:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -60,9 +60,11 @@ #include "storage/fd.h" #include "storage/lmgr.h" #include "storage/smgr.h" +#include "utils/array.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/inval.h" +#include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/relcache.h" #include "utils/resowner.h" @@ -1079,10 +1081,13 @@ RelationInitIndexAccessInfo(Relation relation) memcpy(relation->rd_indoption, indoption->values, natts * sizeof(int16)); /* - * expressions and predicate cache will be filled later + * expressions, predicate, exclusion caches will be filled later */ relation->rd_indexprs = NIL; relation->rd_indpred = NIL; + relation->rd_exclops = NULL; + relation->rd_exclprocs = NULL; + relation->rd_exclstrats = NULL; relation->rd_amcache = NULL; } @@ -3453,6 +3458,130 @@ RelationGetIndexAttrBitmap(Relation relation) return indexattrs; } +/* + * RelationGetExclusionInfo -- get info about index's exclusion constraint + * + * This should be called only for an index that is known to have an + * associated exclusion constraint. It returns arrays (palloc'd in caller's + * context) of the exclusion operator OIDs, their underlying functions' + * OIDs, and their strategy numbers in the index's opclasses. We cache + * all this information since it requires a fair amount of work to get. + */ +void +RelationGetExclusionInfo(Relation indexRelation, + Oid **operators, + Oid **procs, + uint16 **strategies) +{ + int ncols = indexRelation->rd_rel->relnatts; + Oid *ops; + Oid *funcs; + uint16 *strats; + Relation conrel; + SysScanDesc conscan; + ScanKeyData skey[1]; + HeapTuple htup; + bool found; + MemoryContext oldcxt; + int i; + + /* Allocate result space in caller context */ + *operators = ops = (Oid *) palloc(sizeof(Oid) * ncols); + *procs = funcs = (Oid *) palloc(sizeof(Oid) * ncols); + *strategies = strats = (uint16 *) palloc(sizeof(uint16) * ncols); + + /* Quick exit if we have the data cached already */ + if (indexRelation->rd_exclstrats != NULL) + { + memcpy(ops, indexRelation->rd_exclops, sizeof(Oid) * ncols); + memcpy(funcs, indexRelation->rd_exclprocs, sizeof(Oid) * ncols); + memcpy(strats, indexRelation->rd_exclstrats, sizeof(uint16) * ncols); + return; + } + + /* + * Search pg_constraint for the constraint associated with the index. + * To make this not too painfully slow, we use the index on conrelid; + * that will hold the parent relation's OID not the index's own OID. + */ + ScanKeyInit(&skey[0], + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(indexRelation->rd_index->indrelid)); + + conrel = heap_open(ConstraintRelationId, AccessShareLock); + conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true, + SnapshotNow, 1, skey); + found = false; + + while (HeapTupleIsValid(htup = systable_getnext(conscan))) + { + Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(htup); + Datum val; + bool isnull; + ArrayType *arr; + int nelem; + + /* We want the exclusion constraint owning the index */ + if (conform->contype != CONSTRAINT_EXCLUSION || + conform->conindid != RelationGetRelid(indexRelation)) + continue; + + /* There should be only one */ + if (found) + elog(ERROR, "unexpected exclusion constraint record found for rel %s", + RelationGetRelationName(indexRelation)); + found = true; + + /* Extract the operator OIDS from conexclop */ + val = fastgetattr(htup, + Anum_pg_constraint_conexclop, + conrel->rd_att, &isnull); + if (isnull) + elog(ERROR, "null conexclop for rel %s", + RelationGetRelationName(indexRelation)); + + arr = DatumGetArrayTypeP(val); /* ensure not toasted */ + nelem = ARR_DIMS(arr)[0]; + if (ARR_NDIM(arr) != 1 || + nelem != ncols || + ARR_HASNULL(arr) || + ARR_ELEMTYPE(arr) != OIDOID) + elog(ERROR, "conexclop is not a 1-D Oid array"); + + memcpy(ops, ARR_DATA_PTR(arr), sizeof(Oid) * ncols); + } + + systable_endscan(conscan); + heap_close(conrel, AccessShareLock); + + if (!found) + elog(ERROR, "exclusion constraint record missing for rel %s", + RelationGetRelationName(indexRelation)); + + /* We need the func OIDs and strategy numbers too */ + for (i = 0; i < ncols; i++) + { + funcs[i] = get_opcode(ops[i]); + strats[i] = get_op_opfamily_strategy(ops[i], + indexRelation->rd_opfamily[i]); + /* shouldn't fail, since it was checked at index creation */ + if (strats[i] == InvalidStrategy) + elog(ERROR, "could not find strategy for operator %u in family %u", + ops[i], indexRelation->rd_opfamily[i]); + } + + /* Save a copy of the results in the relcache entry. */ + oldcxt = MemoryContextSwitchTo(indexRelation->rd_indexcxt); + indexRelation->rd_exclops = (Oid *) palloc(sizeof(Oid) * ncols); + indexRelation->rd_exclprocs = (Oid *) palloc(sizeof(Oid) * ncols); + indexRelation->rd_exclstrats = (uint16 *) palloc(sizeof(uint16) * ncols); + memcpy(indexRelation->rd_exclops, ops, sizeof(Oid) * ncols); + memcpy(indexRelation->rd_exclprocs, funcs, sizeof(Oid) * ncols); + memcpy(indexRelation->rd_exclstrats, strats, sizeof(uint16) * ncols); + MemoryContextSwitchTo(oldcxt); +} + /* * load_relcache_init_file, write_relcache_init_file @@ -3768,13 +3897,16 @@ load_relcache_init_file(bool shared) * format is complex and subject to change). They must be rebuilt if * needed by RelationCacheInitializePhase3. This is not expected to * be a big performance hit since few system catalogs have such. Ditto - * for index expressions and predicates. + * for index expressions, predicates, and exclusion info. */ rel->rd_rules = NULL; rel->rd_rulescxt = NULL; rel->trigdesc = NULL; rel->rd_indexprs = NIL; rel->rd_indpred = NIL; + rel->rd_exclops = NULL; + rel->rd_exclprocs = NULL; + rel->rd_exclstrats = NULL; /* * Reset transient-state fields in the relcache entry |
