Commit 05a6b379 authored by Bruce Momjian's avatar Bruce Momjian

Re-addd Rod's ALTER DOMAIN patch.

parent 78705d0a
......@@ -15778,7 +15778,7 @@ fi
ac_config_files="$ac_config_files GNUmakefile src/Makefile.global"
ac_config_links="$ac_config_links src/backend/port/dynloader.c:src/backend/port/dynloader/${template}.c src/backend/port/pg_sema.c:${SEMA_IMPLEMENTATION} src/backend/port/pg_shmem.c:${SHMEM_IMPLEMENTATION} src/include/dynloader.h:src/backend/port/dynloader/${template}.h src/include/pg_config_os.h:src/include/port/${template}.h src/Makefile.port:src/makefiles/Makefile.${template} src/interfaces/libpq/v6util.c:src/backend/libpq/v6util.c"
ac_config_links="$ac_config_links src/backend/port/dynloader.c:src/backend/port/dynloader/${template}.c src/backend/port/pg_sema.c:${SEMA_IMPLEMENTATION} src/backend/port/pg_shmem.c:${SHMEM_IMPLEMENTATION} src/include/dynloader.h:src/backend/port/dynloader/${template}.h src/include/pg_config_os.h:src/include/port/${template}.h src/Makefile.port:src/makefiles/Makefile.${template}"
ac_config_headers="$ac_config_headers src/include/pg_config.h"
......@@ -16266,7 +16266,6 @@ do
"src/include/dynloader.h" ) CONFIG_LINKS="$CONFIG_LINKS src/include/dynloader.h:src/backend/port/dynloader/${template}.h" ;;
"src/include/pg_config_os.h" ) CONFIG_LINKS="$CONFIG_LINKS src/include/pg_config_os.h:src/include/port/${template}.h" ;;
"src/Makefile.port" ) CONFIG_LINKS="$CONFIG_LINKS src/Makefile.port:src/makefiles/Makefile.${template}" ;;
"src/interfaces/libpq/v6util.c" ) CONFIG_LINKS="$CONFIG_LINKS src/interfaces/libpq/v6util.c:src/backend/libpq/v6util.c" ;;
"src/include/pg_config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS src/include/pg_config.h" ;;
*) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
......
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_domain.sgml,v 1.4 2002/12/06 05:00:10 momjian Exp $
PostgreSQL documentation
-->
<refentry id="SQL-ALTERDOMAIN">
<refmeta>
<refentrytitle id="sql-alterdomain-title">ALTER DOMAIN</refentrytitle>
<refmiscinfo>SQL - Language Statements</refmiscinfo>
</refmeta>
<refnamediv>
<refname>
ALTER DOMAIN
</refname>
<refpurpose>
change the definition of a table
</refpurpose>
</refnamediv>
<refsynopsisdiv>
<refsynopsisdivinfo>
<date>2002-11-27</date>
</refsynopsisdivinfo>
<synopsis>
ALTER DOMAIN <replaceable class="PARAMETER">domain</replaceable>
{ SET DEFAULT <replaceable class="PARAMETER">value</replaceable> | DROP DEFAULT }
ALTER DOMAIN <replaceable class="PARAMETER">domain</replaceable>
{ SET | DROP } NOT NULL
ALTER DOMAIN <replaceable class="PARAMETER">domain</replaceable>
ADD <replaceable class="PARAMETER">domain_constraint</replaceable>
ALTER DOMAIN <replaceable class="PARAMETER">domain</replaceable>
DROP CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ]
</synopsis>
<refsect2 id="R2-SQL-ALTERDOMAIN-1">
<refsect2info>
<date>2002-11-27</date>
</refsect2info>
<title>
Inputs
</title>
<para>
<variablelist>
<varlistentry>
<term><replaceable class="PARAMETER"> domain </replaceable></term>
<listitem>
<para>
The name (possibly schema-qualified) of an existing domain to
alter.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER"> domain_constraint </replaceable></term>
<listitem>
<para>
New domain constraint for the domain.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="PARAMETER"> constraint_name </replaceable></term>
<listitem>
<para>
Name of an existing constraint to drop.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>CASCADE</term>
<listitem>
<para>
Automatically drop objects that depend constraint.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>RESTRICT</term>
<listitem>
<para>
Refuse to drop the constraint if there are any dependent
objects. This is the default behavior.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
<refsect2 id="R2-SQL-ALTERDOMAIN-2">
<refsect2info>
<date>1998-04-15</date>
</refsect2info>
<title>
Outputs
</title>
<para>
<variablelist>
<varlistentry>
<term><computeroutput>ALTER DOMAIN</computeroutput></term>
<listitem>
<para>
Message returned from domain alteration.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><computeroutput>ERROR</computeroutput></term>
<listitem>
<para>
Message returned if domain is not available.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
</refsynopsisdiv>
<refsect1 id="R1-SQL-ALTERDOMAIN-1">
<refsect1info>
<date>2002-11-27</date>
</refsect1info>
<title>
Description
</title>
<para>
<command>ALTER DOMAIN</command> changes the definition of an existing domain.
There are several sub-forms:
</para>
<variablelist>
<varlistentry>
<term>SET/DROP DEFAULT</term>
<listitem>
<para>
These forms set or remove the default value for a column. Note
that defaults only apply to subsequent <command>INSERT</command>
commands; they do not cause rows already in a table using the domain.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>SET/DROP NOT NULL</term>
<listitem>
<para>
These forms change whether a domain is marked to allow NULL
values or to reject NULL values. You may only <literal>SET NOT NULL</>
when the tables using the domain contain no null values in the domain
based column.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>ADD <replaceable class="PARAMETER">domain_constraint</replaceable></term>
<listitem>
<para>
This form adds a new constraint to a table using the same syntax as
<xref linkend="SQL-CREATEDOMAIN" endterm="SQL-CREATEDOMAIN-TITLE">.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>DROP CONSTRAINT</term>
<listitem>
<para>
This form drops constraints on a domain.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
You must own the domain to use <command>ALTER DOMAIN</>; except for
<command>ALTER TABLE OWNER</>, which may only be executed by a superuser.
</para>
</refsect1>
<refsect1 id="R1-SQL-ALTERDOMAIN-2">
<title>
Usage
</title>
<para>
To add a NOT NULL constraint to a column:
<programlisting>
ALTER DOMAIN zipcode SET NOT NULL;
</programlisting>
To remove a NOT NULL constraint from a column:
<programlisting>
ALTER DOMAIN zipcode DROP NOT NULL;
</programlisting>
</para>
<para>
To add a check constraint to a table:
<programlisting>
ALTER DOMAIN zipcode ADD CONSTRAINT zipchk CHECK (char_length(VALUE) = 5);
</programlisting>
</para>
<para>
To remove a check constraint from a table and all its children:
<programlisting>
ALTER DOMAIN zipcode DROP CONSTRAINT zipchk;
</programlisting>
</para>
</refsect1>
</refentry>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:nil
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
sgml-parent-document:nil
sgml-default-dtd-file:"../reference.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:"/usr/lib/sgml/catalog"
sgml-local-ecat-files:nil
End:
-->
<!-- reference.sgml
$Header: /cvsroot/pgsql/doc/src/sgml/reference.sgml,v 1.40 2002/12/06 03:42:48 momjian Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/reference.sgml,v 1.41 2002/12/06 05:00:10 momjian Exp $
PostgreSQL Reference Manual
-->
......@@ -47,6 +47,7 @@ PostgreSQL Reference Manual
&abort;
&alterDatabase;
&alterDomain;
&alterGroup;
&alterTable;
&alterTrigger;
......
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.16 2002/12/06 03:42:54 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/dependency.c,v 1.17 2002/12/06 05:00:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -36,6 +36,7 @@
#include "commands/proclang.h"
#include "commands/schemacmds.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.10 2002/12/06 03:42:57 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_constraint.c,v 1.11 2002/12/06 05:00:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -199,6 +199,7 @@ CreateConstraintEntry(const char *constraintName,
domobject.classId = RelOid_pg_type;
domobject.objectId = domainId;
domobject.objectSubId = 0;
recordDependencyOn(&conobject, &domobject, DEPENDENCY_AUTO);
}
......@@ -496,15 +497,16 @@ RemoveConstraintById(Oid conId)
HeapTuple typTup;
ScanKeyData typKey[1];
SysScanDesc typScan;
int nkeys = 0;
typRel = heap_openr(TypeRelationName, RowExclusiveLock);
ScanKeyEntryInitialize(&typKey[0], 0x0,
Anum_pg_constraint_contypid, F_OIDEQ,
ScanKeyEntryInitialize(&typKey[nkeys++], 0x0,
ObjectIdAttributeNumber, F_OIDEQ,
ObjectIdGetDatum(con->contypid));
typScan = systable_beginscan(typRel, TypeOidIndex, true,
SnapshotNow, 1, typKey);
SnapshotNow, nkeys, typKey);
typTup = systable_getnext(typScan);
......@@ -517,6 +519,11 @@ RemoveConstraintById(Oid conId)
/* Keep lock on domain type until end of xact */
heap_close(typRel, NoLock);
}
else
{
elog(ERROR, "RemoveConstraintById: Constraint %u is not a known type",
conId);
}
/* Fry the constraint itself */
simple_heap_delete(conDesc, &tup->t_self);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.84 2002/12/06 03:42:57 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.85 2002/12/06 05:00:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -139,7 +139,7 @@ TypeCreate(const char *typeName,
Oid elementType,
Oid baseType,
const char *defaultTypeValue, /* human readable rep */
const char *defaultTypeBin, /* cooked rep */
char *defaultTypeBin, /* cooked rep */
bool passedByValue,
char alignment,
char storage,
......@@ -291,87 +291,125 @@ TypeCreate(const char *typeName,
*/
if (!IsBootstrapProcessingMode())
{
ObjectAddress myself,
referenced;
myself.classId = RelOid_pg_type;
myself.objectId = typeObjectId;
myself.objectSubId = 0;
/* dependency on namespace */
/* skip for relation rowtype, since we have indirect dependency */
if (!OidIsValid(relationOid))
{
referenced.classId = get_system_catalog_relid(NamespaceRelationName);
referenced.objectId = typeNamespace;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/* Normal dependencies on the I/O functions */
referenced.classId = RelOid_pg_proc;
referenced.objectId = inputProcedure;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
GenerateTypeDependencies(typeNamespace,
typeObjectId,
relationOid,
relationKind,
inputProcedure,
outputProcedure,
elementType,
baseType,
defaultTypeBin,
false);
}
/*
* finish up
*/
heap_close(pg_type_desc, RowExclusiveLock);
return typeObjectId;
}
referenced.classId = RelOid_pg_proc;
referenced.objectId = outputProcedure;
void
GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
Oid relationOid, /* only for 'c'atalog typeType */
char relationKind,
Oid inputProcedure,
Oid outputProcedure,
Oid elementType,
Oid baseType,
char *defaultTypeBin, /* cooked rep */
bool rebuild)
{
ObjectAddress myself,
referenced;
/*
* If true, we need to remove all current dependencies that the type
* holds, and rebuild them from scratch. This allows us to easily
* implement alter type, and alter domain statements.
*/
if (rebuild)
deleteDependencyRecordsFor(RelOid_pg_type,
typeObjectId);
myself.classId = RelOid_pg_type;
myself.objectId = typeObjectId;
myself.objectSubId = 0;
/* dependency on namespace */
/* skip for relation rowtype, since we have indirect dependency */
if (!OidIsValid(relationOid))
{
referenced.classId = get_system_catalog_relid(NamespaceRelationName);
referenced.objectId = typeNamespace;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/*
* If the type is a rowtype for a relation, mark it as internally
* dependent on the relation, *unless* it is a stand-alone
* composite type relation. For the latter case, we have to
* reverse the dependency.
*
* In the former case, this allows the type to be auto-dropped when
* the relation is, and not otherwise. And in the latter, of
* course we get the opposite effect.
*/
if (OidIsValid(relationOid))
{
referenced.classId = RelOid_pg_class;
referenced.objectId = relationOid;
referenced.objectSubId = 0;
if (relationKind != RELKIND_COMPOSITE_TYPE)
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
else
recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
}
/* Normal dependencies on the I/O functions */
referenced.classId = RelOid_pg_proc;
referenced.objectId = inputProcedure;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/*
* If the type is an array type, mark it auto-dependent on the
* base type. (This is a compromise between the typical case
* where the array type is automatically generated and the case
* where it is manually created: we'd prefer INTERNAL for the
* former case and NORMAL for the latter.)
*/
if (OidIsValid(elementType))
{
referenced.classId = RelOid_pg_type;
referenced.objectId = elementType;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
/* Normal dependency from a domain to its base type. */
if (OidIsValid(baseType))
{
referenced.classId = RelOid_pg_type;
referenced.objectId = baseType;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
referenced.classId = RelOid_pg_proc;
referenced.objectId = outputProcedure;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/*
* If the type is a rowtype for a relation, mark it as internally
* dependent on the relation, *unless* it is a stand-alone
* composite type relation. For the latter case, we have to
* reverse the dependency.
*
* In the former case, this allows the type to be auto-dropped when
* the relation is, and not otherwise. And in the latter, of
* course we get the opposite effect.
*/
if (OidIsValid(relationOid))
{
referenced.classId = RelOid_pg_class;
referenced.objectId = relationOid;
referenced.objectSubId = 0;
if (relationKind != RELKIND_COMPOSITE_TYPE)
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
else
recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
}
/*
* finish up
* If the type is an array type, mark it auto-dependent on the
* base type. (This is a compromise between the typical case
* where the array type is automatically generated and the case
* where it is manually created: we'd prefer INTERNAL for the
* former case and NORMAL for the latter.)
*/
heap_close(pg_type_desc, RowExclusiveLock);
if (OidIsValid(elementType))
{
referenced.classId = RelOid_pg_type;
referenced.objectId = elementType;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
}
return typeObjectId;
/* Normal dependency from a domain to its base type. */
if (OidIsValid(baseType))
{
referenced.classId = RelOid_pg_type;
referenced.objectId = baseType;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
/* Dependency on the default expression */
if (defaultTypeBin)
recordDependencyOnExpr(&myself, stringToNode(defaultTypeBin),
NIL, DEPENDENCY_NORMAL);
}
/*
......
......@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.101 2002/12/06 03:42:57 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.102 2002/12/06 05:00:10 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -667,7 +667,7 @@ cluster(ClusterStmt *stmt)
tableOid = RangeVarGetRelid(stmt->relation, false);
if (!check_cluster_ownership(tableOid))
elog(ERROR, "CLUSTER: You do not own relation %s",
stmt->relation->relname);
stmt->relation->relname);
tuple = SearchSysCache(RELOID,
ObjectIdGetDatum(tableOid),
......@@ -706,7 +706,7 @@ cluster(ClusterStmt *stmt)
}
if (indexOid == InvalidOid)
elog(ERROR, "CLUSTER: No previously clustered index found on table %s",
stmt->relation->relname);
stmt->relation->relname);
RelationClose(rel);
ReleaseSysCache(ituple);
ReleaseSysCache(idxtuple);
......@@ -721,7 +721,7 @@ cluster(ClusterStmt *stmt)
/* XXX Maybe the namespace should be reported as well */
if (!OidIsValid(indexOid))
elog(ERROR, "CLUSTER: cannot find index \"%s\" for table \"%s\"",
stmt->indexname, stmt->relation->relname);
stmt->indexname, stmt->relation->relname);
rvtc.tableOid = tableOid;
rvtc.indexOid = indexOid;
rvtc.isPrevious = false;
......@@ -819,7 +819,7 @@ get_tables_to_cluster(AclId owner)
List *rvs = NIL;
/*
* Get all indexes that have indisclustered set. System
* Get all indexes that have indisclustered set. System
* relations or nailed-in relations cannot ever have
* indisclustered set, because CLUSTER will refuse to
* set it when called with one of them as argument.
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.19 2002/12/06 03:42:57 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.20 2002/12/06 05:00:11 momjian Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
......@@ -32,14 +32,18 @@
#include "postgres.h"
#include "access/heapam.h"
#include "access/genam.h"
#include "catalog/catname.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
#include "commands/typecmds.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/nodes.h"
#include "optimizer/clauses.h"
......@@ -48,6 +52,7 @@
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/builtins.h"
......@@ -55,8 +60,21 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
static Oid findTypeIOFunction(List *procname, Oid typeOid, bool isOutput);
static List *get_rels_with_domain(Oid domainOid);
static void domainPermissionCheck(HeapTuple tup, TypeName *typename);
static void domainCheckForUnsupportedConstraints(Node *newConstraint);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
Oid baseTypeOid,
int typMod, Constraint *constr,
int *counter, char *domainName);
typedef struct
{
Oid relOid;
int natts;
int *atts;
} relToCheck;
/*
* DefineType
......@@ -501,12 +519,14 @@ DefineDomain(CreateDomainStmt *stmt)
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");
/*
* Check for constraint types which are not supported by
* domains. Throws an error if it finds one.
*/
domainCheckForUnsupportedConstraints(newConstraint);
/* Assume its a CHECK, DEFAULT, NULL or NOT NULL constraint */
colDef = (Constraint *) newConstraint;
switch (colDef->contype)
{
/*
......@@ -560,25 +580,17 @@ DefineDomain(CreateDomainStmt *stmt)
nullDefined = true;
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;
/* Check constraints are handled after domain creation */
/*
* Check constraints are handled after domain creation, as they require
* the Oid of the domain
*/
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;
/*
* If we reach this, then domainCheckForUnsupportedConstraints()
* doesn't have a complete list of unsupported domain constraints
*/
default:
elog(ERROR, "DefineDomain: unrecognized constraint node type");
break;
......@@ -616,142 +628,26 @@ DefineDomain(CreateDomainStmt *stmt)
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 */
char *junk;
/* Returns the cooked constraint which is not needed during creation */
junk = domainAddConstraint(domainoid, domainNamespace,
basetypeoid, stmt->typename->typmod,
constr, &counter, domainName);
}
break;
/* Errors for other constraints are taken care of prior to domain creation */
default:
break;
}
}
/*
* Add any dependencies needed for the default expression.
*/
if (defaultExpr)
{
ObjectAddress domobject;
domobject.classId = RelOid_pg_type;
domobject.objectId = domainoid;
domobject.objectSubId = 0;
recordDependencyOnExpr(&domobject, defaultExpr, NIL,
DEPENDENCY_NORMAL);
}
/*
* Now we can clean up.
*/
......@@ -931,7 +827,8 @@ findTypeIOFunction(List *procname, Oid typeOid, bool isOutput)
if (OidIsValid(procOid))
{
/* Found, but must complain and fix the pg_proc entry */
elog(NOTICE, "TypeCreate: changing argument type of function %s from OPAQUE to CSTRING",
elog(NOTICE, "TypeCreate: changing argument type of function %s "
"from OPAQUE to CSTRING",
NameListToString(procname));
SetFunctionArgType(procOid, 0, CSTRINGOID);
/*
......@@ -992,3 +889,778 @@ DefineCompositeType(const RangeVar *typevar, List *coldeflist)
*/
return DefineRelation(createStmt, RELKIND_COMPOSITE_TYPE);
}
/*
* AlterDomainDefault
*
* Routine implementing ALTER DOMAIN SET/DROP DEFAULT statements.
*/
void
AlterDomainDefault(List *names, Node *defaultRaw)
{
TypeName *typename;
Oid domainoid;
HeapTuple tup;
ParseState *pstate;
Relation rel;
char *defaultValue;
Node *defaultExpr = NULL; /* NULL if no default specified */
Datum new_record[Natts_pg_type];
char new_record_nulls[Natts_pg_type];
char new_record_repl[Natts_pg_type];
HeapTuple newtuple;
Form_pg_type typTup;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeNode(TypeName);
typename->names = names;
typename->typmod = -1;
typename->arrayBounds = NIL;
/* Lock the domain in the type table */
rel = heap_openr(TypeRelationName, RowExclusiveLock);
/* Use LookupTypeName here so that shell types can be removed. */
domainoid = LookupTypeName(typename);
if (!OidIsValid(domainoid))
elog(ERROR, "Type \"%s\" does not exist",
TypeNameToString(typename));
tup = SearchSysCacheCopy(TYPEOID,
ObjectIdGetDatum(domainoid),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "AlterDomain: type \"%s\" does not exist",
TypeNameToString(typename));
/* Doesn't return if user isn't allowed to alter the domain */
domainPermissionCheck(tup, typename);
/* Setup new tuple */
MemSet(new_record, (Datum) 0, sizeof(new_record));
MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
MemSet(new_record_repl, ' ', sizeof(new_record_repl));
/* Useful later */
typTup = (Form_pg_type) GETSTRUCT(tup);
/* Store the new default, if null then skip this step */
if (defaultRaw)
{
/* Create a dummy ParseState for transformExpr */
pstate = make_parsestate(NULL);
/*
* Cook the colDef->raw_expr into an expression. Note:
* Name is strictly for error message
*/
defaultExpr = cookDefault(pstate, defaultRaw,
typTup->typbasetype,
typTup->typtypmod,
NameStr(typTup->typname));
/*
* Expression must be stored as a nodeToString result, but
* we also require a valid textual representation (mainly
* to make life easier for pg_dump).
*/
defaultValue = deparse_expression(defaultExpr,
deparse_context_for(NameStr(typTup->typname),
InvalidOid),
false, false);
/*
* Form an updated tuple with the new default and write it back.
*/
new_record[Anum_pg_type_typdefaultbin - 1] = DirectFunctionCall1(textin,
CStringGetDatum(
nodeToString(defaultExpr)));
new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
new_record[Anum_pg_type_typdefault - 1] = DirectFunctionCall1(textin,
CStringGetDatum(defaultValue));
new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
}
else /* Default is NULL, drop it */
{
new_record_nulls[Anum_pg_type_typdefaultbin - 1] = 'n';
new_record_repl[Anum_pg_type_typdefaultbin - 1] = 'r';
new_record_nulls[Anum_pg_type_typdefault - 1] = 'n';
new_record_repl[Anum_pg_type_typdefault - 1] = 'r';
}
newtuple = heap_modifytuple(tup, rel,
new_record, new_record_nulls, new_record_repl);
simple_heap_update(rel, &tup->t_self, newtuple);
CatalogUpdateIndexes(rel, newtuple);
/* Rebuild dependencies */
GenerateTypeDependencies(typTup->typnamespace,
domainoid,
typTup->typrelid,
InvalidOid,
typTup->typinput,
typTup->typoutput,
typTup->typelem,
typTup->typbasetype,
nodeToString(defaultExpr),
true); /* Rebuild is true */
/* Clean up */
heap_close(rel, NoLock);
heap_freetuple(newtuple);
};
/*
* AlterDomainNotNull
*
* Routine implementing ALTER DOMAIN SET/DROP NOT NULL statements.
*/
void
AlterDomainNotNull(List *names, bool notNull)
{
TypeName *typename;
Oid domainoid;
HeapTuple tup;
Relation rel;
Datum new_record[Natts_pg_type];
char new_record_nulls[Natts_pg_type];
char new_record_repl[Natts_pg_type];
HeapTuple newtuple;
Form_pg_type typTup;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeNode(TypeName);
typename->names = names;
typename->typmod = -1;
typename->arrayBounds = NIL;
/* Lock the type table */
rel = heap_openr(TypeRelationName, RowExclusiveLock);
/* Use LookupTypeName here so that shell types can be removed. */
domainoid = LookupTypeName(typename);
if (!OidIsValid(domainoid))
elog(ERROR, "Type \"%s\" does not exist",
TypeNameToString(typename));
tup = SearchSysCacheCopy(TYPEOID,
ObjectIdGetDatum(domainoid),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "AlterDomain: type \"%s\" does not exist",
TypeNameToString(typename));
/* Doesn't return if user isn't allowed to alter the domain */
domainPermissionCheck(tup, typename);
typTup = (Form_pg_type) GETSTRUCT(tup);
/* Is the domain already set to the destination constraint? */
if (typTup->typnotnull == notNull)
elog(ERROR, "AlterDomain: %s is already set to %s",
TypeNameToString(typename),
notNull ? "NOT NULL" : "NULL");
/* Adding a NOT NULL constraint requires checking current domains */
if (notNull)
{
List *rels;
List *rt;
/* Fetch relation list with attributes based on this domain */
rels = get_rels_with_domain(domainoid);
foreach (rt, rels)
{
Relation typrel;
HeapTuple tuple;
HeapScanDesc scan;
TupleDesc tupdesc;
relToCheck *rtc = (relToCheck *) lfirst(rt);
/* Lock relation */
typrel = heap_open(rtc->relOid, ExclusiveLock);
tupdesc = RelationGetDescr(typrel);
/* Fetch tuples sequentially */
scan = heap_beginscan(typrel, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
/* Test attributes */
for (i = 0; i < rtc->natts; i++)
{
Datum d;
bool isNull;
d = heap_getattr(tuple, rtc->atts[i], tupdesc, &isNull);
if (isNull)
elog(ERROR, "ALTER DOMAIN: Relation \"%s\" Attribute \"%s\" "
"contains NULL values",
RelationGetRelationName(typrel),
NameStr(*attnumAttName(typrel, rtc->atts[i])));
}
}
heap_endscan(scan);
/* Release lock */
heap_close(typrel, NoLock);
}
}
/* Setup new tuple */
MemSet(new_record, (Datum) 0, sizeof(new_record));
MemSet(new_record_nulls, ' ', sizeof(new_record_nulls));
MemSet(new_record_repl, ' ', sizeof(new_record_repl));
new_record[Anum_pg_type_typnotnull - 1] = BoolGetDatum(notNull);
new_record_repl[Anum_pg_type_typnotnull - 1] = 'r';
/* Build the new tuple */
newtuple = heap_modifytuple(tup, rel,
new_record, new_record_nulls, new_record_repl);
simple_heap_update(rel, &tup->t_self, newtuple);
CatalogUpdateIndexes(rel, newtuple);
/* Clean up */
heap_close(rel, NoLock);
heap_freetuple(newtuple);
}
/*
* AlterDomainDropConstraint
*
* Implements the ALTER DOMAIN DROP CONSTRAINT statement
*/
void
AlterDomainDropConstraint(List *names, const char *constrName, DropBehavior behavior)
{
TypeName *typename;
Oid domainoid;
HeapTuple tup;
Relation rel;
Form_pg_type typTup;
Relation conrel;
SysScanDesc conscan;
ScanKeyData key[1];
HeapTuple contup;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeNode(TypeName);
typename->names = names;
typename->typmod = -1;
typename->arrayBounds = NIL;
/* Lock the type table */
rel = heap_openr(TypeRelationName, RowExclusiveLock);
/* Use LookupTypeName here so that shell types can be removed. */
domainoid = LookupTypeName(typename);
if (!OidIsValid(domainoid))
elog(ERROR, "Type \"%s\" does not exist",
TypeNameToString(typename));
tup = SearchSysCacheCopy(TYPEOID,
ObjectIdGetDatum(domainoid),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "AlterDomain: type \"%s\" does not exist",
TypeNameToString(typename));
/* Doesn't return if user isn't allowed to alter the domain */
domainPermissionCheck(tup, typename);
/* Grab an appropriate lock on the pg_constraint relation */
conrel = heap_openr(ConstraintRelationName, RowExclusiveLock);
/* Use the index to scan only constraints of the target relation */
ScanKeyEntryInitialize(&key[0], 0x0,
Anum_pg_constraint_contypid, F_OIDEQ,
ObjectIdGetDatum(HeapTupleGetOid(tup)));
conscan = systable_beginscan(conrel, ConstraintTypidIndex, true,
SnapshotNow, 1, key);
typTup = (Form_pg_type) GETSTRUCT(tup);
/*
* Scan over the result set, removing any matching entries.
*/
while ((contup = systable_getnext(conscan)) != NULL)
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup);
if (strcmp(NameStr(con->conname), constrName) == 0)
{
ObjectAddress conobj;
conobj.classId = RelationGetRelid(conrel);
conobj.objectId = HeapTupleGetOid(contup);
conobj.objectSubId = 0;
performDeletion(&conobj, behavior);
}
}
/* Clean up after the scan */
systable_endscan(conscan);
heap_close(conrel, RowExclusiveLock);
heap_close(rel, NoLock);
};
/*
* AlterDomainAddConstraint
*
* Implements the ALTER DOMAIN .. ADD CONSTRAINT statement.
*/
void
AlterDomainAddConstraint(List *names, Node *newConstraint)
{
TypeName *typename;
Oid domainoid;
HeapTuple tup;
Relation rel;
List *rels;
List *rt;
Form_pg_type typTup;
char *ccbin;
Node *expr;
int counter = 0;
Constraint *constr;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeNode(TypeName);
typename->names = names;
typename->typmod = -1;
typename->arrayBounds = NIL;
/* Lock the type table */
rel = heap_openr(TypeRelationName, RowExclusiveLock);
/* Use LookupTypeName here so that shell types can be removed. */
domainoid = LookupTypeName(typename);
if (!OidIsValid(domainoid))
elog(ERROR, "Type \"%s\" does not exist",
TypeNameToString(typename));
tup = SearchSysCacheCopy(TYPEOID,
ObjectIdGetDatum(domainoid),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "AlterDomain: type \"%s\" does not exist",
TypeNameToString(typename));
/* Doesn't return if user isn't allowed to alter the domain */
domainPermissionCheck(tup, typename);
typTup = (Form_pg_type) GETSTRUCT(tup);
/*
* Check for constraint types which are not supported by
* domains. Throws an error if it finds one.
*/
domainCheckForUnsupportedConstraints(newConstraint);
/* Assume its a CHECK, DEFAULT, NULL or NOT NULL constraint */
constr = (Constraint *) newConstraint;
switch (constr->contype)
{
case CONSTR_DEFAULT:
elog(ERROR, "Use ALTER DOMAIN .. SET DEFAULT instead");
break;
case CONSTR_NOTNULL:
case CONSTR_NULL:
elog(ERROR, "Use ALTER DOMAIN .. [ SET | DROP ] NOT NULL instead");
break;
/*
* Check constraints are handled after domain creation, as they require
* the Oid of the domain
*/
case CONSTR_CHECK:
{
/* Returns the cooked constraint which is not needed during creation */
ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace,
typTup->typbasetype, typTup->typtypmod,
constr, &counter, NameStr(typTup->typname));
}
break;
/*
* If we reach this, then domainCheckForUnsupportedConstraints()
* doesn't have a complete list of unsupported domain constraints
*/
default:
elog(ERROR, "DefineDomain: unrecognized constraint node type");
break;
}
/*
* Since all other constraint types throw errors, this must be
* a check constraint, and ccbin must be set.
*
* Test all values stored in the attributes based on the domain
* the constraint is being added to.
*/
expr = stringToNode(ccbin);
rels = get_rels_with_domain(domainoid);
foreach (rt, rels)
{
Relation typrel;
HeapTuple tuple;
HeapScanDesc scan;
TupleDesc tupdesc;
ExprContext *econtext;
TupleTableSlot *slot;
relToCheck *rtc = (relToCheck *) lfirst(rt);
/* Lock relation */
typrel = heap_open(rtc->relOid, ExclusiveLock);
/* Test attributes */
tupdesc = RelationGetDescr(typrel);
/* Make tuple slot to hold tuples */
slot = MakeTupleTableSlot();
ExecSetSlotDescriptor(slot, RelationGetDescr(typrel), false);
/* Make an expression context for ExecQual */
econtext = MakeExprContext(slot, CurrentMemoryContext);
/* Scan through table */
scan = heap_beginscan(typrel, SnapshotNow, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
/* Loop through each attribute of the tuple with a domain */
for (i = 0; i < rtc->natts; i++)
{
Datum d;
bool isNull;
Datum conResult;
ExprDoneCond isDone;
d = heap_getattr(tuple, rtc->atts[i], tupdesc, &isNull);
if (isNull)
elog(ERROR, "ALTER DOMAIN: Relation \"%s\" Attribute \"%s\" "
"contains NULL values",
RelationGetRelationName(typrel),
NameStr(*attnumAttName(typrel, rtc->atts[i])));
econtext->domainValue_datum = d;
econtext->domainValue_isNull = isNull;
conResult = ExecEvalExpr(expr, econtext, &isNull, &isDone);
if (!DatumGetBool(conResult))
elog(ERROR, "AlterDomainAddConstraint: Domain %s constraint %s failed",
NameStr(typTup->typname), constr->name);
}
ResetExprContext(econtext);
}
heap_endscan(scan);
FreeExprContext(econtext);
pfree(slot);
/* Hold type lock */
heap_close(typrel, NoLock);
}
/* Clean up */
heap_close(rel, NoLock);
}
/*
* get_rels_with_domain
*
* Fetch all relations / attributes which are using the domain
* while maintaining a RowExclusiveLock on the pg_attribute
* entries.
*
* Generally used for retrieving a list of tests when adding
* new constraints to a domain.
*/
List *
get_rels_with_domain(Oid domainOid)
{
Relation classRel;
HeapTuple classTup;
Relation attRel;
HeapScanDesc classScan;
List *rels = NIL;
/*
* We need to lock the domain rows for the length of the transaction,
* but once all of the tables and the appropriate attributes are
* found we can relese the relation lock.
*/
classRel = relation_openr(RelationRelationName, ExclusiveLock);
attRel = relation_openr(AttributeRelationName, RowExclusiveLock);
classScan = heap_beginscan(classRel, SnapshotNow, 0, NULL);
/* Scan through pg_class for tables */
while ((classTup = heap_getnext(classScan, ForwardScanDirection)) != NULL)
{
bool addToList = true;
int nkeys = 0;
HeapTuple attTup;
HeapScanDesc attScan;
ScanKeyData attKey[2];
Form_pg_class pg_class;
/* Get our pg_class struct */
pg_class = (Form_pg_class) GETSTRUCT(classTup);
/* Fetch attributes from pg_attribute for the relation of the type domainOid */
ScanKeyEntryInitialize(&attKey[nkeys++], 0, Anum_pg_attribute_attrelid,
F_OIDEQ, ObjectIdGetDatum(HeapTupleGetOid(classTup)));
ScanKeyEntryInitialize(&attKey[nkeys++], 0, Anum_pg_attribute_atttypid,
F_OIDEQ, ObjectIdGetDatum(domainOid));
/* Setup to scan pg_attribute */
attScan = heap_beginscan(attRel, SnapshotNow, nkeys, attKey);
/* Scan through pg_attribute for attributes based on the domain */
while ((attTup = heap_getnext(attScan, ForwardScanDirection)) != NULL)
{
relToCheck *rtc;
/* Make the list entries for the relation */
if (addToList)
{
addToList = false;
rtc = (relToCheck *)palloc(sizeof(relToCheck));
rtc->atts = (int *)palloc(sizeof(int) * pg_class->relnatts);
rtc->relOid = HeapTupleGetOid(classTup);
rtc->natts = 0;
rels = lcons((void *)rtc, rels);
}
/* Now add the attribute */
rtc->atts[rtc->natts++] = ((Form_pg_attribute) GETSTRUCT(attTup))->attnum;
}
heap_endscan(attScan);
}
heap_endscan(classScan);
/* Release pg_class, hold pg_attribute for further processing */
relation_close(classRel, ExclusiveLock);
relation_close(attRel, NoLock);
return rels;
}
/*
* domainPermissionCheck
*
* Throw an error if the current user doesn't have permission to modify
* the domain in an ALTER DOMAIN statement, or if the type isn't actually
* a domain.
*/
void
domainPermissionCheck(HeapTuple tup, TypeName *typename)
{
Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
/* Permission check: must own type or its namespace */
if (!pg_type_ownercheck(HeapTupleGetOid(tup), GetUserId()) &&
!pg_namespace_ownercheck(typTup->typnamespace,
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
/* Check that this is actually a domain */
if (typTup->typtype != 'd')
elog(ERROR, "%s is not a domain",
TypeNameToString(typename));
}
/*
*
*/
char *
domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid,
int typMod, Constraint *constr, int *counter, char *domainName)
{
Node *expr;
char *ccsrc;
char *ccbin;
ParseState *pstate;
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 = 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 */
/*
* Return the constraint so the calling routine can perform any additional
* required tests.
*/
return ccbin;
}
/*
* domainCheckForUnsupportedConstraints
*
* Throws an error on constraints that are unsupported by the
* domains.
*/
void
domainCheckForUnsupportedConstraints(Node *newConstraint)
{
Constraint *colDef;
if (nodeTag(newConstraint) == T_FkConstraint)
elog(ERROR, "CREATE DOMAIN / FOREIGN KEY constraints not supported");
colDef = (Constraint *) newConstraint;
switch (colDef->contype)
{
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_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:
break;
}
}
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.115 2002/12/06 03:42:57 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.116 2002/12/06 05:00:16 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1610,8 +1610,8 @@ ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext,
conResult = ExecEvalExpr(constraint->check_expr, econtext, isNull, isDone);
if (!DatumGetBool(conResult))
elog(ERROR, "Domain %s constraint %s failed",
constraint->name, constraint->domname);
elog(ERROR, "ExecEvalConstraintTest: Domain %s constraint %s failed",
constraint->domname, constraint->name);
}
break;
default:
......
......@@ -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.228 2002/12/06 03:42:59 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.229 2002/12/06 05:00:18 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1479,6 +1479,20 @@ _copyAlterTableStmt(AlterTableStmt *from)
return newnode;
}
static AlterDomainStmt *
_copyAlterDomainStmt(AlterDomainStmt *from)
{
AlterDomainStmt *newnode = makeNode(AlterDomainStmt);
COPY_SCALAR_FIELD(subtype);
COPY_NODE_FIELD(typename);
COPY_STRING_FIELD(name);
COPY_NODE_FIELD(def);
COPY_SCALAR_FIELD(behavior);
return newnode;
}
static GrantStmt *
_copyGrantStmt(GrantStmt *from)
{
......@@ -2464,6 +2478,9 @@ copyObject(void *from)
case T_AlterTableStmt:
retval = _copyAlterTableStmt(from);
break;
case T_AlterDomainStmt:
retval = _copyAlterDomainStmt(from);
break;
case T_GrantStmt:
retval = _copyGrantStmt(from);
break;
......
......@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.173 2002/12/06 03:43:08 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.174 2002/12/06 05:00:18 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -525,6 +525,18 @@ _equalAlterTableStmt(AlterTableStmt *a, AlterTableStmt *b)
return true;
}
static bool
_equalAlterDomainStmt(AlterDomainStmt *a, AlterDomainStmt *b)
{
COMPARE_SCALAR_FIELD(subtype);
COMPARE_NODE_FIELD(typename);
COMPARE_STRING_FIELD(name);
COMPARE_NODE_FIELD(def);
COMPARE_SCALAR_FIELD(behavior);
return true;
}
static bool
_equalGrantStmt(GrantStmt *a, GrantStmt *b)
{
......@@ -1621,6 +1633,9 @@ equal(void *a, void *b)
case T_AlterTableStmt:
retval = _equalAlterTableStmt(a, b);
break;
case T_AlterDomainStmt:
retval = _equalAlterDomainStmt(a, b);
break;
case T_GrantStmt:
retval = _equalGrantStmt(a, b);
break;
......
......@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.385 2002/12/06 03:43:08 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.386 2002/12/06 05:00:22 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -128,7 +128,7 @@ static void doNegateFloat(Value *v);
}
%type <node> stmt schema_stmt
AlterDatabaseSetStmt AlterGroupStmt
AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt
AlterTableStmt AlterUserStmt AlterUserSetStmt
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
......@@ -479,6 +479,7 @@ stmtmulti: stmtmulti ';' stmt
stmt :
AlterDatabaseSetStmt
| AlterDomainStmt
| AlterGroupStmt
| AlterTableStmt
| AlterUserStmt
......@@ -3729,6 +3730,53 @@ CreateDomainStmt:
}
;
AlterDomainStmt:
/* ALTER DOMAIN <domain> {SET DEFAULT <expr>|DROP DEFAULT} */
ALTER DOMAIN_P any_name alter_column_default
{
AlterDomainStmt *n = makeNode(AlterDomainStmt);
n->subtype = 'T';
n->typename = $3;
n->def = $4;
$$ = (Node *)n;
}
/* ALTER DOMAIN <domain> DROP NOT NULL */
| ALTER DOMAIN_P any_name DROP NOT NULL_P
{
AlterDomainStmt *n = makeNode(AlterDomainStmt);
n->subtype = 'N';
n->typename = $3;
$$ = (Node *)n;
}
/* ALTER DOMAIN <domain> SET NOT NULL */
| ALTER DOMAIN_P any_name SET NOT NULL_P
{
AlterDomainStmt *n = makeNode(AlterDomainStmt);
n->subtype = 'O';
n->typename = $3;
$$ = (Node *)n;
}
/* ALTER DOMAIN <domain> ADD CONSTRAINT ... */
| ALTER DOMAIN_P any_name ADD TableConstraint
{
AlterDomainStmt *n = makeNode(AlterDomainStmt);
n->subtype = 'C';
n->typename = $3;
n->def = $5;
$$ = (Node *)n;
}
/* ALTER DOMAIN <domain> DROP CONSTRAINT <name> [RESTRICT|CASCADE] */
| ALTER DOMAIN_P any_name DROP CONSTRAINT name opt_drop_behavior
{
AlterDomainStmt *n = makeNode(AlterDomainStmt);
n->subtype = 'X';
n->typename = $3;
n->name = $6;
n->behavior = $7;
$$ = (Node *)n;
}
;
opt_as: AS {}
| /* EMPTY */ {}
;
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.134 2002/12/06 03:43:12 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.135 2002/12/06 05:00:26 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -629,6 +629,14 @@ transformExpr(ParseState *pstate, Node *expr, ConstraintTestValue *domVal)
case T_DomainConstraintValue:
{
/*
* If domVal is NULL, we are not translating an expression that
* can use it
*/
if (domVal == NULL)
elog(ERROR, "VALUE is not allowed in expression for node %d",
nodeTag(expr));
result = (Node *) copyObject(domVal);
break;
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.312 2002/12/06 03:43:13 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.313 2002/12/06 05:00:26 momjian Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
......@@ -1781,7 +1781,7 @@ PostgresMain(int argc, char *argv[], const char *username)
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface ");
puts("$Revision: 1.312 $ $Date: 2002/12/06 03:43:13 $\n");
puts("$Revision: 1.313 $ $Date: 2002/12/06 05:00:26 $\n");
}
/*
......@@ -2319,6 +2319,10 @@ CreateCommandTag(Node *parsetree)
tag = "ALTER TABLE";
break;
case T_AlterDomainStmt:
tag = "ALTER DOMAIN";
break;
case T_GrantStmt:
{
GrantStmt *stmt = (GrantStmt *) parsetree;
......
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.184 2002/12/06 03:43:16 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.185 2002/12/06 05:00:31 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -36,6 +36,7 @@
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "commands/user.h"
#include "commands/vacuum.h"
#include "commands/view.h"
......@@ -560,6 +561,49 @@ ProcessUtility(Node *parsetree,
}
break;
case T_AlterDomainStmt:
{
AlterDomainStmt *stmt = (AlterDomainStmt *) parsetree;
/*
* Some or all of these functions are recursive to cover
* inherited things, so permission checks are done there.
*/
switch (stmt->subtype)
{
case 'T': /* ALTER COLUMN DEFAULT */
/*
* Recursively alter column default for table and,
* if requested, for descendants
*/
AlterDomainDefault(stmt->typename,
stmt->def);
break;
case 'N': /* ALTER COLUMN DROP NOT NULL */
AlterDomainNotNull(stmt->typename,
false);
break;
case 'O': /* ALTER COLUMN SET NOT NULL */
AlterDomainNotNull(stmt->typename,
true);
break;
case 'C': /* ADD CONSTRAINT */
AlterDomainAddConstraint(stmt->typename,
stmt->def);
break;
case 'X': /* DROP CONSTRAINT */
AlterDomainDropConstraint(stmt->typename,
stmt->name,
stmt->behavior);
break;
default: /* oops */
elog(ERROR, "T_AlterDomainStmt: unknown subtype");
break;
}
}
break;
case T_GrantStmt:
ExecuteGrantStmt((GrantStmt *) parsetree);
......
......@@ -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_type.h,v 1.136 2002/12/06 03:43:18 momjian Exp $
* $Id: pg_type.h,v 1.137 2002/12/06 05:00:31 momjian Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
......@@ -550,7 +550,7 @@ extern Oid TypeCreate(const char *typeName,
Oid elementType,
Oid baseType,
const char *defaultTypeValue,
const char *defaultTypeBin,
char *defaultTypeBin,
bool passedByValue,
char alignment,
char storage,
......@@ -558,6 +558,18 @@ extern Oid TypeCreate(const char *typeName,
int32 typNDims,
bool typeNotNull);
extern void
GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
Oid relationOid, /* only for 'c'atalog typeType */
char relationKind,
Oid inputProcedure,
Oid outputProcedure,
Oid elementType,
Oid baseType,
char *defaultTypeBin, /* cooked rep */
bool rebuild);
extern void TypeRename(const char *oldTypeName, Oid typeNamespace,
const char *newTypeName);
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: defrem.h,v 1.48 2002/12/06 03:43:28 momjian Exp $
* $Id: defrem.h,v 1.49 2002/12/06 05:00:31 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -52,14 +52,6 @@ extern void RemoveOperatorById(Oid operOid);
extern void DefineAggregate(List *names, List *parameters);
extern void RemoveAggregate(RemoveAggrStmt *stmt);
/* commands/typecmds.c */
extern void DefineType(List *names, List *parameters);
extern void RemoveType(List *names, DropBehavior behavior);
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void RemoveDomain(List *names, DropBehavior behavior);
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
/* commands/opclasscmds.c */
extern void DefineOpClass(CreateOpClassStmt *stmt);
extern void RemoveOpClass(RemoveOpClassStmt *stmt);
......
/*-------------------------------------------------------------------------
*
* tablecmds.h
* prototypes for tablecmds.c.
*
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: typecmds.h,v 1.1 2002/12/06 05:00:31 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef TYPECMDS_H
#define TYPECMDS_H
extern void DefineType(List *names, List *parameters);
extern void RemoveType(List *names, DropBehavior behavior);
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void RemoveDomain(List *names, DropBehavior behavior);
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
extern void AlterDomainDefault(List *names, Node *defaultRaw);
extern void AlterDomainNotNull(List *names, bool notNull);
extern void AlterDomainAddConstraint(List *names, Node *constr);
extern void AlterDomainDropConstraint(List *names, const char *constrName,
DropBehavior behavior);
#endif /* TYPECMDS_H */
......@@ -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.128 2002/12/06 03:43:28 momjian Exp $
* $Id: nodes.h,v 1.129 2002/12/06 05:00:31 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -157,6 +157,7 @@ typedef enum NodeTag
T_UpdateStmt,
T_SelectStmt,
T_AlterTableStmt,
T_AlterDomainStmt,
T_SetOperationStmt,
T_GrantStmt,
T_ClosePortalStmt,
......
......@@ -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.220 2002/12/06 03:43:28 momjian Exp $
* $Id: parsenodes.h,v 1.221 2002/12/06 05:00:32 momjian Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -827,6 +827,33 @@ typedef struct AlterTableStmt
DropBehavior behavior; /* RESTRICT or CASCADE for DROP cases */
} AlterTableStmt;
/* ----------------------
* Alter Domain
*
* The fields are used in different ways by the different variants of
* this command. Subtypes should match AlterTable subtypes
* ----------------------
*/
typedef struct AlterDomainStmt
{
NodeTag type;
char subtype; /*------------
* T = alter column default
* N = alter column drop not null
* O = alter column set not null
* C = add constraint
* X = drop constraint
* U = change owner
*------------
*/
List *typename; /* table to work on */
char *name; /* column or constraint name to act on, or
* new owner */
Node *def; /* definition of default or constraint */
DropBehavior behavior; /* RESTRICT or CASCADE for DROP cases */
} AlterDomainStmt;
/* ----------------------
* Grant|Revoke Statement
* ----------------------
......
......@@ -115,7 +115,7 @@ INSERT INTO nulltest DEFAULT VALUES;
ERROR: Domain dnotnull does not allow NULL values
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
ERROR: ExecEvalConstraintTest: Domain dcheck constraint $1 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');
......@@ -127,7 +127,7 @@ ERROR: ExecInsert: Fail to add null value in not null attribute col3
INSERT INTO nulltest values ('a', 'b', 'c', NULL, 'd'); -- Good
-- Test copy
COPY nulltest FROM stdin; --fail
ERROR: copy: line 1, Domain $1 constraint dcheck failed
ERROR: copy: line 1, ExecEvalConstraintTest: Domain dcheck constraint $1 failed
lost synchronization with server, resetting connection
SET autocommit TO 'on';
-- Last row is bad
......@@ -158,6 +158,7 @@ ERROR: Domain dnotnull does not allow NULL values
drop table nulltest;
drop domain dnotnull restrict;
drop domain dnull restrict;
drop domain dcheck restrict;
create domain ddef1 int4 DEFAULT 3;
create domain ddef2 oid DEFAULT '12';
-- Type mixing, function returns int8
......@@ -191,7 +192,80 @@ select * from defaulttest;
(4 rows)
drop sequence ddef4_seq;
drop table defaulttest;
drop table defaulttest cascade;
-- Test ALTER DOMAIN .. NOT NULL
create domain dnotnulltest integer;
create table domnotnull
( col1 dnotnulltest
, col2 dnotnulltest
);
insert into domnotnull default values;
alter domain dnotnulltest set not null; -- fails
ERROR: ALTER DOMAIN: Relation "domnotnull" Attribute "col1" contains NULL values
update domnotnull set col1 = 5;
alter domain dnotnulltest set not null; -- fails
ERROR: ALTER DOMAIN: Relation "domnotnull" Attribute "col2" contains NULL values
update domnotnull set col2 = 6;
alter domain dnotnulltest set not null;
alter domain dnotnulltest set not null; -- fails
ERROR: AlterDomain: dnotnulltest is already set to NOT NULL
update domnotnull set col1 = null; -- fails
ERROR: Domain dnotnulltest does not allow NULL values
alter domain dnotnulltest drop not null;
alter domain dnotnulltest drop not null; -- fails
ERROR: AlterDomain: dnotnulltest is already set to NULL
update domnotnull set col1 = null;
drop domain dnotnulltest cascade;
NOTICE: Drop cascades to table domnotnull column col2
NOTICE: Drop cascades to table domnotnull column col1
-- Test ALTER DOMAIN .. DEFAULT ..
create table domdeftest (col1 ddef1);
insert into domdeftest default values;
select * from domdeftest;
col1
------
3
(1 row)
alter domain ddef1 set default '42';
insert into domdeftest default values;
select * from domdeftest;
col1
------
3
42
(2 rows)
alter domain ddef1 drop default;
insert into domdeftest default values;
select * from domdeftest;
col1
------
3
42
(3 rows)
drop table domdeftest;
-- Test ALTER DOMAIN .. CONSTRAINT ..
create domain con as integer;
create table domcontest (col1 con);
insert into domcontest values (1);
insert into domcontest values (2);
alter domain con add constraint t check (VALUE < 1); -- fails
ERROR: AlterDomainAddConstraint: Domain con constraint t failed
alter domain con add constraint t check (VALUE < 34);
alter domain con add check (VALUE > 0);
insert into domcontest values (-5); -- fails
ERROR: ExecEvalConstraintTest: Domain con constraint $1 failed
insert into domcontest values (42); -- fails
ERROR: ExecEvalConstraintTest: Domain con constraint t failed
insert into domcontest values (5);
alter domain con drop constraint t;
insert into domcontest values (-5); --fails
ERROR: ExecEvalConstraintTest: Domain con constraint $1 failed
insert into domcontest values (42);
-- cleanup
drop domain ddef1 restrict;
drop domain ddef2 restrict;
drop domain ddef3 restrict;
......
......@@ -14,8 +14,8 @@ float8/.*-qnx=float8-exp-three-digits
float8/alpha.*-dec-osf.*:cc=float8-fp-exception
float8/i.86-pc-cygwin=float8-small-is-zero
geometry/.*-darwin=geometry-positive-zeros
geometry/i.86-.*-freebsd4.[0-5]=geometry-positive-zeros
geometry/alpha.*-freebsd4.[0-5]=geometry-positive-zeros
geometry/i.86-.*-freebsd4.[0-7]=geometry-positive-zeros
geometry/alpha.*-freebsd4.[0-7]=geometry-positive-zeros
geometry/i.86-.*-openbsd=geometry-positive-zeros
geometry/sparc-.*-openbsd=geometry-positive-zeros
geometry/.*-netbsd1.[0-5]=geometry-positive-zeros
......
......@@ -127,6 +127,7 @@ SELECT cast(col4 as dnotnull) from nulltest; -- fail
drop table nulltest;
drop domain dnotnull restrict;
drop domain dnull restrict;
drop domain dcheck restrict;
create domain ddef1 int4 DEFAULT 3;
......@@ -159,7 +160,71 @@ COPY defaulttest(col5) FROM stdin;
select * from defaulttest;
drop sequence ddef4_seq;
drop table defaulttest;
drop table defaulttest cascade;
-- Test ALTER DOMAIN .. NOT NULL
create domain dnotnulltest integer;
create table domnotnull
( col1 dnotnulltest
, col2 dnotnulltest
);
insert into domnotnull default values;
alter domain dnotnulltest set not null; -- fails
update domnotnull set col1 = 5;
alter domain dnotnulltest set not null; -- fails
update domnotnull set col2 = 6;
alter domain dnotnulltest set not null;
alter domain dnotnulltest set not null; -- fails
update domnotnull set col1 = null; -- fails
alter domain dnotnulltest drop not null;
alter domain dnotnulltest drop not null; -- fails
update domnotnull set col1 = null;
drop domain dnotnulltest cascade;
-- Test ALTER DOMAIN .. DEFAULT ..
create table domdeftest (col1 ddef1);
insert into domdeftest default values;
select * from domdeftest;
alter domain ddef1 set default '42';
insert into domdeftest default values;
select * from domdeftest;
alter domain ddef1 drop default;
insert into domdeftest default values;
select * from domdeftest;
drop table domdeftest;
-- Test ALTER DOMAIN .. CONSTRAINT ..
create domain con as integer;
create table domcontest (col1 con);
insert into domcontest values (1);
insert into domcontest values (2);
alter domain con add constraint t check (VALUE < 1); -- fails
alter domain con add constraint t check (VALUE < 34);
alter domain con add check (VALUE > 0);
insert into domcontest values (-5); -- fails
insert into domcontest values (42); -- fails
insert into domcontest values (5);
alter domain con drop constraint t;
insert into domcontest values (-5); --fails
insert into domcontest values (42);
-- cleanup
drop domain ddef1 restrict;
drop domain ddef2 restrict;
drop domain ddef3 restrict;
......
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