mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Determine the set of constraints applied to a domain at executor
startup, not in the parser; this allows ALTER DOMAIN to work correctly with domain constraint operations stored in rules. Rod Taylor; code review by Tom Lane.
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.188 2003/01/10 22:03:27 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.189 2003/02/03 21:15:43 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -853,7 +853,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
}
|
||||
}
|
||||
|
||||
/* If it's a domain type, get info on domain constraints */
|
||||
/* If it's a domain type, set up to check domain constraints */
|
||||
if (get_typtype(attr[i]->atttypid) == 'd')
|
||||
{
|
||||
Param *prm;
|
||||
@@ -863,25 +863,23 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
|
||||
* Easiest way to do this is to use parse_coerce.c to set up
|
||||
* an expression that checks the constraints. (At present,
|
||||
* the expression might contain a length-coercion-function call
|
||||
* and/or ConstraintTest nodes.) The bottom of the expression
|
||||
* and/or CoerceToDomain nodes.) The bottom of the expression
|
||||
* is a Param node so that we can fill in the actual datum during
|
||||
* the data input loop.
|
||||
*/
|
||||
prm = makeNode(Param);
|
||||
prm->paramkind = PARAM_EXEC;
|
||||
prm->paramid = 0;
|
||||
prm->paramtype = attr[i]->atttypid;
|
||||
prm->paramtype = getBaseType(attr[i]->atttypid);
|
||||
|
||||
node = coerce_type_constraints((Node *) prm, attr[i]->atttypid,
|
||||
COERCE_IMPLICIT_CAST);
|
||||
node = coerce_to_domain((Node *) prm,
|
||||
prm->paramtype,
|
||||
attr[i]->atttypid,
|
||||
COERCE_IMPLICIT_CAST);
|
||||
|
||||
/* check whether any constraints actually found */
|
||||
if (node != (Node *) prm)
|
||||
{
|
||||
constraintexprs[i] = ExecPrepareExpr((Expr *) node,
|
||||
estate);
|
||||
hasConstraints = true;
|
||||
}
|
||||
constraintexprs[i] = ExecPrepareExpr((Expr *) node,
|
||||
estate);
|
||||
hasConstraints = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.29 2003/01/08 22:06:23 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.30 2003/02/03 21:15:43 tgl Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@@ -46,8 +46,10 @@
|
||||
#include "commands/typecmds.h"
|
||||
#include "executor/executor.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/execnodes.h"
|
||||
#include "nodes/nodes.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
@@ -1555,7 +1557,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
|
||||
char *ccsrc;
|
||||
char *ccbin;
|
||||
ParseState *pstate;
|
||||
ConstraintTestValue *domVal;
|
||||
CoerceToDomainValue *domVal;
|
||||
|
||||
/*
|
||||
* Assign or validate constraint name
|
||||
@@ -1582,13 +1584,13 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
|
||||
pstate = make_parsestate(NULL);
|
||||
|
||||
/*
|
||||
* Set up a ConstraintTestValue to represent the occurrence of VALUE
|
||||
* Set up a CoerceToDomainValue to represent the occurrence of VALUE
|
||||
* in the expression. Note that it will appear to have the type of the
|
||||
* base type, not the domain. This seems correct since within the
|
||||
* check expression, we should not assume the input value can be considered
|
||||
* a member of the domain.
|
||||
*/
|
||||
domVal = makeNode(ConstraintTestValue);
|
||||
domVal = makeNode(CoerceToDomainValue);
|
||||
domVal->typeId = baseTypeOid;
|
||||
domVal->typeMod = typMod;
|
||||
|
||||
@@ -1669,6 +1671,125 @@ 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.
|
||||
*/
|
||||
List *
|
||||
GetDomainConstraints(Oid typeOid)
|
||||
{
|
||||
List *result = NIL;
|
||||
bool notNull = false;
|
||||
Relation conRel;
|
||||
|
||||
conRel = heap_openr(ConstraintRelationName, AccessShareLock);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
HeapTuple tup;
|
||||
HeapTuple conTup;
|
||||
Form_pg_type typTup;
|
||||
ScanKeyData key[1];
|
||||
SysScanDesc scan;
|
||||
|
||||
tup = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(typeOid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "GetDomainConstraints: failed to lookup type %u",
|
||||
typeOid);
|
||||
typTup = (Form_pg_type) GETSTRUCT(tup);
|
||||
|
||||
/* Test for NOT NULL Constraint */
|
||||
if (typTup->typnotnull)
|
||||
notNull = true;
|
||||
|
||||
/* Look for CHECK Constraints on this domain */
|
||||
ScanKeyEntryInitialize(&key[0], 0x0,
|
||||
Anum_pg_constraint_contypid, F_OIDEQ,
|
||||
ObjectIdGetDatum(typeOid));
|
||||
|
||||
scan = systable_beginscan(conRel, ConstraintTypidIndex, true,
|
||||
SnapshotNow, 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, "GetDomainConstraints: domain %s constraint %s has NULL conbin",
|
||||
NameStr(typTup->typname), NameStr(c->conname));
|
||||
|
||||
check_expr = (Expr *)
|
||||
stringToNode(DatumGetCString(DirectFunctionCall1(textout,
|
||||
val)));
|
||||
|
||||
/* ExecInitExpr assumes we already fixed opfuncids */
|
||||
fix_opfuncids((Node *) 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);
|
||||
|
||||
if (typTup->typtype != 'd')
|
||||
{
|
||||
/* Not a domain, so done */
|
||||
ReleaseSysCache(tup);
|
||||
break;
|
||||
}
|
||||
|
||||
/* else 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* ALTER DOMAIN .. OWNER TO
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user