Add DOMAIN check constraints.

Rod Taylor
This commit is contained in:
Bruce Momjian 2002-11-15 02:50:21 +00:00
parent 2986aa6a66
commit 6b603e67dc
29 changed files with 641 additions and 147 deletions

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_domain.sgml,v 1.6 2002/09/20 03:39:15 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_domain.sgml,v 1.7 2002/11/15 02:50:05 momjian Exp $
PostgreSQL documentation
-->
@ -200,16 +200,6 @@ CREATE TABLE countrylist (id INT4, country country_code);
</para>
</refsect1>
<refsect1 id="SQL-CREATEDOMAIN-compatibility">
<title>Compatibility</title>
<para>
SQL99 defines CREATE DOMAIN, but says that the only allowed constraint
type is CHECK constraints. CHECK constraints for domains are not yet
supported by <productname>PostgreSQL</productname>.
</para>
</refsect1>
<refsect1 id="SQL-CREATEDOMAIN-see-also">
<title>See Also</title>

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.234 2002/11/11 22:19:21 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.235 2002/11/15 02:50:05 momjian Exp $
*
*
* INTERFACE ROUTINES
@ -1500,7 +1500,8 @@ AddRelationRawConstraints(Relation rel,
ccname = cdef->name;
/* Check against pre-existing constraints */
if (ConstraintNameIsUsed(RelationGetRelid(rel),
if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
RelationGetRelid(rel),
RelationGetNamespace(rel),
ccname))
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
@ -1534,7 +1535,8 @@ AddRelationRawConstraints(Relation rel,
* pre-existing constraints, nor with any auto-generated
* names so far.
*/
ccname = GenerateConstraintName(RelationGetRelid(rel),
ccname = GenerateConstraintName(CONSTRAINT_RELATION,
RelationGetRelid(rel),
RelationGetNamespace(rel),
&constr_name_ctr);
@ -1565,7 +1567,7 @@ AddRelationRawConstraints(Relation rel,
/*
* Transform raw parsetree to executable expression.
*/
expr = transformExpr(pstate, cdef->raw_expr);
expr = transformExpr(pstate, cdef->raw_expr, NULL);
/*
* Make sure it yields a boolean result.
@ -1694,7 +1696,7 @@ cookDefault(ParseState *pstate,
/*
* Transform raw parsetree to executable expression.
*/
expr = transformExpr(pstate, raw_default);
expr = transformExpr(pstate, raw_default, NULL);
/*
* Make sure default expr does not refer to any vars.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.7 2002/09/22 00:37:09 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.8 2002/11/15 02:50:05 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -190,6 +190,19 @@ CreateConstraintEntry(const char *constraintName,
}
}
if (OidIsValid(domainId))
{
/*
* Register auto dependency from constraint to owning domain
*/
ObjectAddress domobject;
domobject.classId = RelOid_pg_type;
domobject.objectId = domainId;
recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
}
if (OidIsValid(foreignRelId))
{
/*
@ -262,7 +275,7 @@ CreateConstraintEntry(const char *constraintName,
* this test is not very meaningful.
*/
bool
ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
ConstraintNameIsUsed(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, const char *cname)
{
bool found;
Relation conDesc;
@ -280,7 +293,7 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
ScanKeyEntryInitialize(&skey[1], 0x0,
Anum_pg_constraint_connamespace, F_OIDEQ,
ObjectIdGetDatum(relNamespace));
ObjectIdGetDatum(objNamespace));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
SnapshotNow, 2, skey);
@ -289,7 +302,12 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
if (con->conrelid == relId)
if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
{
found = true;
break;
}
else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
{
found = true;
break;
@ -314,7 +332,7 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
* someone else might choose the same name concurrently!
*/
char *
GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
GenerateConstraintName(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, int *counter)
{
bool found;
Relation conDesc;
@ -347,7 +365,7 @@ GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
ScanKeyEntryInitialize(&skey[1], 0x0,
Anum_pg_constraint_connamespace, F_OIDEQ,
ObjectIdGetDatum(relNamespace));
ObjectIdGetDatum(objNamespace));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
SnapshotNow, 2, skey);
@ -356,7 +374,12 @@ GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
if (con->conrelid == relId)
if (conCat == CONSTRAINT_RELATION && con->conrelid == objId)
{
found = true;
break;
}
else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId)
{
found = true;
break;
@ -415,10 +438,13 @@ RemoveConstraintById(Oid conId)
con = (Form_pg_constraint) GETSTRUCT(tup);
/*
* If the constraint is for a relation, open and exclusive-lock the
* relation it's for.
* If the constraint is for a relation, open and exclusive-lock
* the relation it's for.
*
* XXX not clear what we should lock, if anything, for other constraints.
* If the constraint is for a domain, open and lock the pg_type entry
* tye constraint is used on.
*
* XXX not clear what we should lock, if anything, for assert constraints.
*/
if (OidIsValid(con->conrelid))
{
@ -463,6 +489,34 @@ RemoveConstraintById(Oid conId)
/* Keep lock on constraint's rel until end of xact */
heap_close(rel, NoLock);
}
/* Lock the domain row in pg_type */
else if (OidIsValid(con->contypid))
{
Relation typRel;
HeapTuple typTup;
ScanKeyData typKey[1];
SysScanDesc typScan;
typRel = heap_openr(TypeRelationName, RowExclusiveLock);
ScanKeyEntryInitialize(&typKey[0], 0x0,
Anum_pg_constraint_contypid, F_OIDEQ,
ObjectIdGetDatum(con->contypid));
typScan = systable_beginscan(typRel, TypeOidIndex, true,
SnapshotNow, 1, typKey);
typTup = systable_getnext(typScan);
if (!HeapTupleIsValid(typTup))
elog(ERROR, "RemoveConstraintById: Type %d does not exist",
con->contypid);
systable_endscan(typScan);
/* Keep lock on domain type until end of xact */
heap_close(typRel, NoLock);
}
/* Fry the constraint itself */
simple_heap_delete(conDesc, &tup->t_self);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.53 2002/11/11 22:19:21 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.54 2002/11/15 02:50:05 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -2632,14 +2632,16 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
*/
if (constr->name)
{
if (ConstraintNameIsUsed(RelationGetRelid(rel),
if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
RelationGetRelid(rel),
RelationGetNamespace(rel),
constr->name))
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
constr->name, RelationGetRelationName(rel));
}
else
constr->name = GenerateConstraintName(RelationGetRelid(rel),
constr->name = GenerateConstraintName(CONSTRAINT_RELATION,
RelationGetRelid(rel),
RelationGetNamespace(rel),
&counter);
@ -2668,7 +2670,8 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
*/
if (fkconstraint->constr_name)
{
if (ConstraintNameIsUsed(RelationGetRelid(rel),
if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
RelationGetRelid(rel),
RelationGetNamespace(rel),
fkconstraint->constr_name))
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
@ -2676,7 +2679,8 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
RelationGetRelationName(rel));
}
else
fkconstraint->constr_name = GenerateConstraintName(RelationGetRelid(rel),
fkconstraint->constr_name = GenerateConstraintName(CONSTRAINT_RELATION,
RelationGetRelid(rel),
RelationGetNamespace(rel),
&counter);
@ -2734,7 +2738,7 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr)
/*
* Convert the A_EXPR in raw_expr into an EXPR
*/
expr = transformExpr(pstate, constr->raw_expr);
expr = transformExpr(pstate, constr->raw_expr, NULL);
/*
* Make sure it yields a boolean result.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.16 2002/11/11 22:19:22 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.17 2002/11/15 02:50:06 momjian Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@ -36,10 +36,17 @@
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
#include "miscadmin.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"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
@ -406,7 +413,8 @@ DefineDomain(CreateDomainStmt *stmt)
List *listptr;
Oid basetypeoid;
Oid domainoid;
Form_pg_type baseType;
Form_pg_type baseType;
int counter = 0;
/* Convert list of names to a name and namespace */
domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
@ -484,17 +492,21 @@ DefineDomain(CreateDomainStmt *stmt)
basetypelem = baseType->typelem;
/*
* Run through constraints manually to avoid the additional processing
* conducted by DefineRelation() and friends.
*
* Besides, we don't want any constraints to be cooked. We'll do that
* when the table is created via MergeDomainAttributes().
* Run through constraints manually to avoid the additional
* processing conducted by DefineRelation() and friends.
*/
foreach(listptr, schema)
{
Constraint *colDef = lfirst(listptr);
Node *newConstraint = lfirst(listptr);
Constraint *colDef;
ParseState *pstate;
/* Prior to processing, confirm that it is not a foreign key constraint */
if (nodeTag(newConstraint) == T_FkConstraint)
elog(ERROR, "CREATE DOMAIN / FOREIGN KEY constraints not supported");
colDef = (Constraint *) newConstraint;
switch (colDef->contype)
{
/*
@ -546,26 +558,26 @@ DefineDomain(CreateDomainStmt *stmt)
elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
typNotNull = false;
nullDefined = true;
break;
break;
case CONSTR_UNIQUE:
elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported");
break;
case CONSTR_UNIQUE:
elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported");
break;
case CONSTR_PRIMARY:
elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported");
break;
case CONSTR_PRIMARY:
elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported");
break;
case CONSTR_CHECK:
elog(ERROR, "DefineDomain: CHECK Constraints not supported");
break;
/* Check constraints are handled after domain creation */
case CONSTR_CHECK:
break;
case CONSTR_ATTR_DEFERRABLE:
case CONSTR_ATTR_NOT_DEFERRABLE:
case CONSTR_ATTR_DEFERRED:
case CONSTR_ATTR_IMMEDIATE:
elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported");
break;
case CONSTR_ATTR_DEFERRABLE:
case CONSTR_ATTR_NOT_DEFERRABLE:
case CONSTR_ATTR_DEFERRED:
case CONSTR_ATTR_IMMEDIATE:
elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported");
break;
default:
elog(ERROR, "DefineDomain: unrecognized constraint node type");
@ -591,12 +603,139 @@ DefineDomain(CreateDomainStmt *stmt)
basetypeoid, /* base type ID */
defaultValue, /* default type value (text) */
defaultValueBin, /* default type value (binary) */
byValue, /* passed by value */
alignment, /* required alignment */
storage, /* TOAST strategy */
stmt->typename->typmod, /* typeMod value */
typNDims, /* Array dimensions for base type */
typNotNull); /* Type NOT NULL */
byValue, /* passed by value */
alignment, /* required alignment */
storage, /* TOAST strategy */
stmt->typename->typmod, /* typeMod value */
typNDims, /* Array dimensions for base type */
typNotNull); /* Type NOT NULL */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
*/
foreach(listptr, schema)
{
Constraint *constr = lfirst(listptr);
ParseState *pstate;
switch (constr->contype)
{
case CONSTR_CHECK:
{
Node *expr;
char *ccsrc;
char *ccbin;
ConstraintTestValue *domVal;
/*
* Assign or validate constraint name
*/
if (constr->name)
{
if (ConstraintNameIsUsed(CONSTRAINT_DOMAIN,
domainoid,
domainNamespace,
constr->name))
elog(ERROR, "constraint \"%s\" already exists for domain \"%s\"",
constr->name,
domainName);
}
else
constr->name = GenerateConstraintName(CONSTRAINT_DOMAIN,
domainoid,
domainNamespace,
&counter);
/*
* Convert the A_EXPR in raw_expr into an
* EXPR
*/
pstate = make_parsestate(NULL);
/*
* We want to have the domain VALUE node type filled in so
* that proper casting can occur.
*/
domVal = makeNode(ConstraintTestValue);
domVal->typeId = basetypeoid;
domVal->typeMod = stmt->typename->typmod;
expr = transformExpr(pstate, constr->raw_expr, domVal);
/*
* Domains don't allow var clauses
*/
if (contain_var_clause(expr))
elog(ERROR, "cannot use column references in domain CHECK clause");
/*
* Make sure it yields a boolean result.
*/
expr = coerce_to_boolean(expr, "CHECK");
/*
* Make sure no outside relations are
* referred to.
*/
if (length(pstate->p_rtable) != 0)
elog(ERROR, "Relations cannot be referenced in domain CHECK constraint");
/*
* No subplans or aggregates, either...
*/
if (contain_subplans(expr))
elog(ERROR, "cannot use subselect in CHECK constraint expression");
if (contain_agg_clause(expr))
elog(ERROR, "cannot use aggregate function in CHECK constraint expression");
/*
* Might as well try to reduce any constant expressions.
*/
expr = eval_const_expressions(expr);
/*
* Must fix opids in operator clauses.
*/
fix_opids(expr);
ccbin = nodeToString(expr);
/*
* Deparse it. Since VARNOs aren't allowed in domain
* constraints, relation context isn't required as anything
* other than a shell.
*/
ccsrc = deparse_expression(expr,
deparse_context_for(domainName,
InvalidOid),
false, false);
/* Write the constraint */
CreateConstraintEntry(constr->name, /* Constraint Name */
domainNamespace, /* namespace */
CONSTRAINT_CHECK, /* Constraint Type */
false, /* Is Deferrable */
false, /* Is Deferred */
InvalidOid, /* not a relation constraint */
NULL,
0,
domainoid, /* domain constraint */
InvalidOid, /* Foreign key fields */
NULL,
0,
' ',
' ',
' ',
InvalidOid,
expr, /* Tree form check constraint */
ccbin, /* Binary form check constraint */
ccsrc); /* Source form check constraint */
}
break;
default:
break;
}
}
/*
* Add any dependencies needed for the default expression.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.108 2002/09/04 20:31:17 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.109 2002/11/15 02:50:06 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -72,6 +72,9 @@ static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext,
static Datum ExecEvalConstraintTest(ConstraintTest *constraint,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalConstraintTestValue(ConstraintTestValue *conVal,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
/*----------
@ -1551,6 +1554,23 @@ ExecEvalBooleanTest(BooleanTest *btest,
}
}
/*
* ExecEvalConstraintTestValue
*
* Return the value stored by constraintTest.
*/
static Datum
ExecEvalConstraintTestValue(ConstraintTestValue *conVal, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
/*
* If the Datum hasn't been set, then it's ExecEvalConstraintTest
* hasn't been called.
*/
*isNull = econtext->domainValue_isNull;
return econtext->domainValue_datum;
}
/*
* ExecEvalConstraintTest
*
@ -1571,11 +1591,22 @@ ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext,
case CONSTR_TEST_NOTNULL:
if (*isNull)
elog(ERROR, "Domain %s does not allow NULL values",
constraint->name);
constraint->domname);
break;
case CONSTR_TEST_CHECK:
/* TODO: Add CHECK Constraints to domains */
elog(ERROR, "Domain CHECK Constraints not yet implemented");
{
Datum conResult;
/* Var with attnum == UnassignedAttrNum uses the result */
econtext->domainValue_datum = result;
econtext->domainValue_isNull = *isNull;
conResult = ExecEvalExpr(constraint->check_expr, econtext, isNull, isDone);
if (!DatumGetBool(conResult))
elog(ERROR, "Domain %s constraint %s failed",
constraint->name, constraint->domname);
}
break;
default:
elog(ERROR, "ExecEvalConstraintTest: Constraint type unknown");
@ -1777,7 +1808,12 @@ ExecEvalExpr(Node *expression,
isNull,
isDone);
break;
case T_ConstraintTestValue:
retDatum = ExecEvalConstraintTestValue((ConstraintTestValue *) expression,
econtext,
isNull,
isDone);
break;
default:
elog(ERROR, "ExecEvalExpr: unknown expression type %d",
nodeTag(expression));

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.217 2002/11/11 22:19:22 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.218 2002/11/15 02:50:06 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -1056,11 +1056,35 @@ _copyConstraintTest(ConstraintTest *from)
newnode->testtype = from->testtype;
if (from->name)
newnode->name = pstrdup(from->name);
if (from->domname)
newnode->domname = pstrdup(from->domname);
Node_Copy(from, newnode, check_expr);
return newnode;
}
static ConstraintTestValue *
_copyConstraintTestValue(ConstraintTestValue *from)
{
ConstraintTestValue *newnode = makeNode(ConstraintTestValue);
/*
* copy remainder of node
*/
newnode->typeId = from->typeId;
newnode->typeMod = from->typeMod;
return newnode;
}
static DomainConstraintValue *
_copyDomainConstraintValue(DomainConstraintValue *from)
{
DomainConstraintValue *newnode = makeNode(DomainConstraintValue);
return newnode;
}
static ArrayRef *
_copyArrayRef(ArrayRef *from)
{
@ -3252,6 +3276,9 @@ copyObject(void *from)
case T_ConstraintTest:
retval = _copyConstraintTest(from);
break;
case T_ConstraintTestValue:
retval = _copyConstraintTestValue(from);
break;
case T_FkConstraint:
retval = _copyFkConstraint(from);
break;
@ -3264,6 +3291,9 @@ copyObject(void *from)
case T_InsertDefault:
retval = _copyInsertDefault(from);
break;
case T_DomainConstraintValue:
retval = _copyDomainConstraintValue(from);
break;
default:
elog(ERROR, "copyObject: don't know how to copy node type %d",

View File

@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.163 2002/11/11 22:19:22 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.164 2002/11/15 02:50:06 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -1971,15 +1971,32 @@ _equalConstraintTest(ConstraintTest *a, ConstraintTest *b)
return false;
if (!equalstr(a->name, b->name))
return false;
if (!equalstr(a->domname, b->domname))
return false;
if (!equal(a->check_expr, b->check_expr))
return false;
return true;
}
static bool
_equalConstraintTestValue(ConstraintTestValue *a, ConstraintTestValue *b)
{
if (a->typeId != b->typeId)
return false;
if (a->typeMod != b->typeMod)
return false;
return true;
}
static bool
_equalDomainConstraintValue(DomainConstraintValue *a, DomainConstraintValue *b)
{
return true;
}
/*
* Stuff from pg_list.h
*/
static bool
_equalValue(Value *a, Value *b)
{
@ -2438,6 +2455,9 @@ equal(void *a, void *b)
case T_ConstraintTest:
retval = _equalConstraintTest(a, b);
break;
case T_ConstraintTestValue:
retval = _equalConstraintTestValue(a, b);
break;
case T_FkConstraint:
retval = _equalFkConstraint(a, b);
break;
@ -2450,6 +2470,9 @@ equal(void *a, void *b)
case T_InsertDefault:
retval = _equalInsertDefault(a, b);
break;
case T_DomainConstraintValue:
retval = _equalDomainConstraintValue(a, b);
break;
default:
elog(WARNING, "equal: don't know whether nodes of type %d are equal",

View File

@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.179 2002/11/11 22:19:22 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.180 2002/11/15 02:50:07 momjian Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
@ -1525,10 +1525,32 @@ _outConstraintTest(StringInfo str, ConstraintTest *node)
appendStringInfo(str, " :testtype %d :name ",
(int) node->testtype);
_outToken(str, node->name);
appendStringInfo(str, " :domain ");
_outToken(str, node->domname);
appendStringInfo(str, " :check_expr ");
_outNode(str, node->check_expr);
}
/*
* ConstraintTestValue
*/
static void
_outConstraintTestValue(StringInfo str, ConstraintTestValue *node)
{
appendStringInfo(str, " CONSTRAINTTESTVALUE :typeid %u :typemod %d ",
node->typeId,
node->typeMod);
}
/*
* DomainConstraintValue
*/
static void
_outDomainConstraintValue(StringInfo str, DomainConstraintValue *node)
{
appendStringInfo(str, " DOMAINCONSTRAINTVALUE ");
}
/*
* _outNode -
* converts a Node into ascii string and append it to 'str'
@ -1796,9 +1818,15 @@ _outNode(StringInfo str, void *obj)
case T_ConstraintTest:
_outConstraintTest(str, obj);
break;
case T_ConstraintTestValue:
_outConstraintTestValue(str, obj);
break;
case T_FuncCall:
_outFuncCall(str, obj);
break;
case T_DomainConstraintValue:
_outDomainConstraintValue(str, obj);
break;
default:
elog(WARNING, "_outNode: don't know how to print type %d ",

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.136 2002/11/06 00:00:44 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.137 2002/11/15 02:50:07 momjian Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
@ -949,12 +949,56 @@ _readConstraintTest(void)
token = pg_strtok(&length); /* now read it */
local_node->name = nullable_string(token, length);
token = pg_strtok(&length); /* get :domname */
token = pg_strtok(&length); /* get domname */
local_node->domname = nullable_string(token, length);
token = pg_strtok(&length); /* eat :check_expr */
local_node->check_expr = nodeRead(true); /* now read it */
return local_node;
}
/* ----------------
* _readConstraintTestValue
*
* ConstraintTestValue is a subclass of Node
* ----------------
*/
static ConstraintTestValue *
_readConstraintTestValue(void)
{
ConstraintTestValue *local_node;
char *token;
int length;
local_node = makeNode(ConstraintTestValue);
token = pg_strtok(&length); /* eat :typeid */
token = pg_strtok(&length); /* get typeid */
local_node->typeId = atooid(token);
token = pg_strtok(&length); /* eat :typemod */
token = pg_strtok(&length); /* get typemod */
local_node->typeMod = atoi(token);
return local_node;
}
/* ----------------
* _readDomainConstraintValue
*
* DomainConstraintValue is a subclass of Node
* ----------------
*/
static DomainConstraintValue *
_readDomainConstraintValue(void)
{
DomainConstraintValue *local_node;
local_node = makeNode(DomainConstraintValue);
return local_node;
}
/* ----------------
* _readVar
*
@ -2300,6 +2344,10 @@ parsePlanString(void)
return_value = _readBooleanTest();
else if (length == 14 && strncmp(token, "CONSTRAINTTEST", length) == 0)
return_value = _readConstraintTest();
else if (length == 21 && strncmp(token, "DOMAINCONSTRAINTVALUE", length) == 0)
return_value = _readDomainConstraintValue();
else if (length == 19 && strncmp(token, "CONSTRAINTTESTVALUE", length) == 0)
return_value = _readConstraintTestValue();
else
elog(ERROR, "badly formatted planstring \"%.10s\"...", token);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.110 2002/11/06 22:31:24 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.111 2002/11/15 02:50:07 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -1926,6 +1926,8 @@ expression_tree_walker(Node *node,
if (walker(((ConstraintTest *) node)->arg, context))
return true;
return walker(((ConstraintTest *) node)->check_expr, context);
case T_ConstraintTestValue:
break;
case T_SubLink:
{
SubLink *sublink = (SubLink *) node;
@ -2310,6 +2312,15 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
case T_ConstraintTestValue:
{
ConstraintTestValue *ctest = (ConstraintTestValue *) node;
ConstraintTestValue *newnode;
FLATCOPY(newnode, ctest, ConstraintTestValue);
return (Node *) newnode;
}
break;
case T_SubLink:
{
/*

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.253 2002/10/21 22:06:19 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.254 2002/11/15 02:50:07 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -2401,7 +2401,7 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
Oid expected_type_id,
given_type_id;
expr = transformExpr(pstate, expr);
expr = transformExpr(pstate, expr, NULL);
/* Cannot contain subselects or aggregates */
if (contain_subplans(expr))

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.377 2002/11/13 00:44:08 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.378 2002/11/15 02:50:08 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -393,7 +393,7 @@ static void doNegateFloat(Value *v);
UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
UPDATE USAGE USER USING
VACUUM VALID VALIDATOR VALUES VARCHAR VARYING
VACUUM VALID VALIDATOR VALUE VALUES VARCHAR VARYING
VERBOSE VERSION VIEW VOLATILE
WHEN WHERE WITH WITHOUT WORK WRITE
@ -6406,6 +6406,11 @@ c_expr: columnref { $$ = (Node *) $1; }
n->subselect = $2;
$$ = (Node *)n;
}
| VALUE
{
DomainConstraintValue *n = makeNode(DomainConstraintValue);
$$ = (Node *)n;
}
;
/*
@ -7315,6 +7320,7 @@ reserved_keyword:
| UNIQUE
| USER
| USING
| VALUE
| WHEN
| WHERE
;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.130 2002/11/13 00:44:09 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.131 2002/11/15 02:50:08 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -314,6 +314,7 @@ static const ScanKeyword ScanKeywords[] = {
{"vacuum", VACUUM},
{"valid", VALID},
{"validator", VALIDATOR},
{"value", VALUE},
{"values", VALUES},
{"varchar", VARCHAR},
{"varying", VARYING},

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.98 2002/09/18 21:35:22 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.99 2002/11/15 02:50:08 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -283,7 +283,7 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
* transformJoinOnClause() does. Just invoke transformExpr() to fix
* up the operators, and we're done.
*/
result = transformExpr(pstate, result);
result = transformExpr(pstate, result, NULL);
result = coerce_to_boolean(result, "JOIN/USING");
@ -317,7 +317,7 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
pstate->p_namespace = makeList2(j->larg, j->rarg);
/* This part is just like transformWhereClause() */
result = transformExpr(pstate, j->quals);
result = transformExpr(pstate, j->quals, NULL);
result = coerce_to_boolean(result, "JOIN/ON");
@ -478,7 +478,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
save_namespace = pstate->p_namespace;
pstate->p_namespace = NIL;
funcexpr = transformExpr(pstate, r->funccallnode);
funcexpr = transformExpr(pstate, r->funccallnode, NULL);
pstate->p_namespace = save_namespace;
@ -961,7 +961,7 @@ transformWhereClause(ParseState *pstate, Node *clause)
if (clause == NULL)
return NULL;
qual = transformExpr(pstate, clause);
qual = transformExpr(pstate, clause, NULL);
qual = coerce_to_boolean(qual, "WHERE");
@ -1104,7 +1104,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
* willing to match a resjunk target here, though the above cases must
* ignore resjunk targets.
*/
expr = transformExpr(pstate, node);
expr = transformExpr(pstate, node, NULL);
foreach(tl, tlist)
{

View File

@ -8,13 +8,18 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.85 2002/10/24 22:09:00 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.86 2002/11/15 02:50:09 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_proc.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
@ -405,8 +410,14 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
for (;;)
{
HeapTuple tup;
HeapTuple conTup;
Form_pg_type typTup;
ScanKeyData key[1];
int nkeys = 0;
SysScanDesc scan;
Relation conRel;
tup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(typeId),
0, 0, 0);
@ -419,7 +430,45 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
if (typTup->typnotnull && notNull == NULL)
notNull = pstrdup(NameStr(typTup->typname));
/* TODO: Add CHECK Constraints to domains */
/* Add CHECK Constraints to domains */
conRel = heap_openr(ConstraintRelationName, RowShareLock);
ScanKeyEntryInitialize(&key[nkeys++], 0x0,
Anum_pg_constraint_contypid, F_OIDEQ,
ObjectIdGetDatum(typeId));
scan = systable_beginscan(conRel, ConstraintTypidIndex, true,
SnapshotNow, nkeys, key);
while (HeapTupleIsValid(conTup = systable_getnext(scan)))
{
Datum val;
bool isNull;
ConstraintTest *r = makeNode(ConstraintTest);
Form_pg_constraint c = (Form_pg_constraint) GETSTRUCT(conTup);
/* 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, "coerce_type_constraints: domain %s constraint %s has NULL conbin",
NameStr(typTup->typname), NameStr(c->conname));
r->arg = arg;
r->testtype = CONSTR_TEST_CHECK;
r->name = NameStr(c->conname);
r->domname = NameStr(typTup->typname);
r->check_expr = stringToNode(MemoryContextStrdup(CacheMemoryContext,
DatumGetCString(DirectFunctionCall1(textout,
val))));
arg = (Node *) r;
}
systable_endscan(scan);
heap_close(conRel, RowShareLock);
if (typTup->typtype != 'd')
{
@ -452,7 +501,8 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
r->arg = arg;
r->testtype = CONSTR_TEST_NOTNULL;
r->name = notNull;
r->name = "NOT NULL";
r->domname = notNull;
r->check_expr = NULL;
arg = (Node *) r;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.129 2002/09/18 21:35:22 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.130 2002/11/15 02:50:09 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -20,6 +20,7 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/params.h"
#include "optimizer/clauses.h"
#include "parser/analyze.h"
#include "parser/gramparse.h"
#include "parser/parse.h"
@ -83,7 +84,7 @@ parse_expr_init(void)
* input and output of transformExpr; see SubLink for example.
*/
Node *
transformExpr(ParseState *pstate, Node *expr)
transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
{
Node *result = NULL;
@ -152,7 +153,7 @@ transformExpr(ParseState *pstate, Node *expr)
ExprFieldSelect *efs = (ExprFieldSelect *) expr;
List *fields;
result = transformExpr(pstate, efs->arg);
result = transformExpr(pstate, efs->arg, domVal);
/* handle qualification, if any */
foreach(fields, efs->fields)
{
@ -169,7 +170,7 @@ transformExpr(ParseState *pstate, Node *expr)
case T_TypeCast:
{
TypeCast *tc = (TypeCast *) expr;
Node *arg = transformExpr(pstate, tc->arg);
Node *arg = transformExpr(pstate, tc->arg, domVal);
result = typecast_expression(arg, tc->typename);
break;
@ -204,14 +205,14 @@ transformExpr(ParseState *pstate, Node *expr)
n->arg = a->lexpr;
result = transformExpr(pstate,
(Node *) n);
(Node *) n, domVal);
}
else
{
Node *lexpr = transformExpr(pstate,
a->lexpr);
a->lexpr, domVal);
Node *rexpr = transformExpr(pstate,
a->rexpr);
a->rexpr, domVal);
result = (Node *) make_op(a->name,
lexpr,
@ -222,9 +223,9 @@ transformExpr(ParseState *pstate, Node *expr)
case AND:
{
Node *lexpr = transformExpr(pstate,
a->lexpr);
a->lexpr, domVal);
Node *rexpr = transformExpr(pstate,
a->rexpr);
a->rexpr, domVal);
Expr *expr = makeNode(Expr);
lexpr = coerce_to_boolean(lexpr, "AND");
@ -239,9 +240,9 @@ transformExpr(ParseState *pstate, Node *expr)
case OR:
{
Node *lexpr = transformExpr(pstate,
a->lexpr);
a->lexpr, domVal);
Node *rexpr = transformExpr(pstate,
a->rexpr);
a->rexpr, domVal);
Expr *expr = makeNode(Expr);
lexpr = coerce_to_boolean(lexpr, "OR");
@ -256,7 +257,7 @@ transformExpr(ParseState *pstate, Node *expr)
case NOT:
{
Node *rexpr = transformExpr(pstate,
a->rexpr);
a->rexpr, domVal);
Expr *expr = makeNode(Expr);
rexpr = coerce_to_boolean(rexpr, "NOT");
@ -270,9 +271,9 @@ transformExpr(ParseState *pstate, Node *expr)
case DISTINCT:
{
Node *lexpr = transformExpr(pstate,
a->lexpr);
a->lexpr, domVal);
Node *rexpr = transformExpr(pstate,
a->rexpr);
a->rexpr, domVal);
result = (Node *) make_op(a->name,
lexpr,
@ -293,7 +294,7 @@ transformExpr(ParseState *pstate, Node *expr)
* Will result in a boolean constant node.
*/
Node *lexpr = transformExpr(pstate,
a->lexpr);
a->lexpr, domVal);
ltype = exprType(lexpr);
foreach(telem, (List *) a->rexpr)
@ -317,7 +318,7 @@ transformExpr(ParseState *pstate, Node *expr)
n->val.val.str = (matched ? "t" : "f");
n->typename = SystemTypeName("bool");
result = transformExpr(pstate, (Node *) n);
result = transformExpr(pstate, (Node *) n, domVal);
}
break;
}
@ -331,7 +332,7 @@ transformExpr(ParseState *pstate, Node *expr)
/* transform the list of arguments */
foreach(args, fn->args)
lfirst(args) = transformExpr(pstate,
(Node *) lfirst(args));
(Node *) lfirst(args), domVal);
result = ParseFuncOrColumn(pstate,
fn->funcname,
fn->args,
@ -405,7 +406,7 @@ transformExpr(ParseState *pstate, Node *expr)
List *elist;
foreach(elist, left_list)
lfirst(elist) = transformExpr(pstate, lfirst(elist));
lfirst(elist) = transformExpr(pstate, lfirst(elist), domVal);
Assert(IsA(sublink->oper, A_Expr));
op = ((A_Expr *) sublink->oper)->name;
@ -504,7 +505,7 @@ transformExpr(ParseState *pstate, Node *expr)
warg = (Node *) makeSimpleA_Expr(OP, "=",
c->arg, warg);
}
neww->expr = transformExpr(pstate, warg);
neww->expr = transformExpr(pstate, warg, domVal);
neww->expr = coerce_to_boolean(neww->expr, "CASE/WHEN");
@ -520,7 +521,7 @@ transformExpr(ParseState *pstate, Node *expr)
n->val.type = T_Null;
warg = (Node *) n;
}
neww->result = transformExpr(pstate, warg);
neww->result = transformExpr(pstate, warg, domVal);
newargs = lappend(newargs, neww);
typeids = lappendi(typeids, exprType(neww->result));
@ -544,7 +545,7 @@ transformExpr(ParseState *pstate, Node *expr)
n->val.type = T_Null;
defresult = (Node *) n;
}
newc->defresult = transformExpr(pstate, defresult);
newc->defresult = transformExpr(pstate, defresult, domVal);
/*
* Note: default result is considered the most significant
@ -580,7 +581,7 @@ transformExpr(ParseState *pstate, Node *expr)
{
NullTest *n = (NullTest *) expr;
n->arg = transformExpr(pstate, n->arg);
n->arg = transformExpr(pstate, n->arg, domVal);
/* the argument can be any type, so don't coerce it */
result = expr;
break;
@ -617,7 +618,7 @@ transformExpr(ParseState *pstate, Node *expr)
clausename = NULL; /* keep compiler quiet */
}
b->arg = transformExpr(pstate, b->arg);
b->arg = transformExpr(pstate, b->arg, domVal);
b->arg = coerce_to_boolean(b->arg, clausename);
@ -625,6 +626,13 @@ transformExpr(ParseState *pstate, Node *expr)
break;
}
case T_DomainConstraintValue:
{
result = (Node *) copyObject(domVal);
break;
}
/*********************************************
* Quietly accept node types that may be presented when we are
* called on an already-transformed tree.
@ -936,6 +944,9 @@ exprType(Node *expr)
case T_ConstraintTest:
type = exprType(((ConstraintTest *) expr)->arg);
break;
case T_ConstraintTestValue:
type = ((ConstraintTestValue *) expr)->typeId;
break;
default:
elog(ERROR, "exprType: Do not know how to get type for %d node",
nodeTag(expr));

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.72 2002/11/13 00:39:47 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.73 2002/11/15 02:50:09 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -277,7 +277,7 @@ transformArraySubscripts(ParseState *pstate,
{
if (ai->lidx)
{
subexpr = transformExpr(pstate, ai->lidx);
subexpr = transformExpr(pstate, ai->lidx, NULL);
/* If it's not int4 already, try to coerce */
subexpr = coerce_to_target_type(subexpr, exprType(subexpr),
INT4OID, -1,
@ -299,7 +299,7 @@ transformArraySubscripts(ParseState *pstate,
}
lowerIndexpr = lappend(lowerIndexpr, subexpr);
}
subexpr = transformExpr(pstate, ai->uidx);
subexpr = transformExpr(pstate, ai->uidx, NULL);
/* If it's not int4 already, try to coerce */
subexpr = coerce_to_target_type(subexpr, exprType(subexpr),
INT4OID, -1,

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.91 2002/09/28 20:00:19 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.92 2002/11/15 02:50:09 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -56,7 +56,7 @@ transformTargetEntry(ParseState *pstate,
/* Transform the node if caller didn't do it already */
if (expr == NULL)
expr = transformExpr(pstate, node);
expr = transformExpr(pstate, node, NULL);
if (IsA(expr, RangeVar))
elog(ERROR, "You can't use relation names alone in the target list, try relation.*.");

View File

@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.124 2002/09/19 23:40:56 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.125 2002/11/15 02:50:09 momjian Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@ -2226,6 +2226,12 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
case T_ConstraintTestValue:
{
appendStringInfo(buf, "VALUE");
}
break;
case T_SubLink:
get_sublink_expr(node, context);
break;

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: indexing.h,v 1.76 2002/10/18 20:33:57 tgl Exp $
* $Id: indexing.h,v 1.77 2002/11/15 02:50:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -40,6 +40,7 @@
#define ConstraintNameNspIndex "pg_constraint_conname_nsp_index"
#define ConstraintOidIndex "pg_constraint_oid_index"
#define ConstraintRelidIndex "pg_constraint_conrelid_index"
#define ConstraintTypidIndex "pg_constraint_contypid_index"
#define ConversionDefaultIndex "pg_conversion_default_index"
#define ConversionNameNspIndex "pg_conversion_name_nsp_index"
#define ConversionOidIndex "pg_conversion_oid_index"
@ -129,6 +130,8 @@ DECLARE_UNIQUE_INDEX(pg_class_relname_nsp_index on pg_class using btree(relname
DECLARE_INDEX(pg_constraint_conname_nsp_index on pg_constraint using btree(conname name_ops, connamespace oid_ops));
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_constraint_conrelid_index on pg_constraint using btree(conrelid oid_ops));
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_constraint_contypid_index on pg_constraint using btree(contypid oid_ops));
DECLARE_UNIQUE_INDEX(pg_constraint_oid_index on pg_constraint using btree(oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_conversion_default_index on pg_conversion using btree(connamespace oid_ops, conforencoding int4_ops, contoencoding int4_ops, oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_conversion_name_nsp_index on pg_conversion using btree(conname name_ops, connamespace oid_ops));

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_constraint.h,v 1.4 2002/09/22 00:37:09 tgl Exp $
* $Id: pg_constraint.h,v 1.5 2002/11/15 02:50:10 momjian Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -140,6 +140,15 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* the FKCONSTR_MATCH_xxx constants defined in parsenodes.h.
*/
/*
* Used for constraint support functions where the
* and conrelid, contypid columns being looked up
*/
typedef enum CONSTRAINTCATEGORY {
CONSTRAINT_RELATION,
CONSTRAINT_DOMAIN,
CONSTRAINT_ASSERTION
} CONSTRAINTCATEGORY;
/*
* prototypes for functions in pg_constraint.c
@ -166,10 +175,10 @@ extern Oid CreateConstraintEntry(const char *constraintName,
extern void RemoveConstraintById(Oid conId);
extern bool ConstraintNameIsUsed(Oid relId, Oid relNamespace,
const char *cname);
extern char *GenerateConstraintName(Oid relId, Oid relNamespace,
int *counter);
extern bool ConstraintNameIsUsed(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace,
const char *cname);
extern char *GenerateConstraintName(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace,
int *counter);
extern bool ConstraintNameIsGenerated(const char *cname);
#endif /* PG_CONSTRAINT_H */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: execnodes.h,v 1.77 2002/11/06 22:31:24 tgl Exp $
* $Id: execnodes.h,v 1.78 2002/11/15 02:50:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -113,6 +113,13 @@ typedef struct ExprContext
Datum *ecxt_aggvalues; /* precomputed values for Aggref nodes */
bool *ecxt_aggnulls; /* null flags for Aggref nodes */
/*
* Carry the domain value through the executor for application
* in a domain constraint
*/
Datum domainValue_datum;
bool domainValue_isNull;
/* Functions to call back when ExprContext is shut down */
ExprContext_CB *ecxt_callbacks;
} ExprContext;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: nodes.h,v 1.122 2002/11/10 02:17:25 momjian Exp $
* $Id: nodes.h,v 1.123 2002/11/15 02:50:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -171,6 +171,7 @@ typedef enum NodeTag
T_ViewStmt,
T_LoadStmt,
T_CreateDomainStmt,
T_DomainConstraintValue,
T_CreatedbStmt,
T_DropdbStmt,
T_VacuumStmt,
@ -231,6 +232,7 @@ typedef enum NodeTag
T_NullTest,
T_BooleanTest,
T_ConstraintTest,
T_ConstraintTestValue,
T_CaseExpr,
T_CaseWhen,
T_FkConstraint,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parsenodes.h,v 1.213 2002/11/13 00:44:09 momjian Exp $
* $Id: parsenodes.h,v 1.214 2002/11/15 02:50:12 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -285,9 +285,26 @@ typedef struct ConstraintTest
Node *arg; /* input expression */
ConstraintTestType testtype; /* test type */
char *name; /* name of constraint (for error msgs) */
char *domname; /* name of domain (for error messages) */
Node *check_expr; /* for CHECK test, a boolean expression */
} ConstraintTest;
/*
* Placeholder node for the value to be processed by a domains
* check constraint.
*/
typedef struct DomainConstraintValue
{
NodeTag type;
} DomainConstraintValue;
typedef struct ConstraintTestValue
{
NodeTag type;
Oid typeId;
int32 typeMod;
} ConstraintTestValue;
/*
* ColumnDef - column definition (used in various creates)
*

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: var.h,v 1.21 2002/06/20 20:29:51 momjian Exp $
* $Id: var.h,v 1.22 2002/11/15 02:50:21 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -22,6 +22,7 @@ extern bool contain_var_reference(Node *node, int varno, int varattno,
int levelsup);
extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
extern bool contain_var_clause(Node *node);
extern bool contain_var_tuple_clause(Node *node);
extern List *pull_var_clause(Node *node, bool includeUpperVars);
extern Node *flatten_join_alias_vars(Node *node, List *rtable, bool force);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parse_expr.h,v 1.28 2002/06/20 20:29:51 momjian Exp $
* $Id: parse_expr.h,v 1.29 2002/11/15 02:50:21 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -21,7 +21,8 @@
extern int max_expr_depth;
extern bool Transform_null_equals;
extern Node *transformExpr(ParseState *pstate, Node *expr);
extern Node *transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal);
extern Oid exprType(Node *expr);
extern int32 exprTypmod(Node *expr);
extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod);

View File

@ -103,35 +103,43 @@ drop domain domainint4arr restrict;
drop domain domaintextarr restrict;
create domain dnotnull varchar(15) NOT NULL;
create domain dnull varchar(15);
create domain dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd');
create table nulltest
( col1 dnotnull
, col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden
, col3 dnull NOT NULL
, col4 dnull
, col5 dcheck CHECK (col5 IN ('c', 'd'))
);
INSERT INTO nulltest DEFAULT VALUES;
ERROR: Domain dnotnull does not allow NULL values
INSERT INTO nulltest values ('a', 'b', 'c', 'd'); -- Good
INSERT INTO nulltest values (NULL, 'b', 'c', 'd');
INSERT INTO nulltest values ('a', 'b', 'c', 'd', 'c'); -- Good
insert into nulltest values ('a', 'b', 'c', 'd', NULL);
ERROR: Domain $1 constraint dcheck failed
insert into nulltest values ('a', 'b', 'c', 'd', 'a');
ERROR: ExecInsert: rejected due to CHECK constraint "nulltest_col5" on "nulltest"
INSERT INTO nulltest values (NULL, 'b', 'c', 'd', 'd');
ERROR: Domain dnotnull does not allow NULL values
INSERT INTO nulltest values ('a', NULL, 'c', 'd');
INSERT INTO nulltest values ('a', NULL, 'c', 'd', 'c');
ERROR: Domain dnotnull does not allow NULL values
INSERT INTO nulltest values ('a', 'b', NULL, 'd');
INSERT INTO nulltest values ('a', 'b', NULL, 'd', 'c');
ERROR: ExecInsert: Fail to add null value in not null attribute col3
INSERT INTO nulltest values ('a', 'b', 'c', NULL); -- Good
INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
-- Test copy
COPY nulltest FROM stdin; --fail
ERROR: copy: line 1, CopyFrom: Fail to add null value in not null attribute col3
ERROR: copy: line 1, Domain $1 constraint dcheck failed
lost synchronization with server, resetting connection
SET autocommit TO 'on';
-- Last row is bad
COPY nulltest FROM stdin;
ERROR: copy: line 3, CopyFrom: rejected due to CHECK constraint "nulltest_col5" on "nulltest"
lost synchronization with server, resetting connection
select * from nulltest;
col1 | col2 | col3 | col4
------+------+------+------
a | b | c | d
a | b | c |
a | b | c |
(3 rows)
col1 | col2 | col3 | col4 | col5
------+------+------+------+------
a | b | c | d | c
a | b | c | | d
(2 rows)
-- Test out coerced (casted) constraints
SELECT cast('1' as dnotnull);

View File

@ -83,29 +83,36 @@ drop domain domaintextarr restrict;
create domain dnotnull varchar(15) NOT NULL;
create domain dnull varchar(15);
create domain dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd');
create table nulltest
( col1 dnotnull
, col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden
, col3 dnull NOT NULL
, col4 dnull
, col5 dcheck CHECK (col5 IN ('c', 'd'))
);
INSERT INTO nulltest DEFAULT VALUES;
INSERT INTO nulltest values ('a', 'b', 'c', 'd'); -- Good
INSERT INTO nulltest values (NULL, 'b', 'c', 'd');
INSERT INTO nulltest values ('a', NULL, 'c', 'd');
INSERT INTO nulltest values ('a', 'b', NULL, 'd');
INSERT INTO nulltest values ('a', 'b', 'c', NULL); -- Good
INSERT INTO nulltest values ('a', 'b', 'c', 'd', 'c'); -- Good
insert into nulltest values ('a', 'b', 'c', 'd', NULL);
insert into nulltest values ('a', 'b', 'c', 'd', 'a');
INSERT INTO nulltest values (NULL, 'b', 'c', 'd', 'd');
INSERT INTO nulltest values ('a', NULL, 'c', 'd', 'c');
INSERT INTO nulltest values ('a', 'b', NULL, 'd', 'c');
INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
-- Test copy
COPY nulltest FROM stdin; --fail
a b \N d
a b \N d \N
\.
SET autocommit TO 'on';
-- Last row is bad
COPY nulltest FROM stdin;
a b c \N
a b c \N c
a b c \N d
a b c \N a
\.
select * from nulltest;