Commit 6b603e67 authored by Bruce Momjian's avatar Bruce Momjian

Add DOMAIN check constraints.

Rod Taylor
parent 2986aa6a
<!-- <!--
$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 PostgreSQL documentation
--> -->
...@@ -200,16 +200,6 @@ CREATE TABLE countrylist (id INT4, country country_code); ...@@ -200,16 +200,6 @@ CREATE TABLE countrylist (id INT4, country country_code);
</para> </para>
</refsect1> </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"> <refsect1 id="SQL-CREATEDOMAIN-see-also">
<title>See Also</title> <title>See Also</title>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * INTERFACE ROUTINES
...@@ -1500,7 +1500,8 @@ AddRelationRawConstraints(Relation rel, ...@@ -1500,7 +1500,8 @@ AddRelationRawConstraints(Relation rel,
ccname = cdef->name; ccname = cdef->name;
/* Check against pre-existing constraints */ /* Check against pre-existing constraints */
if (ConstraintNameIsUsed(RelationGetRelid(rel), if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
RelationGetRelid(rel),
RelationGetNamespace(rel), RelationGetNamespace(rel),
ccname)) ccname))
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"", elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
...@@ -1534,7 +1535,8 @@ AddRelationRawConstraints(Relation rel, ...@@ -1534,7 +1535,8 @@ AddRelationRawConstraints(Relation rel,
* pre-existing constraints, nor with any auto-generated * pre-existing constraints, nor with any auto-generated
* names so far. * names so far.
*/ */
ccname = GenerateConstraintName(RelationGetRelid(rel), ccname = GenerateConstraintName(CONSTRAINT_RELATION,
RelationGetRelid(rel),
RelationGetNamespace(rel), RelationGetNamespace(rel),
&constr_name_ctr); &constr_name_ctr);
...@@ -1565,7 +1567,7 @@ AddRelationRawConstraints(Relation rel, ...@@ -1565,7 +1567,7 @@ AddRelationRawConstraints(Relation rel,
/* /*
* Transform raw parsetree to executable expression. * 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. * Make sure it yields a boolean result.
...@@ -1694,7 +1696,7 @@ cookDefault(ParseState *pstate, ...@@ -1694,7 +1696,7 @@ cookDefault(ParseState *pstate,
/* /*
* Transform raw parsetree to executable expression. * 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. * Make sure default expr does not refer to any vars.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -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)) if (OidIsValid(foreignRelId))
{ {
/* /*
...@@ -262,7 +275,7 @@ CreateConstraintEntry(const char *constraintName, ...@@ -262,7 +275,7 @@ CreateConstraintEntry(const char *constraintName,
* this test is not very meaningful. * this test is not very meaningful.
*/ */
bool bool
ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname) ConstraintNameIsUsed(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, const char *cname)
{ {
bool found; bool found;
Relation conDesc; Relation conDesc;
...@@ -280,7 +293,7 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname) ...@@ -280,7 +293,7 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
ScanKeyEntryInitialize(&skey[1], 0x0, ScanKeyEntryInitialize(&skey[1], 0x0,
Anum_pg_constraint_connamespace, F_OIDEQ, Anum_pg_constraint_connamespace, F_OIDEQ,
ObjectIdGetDatum(relNamespace)); ObjectIdGetDatum(objNamespace));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true, conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
SnapshotNow, 2, skey); SnapshotNow, 2, skey);
...@@ -289,7 +302,12 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname) ...@@ -289,7 +302,12 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
{ {
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup); 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; found = true;
break; break;
...@@ -314,7 +332,7 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname) ...@@ -314,7 +332,7 @@ ConstraintNameIsUsed(Oid relId, Oid relNamespace, const char *cname)
* someone else might choose the same name concurrently! * someone else might choose the same name concurrently!
*/ */
char * char *
GenerateConstraintName(Oid relId, Oid relNamespace, int *counter) GenerateConstraintName(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, int *counter)
{ {
bool found; bool found;
Relation conDesc; Relation conDesc;
...@@ -347,7 +365,7 @@ GenerateConstraintName(Oid relId, Oid relNamespace, int *counter) ...@@ -347,7 +365,7 @@ GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
ScanKeyEntryInitialize(&skey[1], 0x0, ScanKeyEntryInitialize(&skey[1], 0x0,
Anum_pg_constraint_connamespace, F_OIDEQ, Anum_pg_constraint_connamespace, F_OIDEQ,
ObjectIdGetDatum(relNamespace)); ObjectIdGetDatum(objNamespace));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true, conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true,
SnapshotNow, 2, skey); SnapshotNow, 2, skey);
...@@ -356,7 +374,12 @@ GenerateConstraintName(Oid relId, Oid relNamespace, int *counter) ...@@ -356,7 +374,12 @@ GenerateConstraintName(Oid relId, Oid relNamespace, int *counter)
{ {
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup); 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; found = true;
break; break;
...@@ -415,10 +438,13 @@ RemoveConstraintById(Oid conId) ...@@ -415,10 +438,13 @@ RemoveConstraintById(Oid conId)
con = (Form_pg_constraint) GETSTRUCT(tup); con = (Form_pg_constraint) GETSTRUCT(tup);
/* /*
* If the constraint is for a relation, open and exclusive-lock the * If the constraint is for a relation, open and exclusive-lock
* relation it's for. * the relation it's for.
*
* 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 other constraints. * XXX not clear what we should lock, if anything, for assert constraints.
*/ */
if (OidIsValid(con->conrelid)) if (OidIsValid(con->conrelid))
{ {
...@@ -463,6 +489,34 @@ RemoveConstraintById(Oid conId) ...@@ -463,6 +489,34 @@ RemoveConstraintById(Oid conId)
/* Keep lock on constraint's rel until end of xact */ /* Keep lock on constraint's rel until end of xact */
heap_close(rel, NoLock); 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 */ /* Fry the constraint itself */
simple_heap_delete(conDesc, &tup->t_self); simple_heap_delete(conDesc, &tup->t_self);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -2632,14 +2632,16 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
*/ */
if (constr->name) if (constr->name)
{ {
if (ConstraintNameIsUsed(RelationGetRelid(rel), if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
RelationGetRelid(rel),
RelationGetNamespace(rel), RelationGetNamespace(rel),
constr->name)) constr->name))
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"", elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
constr->name, RelationGetRelationName(rel)); constr->name, RelationGetRelationName(rel));
} }
else else
constr->name = GenerateConstraintName(RelationGetRelid(rel), constr->name = GenerateConstraintName(CONSTRAINT_RELATION,
RelationGetRelid(rel),
RelationGetNamespace(rel), RelationGetNamespace(rel),
&counter); &counter);
...@@ -2668,7 +2670,8 @@ AlterTableAddConstraint(Oid myrelid, bool recurse, ...@@ -2668,7 +2670,8 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
*/ */
if (fkconstraint->constr_name) if (fkconstraint->constr_name)
{ {
if (ConstraintNameIsUsed(RelationGetRelid(rel), if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
RelationGetRelid(rel),
RelationGetNamespace(rel), RelationGetNamespace(rel),
fkconstraint->constr_name)) fkconstraint->constr_name))
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"", elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
...@@ -2676,7 +2679,8 @@ AlterTableAddConstraint(Oid myrelid, bool recurse, ...@@ -2676,7 +2679,8 @@ AlterTableAddConstraint(Oid myrelid, bool recurse,
RelationGetRelationName(rel)); RelationGetRelationName(rel));
} }
else else
fkconstraint->constr_name = GenerateConstraintName(RelationGetRelid(rel), fkconstraint->constr_name = GenerateConstraintName(CONSTRAINT_RELATION,
RelationGetRelid(rel),
RelationGetNamespace(rel), RelationGetNamespace(rel),
&counter); &counter);
...@@ -2734,7 +2738,7 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr) ...@@ -2734,7 +2738,7 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr)
/* /*
* Convert the A_EXPR in raw_expr into an EXPR * 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. * Make sure it yields a boolean result.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the * The "DefineFoo" routines take the parse tree and pick out the
...@@ -36,10 +36,17 @@ ...@@ -36,10 +36,17 @@
#include "catalog/dependency.h" #include "catalog/dependency.h"
#include "catalog/heap.h" #include "catalog/heap.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/tablecmds.h" #include "commands/tablecmds.h"
#include "miscadmin.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_func.h"
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "utils/acl.h" #include "utils/acl.h"
...@@ -407,6 +414,7 @@ DefineDomain(CreateDomainStmt *stmt) ...@@ -407,6 +414,7 @@ DefineDomain(CreateDomainStmt *stmt)
Oid basetypeoid; Oid basetypeoid;
Oid domainoid; Oid domainoid;
Form_pg_type baseType; Form_pg_type baseType;
int counter = 0;
/* Convert list of names to a name and namespace */ /* Convert list of names to a name and namespace */
domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname, domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
...@@ -484,17 +492,21 @@ DefineDomain(CreateDomainStmt *stmt) ...@@ -484,17 +492,21 @@ DefineDomain(CreateDomainStmt *stmt)
basetypelem = baseType->typelem; basetypelem = baseType->typelem;
/* /*
* Run through constraints manually to avoid the additional processing * Run through constraints manually to avoid the additional
* conducted by DefineRelation() and friends. * 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().
*/ */
foreach(listptr, schema) foreach(listptr, schema)
{ {
Constraint *colDef = lfirst(listptr); Node *newConstraint = lfirst(listptr);
Constraint *colDef;
ParseState *pstate; 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) switch (colDef->contype)
{ {
/* /*
...@@ -556,8 +568,8 @@ DefineDomain(CreateDomainStmt *stmt) ...@@ -556,8 +568,8 @@ DefineDomain(CreateDomainStmt *stmt)
elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported"); elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported");
break; break;
/* Check constraints are handled after domain creation */
case CONSTR_CHECK: case CONSTR_CHECK:
elog(ERROR, "DefineDomain: CHECK Constraints not supported");
break; break;
case CONSTR_ATTR_DEFERRABLE: case CONSTR_ATTR_DEFERRABLE:
...@@ -598,6 +610,133 @@ DefineDomain(CreateDomainStmt *stmt) ...@@ -598,6 +610,133 @@ DefineDomain(CreateDomainStmt *stmt)
typNDims, /* Array dimensions for base type */ typNDims, /* Array dimensions for base type */
typNotNull); /* Type NOT NULL */ 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. * Add any dependencies needed for the default expression.
*/ */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -72,6 +72,9 @@ static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext,
static Datum ExecEvalConstraintTest(ConstraintTest *constraint, static Datum ExecEvalConstraintTest(ConstraintTest *constraint,
ExprContext *econtext, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone); bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalConstraintTestValue(ConstraintTestValue *conVal,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
/*---------- /*----------
...@@ -1551,6 +1554,23 @@ ExecEvalBooleanTest(BooleanTest *btest, ...@@ -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 * ExecEvalConstraintTest
* *
...@@ -1571,11 +1591,22 @@ ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext, ...@@ -1571,11 +1591,22 @@ ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext,
case CONSTR_TEST_NOTNULL: case CONSTR_TEST_NOTNULL:
if (*isNull) if (*isNull)
elog(ERROR, "Domain %s does not allow NULL values", elog(ERROR, "Domain %s does not allow NULL values",
constraint->name); constraint->domname);
break; break;
case CONSTR_TEST_CHECK: 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; break;
default: default:
elog(ERROR, "ExecEvalConstraintTest: Constraint type unknown"); elog(ERROR, "ExecEvalConstraintTest: Constraint type unknown");
...@@ -1777,7 +1808,12 @@ ExecEvalExpr(Node *expression, ...@@ -1777,7 +1808,12 @@ ExecEvalExpr(Node *expression,
isNull, isNull,
isDone); isDone);
break; break;
case T_ConstraintTestValue:
retDatum = ExecEvalConstraintTestValue((ConstraintTestValue *) expression,
econtext,
isNull,
isDone);
break;
default: default:
elog(ERROR, "ExecEvalExpr: unknown expression type %d", elog(ERROR, "ExecEvalExpr: unknown expression type %d",
nodeTag(expression)); nodeTag(expression));
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -1056,11 +1056,35 @@ _copyConstraintTest(ConstraintTest *from)
newnode->testtype = from->testtype; newnode->testtype = from->testtype;
if (from->name) if (from->name)
newnode->name = pstrdup(from->name); newnode->name = pstrdup(from->name);
if (from->domname)
newnode->domname = pstrdup(from->domname);
Node_Copy(from, newnode, check_expr); Node_Copy(from, newnode, check_expr);
return newnode; 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 * static ArrayRef *
_copyArrayRef(ArrayRef *from) _copyArrayRef(ArrayRef *from)
{ {
...@@ -3252,6 +3276,9 @@ copyObject(void *from) ...@@ -3252,6 +3276,9 @@ copyObject(void *from)
case T_ConstraintTest: case T_ConstraintTest:
retval = _copyConstraintTest(from); retval = _copyConstraintTest(from);
break; break;
case T_ConstraintTestValue:
retval = _copyConstraintTestValue(from);
break;
case T_FkConstraint: case T_FkConstraint:
retval = _copyFkConstraint(from); retval = _copyFkConstraint(from);
break; break;
...@@ -3264,6 +3291,9 @@ copyObject(void *from) ...@@ -3264,6 +3291,9 @@ copyObject(void *from)
case T_InsertDefault: case T_InsertDefault:
retval = _copyInsertDefault(from); retval = _copyInsertDefault(from);
break; break;
case T_DomainConstraintValue:
retval = _copyDomainConstraintValue(from);
break;
default: default:
elog(ERROR, "copyObject: don't know how to copy node type %d", elog(ERROR, "copyObject: don't know how to copy node type %d",
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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) ...@@ -1971,15 +1971,32 @@ _equalConstraintTest(ConstraintTest *a, ConstraintTest *b)
return false; return false;
if (!equalstr(a->name, b->name)) if (!equalstr(a->name, b->name))
return false; return false;
if (!equalstr(a->domname, b->domname))
return false;
if (!equal(a->check_expr, b->check_expr)) if (!equal(a->check_expr, b->check_expr))
return false; return false;
return true; 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 * Stuff from pg_list.h
*/ */
static bool static bool
_equalValue(Value *a, Value *b) _equalValue(Value *a, Value *b)
{ {
...@@ -2438,6 +2455,9 @@ equal(void *a, void *b) ...@@ -2438,6 +2455,9 @@ equal(void *a, void *b)
case T_ConstraintTest: case T_ConstraintTest:
retval = _equalConstraintTest(a, b); retval = _equalConstraintTest(a, b);
break; break;
case T_ConstraintTestValue:
retval = _equalConstraintTestValue(a, b);
break;
case T_FkConstraint: case T_FkConstraint:
retval = _equalFkConstraint(a, b); retval = _equalFkConstraint(a, b);
break; break;
...@@ -2450,6 +2470,9 @@ equal(void *a, void *b) ...@@ -2450,6 +2470,9 @@ equal(void *a, void *b)
case T_InsertDefault: case T_InsertDefault:
retval = _equalInsertDefault(a, b); retval = _equalInsertDefault(a, b);
break; break;
case T_DomainConstraintValue:
retval = _equalDomainConstraintValue(a, b);
break;
default: default:
elog(WARNING, "equal: don't know whether nodes of type %d are equal", elog(WARNING, "equal: don't know whether nodes of type %d are equal",
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which * Every (plan) node in POSTGRES has an associated "out" routine which
...@@ -1525,10 +1525,32 @@ _outConstraintTest(StringInfo str, ConstraintTest *node) ...@@ -1525,10 +1525,32 @@ _outConstraintTest(StringInfo str, ConstraintTest *node)
appendStringInfo(str, " :testtype %d :name ", appendStringInfo(str, " :testtype %d :name ",
(int) node->testtype); (int) node->testtype);
_outToken(str, node->name); _outToken(str, node->name);
appendStringInfo(str, " :domain ");
_outToken(str, node->domname);
appendStringInfo(str, " :check_expr "); appendStringInfo(str, " :check_expr ");
_outNode(str, node->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 - * _outNode -
* converts a Node into ascii string and append it to 'str' * converts a Node into ascii string and append it to 'str'
...@@ -1796,9 +1818,15 @@ _outNode(StringInfo str, void *obj) ...@@ -1796,9 +1818,15 @@ _outNode(StringInfo str, void *obj)
case T_ConstraintTest: case T_ConstraintTest:
_outConstraintTest(str, obj); _outConstraintTest(str, obj);
break; break;
case T_ConstraintTestValue:
_outConstraintTestValue(str, obj);
break;
case T_FuncCall: case T_FuncCall:
_outFuncCall(str, obj); _outFuncCall(str, obj);
break; break;
case T_DomainConstraintValue:
_outDomainConstraintValue(str, obj);
break;
default: default:
elog(WARNING, "_outNode: don't know how to print type %d ", elog(WARNING, "_outNode: don't know how to print type %d ",
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Most of the read functions for plan nodes are tested. (In fact, they * Most of the read functions for plan nodes are tested. (In fact, they
...@@ -949,12 +949,56 @@ _readConstraintTest(void) ...@@ -949,12 +949,56 @@ _readConstraintTest(void)
token = pg_strtok(&length); /* now read it */ token = pg_strtok(&length); /* now read it */
local_node->name = nullable_string(token, length); 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 */ token = pg_strtok(&length); /* eat :check_expr */
local_node->check_expr = nodeRead(true); /* now read it */ local_node->check_expr = nodeRead(true); /* now read it */
return local_node; 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 * _readVar
* *
...@@ -2300,6 +2344,10 @@ parsePlanString(void) ...@@ -2300,6 +2344,10 @@ parsePlanString(void)
return_value = _readBooleanTest(); return_value = _readBooleanTest();
else if (length == 14 && strncmp(token, "CONSTRAINTTEST", length) == 0) else if (length == 14 && strncmp(token, "CONSTRAINTTEST", length) == 0)
return_value = _readConstraintTest(); 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 else
elog(ERROR, "badly formatted planstring \"%.10s\"...", token); elog(ERROR, "badly formatted planstring \"%.10s\"...", token);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -1926,6 +1926,8 @@ expression_tree_walker(Node *node, ...@@ -1926,6 +1926,8 @@ expression_tree_walker(Node *node,
if (walker(((ConstraintTest *) node)->arg, context)) if (walker(((ConstraintTest *) node)->arg, context))
return true; return true;
return walker(((ConstraintTest *) node)->check_expr, context); return walker(((ConstraintTest *) node)->check_expr, context);
case T_ConstraintTestValue:
break;
case T_SubLink: case T_SubLink:
{ {
SubLink *sublink = (SubLink *) node; SubLink *sublink = (SubLink *) node;
...@@ -2310,6 +2312,15 @@ expression_tree_mutator(Node *node, ...@@ -2310,6 +2312,15 @@ expression_tree_mutator(Node *node,
return (Node *) newnode; return (Node *) newnode;
} }
break; break;
case T_ConstraintTestValue:
{
ConstraintTestValue *ctest = (ConstraintTestValue *) node;
ConstraintTestValue *newnode;
FLATCOPY(newnode, ctest, ConstraintTestValue);
return (Node *) newnode;
}
break;
case T_SubLink: case T_SubLink:
{ {
/* /*
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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) ...@@ -2401,7 +2401,7 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
Oid expected_type_id, Oid expected_type_id,
given_type_id; given_type_id;
expr = transformExpr(pstate, expr); expr = transformExpr(pstate, expr, NULL);
/* Cannot contain subselects or aggregates */ /* Cannot contain subselects or aggregates */
if (contain_subplans(expr)) if (contain_subplans(expr))
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * 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 * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -393,7 +393,7 @@ static void doNegateFloat(Value *v); ...@@ -393,7 +393,7 @@ static void doNegateFloat(Value *v);
UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
UPDATE USAGE USER USING UPDATE USAGE USER USING
VACUUM VALID VALIDATOR VALUES VARCHAR VARYING VACUUM VALID VALIDATOR VALUE VALUES VARCHAR VARYING
VERBOSE VERSION VIEW VOLATILE VERBOSE VERSION VIEW VOLATILE
WHEN WHERE WITH WITHOUT WORK WRITE WHEN WHERE WITH WITHOUT WORK WRITE
...@@ -6406,6 +6406,11 @@ c_expr: columnref { $$ = (Node *) $1; } ...@@ -6406,6 +6406,11 @@ c_expr: columnref { $$ = (Node *) $1; }
n->subselect = $2; n->subselect = $2;
$$ = (Node *)n; $$ = (Node *)n;
} }
| VALUE
{
DomainConstraintValue *n = makeNode(DomainConstraintValue);
$$ = (Node *)n;
}
; ;
/* /*
...@@ -7315,6 +7320,7 @@ reserved_keyword: ...@@ -7315,6 +7320,7 @@ reserved_keyword:
| UNIQUE | UNIQUE
| USER | USER
| USING | USING
| VALUE
| WHEN | WHEN
| WHERE | WHERE
; ;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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[] = { ...@@ -314,6 +314,7 @@ static const ScanKeyword ScanKeywords[] = {
{"vacuum", VACUUM}, {"vacuum", VACUUM},
{"valid", VALID}, {"valid", VALID},
{"validator", VALIDATOR}, {"validator", VALIDATOR},
{"value", VALUE},
{"values", VALUES}, {"values", VALUES},
{"varchar", VARCHAR}, {"varchar", VARCHAR},
{"varying", VARYING}, {"varying", VARYING},
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -283,7 +283,7 @@ transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars)
* transformJoinOnClause() does. Just invoke transformExpr() to fix * transformJoinOnClause() does. Just invoke transformExpr() to fix
* up the operators, and we're done. * up the operators, and we're done.
*/ */
result = transformExpr(pstate, result); result = transformExpr(pstate, result, NULL);
result = coerce_to_boolean(result, "JOIN/USING"); result = coerce_to_boolean(result, "JOIN/USING");
...@@ -317,7 +317,7 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j, ...@@ -317,7 +317,7 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
pstate->p_namespace = makeList2(j->larg, j->rarg); pstate->p_namespace = makeList2(j->larg, j->rarg);
/* This part is just like transformWhereClause() */ /* This part is just like transformWhereClause() */
result = transformExpr(pstate, j->quals); result = transformExpr(pstate, j->quals, NULL);
result = coerce_to_boolean(result, "JOIN/ON"); result = coerce_to_boolean(result, "JOIN/ON");
...@@ -478,7 +478,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) ...@@ -478,7 +478,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
save_namespace = pstate->p_namespace; save_namespace = pstate->p_namespace;
pstate->p_namespace = NIL; pstate->p_namespace = NIL;
funcexpr = transformExpr(pstate, r->funccallnode); funcexpr = transformExpr(pstate, r->funccallnode, NULL);
pstate->p_namespace = save_namespace; pstate->p_namespace = save_namespace;
...@@ -961,7 +961,7 @@ transformWhereClause(ParseState *pstate, Node *clause) ...@@ -961,7 +961,7 @@ transformWhereClause(ParseState *pstate, Node *clause)
if (clause == NULL) if (clause == NULL)
return NULL; return NULL;
qual = transformExpr(pstate, clause); qual = transformExpr(pstate, clause, NULL);
qual = coerce_to_boolean(qual, "WHERE"); qual = coerce_to_boolean(qual, "WHERE");
...@@ -1104,7 +1104,7 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause) ...@@ -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 * willing to match a resjunk target here, though the above cases must
* ignore resjunk targets. * ignore resjunk targets.
*/ */
expr = transformExpr(pstate, node); expr = transformExpr(pstate, node, NULL);
foreach(tl, tlist) foreach(tl, tlist)
{ {
......
...@@ -8,13 +8,18 @@ ...@@ -8,13 +8,18 @@
* *
* *
* IDENTIFICATION * 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 "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_cast.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_proc.h" #include "catalog/pg_proc.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
...@@ -405,8 +410,14 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat) ...@@ -405,8 +410,14 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
for (;;) for (;;)
{ {
HeapTuple tup; HeapTuple tup;
HeapTuple conTup;
Form_pg_type typTup; Form_pg_type typTup;
ScanKeyData key[1];
int nkeys = 0;
SysScanDesc scan;
Relation conRel;
tup = SearchSysCache(TYPEOID, tup = SearchSysCache(TYPEOID,
ObjectIdGetDatum(typeId), ObjectIdGetDatum(typeId),
0, 0, 0); 0, 0, 0);
...@@ -419,7 +430,45 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat) ...@@ -419,7 +430,45 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
if (typTup->typnotnull && notNull == NULL) if (typTup->typnotnull && notNull == NULL)
notNull = pstrdup(NameStr(typTup->typname)); 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') if (typTup->typtype != 'd')
{ {
...@@ -452,7 +501,8 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat) ...@@ -452,7 +501,8 @@ coerce_type_constraints(Node *arg, Oid typeId, CoercionForm cformat)
r->arg = arg; r->arg = arg;
r->testtype = CONSTR_TEST_NOTNULL; r->testtype = CONSTR_TEST_NOTNULL;
r->name = notNull; r->name = "NOT NULL";
r->domname = notNull;
r->check_expr = NULL; r->check_expr = NULL;
arg = (Node *) r; arg = (Node *) r;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -20,6 +20,7 @@
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "nodes/params.h" #include "nodes/params.h"
#include "optimizer/clauses.h"
#include "parser/analyze.h" #include "parser/analyze.h"
#include "parser/gramparse.h" #include "parser/gramparse.h"
#include "parser/parse.h" #include "parser/parse.h"
...@@ -83,7 +84,7 @@ parse_expr_init(void) ...@@ -83,7 +84,7 @@ parse_expr_init(void)
* input and output of transformExpr; see SubLink for example. * input and output of transformExpr; see SubLink for example.
*/ */
Node * Node *
transformExpr(ParseState *pstate, Node *expr) transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
{ {
Node *result = NULL; Node *result = NULL;
...@@ -152,7 +153,7 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -152,7 +153,7 @@ transformExpr(ParseState *pstate, Node *expr)
ExprFieldSelect *efs = (ExprFieldSelect *) expr; ExprFieldSelect *efs = (ExprFieldSelect *) expr;
List *fields; List *fields;
result = transformExpr(pstate, efs->arg); result = transformExpr(pstate, efs->arg, domVal);
/* handle qualification, if any */ /* handle qualification, if any */
foreach(fields, efs->fields) foreach(fields, efs->fields)
{ {
...@@ -169,7 +170,7 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -169,7 +170,7 @@ transformExpr(ParseState *pstate, Node *expr)
case T_TypeCast: case T_TypeCast:
{ {
TypeCast *tc = (TypeCast *) expr; TypeCast *tc = (TypeCast *) expr;
Node *arg = transformExpr(pstate, tc->arg); Node *arg = transformExpr(pstate, tc->arg, domVal);
result = typecast_expression(arg, tc->typename); result = typecast_expression(arg, tc->typename);
break; break;
...@@ -204,14 +205,14 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -204,14 +205,14 @@ transformExpr(ParseState *pstate, Node *expr)
n->arg = a->lexpr; n->arg = a->lexpr;
result = transformExpr(pstate, result = transformExpr(pstate,
(Node *) n); (Node *) n, domVal);
} }
else else
{ {
Node *lexpr = transformExpr(pstate, Node *lexpr = transformExpr(pstate,
a->lexpr); a->lexpr, domVal);
Node *rexpr = transformExpr(pstate, Node *rexpr = transformExpr(pstate,
a->rexpr); a->rexpr, domVal);
result = (Node *) make_op(a->name, result = (Node *) make_op(a->name,
lexpr, lexpr,
...@@ -222,9 +223,9 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -222,9 +223,9 @@ transformExpr(ParseState *pstate, Node *expr)
case AND: case AND:
{ {
Node *lexpr = transformExpr(pstate, Node *lexpr = transformExpr(pstate,
a->lexpr); a->lexpr, domVal);
Node *rexpr = transformExpr(pstate, Node *rexpr = transformExpr(pstate,
a->rexpr); a->rexpr, domVal);
Expr *expr = makeNode(Expr); Expr *expr = makeNode(Expr);
lexpr = coerce_to_boolean(lexpr, "AND"); lexpr = coerce_to_boolean(lexpr, "AND");
...@@ -239,9 +240,9 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -239,9 +240,9 @@ transformExpr(ParseState *pstate, Node *expr)
case OR: case OR:
{ {
Node *lexpr = transformExpr(pstate, Node *lexpr = transformExpr(pstate,
a->lexpr); a->lexpr, domVal);
Node *rexpr = transformExpr(pstate, Node *rexpr = transformExpr(pstate,
a->rexpr); a->rexpr, domVal);
Expr *expr = makeNode(Expr); Expr *expr = makeNode(Expr);
lexpr = coerce_to_boolean(lexpr, "OR"); lexpr = coerce_to_boolean(lexpr, "OR");
...@@ -256,7 +257,7 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -256,7 +257,7 @@ transformExpr(ParseState *pstate, Node *expr)
case NOT: case NOT:
{ {
Node *rexpr = transformExpr(pstate, Node *rexpr = transformExpr(pstate,
a->rexpr); a->rexpr, domVal);
Expr *expr = makeNode(Expr); Expr *expr = makeNode(Expr);
rexpr = coerce_to_boolean(rexpr, "NOT"); rexpr = coerce_to_boolean(rexpr, "NOT");
...@@ -270,9 +271,9 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -270,9 +271,9 @@ transformExpr(ParseState *pstate, Node *expr)
case DISTINCT: case DISTINCT:
{ {
Node *lexpr = transformExpr(pstate, Node *lexpr = transformExpr(pstate,
a->lexpr); a->lexpr, domVal);
Node *rexpr = transformExpr(pstate, Node *rexpr = transformExpr(pstate,
a->rexpr); a->rexpr, domVal);
result = (Node *) make_op(a->name, result = (Node *) make_op(a->name,
lexpr, lexpr,
...@@ -293,7 +294,7 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -293,7 +294,7 @@ transformExpr(ParseState *pstate, Node *expr)
* Will result in a boolean constant node. * Will result in a boolean constant node.
*/ */
Node *lexpr = transformExpr(pstate, Node *lexpr = transformExpr(pstate,
a->lexpr); a->lexpr, domVal);
ltype = exprType(lexpr); ltype = exprType(lexpr);
foreach(telem, (List *) a->rexpr) foreach(telem, (List *) a->rexpr)
...@@ -317,7 +318,7 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -317,7 +318,7 @@ transformExpr(ParseState *pstate, Node *expr)
n->val.val.str = (matched ? "t" : "f"); n->val.val.str = (matched ? "t" : "f");
n->typename = SystemTypeName("bool"); n->typename = SystemTypeName("bool");
result = transformExpr(pstate, (Node *) n); result = transformExpr(pstate, (Node *) n, domVal);
} }
break; break;
} }
...@@ -331,7 +332,7 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -331,7 +332,7 @@ transformExpr(ParseState *pstate, Node *expr)
/* transform the list of arguments */ /* transform the list of arguments */
foreach(args, fn->args) foreach(args, fn->args)
lfirst(args) = transformExpr(pstate, lfirst(args) = transformExpr(pstate,
(Node *) lfirst(args)); (Node *) lfirst(args), domVal);
result = ParseFuncOrColumn(pstate, result = ParseFuncOrColumn(pstate,
fn->funcname, fn->funcname,
fn->args, fn->args,
...@@ -405,7 +406,7 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -405,7 +406,7 @@ transformExpr(ParseState *pstate, Node *expr)
List *elist; List *elist;
foreach(elist, left_list) foreach(elist, left_list)
lfirst(elist) = transformExpr(pstate, lfirst(elist)); lfirst(elist) = transformExpr(pstate, lfirst(elist), domVal);
Assert(IsA(sublink->oper, A_Expr)); Assert(IsA(sublink->oper, A_Expr));
op = ((A_Expr *) sublink->oper)->name; op = ((A_Expr *) sublink->oper)->name;
...@@ -504,7 +505,7 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -504,7 +505,7 @@ transformExpr(ParseState *pstate, Node *expr)
warg = (Node *) makeSimpleA_Expr(OP, "=", warg = (Node *) makeSimpleA_Expr(OP, "=",
c->arg, warg); c->arg, warg);
} }
neww->expr = transformExpr(pstate, warg); neww->expr = transformExpr(pstate, warg, domVal);
neww->expr = coerce_to_boolean(neww->expr, "CASE/WHEN"); neww->expr = coerce_to_boolean(neww->expr, "CASE/WHEN");
...@@ -520,7 +521,7 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -520,7 +521,7 @@ transformExpr(ParseState *pstate, Node *expr)
n->val.type = T_Null; n->val.type = T_Null;
warg = (Node *) n; warg = (Node *) n;
} }
neww->result = transformExpr(pstate, warg); neww->result = transformExpr(pstate, warg, domVal);
newargs = lappend(newargs, neww); newargs = lappend(newargs, neww);
typeids = lappendi(typeids, exprType(neww->result)); typeids = lappendi(typeids, exprType(neww->result));
...@@ -544,7 +545,7 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -544,7 +545,7 @@ transformExpr(ParseState *pstate, Node *expr)
n->val.type = T_Null; n->val.type = T_Null;
defresult = (Node *) n; defresult = (Node *) n;
} }
newc->defresult = transformExpr(pstate, defresult); newc->defresult = transformExpr(pstate, defresult, domVal);
/* /*
* Note: default result is considered the most significant * Note: default result is considered the most significant
...@@ -580,7 +581,7 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -580,7 +581,7 @@ transformExpr(ParseState *pstate, Node *expr)
{ {
NullTest *n = (NullTest *) 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 */ /* the argument can be any type, so don't coerce it */
result = expr; result = expr;
break; break;
...@@ -617,7 +618,7 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -617,7 +618,7 @@ transformExpr(ParseState *pstate, Node *expr)
clausename = NULL; /* keep compiler quiet */ 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); b->arg = coerce_to_boolean(b->arg, clausename);
...@@ -625,6 +626,13 @@ transformExpr(ParseState *pstate, Node *expr) ...@@ -625,6 +626,13 @@ transformExpr(ParseState *pstate, Node *expr)
break; break;
} }
case T_DomainConstraintValue:
{
result = (Node *) copyObject(domVal);
break;
}
/********************************************* /*********************************************
* Quietly accept node types that may be presented when we are * Quietly accept node types that may be presented when we are
* called on an already-transformed tree. * called on an already-transformed tree.
...@@ -936,6 +944,9 @@ exprType(Node *expr) ...@@ -936,6 +944,9 @@ exprType(Node *expr)
case T_ConstraintTest: case T_ConstraintTest:
type = exprType(((ConstraintTest *) expr)->arg); type = exprType(((ConstraintTest *) expr)->arg);
break; break;
case T_ConstraintTestValue:
type = ((ConstraintTestValue *) expr)->typeId;
break;
default: default:
elog(ERROR, "exprType: Do not know how to get type for %d node", elog(ERROR, "exprType: Do not know how to get type for %d node",
nodeTag(expr)); nodeTag(expr));
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -277,7 +277,7 @@ transformArraySubscripts(ParseState *pstate,
{ {
if (ai->lidx) if (ai->lidx)
{ {
subexpr = transformExpr(pstate, ai->lidx); subexpr = transformExpr(pstate, ai->lidx, NULL);
/* If it's not int4 already, try to coerce */ /* If it's not int4 already, try to coerce */
subexpr = coerce_to_target_type(subexpr, exprType(subexpr), subexpr = coerce_to_target_type(subexpr, exprType(subexpr),
INT4OID, -1, INT4OID, -1,
...@@ -299,7 +299,7 @@ transformArraySubscripts(ParseState *pstate, ...@@ -299,7 +299,7 @@ transformArraySubscripts(ParseState *pstate,
} }
lowerIndexpr = lappend(lowerIndexpr, subexpr); lowerIndexpr = lappend(lowerIndexpr, subexpr);
} }
subexpr = transformExpr(pstate, ai->uidx); subexpr = transformExpr(pstate, ai->uidx, NULL);
/* If it's not int4 already, try to coerce */ /* If it's not int4 already, try to coerce */
subexpr = coerce_to_target_type(subexpr, exprType(subexpr), subexpr = coerce_to_target_type(subexpr, exprType(subexpr),
INT4OID, -1, INT4OID, -1,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -56,7 +56,7 @@ transformTargetEntry(ParseState *pstate,
/* Transform the node if caller didn't do it already */ /* Transform the node if caller didn't do it already */
if (expr == NULL) if (expr == NULL)
expr = transformExpr(pstate, node); expr = transformExpr(pstate, node, NULL);
if (IsA(expr, RangeVar)) if (IsA(expr, RangeVar))
elog(ERROR, "You can't use relation names alone in the target list, try relation.*."); elog(ERROR, "You can't use relation names alone in the target list, try relation.*.");
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * 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. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -2226,6 +2226,12 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -2226,6 +2226,12 @@ get_rule_expr(Node *node, deparse_context *context,
} }
break; break;
case T_ConstraintTestValue:
{
appendStringInfo(buf, "VALUE");
}
break;
case T_SubLink: case T_SubLink:
get_sublink_expr(node, context); get_sublink_expr(node, context);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -40,6 +40,7 @@
#define ConstraintNameNspIndex "pg_constraint_conname_nsp_index" #define ConstraintNameNspIndex "pg_constraint_conname_nsp_index"
#define ConstraintOidIndex "pg_constraint_oid_index" #define ConstraintOidIndex "pg_constraint_oid_index"
#define ConstraintRelidIndex "pg_constraint_conrelid_index" #define ConstraintRelidIndex "pg_constraint_conrelid_index"
#define ConstraintTypidIndex "pg_constraint_contypid_index"
#define ConversionDefaultIndex "pg_conversion_default_index" #define ConversionDefaultIndex "pg_conversion_default_index"
#define ConversionNameNspIndex "pg_conversion_name_nsp_index" #define ConversionNameNspIndex "pg_conversion_name_nsp_index"
#define ConversionOidIndex "pg_conversion_oid_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 ...@@ -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)); 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 */ /* 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)); 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_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_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)); DECLARE_UNIQUE_INDEX(pg_conversion_name_nsp_index on pg_conversion using btree(conname name_ops, connamespace oid_ops));
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * NOTES
* the genbki.sh script reads this file and generates .bki * the genbki.sh script reads this file and generates .bki
...@@ -140,6 +140,15 @@ typedef FormData_pg_constraint *Form_pg_constraint; ...@@ -140,6 +140,15 @@ typedef FormData_pg_constraint *Form_pg_constraint;
* the FKCONSTR_MATCH_xxx constants defined in parsenodes.h. * 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 * prototypes for functions in pg_constraint.c
...@@ -166,9 +175,9 @@ extern Oid CreateConstraintEntry(const char *constraintName, ...@@ -166,9 +175,9 @@ extern Oid CreateConstraintEntry(const char *constraintName,
extern void RemoveConstraintById(Oid conId); extern void RemoveConstraintById(Oid conId);
extern bool ConstraintNameIsUsed(Oid relId, Oid relNamespace, extern bool ConstraintNameIsUsed(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace,
const char *cname); const char *cname);
extern char *GenerateConstraintName(Oid relId, Oid relNamespace, extern char *GenerateConstraintName(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace,
int *counter); int *counter);
extern bool ConstraintNameIsGenerated(const char *cname); extern bool ConstraintNameIsGenerated(const char *cname);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -113,6 +113,13 @@ typedef struct ExprContext
Datum *ecxt_aggvalues; /* precomputed values for Aggref nodes */ Datum *ecxt_aggvalues; /* precomputed values for Aggref nodes */
bool *ecxt_aggnulls; /* null flags 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 */ /* Functions to call back when ExprContext is shut down */
ExprContext_CB *ecxt_callbacks; ExprContext_CB *ecxt_callbacks;
} ExprContext; } ExprContext;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -171,6 +171,7 @@ typedef enum NodeTag
T_ViewStmt, T_ViewStmt,
T_LoadStmt, T_LoadStmt,
T_CreateDomainStmt, T_CreateDomainStmt,
T_DomainConstraintValue,
T_CreatedbStmt, T_CreatedbStmt,
T_DropdbStmt, T_DropdbStmt,
T_VacuumStmt, T_VacuumStmt,
...@@ -231,6 +232,7 @@ typedef enum NodeTag ...@@ -231,6 +232,7 @@ typedef enum NodeTag
T_NullTest, T_NullTest,
T_BooleanTest, T_BooleanTest,
T_ConstraintTest, T_ConstraintTest,
T_ConstraintTestValue,
T_CaseExpr, T_CaseExpr,
T_CaseWhen, T_CaseWhen,
T_FkConstraint, T_FkConstraint,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -285,9 +285,26 @@ typedef struct ConstraintTest
Node *arg; /* input expression */ Node *arg; /* input expression */
ConstraintTestType testtype; /* test type */ ConstraintTestType testtype; /* test type */
char *name; /* name of constraint (for error msgs) */ 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 */ Node *check_expr; /* for CHECK test, a boolean expression */
} ConstraintTest; } 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) * ColumnDef - column definition (used in various creates)
* *
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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, ...@@ -22,6 +22,7 @@ extern bool contain_var_reference(Node *node, int varno, int varattno,
int levelsup); int levelsup);
extern bool contain_whole_tuple_var(Node *node, int varno, 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_clause(Node *node);
extern bool contain_var_tuple_clause(Node *node);
extern List *pull_var_clause(Node *node, bool includeUpperVars); extern List *pull_var_clause(Node *node, bool includeUpperVars);
extern Node *flatten_join_alias_vars(Node *node, List *rtable, bool force); extern Node *flatten_join_alias_vars(Node *node, List *rtable, bool force);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -21,7 +21,8 @@
extern int max_expr_depth; extern int max_expr_depth;
extern bool Transform_null_equals; 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 Oid exprType(Node *expr);
extern int32 exprTypmod(Node *expr); extern int32 exprTypmod(Node *expr);
extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod); extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod);
......
...@@ -103,35 +103,43 @@ drop domain domainint4arr restrict; ...@@ -103,35 +103,43 @@ drop domain domainint4arr restrict;
drop domain domaintextarr restrict; drop domain domaintextarr restrict;
create domain dnotnull varchar(15) NOT NULL; create domain dnotnull varchar(15) NOT NULL;
create domain dnull varchar(15); create domain dnull varchar(15);
create domain dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd');
create table nulltest create table nulltest
( col1 dnotnull ( col1 dnotnull
, col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden , col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden
, col3 dnull NOT NULL , col3 dnull NOT NULL
, col4 dnull , col4 dnull
, col5 dcheck CHECK (col5 IN ('c', 'd'))
); );
INSERT INTO nulltest DEFAULT VALUES; INSERT INTO nulltest DEFAULT VALUES;
ERROR: Domain dnotnull does not allow NULL values ERROR: Domain dnotnull does not allow NULL values
INSERT INTO nulltest values ('a', 'b', 'c', 'd'); -- Good INSERT INTO nulltest values ('a', 'b', 'c', 'd', 'c'); -- Good
INSERT INTO nulltest values (NULL, 'b', 'c', 'd'); 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 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 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 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 -- Test copy
COPY nulltest FROM stdin; --fail 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 lost synchronization with server, resetting connection
SET autocommit TO 'on'; SET autocommit TO 'on';
-- Last row is bad
COPY nulltest FROM stdin; 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; select * from nulltest;
col1 | col2 | col3 | col4 col1 | col2 | col3 | col4 | col5
------+------+------+------ ------+------+------+------+------
a | b | c | d a | b | c | d | c
a | b | c | a | b | c | | d
a | b | c | (2 rows)
(3 rows)
-- Test out coerced (casted) constraints -- Test out coerced (casted) constraints
SELECT cast('1' as dnotnull); SELECT cast('1' as dnotnull);
......
...@@ -83,29 +83,36 @@ drop domain domaintextarr restrict; ...@@ -83,29 +83,36 @@ drop domain domaintextarr restrict;
create domain dnotnull varchar(15) NOT NULL; create domain dnotnull varchar(15) NOT NULL;
create domain dnull varchar(15); create domain dnull varchar(15);
create domain dcheck varchar(15) NOT NULL CHECK (VALUE = 'a' OR VALUE = 'c' OR VALUE = 'd');
create table nulltest create table nulltest
( col1 dnotnull ( col1 dnotnull
, col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden , col2 dnotnull NULL -- NOT NULL in the domain cannot be overridden
, col3 dnull NOT NULL , col3 dnull NOT NULL
, col4 dnull , col4 dnull
, col5 dcheck CHECK (col5 IN ('c', 'd'))
); );
INSERT INTO nulltest DEFAULT VALUES; INSERT INTO nulltest DEFAULT VALUES;
INSERT INTO nulltest values ('a', 'b', 'c', 'd'); -- Good INSERT INTO nulltest values ('a', 'b', 'c', 'd', 'c'); -- Good
INSERT INTO nulltest values (NULL, 'b', 'c', 'd'); insert into nulltest values ('a', 'b', 'c', 'd', NULL);
INSERT INTO nulltest values ('a', NULL, 'c', 'd'); insert into nulltest values ('a', 'b', 'c', 'd', 'a');
INSERT INTO nulltest values ('a', 'b', NULL, 'd'); INSERT INTO nulltest values (NULL, 'b', 'c', 'd', 'd');
INSERT INTO nulltest values ('a', 'b', 'c', NULL); -- Good 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 -- Test copy
COPY nulltest FROM stdin; --fail COPY nulltest FROM stdin; --fail
a b \N d a b \N d \N
\. \.
SET autocommit TO 'on'; SET autocommit TO 'on';
-- Last row is bad
COPY nulltest FROM stdin; 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; select * from nulltest;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment