summaryrefslogtreecommitdiff
path: root/src/backend/commands
diff options
context:
space:
mode:
authorTom Lane2015-03-01 19:06:50 +0000
committerTom Lane2015-03-01 19:06:55 +0000
commit8abb3cda0ddc00a0ab98977a1633a95b97068d4e (patch)
tree9ce473ff40b28839e2fb9c2656babae23ce35bb8 /src/backend/commands
parentb8a18ad4850ea5ad7884aa6ab731fd392e73b4ad (diff)
Use the typcache to cache constraints for domain types.
Previously, we cached domain constraints for the life of a query, or really for the life of the FmgrInfo struct that was used to invoke domain_in() or domain_check(). But plpgsql (and probably other places) are set up to cache such FmgrInfos for the whole lifespan of a session, which meant they could be enforcing really stale sets of constraints. On the other hand, searching pg_constraint once per query gets kind of expensive too: testing says that as much as half the runtime of a trivial query such as "SELECT 0::domaintype" went into that. To fix this, delegate the responsibility for tracking a domain's constraints to the typcache, which has the infrastructure needed to detect syscache invalidation events that signal possible changes. This not only removes unnecessary repeat reads of pg_constraint, but ensures that we never apply stale constraint data: whatever we use is the current data according to syscache rules. Unfortunately, the current configuration of the system catalogs means we have to flush cached domain-constraint data whenever either pg_type or pg_constraint changes, which happens rather a lot (eg, creation or deletion of a temp table will do it). It might be worth rearranging things to split pg_constraint into two catalogs, of which the domain constraint one would probably be very low-traffic. That's a job for another patch though, and in any case this patch should improve matters materially even with that handicap. This patch makes use of the recently-added memory context reset callback feature to manage the lifespan of domain constraint caches, so that we don't risk deleting a cache that might be in the midst of evaluation. Although this is a bug fix as well as a performance improvement, no back-patch. There haven't been many if any field complaints about stale domain constraint checks, so it doesn't seem worth taking the risk of modifying data structures as basic as MemoryContexts in back branches.
Diffstat (limited to 'src/backend/commands')
-rw-r--r--src/backend/commands/tablecmds.c4
-rw-r--r--src/backend/commands/typecmds.c127
2 files changed, 2 insertions, 129 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 07ab4b434f5..745502072e6 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4824,7 +4824,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
{
defval = (Expr *) build_column_default(rel, attribute.attnum);
- if (!defval && GetDomainConstraints(typeOid) != NIL)
+ if (!defval && DomainHasConstraints(typeOid))
{
Oid baseTypeId;
int32 baseTypeMod;
@@ -7778,7 +7778,7 @@ ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
{
CoerceToDomain *d = (CoerceToDomain *) expr;
- if (GetDomainConstraints(d->resulttype) != NIL)
+ if (DomainHasConstraints(d->resulttype))
return true;
expr = (Node *) d->arg;
}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index b77e1b4140e..60ab3aaf12f 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -31,15 +31,11 @@
*/
#include "postgres.h"
-#include "access/genam.h"
-#include "access/heapam.h"
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
-#include "catalog/dependency.h"
#include "catalog/heap.h"
-#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
@@ -59,14 +55,12 @@
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
-#include "optimizer/planner.h"
#include "optimizer/var.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
-#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
@@ -75,7 +69,6 @@
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
-#include "utils/tqual.h"
/* result structure for get_rels_with_domain() */
@@ -3081,126 +3074,6 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
return ccbin;
}
-/*
- * GetDomainConstraints - get a list of the current constraints of domain
- *
- * Returns a possibly-empty list of DomainConstraintState nodes.
- *
- * This is called by the executor during plan startup for a CoerceToDomain
- * expression node. The given constraints will be checked for each value
- * passed through the node.
- *
- * We allow this to be called for non-domain types, in which case the result
- * is always NIL.
- */
-List *
-GetDomainConstraints(Oid typeOid)
-{
- List *result = NIL;
- bool notNull = false;
- Relation conRel;
-
- conRel = heap_open(ConstraintRelationId, AccessShareLock);
-
- for (;;)
- {
- HeapTuple tup;
- HeapTuple conTup;
- Form_pg_type typTup;
- ScanKeyData key[1];
- SysScanDesc scan;
-
- tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "cache lookup failed for type %u", typeOid);
- typTup = (Form_pg_type) GETSTRUCT(tup);
-
- if (typTup->typtype != TYPTYPE_DOMAIN)
- {
- /* Not a domain, so done */
- ReleaseSysCache(tup);
- break;
- }
-
- /* Test for NOT NULL Constraint */
- if (typTup->typnotnull)
- notNull = true;
-
- /* Look for CHECK Constraints on this domain */
- ScanKeyInit(&key[0],
- Anum_pg_constraint_contypid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(typeOid));
-
- scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
- NULL, 1, key);
-
- while (HeapTupleIsValid(conTup = systable_getnext(scan)))
- {
- Form_pg_constraint c = (Form_pg_constraint) GETSTRUCT(conTup);
- Datum val;
- bool isNull;
- Expr *check_expr;
- DomainConstraintState *r;
-
- /* Ignore non-CHECK constraints (presently, shouldn't be any) */
- if (c->contype != CONSTRAINT_CHECK)
- continue;
-
- /*
- * Not expecting conbin to be NULL, but we'll test for it anyway
- */
- val = fastgetattr(conTup, Anum_pg_constraint_conbin,
- conRel->rd_att, &isNull);
- if (isNull)
- elog(ERROR, "domain \"%s\" constraint \"%s\" has NULL conbin",
- NameStr(typTup->typname), NameStr(c->conname));
-
- check_expr = (Expr *) stringToNode(TextDatumGetCString(val));
-
- /* ExecInitExpr assumes we've planned the expression */
- check_expr = expression_planner(check_expr);
-
- r = makeNode(DomainConstraintState);
- r->constrainttype = DOM_CONSTRAINT_CHECK;
- r->name = pstrdup(NameStr(c->conname));
- r->check_expr = ExecInitExpr(check_expr, NULL);
-
- /*
- * use lcons() here because constraints of lower domains should be
- * applied earlier.
- */
- result = lcons(r, result);
- }
-
- systable_endscan(scan);
-
- /* loop to next domain in stack */
- typeOid = typTup->typbasetype;
- ReleaseSysCache(tup);
- }
-
- heap_close(conRel, AccessShareLock);
-
- /*
- * Only need to add one NOT NULL check regardless of how many domains in
- * the stack request it.
- */
- if (notNull)
- {
- DomainConstraintState *r = makeNode(DomainConstraintState);
-
- r->constrainttype = DOM_CONSTRAINT_NOTNULL;
- r->name = pstrdup("NOT NULL");
- r->check_expr = NULL;
-
- /* lcons to apply the nullness check FIRST */
- result = lcons(r, result);
- }
-
- return result;
-}
-
/*
* Execute ALTER TYPE RENAME