mirror of
https://github.com/postgres/postgres.git
synced 2025-07-17 06:41:09 +03:00
Second phase of committing Rod Taylor's pg_depend/pg_constraint patch.
pg_relcheck is gone; CHECK, UNIQUE, PRIMARY KEY, and FOREIGN KEY constraints all have real live entries in pg_constraint. pg_depend exists, and RESTRICT/CASCADE options work on most kinds of DROP; however, pg_depend is not yet very well populated with dependencies. (Most of the ones that are present at this point just replace formerly hardwired associations, such as the implicit drop of a relation's pg_type entry when the relation is dropped.) Need to add more logic to create dependency entries, improve pg_dump to dump constraints in place of indexes and triggers, and add some regression tests.
This commit is contained in:
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.2 2002/04/27 03:45:00 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.3 2002/07/12 18:43:15 tgl Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@ -24,10 +24,10 @@
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_aggregate.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "commands/comment.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "miscadmin.h"
|
||||
#include "parser/parse_func.h"
|
||||
@ -141,13 +141,19 @@ DefineAggregate(List *names, List *parameters)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RemoveAggregate
|
||||
* Deletes an aggregate.
|
||||
*/
|
||||
void
|
||||
RemoveAggregate(List *aggName, TypeName *aggType)
|
||||
RemoveAggregate(RemoveAggrStmt *stmt)
|
||||
{
|
||||
Relation relation;
|
||||
HeapTuple tup;
|
||||
List *aggName = stmt->aggname;
|
||||
TypeName *aggType = stmt->aggtype;
|
||||
Oid basetypeID;
|
||||
Oid procOid;
|
||||
HeapTuple tup;
|
||||
ObjectAddress object;
|
||||
|
||||
/*
|
||||
* if a basetype is passed in, then attempt to find an aggregate for
|
||||
@ -164,8 +170,9 @@ RemoveAggregate(List *aggName, TypeName *aggType)
|
||||
|
||||
procOid = find_aggregate_func("RemoveAggregate", aggName, basetypeID);
|
||||
|
||||
relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* Find the function tuple, do permissions and validity checks
|
||||
*/
|
||||
tup = SearchSysCache(PROCOID,
|
||||
ObjectIdGetDatum(procOid),
|
||||
0, 0, 0);
|
||||
@ -179,30 +186,16 @@ RemoveAggregate(List *aggName, TypeName *aggType)
|
||||
GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(aggName));
|
||||
|
||||
/* Delete any comments associated with this function */
|
||||
DeleteComments(procOid, RelationGetRelid(relation));
|
||||
|
||||
/* Remove the pg_proc tuple */
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
/* find_aggregate_func already checked it is an aggregate */
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
heap_close(relation, RowExclusiveLock);
|
||||
/*
|
||||
* Do the deletion
|
||||
*/
|
||||
object.classId = RelOid_pg_proc;
|
||||
object.objectId = procOid;
|
||||
object.objectSubId = 0;
|
||||
|
||||
/* Remove the pg_aggregate tuple */
|
||||
|
||||
relation = heap_openr(AggregateRelationName, RowExclusiveLock);
|
||||
|
||||
tup = SearchSysCache(AGGFNOID,
|
||||
ObjectIdGetDatum(procOid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||
elog(ERROR, "RemoveAggregate: couldn't find pg_aggregate tuple for %s",
|
||||
NameListToString(aggName));
|
||||
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
heap_close(relation, RowExclusiveLock);
|
||||
performDeletion(&object, stmt->behavior);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.82 2002/06/20 20:29:26 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.83 2002/07/12 18:43:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
#include "access/genam.h"
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/index.h"
|
||||
#include "catalog/pg_index.h"
|
||||
@ -64,6 +65,7 @@ cluster(RangeVar *oldrelation, char *oldindexname)
|
||||
OldIndex;
|
||||
char NewHeapName[NAMEDATALEN];
|
||||
char NewIndexName[NAMEDATALEN];
|
||||
ObjectAddress object;
|
||||
|
||||
/*
|
||||
* We grab exclusive access to the target rel and index for the
|
||||
@ -119,9 +121,14 @@ cluster(RangeVar *oldrelation, char *oldindexname)
|
||||
CommandCounterIncrement();
|
||||
|
||||
/* Destroy old heap (along with its index) and rename new. */
|
||||
heap_drop_with_catalog(OIDOldHeap, allowSystemTableMods);
|
||||
object.classId = RelOid_pg_class;
|
||||
object.objectId = OIDOldHeap;
|
||||
object.objectSubId = 0;
|
||||
|
||||
CommandCounterIncrement();
|
||||
/* XXX better to use DROP_CASCADE here? */
|
||||
performDeletion(&object, DROP_RESTRICT);
|
||||
|
||||
/* performDeletion does CommandCounterIncrement at end */
|
||||
|
||||
renamerel(OIDNewHeap, oldrelation->relname);
|
||||
|
||||
@ -198,6 +205,7 @@ copy_index(Oid OIDOldIndex, Oid OIDNewHeap, const char *NewIndexName)
|
||||
OldIndex->rd_rel->relam,
|
||||
OldIndex->rd_index->indclass,
|
||||
OldIndex->rd_index->indisprimary,
|
||||
false, /* XXX losing constraint status */
|
||||
allowSystemTableMods);
|
||||
|
||||
setRelhasindex(OIDNewHeap, true,
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.49 2002/06/20 20:51:45 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.50 2002/07/12 18:43:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -225,38 +225,45 @@ CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
|
||||
}
|
||||
|
||||
/*
|
||||
* DeleteComments --
|
||||
* DeleteComments -- remove comments for an object
|
||||
*
|
||||
* This routine is used to purge all comments associated with an object,
|
||||
* regardless of their objsubid. It is called, for example, when a relation
|
||||
* is destroyed.
|
||||
* If subid is nonzero then only comments matching it will be removed.
|
||||
* If subid is zero, all comments matching the oid/classoid will be removed
|
||||
* (this corresponds to deleting a whole object).
|
||||
*/
|
||||
void
|
||||
DeleteComments(Oid oid, Oid classoid)
|
||||
DeleteComments(Oid oid, Oid classoid, int32 subid)
|
||||
{
|
||||
Relation description;
|
||||
ScanKeyData skey[2];
|
||||
ScanKeyData skey[3];
|
||||
int nkeys;
|
||||
SysScanDesc sd;
|
||||
HeapTuple oldtuple;
|
||||
|
||||
/* Use the index to search for all matching old tuples */
|
||||
|
||||
ScanKeyEntryInitialize(&skey[0],
|
||||
(bits16) 0x0,
|
||||
(AttrNumber) 1,
|
||||
(RegProcedure) F_OIDEQ,
|
||||
ScanKeyEntryInitialize(&skey[0], 0x0,
|
||||
Anum_pg_description_objoid, F_OIDEQ,
|
||||
ObjectIdGetDatum(oid));
|
||||
|
||||
ScanKeyEntryInitialize(&skey[1],
|
||||
(bits16) 0x0,
|
||||
(AttrNumber) 2,
|
||||
(RegProcedure) F_OIDEQ,
|
||||
ScanKeyEntryInitialize(&skey[1], 0x0,
|
||||
Anum_pg_description_classoid, F_OIDEQ,
|
||||
ObjectIdGetDatum(classoid));
|
||||
|
||||
if (subid != 0)
|
||||
{
|
||||
ScanKeyEntryInitialize(&skey[2], 0x0,
|
||||
Anum_pg_description_objsubid, F_INT4EQ,
|
||||
Int32GetDatum(subid));
|
||||
nkeys = 3;
|
||||
}
|
||||
else
|
||||
nkeys = 2;
|
||||
|
||||
description = heap_openr(DescriptionRelationName, RowExclusiveLock);
|
||||
|
||||
sd = systable_beginscan(description, DescriptionObjIndex, true,
|
||||
SnapshotNow, 2, skey);
|
||||
SnapshotNow, nkeys, skey);
|
||||
|
||||
while ((oldtuple = systable_getnext(sd)) != NULL)
|
||||
{
|
||||
@ -266,7 +273,7 @@ DeleteComments(Oid oid, Oid classoid)
|
||||
/* Done */
|
||||
|
||||
systable_endscan(sd);
|
||||
heap_close(description, NoLock);
|
||||
heap_close(description, RowExclusiveLock);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.95 2002/06/20 20:29:27 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.96 2002/07/12 18:43:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -456,8 +456,13 @@ dropdb(const char *dbname)
|
||||
|
||||
heap_endscan(pgdbscan);
|
||||
|
||||
/* Delete any comments associated with the database */
|
||||
DeleteComments(db_id, RelationGetRelid(pgdbrel));
|
||||
/*
|
||||
* Delete any comments associated with the database
|
||||
*
|
||||
* NOTE: this is probably dead code since any such comments should have
|
||||
* been in that database, not mine.
|
||||
*/
|
||||
DeleteComments(db_id, RelationGetRelid(pgdbrel), 0);
|
||||
|
||||
/*
|
||||
* Close pg_database, but keep exclusive lock till commit to ensure
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.7 2002/06/20 20:29:27 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.8 2002/07/12 18:43:16 tgl Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* These routines take the parse tree and pick out the
|
||||
@ -33,11 +33,11 @@
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_language.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/comment.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "miscadmin.h"
|
||||
#include "optimizer/cost.h"
|
||||
@ -532,25 +532,22 @@ CreateFunction(CreateFunctionStmt *stmt)
|
||||
/*
|
||||
* RemoveFunction
|
||||
* Deletes a function.
|
||||
*
|
||||
* Exceptions:
|
||||
* BadArg if name is invalid.
|
||||
* "ERROR" if function nonexistent.
|
||||
* ...
|
||||
*/
|
||||
void
|
||||
RemoveFunction(List *functionName, /* function name to be removed */
|
||||
List *argTypes) /* list of TypeName nodes */
|
||||
RemoveFunction(RemoveFuncStmt *stmt)
|
||||
{
|
||||
List *functionName = stmt->funcname;
|
||||
List *argTypes = stmt->args; /* list of TypeName nodes */
|
||||
Oid funcOid;
|
||||
Relation relation;
|
||||
HeapTuple tup;
|
||||
ObjectAddress object;
|
||||
|
||||
/*
|
||||
* Find the function, do permissions and validity checks
|
||||
*/
|
||||
funcOid = LookupFuncNameTypeNames(functionName, argTypes,
|
||||
true, "RemoveFunction");
|
||||
|
||||
relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
|
||||
|
||||
tup = SearchSysCache(PROCOID,
|
||||
ObjectIdGetDatum(funcOid),
|
||||
0, 0, 0);
|
||||
@ -576,12 +573,69 @@ RemoveFunction(List *functionName, /* function name to be removed */
|
||||
NameListToString(functionName));
|
||||
}
|
||||
|
||||
/* Delete any comments associated with this function */
|
||||
DeleteComments(funcOid, RelationGetRelid(relation));
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
/*
|
||||
* Do the deletion
|
||||
*/
|
||||
object.classId = RelOid_pg_proc;
|
||||
object.objectId = funcOid;
|
||||
object.objectSubId = 0;
|
||||
|
||||
performDeletion(&object, stmt->behavior);
|
||||
}
|
||||
|
||||
/*
|
||||
* Guts of function deletion.
|
||||
*
|
||||
* Note: this is also used for aggregate deletion, since the OIDs of
|
||||
* both functions and aggregates point to pg_proc.
|
||||
*/
|
||||
void
|
||||
RemoveFunctionById(Oid funcOid)
|
||||
{
|
||||
Relation relation;
|
||||
HeapTuple tup;
|
||||
bool isagg;
|
||||
|
||||
/*
|
||||
* Delete the pg_proc tuple.
|
||||
*/
|
||||
relation = heap_openr(ProcedureRelationName, RowExclusiveLock);
|
||||
|
||||
tup = SearchSysCache(PROCOID,
|
||||
ObjectIdGetDatum(funcOid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||
elog(ERROR, "RemoveFunctionById: couldn't find tuple for function %u",
|
||||
funcOid);
|
||||
|
||||
isagg = ((Form_pg_proc) GETSTRUCT(tup))->proisagg;
|
||||
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
heap_close(relation, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* If there's a pg_aggregate tuple, delete that too.
|
||||
*/
|
||||
if (isagg)
|
||||
{
|
||||
relation = heap_openr(AggregateRelationName, RowExclusiveLock);
|
||||
|
||||
tup = SearchSysCache(AGGFNOID,
|
||||
ObjectIdGetDatum(funcOid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||
elog(ERROR, "RemoveFunctionById: couldn't find pg_aggregate tuple for %u",
|
||||
funcOid);
|
||||
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
heap_close(relation, RowExclusiveLock);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.76 2002/07/01 15:27:45 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.77 2002/07/12 18:43:16 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -18,6 +18,7 @@
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/index.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_opclass.h"
|
||||
@ -68,6 +69,7 @@ DefineIndex(RangeVar *heapRelation,
|
||||
List *attributeList,
|
||||
bool unique,
|
||||
bool primary,
|
||||
bool isconstraint,
|
||||
Expr *predicate,
|
||||
List *rangetable)
|
||||
{
|
||||
@ -208,7 +210,7 @@ DefineIndex(RangeVar *heapRelation,
|
||||
|
||||
index_create(relationId, indexRelationName,
|
||||
indexInfo, accessMethodId, classObjectId,
|
||||
primary, allowSystemTableMods);
|
||||
primary, isconstraint, allowSystemTableMods);
|
||||
|
||||
/*
|
||||
* We update the relation's pg_class tuple even if it already has
|
||||
@ -566,6 +568,7 @@ RemoveIndex(RangeVar *relation, DropBehavior behavior)
|
||||
{
|
||||
Oid indOid;
|
||||
HeapTuple tuple;
|
||||
ObjectAddress object;
|
||||
|
||||
indOid = RangeVarGetRelid(relation, false);
|
||||
tuple = SearchSysCache(RELOID,
|
||||
@ -580,7 +583,11 @@ RemoveIndex(RangeVar *relation, DropBehavior behavior)
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
index_drop(indOid);
|
||||
object.classId = RelOid_pg_class;
|
||||
object.objectId = indOid;
|
||||
object.objectSubId = 0;
|
||||
|
||||
performDeletion(&object, behavior);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.4 2002/07/01 15:27:46 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.5 2002/07/12 18:43:16 tgl Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@ -36,9 +36,9 @@
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_operator.h"
|
||||
#include "commands/comment.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "miscadmin.h"
|
||||
#include "parser/parse_oper.h"
|
||||
@ -217,17 +217,15 @@ RemoveOperator(RemoveOperStmt *stmt)
|
||||
TypeName *typeName1 = (TypeName *) lfirst(stmt->args);
|
||||
TypeName *typeName2 = (TypeName *) lsecond(stmt->args);
|
||||
Oid operOid;
|
||||
Relation relation;
|
||||
HeapTuple tup;
|
||||
ObjectAddress object;
|
||||
|
||||
operOid = LookupOperNameTypeNames(operatorName, typeName1, typeName2,
|
||||
"RemoveOperator");
|
||||
|
||||
relation = heap_openr(OperatorRelationName, RowExclusiveLock);
|
||||
|
||||
tup = SearchSysCacheCopy(OPEROID,
|
||||
ObjectIdGetDatum(operOid),
|
||||
0, 0, 0);
|
||||
tup = SearchSysCache(OPEROID,
|
||||
ObjectIdGetDatum(operOid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||
elog(ERROR, "RemoveOperator: failed to find tuple for operator '%s'",
|
||||
NameListToString(operatorName));
|
||||
@ -238,11 +236,39 @@ RemoveOperator(RemoveOperStmt *stmt)
|
||||
GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(operatorName));
|
||||
|
||||
/* Delete any comments associated with this operator */
|
||||
DeleteComments(operOid, RelationGetRelid(relation));
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
/*
|
||||
* Do the deletion
|
||||
*/
|
||||
object.classId = get_system_catalog_relid(OperatorRelationName);
|
||||
object.objectId = operOid;
|
||||
object.objectSubId = 0;
|
||||
|
||||
performDeletion(&object, stmt->behavior);
|
||||
}
|
||||
|
||||
/*
|
||||
* Guts of operator deletion.
|
||||
*/
|
||||
void
|
||||
RemoveOperatorById(Oid operOid)
|
||||
{
|
||||
Relation relation;
|
||||
HeapTuple tup;
|
||||
|
||||
relation = heap_openr(OperatorRelationName, RowExclusiveLock);
|
||||
|
||||
tup = SearchSysCache(OPEROID,
|
||||
ObjectIdGetDatum(operOid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup)) /* should not happen */
|
||||
elog(ERROR, "RemoveOperatorById: failed to find tuple for operator %u",
|
||||
operOid);
|
||||
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
|
||||
heap_freetuple(tup);
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
heap_close(relation, RowExclusiveLock);
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.34 2002/06/20 20:29:27 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.35 2002/07/12 18:43:16 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/indexing.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_language.h"
|
||||
@ -140,7 +141,7 @@ DropProceduralLanguage(DropPLangStmt *stmt)
|
||||
{
|
||||
char languageName[NAMEDATALEN];
|
||||
HeapTuple langTup;
|
||||
Relation rel;
|
||||
ObjectAddress object;
|
||||
|
||||
/*
|
||||
* Check permission
|
||||
@ -155,11 +156,9 @@ DropProceduralLanguage(DropPLangStmt *stmt)
|
||||
*/
|
||||
case_translate_language_name(stmt->plname, languageName);
|
||||
|
||||
rel = heap_openr(LanguageRelationName, RowExclusiveLock);
|
||||
|
||||
langTup = SearchSysCacheCopy(LANGNAME,
|
||||
PointerGetDatum(languageName),
|
||||
0, 0, 0);
|
||||
langTup = SearchSysCache(LANGNAME,
|
||||
CStringGetDatum(languageName),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(langTup))
|
||||
elog(ERROR, "Language %s doesn't exist", languageName);
|
||||
|
||||
@ -167,8 +166,39 @@ DropProceduralLanguage(DropPLangStmt *stmt)
|
||||
elog(ERROR, "Language %s isn't a created procedural language",
|
||||
languageName);
|
||||
|
||||
object.classId = get_system_catalog_relid(LanguageRelationName);
|
||||
object.objectId = langTup->t_data->t_oid;
|
||||
object.objectSubId = 0;
|
||||
|
||||
ReleaseSysCache(langTup);
|
||||
|
||||
/*
|
||||
* Do the deletion
|
||||
*/
|
||||
performDeletion(&object, stmt->behavior);
|
||||
}
|
||||
|
||||
/*
|
||||
* Guts of language dropping.
|
||||
*/
|
||||
void
|
||||
DropProceduralLanguageById(Oid langOid)
|
||||
{
|
||||
Relation rel;
|
||||
HeapTuple langTup;
|
||||
|
||||
rel = heap_openr(LanguageRelationName, RowExclusiveLock);
|
||||
|
||||
langTup = SearchSysCache(LANGOID,
|
||||
ObjectIdGetDatum(langOid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(langTup))
|
||||
elog(ERROR, "DropProceduralLanguageById: language %u not found",
|
||||
langOid);
|
||||
|
||||
simple_heap_delete(rel, &langTup->t_self);
|
||||
|
||||
heap_freetuple(langTup);
|
||||
ReleaseSysCache(langTup);
|
||||
|
||||
heap_close(rel, RowExclusiveLock);
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.19 2002/07/06 20:16:35 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.20 2002/07/12 18:43:16 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -18,11 +18,13 @@
|
||||
#include "access/tuptoaster.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/index.h"
|
||||
#include "catalog/indexing.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_attrdef.h"
|
||||
#include "catalog/pg_constraint.h"
|
||||
#include "catalog/pg_inherits.h"
|
||||
#include "catalog/pg_namespace.h"
|
||||
#include "catalog/pg_opclass.h"
|
||||
@ -36,6 +38,7 @@
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/prep.h"
|
||||
#include "parser/gramparse.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_relation.h"
|
||||
@ -57,6 +60,13 @@ static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
|
||||
static void drop_default(Oid relid, int16 attnum);
|
||||
static void CheckTupleType(Form_pg_class tuple_class);
|
||||
static bool needs_toast_table(Relation rel);
|
||||
static void validateForeignKeyConstraint(FkConstraint *fkconstraint,
|
||||
Relation rel, Relation pkrel);
|
||||
static Oid createForeignKeyConstraint(Relation rel, Relation pkrel,
|
||||
FkConstraint *fkconstraint);
|
||||
static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
|
||||
Oid constrOid);
|
||||
static char *fkMatchTypeToString(char match_type);
|
||||
|
||||
/* Used by attribute and relation renaming routines: */
|
||||
|
||||
@ -147,6 +157,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
||||
ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) *
|
||||
sizeof(ConstrCheck));
|
||||
int ncheck = 0;
|
||||
int constr_name_ctr = 0;
|
||||
|
||||
foreach(listptr, old_constraints)
|
||||
{
|
||||
@ -167,8 +178,16 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Generate a constraint name. NB: this should match the
|
||||
* form of names that GenerateConstraintName() may produce
|
||||
* for names added later. We are assured that there is
|
||||
* no name conflict, because MergeAttributes() did not pass
|
||||
* back any names of this form.
|
||||
*/
|
||||
check[ncheck].ccname = (char *) palloc(NAMEDATALEN);
|
||||
snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1);
|
||||
snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d",
|
||||
++constr_name_ctr);
|
||||
}
|
||||
Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL);
|
||||
check[ncheck].ccbin = pstrdup(cdef->cooked_expr);
|
||||
@ -262,21 +281,20 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
||||
/*
|
||||
* RemoveRelation
|
||||
* Deletes a relation.
|
||||
*
|
||||
* Exceptions:
|
||||
* BadArg if name is invalid.
|
||||
*
|
||||
* Note:
|
||||
* If the relation has indices defined on it, then the index relations
|
||||
* themselves will be destroyed, too.
|
||||
*/
|
||||
void
|
||||
RemoveRelation(const RangeVar *relation, DropBehavior behavior)
|
||||
{
|
||||
Oid relOid;
|
||||
ObjectAddress object;
|
||||
|
||||
relOid = RangeVarGetRelid(relation, false);
|
||||
heap_drop_with_catalog(relOid, allowSystemTableMods);
|
||||
|
||||
object.classId = RelOid_pg_class;
|
||||
object.objectId = relOid;
|
||||
object.objectSubId = 0;
|
||||
|
||||
performDeletion(&object, behavior);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -580,7 +598,13 @@ MergeAttributes(List *schema, List *supers, bool istemp,
|
||||
Node *expr;
|
||||
|
||||
cdef->contype = CONSTR_CHECK;
|
||||
if (check[i].ccname[0] == '$')
|
||||
/*
|
||||
* Do not inherit generated constraint names, since they
|
||||
* might conflict across multiple inheritance parents.
|
||||
* (But conflicts between user-assigned names will cause
|
||||
* an error.)
|
||||
*/
|
||||
if (ConstraintNameIsGenerated(check[i].ccname))
|
||||
cdef->name = NULL;
|
||||
else
|
||||
cdef->name = pstrdup(check[i].ccname);
|
||||
@ -684,7 +708,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
|
||||
/*
|
||||
* complementary static functions for MergeAttributes().
|
||||
*
|
||||
* Varattnos of pg_relcheck.rcbin must be rewritten when subclasses inherit
|
||||
* Varattnos of pg_constraint.conbin must be rewritten when subclasses inherit
|
||||
* constraints from parent classes, since the inherited attributes could
|
||||
* be given different column numbers in multiple-inheritance cases.
|
||||
*
|
||||
@ -747,7 +771,8 @@ StoreCatalogInheritance(Oid relationId, List *supers)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Catalog INHERITS information using direct ancestors only.
|
||||
* Store INHERITS information in pg_inherits using direct ancestors only.
|
||||
* Also enter dependencies on the direct ancestors.
|
||||
*/
|
||||
relation = heap_openr(InheritsRelationName, RowExclusiveLock);
|
||||
desc = RelationGetDescr(relation);
|
||||
@ -758,6 +783,8 @@ StoreCatalogInheritance(Oid relationId, List *supers)
|
||||
Oid entryOid = lfirsti(entry);
|
||||
Datum datum[Natts_pg_inherits];
|
||||
char nullarr[Natts_pg_inherits];
|
||||
ObjectAddress childobject,
|
||||
parentobject;
|
||||
|
||||
datum[0] = ObjectIdGetDatum(relationId); /* inhrel */
|
||||
datum[1] = ObjectIdGetDatum(entryOid); /* inhparent */
|
||||
@ -782,6 +809,18 @@ StoreCatalogInheritance(Oid relationId, List *supers)
|
||||
|
||||
heap_freetuple(tuple);
|
||||
|
||||
/*
|
||||
* Store a dependency too
|
||||
*/
|
||||
parentobject.classId = RelOid_pg_class;
|
||||
parentobject.objectId = entryOid;
|
||||
parentobject.objectSubId = 0;
|
||||
childobject.classId = RelOid_pg_class;
|
||||
childobject.objectId = relationId;
|
||||
childobject.objectSubId = 0;
|
||||
|
||||
recordDependencyOn(&childobject, &parentobject, DEPENDENCY_NORMAL);
|
||||
|
||||
seqNumber += 1;
|
||||
}
|
||||
|
||||
@ -2299,6 +2338,7 @@ AlterTableAddConstraint(Oid myrelid,
|
||||
{
|
||||
Relation rel;
|
||||
List *listptr;
|
||||
int counter = 0;
|
||||
|
||||
/*
|
||||
* Grab an exclusive lock on the target table, which we will NOT
|
||||
@ -2343,7 +2383,12 @@ AlterTableAddConstraint(Oid myrelid,
|
||||
|
||||
foreach(listptr, newConstraints)
|
||||
{
|
||||
Node *newConstraint = lfirst(listptr);
|
||||
/*
|
||||
* copy is because we may destructively alter the node below
|
||||
* by inserting a generated name; this name is not necessarily
|
||||
* correct for children or parents.
|
||||
*/
|
||||
Node *newConstraint = copyObject(lfirst(listptr));
|
||||
|
||||
switch (nodeTag(newConstraint))
|
||||
{
|
||||
@ -2370,12 +2415,23 @@ AlterTableAddConstraint(Oid myrelid,
|
||||
RangeTblEntry *rte;
|
||||
List *qual;
|
||||
Node *expr;
|
||||
char *name;
|
||||
|
||||
/*
|
||||
* Assign or validate constraint name
|
||||
*/
|
||||
if (constr->name)
|
||||
name = constr->name;
|
||||
{
|
||||
if (ConstraintNameIsUsed(RelationGetRelid(rel),
|
||||
RelationGetNamespace(rel),
|
||||
constr->name))
|
||||
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
|
||||
constr->name,
|
||||
RelationGetRelationName(rel));
|
||||
}
|
||||
else
|
||||
name = "<unnamed>";
|
||||
constr->name = GenerateConstraintName(RelationGetRelid(rel),
|
||||
RelationGetNamespace(rel),
|
||||
&counter);
|
||||
|
||||
/*
|
||||
* We need to make a parse state and range
|
||||
@ -2458,7 +2514,8 @@ AlterTableAddConstraint(Oid myrelid,
|
||||
pfree(slot);
|
||||
|
||||
if (!successful)
|
||||
elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name);
|
||||
elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s",
|
||||
constr->name);
|
||||
|
||||
/*
|
||||
* Call AddRelationRawConstraints to do
|
||||
@ -2481,17 +2538,32 @@ AlterTableAddConstraint(Oid myrelid,
|
||||
{
|
||||
FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
|
||||
Relation pkrel;
|
||||
HeapScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
Trigger trig;
|
||||
List *list;
|
||||
int count;
|
||||
Oid constrOid;
|
||||
|
||||
/*
|
||||
* Assign or validate constraint name
|
||||
*/
|
||||
if (fkconstraint->constr_name)
|
||||
{
|
||||
if (ConstraintNameIsUsed(RelationGetRelid(rel),
|
||||
RelationGetNamespace(rel),
|
||||
fkconstraint->constr_name))
|
||||
elog(ERROR, "constraint \"%s\" already exists for relation \"%s\"",
|
||||
fkconstraint->constr_name,
|
||||
RelationGetRelationName(rel));
|
||||
}
|
||||
else
|
||||
fkconstraint->constr_name = GenerateConstraintName(RelationGetRelid(rel),
|
||||
RelationGetNamespace(rel),
|
||||
&counter);
|
||||
|
||||
/*
|
||||
* Grab an exclusive lock on the pk table, so that
|
||||
* someone doesn't delete rows out from under us.
|
||||
*
|
||||
* XXX wouldn't a lesser lock be sufficient?
|
||||
* (Although a lesser lock would do for that purpose,
|
||||
* we'll need exclusive lock anyway to add triggers
|
||||
* to the pk table; trying to start with a lesser lock
|
||||
* will just create a risk of deadlock.)
|
||||
*/
|
||||
pkrel = heap_openrv(fkconstraint->pktable,
|
||||
AccessExclusiveLock);
|
||||
@ -2500,100 +2572,46 @@ AlterTableAddConstraint(Oid myrelid,
|
||||
* Validity checks
|
||||
*/
|
||||
if (pkrel->rd_rel->relkind != RELKIND_RELATION)
|
||||
elog(ERROR, "referenced table \"%s\" not a relation",
|
||||
fkconstraint->pktable->relname);
|
||||
elog(ERROR, "referenced relation \"%s\" is not a table",
|
||||
RelationGetRelationName(pkrel));
|
||||
|
||||
if (!allowSystemTableMods
|
||||
&& IsSystemRelation(pkrel))
|
||||
elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
|
||||
RelationGetRelationName(pkrel));
|
||||
|
||||
/* XXX shouldn't there be a permission check too? */
|
||||
|
||||
if (isTempNamespace(RelationGetNamespace(pkrel)) &&
|
||||
!isTempNamespace(RelationGetNamespace(rel)))
|
||||
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint.");
|
||||
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint");
|
||||
|
||||
/*
|
||||
* First we check for limited correctness of the
|
||||
* constraint.
|
||||
* Check that the constraint is satisfied by existing
|
||||
* rows (we can skip this during table creation).
|
||||
*
|
||||
* NOTE: we assume parser has already checked for
|
||||
* existence of an appropriate unique index on the
|
||||
* referenced relation, and that the column datatypes
|
||||
* are comparable.
|
||||
*
|
||||
* Scan through each tuple, calling RI_FKey_check_ins
|
||||
* (insert trigger) as if that tuple had just been
|
||||
* inserted. If any of those fail, it should
|
||||
* elog(ERROR) and that's that.
|
||||
*/
|
||||
MemSet(&trig, 0, sizeof(trig));
|
||||
trig.tgoid = InvalidOid;
|
||||
if (fkconstraint->constr_name)
|
||||
trig.tgname = fkconstraint->constr_name;
|
||||
else
|
||||
trig.tgname = "<unknown>";
|
||||
trig.tgenabled = TRUE;
|
||||
trig.tgisconstraint = TRUE;
|
||||
trig.tgconstrrelid = RelationGetRelid(pkrel);
|
||||
trig.tgdeferrable = FALSE;
|
||||
trig.tginitdeferred = FALSE;
|
||||
if (!fkconstraint->skip_validation)
|
||||
validateForeignKeyConstraint(fkconstraint, rel, pkrel);
|
||||
|
||||
trig.tgargs = (char **) palloc(
|
||||
sizeof(char *) * (4 + length(fkconstraint->fk_attrs)
|
||||
+ length(fkconstraint->pk_attrs)));
|
||||
/*
|
||||
* Record the FK constraint in pg_constraint.
|
||||
*/
|
||||
constrOid = createForeignKeyConstraint(rel, pkrel,
|
||||
fkconstraint);
|
||||
|
||||
trig.tgargs[0] = trig.tgname;
|
||||
trig.tgargs[1] = RelationGetRelationName(rel);
|
||||
trig.tgargs[2] = RelationGetRelationName(pkrel);
|
||||
trig.tgargs[3] = fkconstraint->match_type;
|
||||
count = 4;
|
||||
foreach(list, fkconstraint->fk_attrs)
|
||||
{
|
||||
Ident *fk_at = lfirst(list);
|
||||
|
||||
trig.tgargs[count] = fk_at->name;
|
||||
count += 2;
|
||||
}
|
||||
count = 5;
|
||||
foreach(list, fkconstraint->pk_attrs)
|
||||
{
|
||||
Ident *pk_at = lfirst(list);
|
||||
|
||||
trig.tgargs[count] = pk_at->name;
|
||||
count += 2;
|
||||
}
|
||||
trig.tgnargs = count - 1;
|
||||
|
||||
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
|
||||
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
||||
{
|
||||
/* Make a call to the check function */
|
||||
|
||||
/*
|
||||
* No parameters are passed, but we do set a
|
||||
* context
|
||||
*/
|
||||
FunctionCallInfoData fcinfo;
|
||||
TriggerData trigdata;
|
||||
|
||||
MemSet(&fcinfo, 0, sizeof(fcinfo));
|
||||
|
||||
/*
|
||||
* We assume RI_FKey_check_ins won't look at
|
||||
* flinfo...
|
||||
*/
|
||||
|
||||
trigdata.type = T_TriggerData;
|
||||
trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
|
||||
trigdata.tg_relation = rel;
|
||||
trigdata.tg_trigtuple = tuple;
|
||||
trigdata.tg_newtuple = NULL;
|
||||
trigdata.tg_trigger = &trig;
|
||||
|
||||
fcinfo.context = (Node *) &trigdata;
|
||||
|
||||
RI_FKey_check_ins(&fcinfo);
|
||||
}
|
||||
heap_endscan(scan);
|
||||
|
||||
pfree(trig.tgargs);
|
||||
/*
|
||||
* Create the triggers that will enforce the constraint.
|
||||
*/
|
||||
createForeignKeyTriggers(rel, fkconstraint, constrOid);
|
||||
|
||||
/*
|
||||
* Close pk table, but keep lock until we've committed.
|
||||
*/
|
||||
heap_close(pkrel, NoLock);
|
||||
|
||||
break;
|
||||
@ -2607,12 +2625,418 @@ AlterTableAddConstraint(Oid myrelid,
|
||||
heap_close(rel, NoLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the existing rows in a table to verify they meet a proposed FK
|
||||
* constraint.
|
||||
*
|
||||
* Caller must have opened and locked both relations.
|
||||
*/
|
||||
static void
|
||||
validateForeignKeyConstraint(FkConstraint *fkconstraint,
|
||||
Relation rel,
|
||||
Relation pkrel)
|
||||
{
|
||||
HeapScanDesc scan;
|
||||
HeapTuple tuple;
|
||||
Trigger trig;
|
||||
List *list;
|
||||
int count;
|
||||
|
||||
/*
|
||||
* Scan through each tuple, calling RI_FKey_check_ins
|
||||
* (insert trigger) as if that tuple had just been
|
||||
* inserted. If any of those fail, it should
|
||||
* elog(ERROR) and that's that.
|
||||
*/
|
||||
MemSet(&trig, 0, sizeof(trig));
|
||||
trig.tgoid = InvalidOid;
|
||||
trig.tgname = fkconstraint->constr_name;
|
||||
trig.tgenabled = TRUE;
|
||||
trig.tgisconstraint = TRUE;
|
||||
trig.tgconstrrelid = RelationGetRelid(pkrel);
|
||||
trig.tgdeferrable = FALSE;
|
||||
trig.tginitdeferred = FALSE;
|
||||
|
||||
trig.tgargs = (char **) palloc(sizeof(char *) *
|
||||
(4 + length(fkconstraint->fk_attrs)
|
||||
+ length(fkconstraint->pk_attrs)));
|
||||
|
||||
trig.tgargs[0] = trig.tgname;
|
||||
trig.tgargs[1] = RelationGetRelationName(rel);
|
||||
trig.tgargs[2] = RelationGetRelationName(pkrel);
|
||||
trig.tgargs[3] = fkMatchTypeToString(fkconstraint->fk_matchtype);
|
||||
count = 4;
|
||||
foreach(list, fkconstraint->fk_attrs)
|
||||
{
|
||||
Ident *fk_at = lfirst(list);
|
||||
|
||||
trig.tgargs[count] = fk_at->name;
|
||||
count += 2;
|
||||
}
|
||||
count = 5;
|
||||
foreach(list, fkconstraint->pk_attrs)
|
||||
{
|
||||
Ident *pk_at = lfirst(list);
|
||||
|
||||
trig.tgargs[count] = pk_at->name;
|
||||
count += 2;
|
||||
}
|
||||
trig.tgnargs = count - 1;
|
||||
|
||||
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
|
||||
|
||||
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
||||
{
|
||||
FunctionCallInfoData fcinfo;
|
||||
TriggerData trigdata;
|
||||
|
||||
/*
|
||||
* Make a call to the trigger function
|
||||
*
|
||||
* No parameters are passed, but we do set a context
|
||||
*/
|
||||
MemSet(&fcinfo, 0, sizeof(fcinfo));
|
||||
|
||||
/*
|
||||
* We assume RI_FKey_check_ins won't look at flinfo...
|
||||
*/
|
||||
trigdata.type = T_TriggerData;
|
||||
trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
|
||||
trigdata.tg_relation = rel;
|
||||
trigdata.tg_trigtuple = tuple;
|
||||
trigdata.tg_newtuple = NULL;
|
||||
trigdata.tg_trigger = &trig;
|
||||
|
||||
fcinfo.context = (Node *) &trigdata;
|
||||
|
||||
RI_FKey_check_ins(&fcinfo);
|
||||
}
|
||||
|
||||
heap_endscan(scan);
|
||||
|
||||
pfree(trig.tgargs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Record an FK constraint in pg_constraint.
|
||||
*/
|
||||
static Oid
|
||||
createForeignKeyConstraint(Relation rel, Relation pkrel,
|
||||
FkConstraint *fkconstraint)
|
||||
{
|
||||
int16 *fkattr;
|
||||
int16 *pkattr;
|
||||
int fkcount;
|
||||
int pkcount;
|
||||
List *l;
|
||||
int i;
|
||||
|
||||
/* Convert foreign-key attr names to attr number array */
|
||||
fkcount = length(fkconstraint->fk_attrs);
|
||||
fkattr = (int16 *) palloc(fkcount * sizeof(int16));
|
||||
i = 0;
|
||||
foreach(l, fkconstraint->fk_attrs)
|
||||
{
|
||||
Ident *id = (Ident *) lfirst(l);
|
||||
|
||||
fkattr[i++] = get_attnum(RelationGetRelid(rel), id->name);
|
||||
}
|
||||
|
||||
/* The same for the referenced primary key attrs */
|
||||
pkcount = length(fkconstraint->pk_attrs);
|
||||
pkattr = (int16 *) palloc(pkcount * sizeof(int16));
|
||||
i = 0;
|
||||
foreach(l, fkconstraint->pk_attrs)
|
||||
{
|
||||
Ident *id = (Ident *) lfirst(l);
|
||||
|
||||
pkattr[i++] = get_attnum(RelationGetRelid(pkrel), id->name);
|
||||
}
|
||||
|
||||
/* Now we can make the pg_constraint entry */
|
||||
return CreateConstraintEntry(fkconstraint->constr_name,
|
||||
RelationGetNamespace(rel),
|
||||
CONSTRAINT_FOREIGN,
|
||||
fkconstraint->deferrable,
|
||||
fkconstraint->initdeferred,
|
||||
RelationGetRelid(rel),
|
||||
fkattr,
|
||||
fkcount,
|
||||
InvalidOid, /* not a domain constraint */
|
||||
RelationGetRelid(pkrel),
|
||||
pkattr,
|
||||
pkcount,
|
||||
fkconstraint->fk_upd_action,
|
||||
fkconstraint->fk_del_action,
|
||||
fkconstraint->fk_matchtype,
|
||||
NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the triggers that implement an FK constraint.
|
||||
*/
|
||||
static void
|
||||
createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
|
||||
Oid constrOid)
|
||||
{
|
||||
RangeVar *myRel;
|
||||
CreateTrigStmt *fk_trigger;
|
||||
List *fk_attr;
|
||||
List *pk_attr;
|
||||
Ident *id;
|
||||
ObjectAddress trigobj,
|
||||
constrobj;
|
||||
|
||||
/*
|
||||
* Reconstruct a RangeVar for my relation (not passed in, unfortunately).
|
||||
*/
|
||||
myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
|
||||
RelationGetRelationName(rel));
|
||||
|
||||
/*
|
||||
* Preset objectAddress fields
|
||||
*/
|
||||
constrobj.classId = get_system_catalog_relid(ConstraintRelationName);
|
||||
constrobj.objectId = constrOid;
|
||||
constrobj.objectSubId = 0;
|
||||
trigobj.classId = get_system_catalog_relid(TriggerRelationName);
|
||||
trigobj.objectSubId = 0;
|
||||
|
||||
/* Make changes-so-far visible */
|
||||
CommandCounterIncrement();
|
||||
|
||||
/*
|
||||
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the
|
||||
* CHECK action.
|
||||
*/
|
||||
fk_trigger = makeNode(CreateTrigStmt);
|
||||
fk_trigger->trigname = fkconstraint->constr_name;
|
||||
fk_trigger->relation = myRel;
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
|
||||
fk_trigger->before = false;
|
||||
fk_trigger->row = true;
|
||||
fk_trigger->actions[0] = 'i';
|
||||
fk_trigger->actions[1] = 'u';
|
||||
fk_trigger->actions[2] = '\0';
|
||||
fk_trigger->lang = NULL;
|
||||
fk_trigger->text = NULL;
|
||||
|
||||
fk_trigger->attr = NIL;
|
||||
fk_trigger->when = NULL;
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||
fk_trigger->constrrel = fkconstraint->pktable;
|
||||
|
||||
fk_trigger->args = NIL;
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->constr_name));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(myRel->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->pktable->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
|
||||
fk_attr = fkconstraint->fk_attrs;
|
||||
pk_attr = fkconstraint->pk_attrs;
|
||||
if (length(fk_attr) != length(pk_attr))
|
||||
elog(ERROR, "number of key attributes in referenced table must be equal to foreign key"
|
||||
"\n\tIllegal FOREIGN KEY definition references \"%s\"",
|
||||
fkconstraint->pktable->relname);
|
||||
|
||||
while (fk_attr != NIL)
|
||||
{
|
||||
id = (Ident *) lfirst(fk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
|
||||
|
||||
id = (Ident *) lfirst(pk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
|
||||
|
||||
fk_attr = lnext(fk_attr);
|
||||
pk_attr = lnext(pk_attr);
|
||||
}
|
||||
|
||||
trigobj.objectId = CreateTrigger(fk_trigger, true);
|
||||
|
||||
/* Register dependency from trigger to constraint */
|
||||
recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
|
||||
|
||||
/* Make changes-so-far visible */
|
||||
CommandCounterIncrement();
|
||||
|
||||
/*
|
||||
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the
|
||||
* ON DELETE action on the referenced table.
|
||||
*/
|
||||
fk_trigger = makeNode(CreateTrigStmt);
|
||||
fk_trigger->trigname = fkconstraint->constr_name;
|
||||
fk_trigger->relation = fkconstraint->pktable;
|
||||
fk_trigger->before = false;
|
||||
fk_trigger->row = true;
|
||||
fk_trigger->actions[0] = 'd';
|
||||
fk_trigger->actions[1] = '\0';
|
||||
fk_trigger->lang = NULL;
|
||||
fk_trigger->text = NULL;
|
||||
|
||||
fk_trigger->attr = NIL;
|
||||
fk_trigger->when = NULL;
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||
fk_trigger->constrrel = myRel;
|
||||
switch (fkconstraint->fk_del_action)
|
||||
{
|
||||
case FKCONSTR_ACTION_NOACTION:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
|
||||
break;
|
||||
case FKCONSTR_ACTION_RESTRICT:
|
||||
fk_trigger->deferrable = false;
|
||||
fk_trigger->initdeferred = false;
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
|
||||
break;
|
||||
case FKCONSTR_ACTION_CASCADE:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
|
||||
break;
|
||||
case FKCONSTR_ACTION_SETNULL:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
|
||||
break;
|
||||
case FKCONSTR_ACTION_SETDEFAULT:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "Unrecognized ON DELETE action for FOREIGN KEY constraint");
|
||||
break;
|
||||
}
|
||||
|
||||
fk_trigger->args = NIL;
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->constr_name));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(myRel->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->pktable->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
|
||||
fk_attr = fkconstraint->fk_attrs;
|
||||
pk_attr = fkconstraint->pk_attrs;
|
||||
while (fk_attr != NIL)
|
||||
{
|
||||
id = (Ident *) lfirst(fk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
|
||||
|
||||
id = (Ident *) lfirst(pk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
|
||||
|
||||
fk_attr = lnext(fk_attr);
|
||||
pk_attr = lnext(pk_attr);
|
||||
}
|
||||
|
||||
trigobj.objectId = CreateTrigger(fk_trigger, true);
|
||||
|
||||
/* Register dependency from trigger to constraint */
|
||||
recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
|
||||
|
||||
/* Make changes-so-far visible */
|
||||
CommandCounterIncrement();
|
||||
|
||||
/*
|
||||
* Build and execute a CREATE CONSTRAINT TRIGGER statement for the
|
||||
* ON UPDATE action on the referenced table.
|
||||
*/
|
||||
fk_trigger = makeNode(CreateTrigStmt);
|
||||
fk_trigger->trigname = fkconstraint->constr_name;
|
||||
fk_trigger->relation = fkconstraint->pktable;
|
||||
fk_trigger->before = false;
|
||||
fk_trigger->row = true;
|
||||
fk_trigger->actions[0] = 'u';
|
||||
fk_trigger->actions[1] = '\0';
|
||||
fk_trigger->lang = NULL;
|
||||
fk_trigger->text = NULL;
|
||||
|
||||
fk_trigger->attr = NIL;
|
||||
fk_trigger->when = NULL;
|
||||
fk_trigger->isconstraint = true;
|
||||
fk_trigger->deferrable = fkconstraint->deferrable;
|
||||
fk_trigger->initdeferred = fkconstraint->initdeferred;
|
||||
fk_trigger->constrrel = myRel;
|
||||
switch (fkconstraint->fk_upd_action)
|
||||
{
|
||||
case FKCONSTR_ACTION_NOACTION:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
|
||||
break;
|
||||
case FKCONSTR_ACTION_RESTRICT:
|
||||
fk_trigger->deferrable = false;
|
||||
fk_trigger->initdeferred = false;
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
|
||||
break;
|
||||
case FKCONSTR_ACTION_CASCADE:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
|
||||
break;
|
||||
case FKCONSTR_ACTION_SETNULL:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
|
||||
break;
|
||||
case FKCONSTR_ACTION_SETDEFAULT:
|
||||
fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "Unrecognized ON UPDATE action for FOREIGN KEY constraint");
|
||||
break;
|
||||
}
|
||||
|
||||
fk_trigger->args = NIL;
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->constr_name));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(myRel->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkconstraint->pktable->relname));
|
||||
fk_trigger->args = lappend(fk_trigger->args,
|
||||
makeString(fkMatchTypeToString(fkconstraint->fk_matchtype)));
|
||||
fk_attr = fkconstraint->fk_attrs;
|
||||
pk_attr = fkconstraint->pk_attrs;
|
||||
while (fk_attr != NIL)
|
||||
{
|
||||
id = (Ident *) lfirst(fk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
|
||||
|
||||
id = (Ident *) lfirst(pk_attr);
|
||||
fk_trigger->args = lappend(fk_trigger->args, makeString(id->name));
|
||||
|
||||
fk_attr = lnext(fk_attr);
|
||||
pk_attr = lnext(pk_attr);
|
||||
}
|
||||
|
||||
trigobj.objectId = CreateTrigger(fk_trigger, true);
|
||||
|
||||
/* Register dependency from trigger to constraint */
|
||||
recordDependencyOn(&trigobj, &constrobj, DEPENDENCY_INTERNAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* fkMatchTypeToString -
|
||||
* convert FKCONSTR_MATCH_xxx code to string to use in trigger args
|
||||
*/
|
||||
static char *
|
||||
fkMatchTypeToString(char match_type)
|
||||
{
|
||||
switch (match_type)
|
||||
{
|
||||
case FKCONSTR_MATCH_FULL:
|
||||
return pstrdup("FULL");
|
||||
case FKCONSTR_MATCH_PARTIAL:
|
||||
return pstrdup("PARTIAL");
|
||||
case FKCONSTR_MATCH_UNSPECIFIED:
|
||||
return pstrdup("UNSPECIFIED");
|
||||
default:
|
||||
elog(ERROR, "fkMatchTypeToString: Unknown MATCH TYPE '%c'",
|
||||
match_type);
|
||||
}
|
||||
return NULL; /* can't get here */
|
||||
}
|
||||
|
||||
/*
|
||||
* ALTER TABLE DROP CONSTRAINT
|
||||
* Note: It is legal to remove a constraint with name "" as it is possible
|
||||
* to add a constraint with name "".
|
||||
* Christopher Kings-Lynne
|
||||
*/
|
||||
void
|
||||
AlterTableDropConstraint(Oid myrelid,
|
||||
@ -2620,14 +3044,7 @@ AlterTableDropConstraint(Oid myrelid,
|
||||
DropBehavior behavior)
|
||||
{
|
||||
Relation rel;
|
||||
int deleted;
|
||||
|
||||
/*
|
||||
* We don't support CASCADE yet - in fact, RESTRICT doesn't work to
|
||||
* the spec either!
|
||||
*/
|
||||
if (behavior == DROP_CASCADE)
|
||||
elog(ERROR, "ALTER TABLE / DROP CONSTRAINT does not support the CASCADE keyword");
|
||||
int deleted = 0;
|
||||
|
||||
/*
|
||||
* Acquire an exclusive lock on the target relation for the duration
|
||||
@ -2649,26 +3066,39 @@ AlterTableDropConstraint(Oid myrelid,
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
|
||||
|
||||
/*
|
||||
* Since all we have is the name of the constraint, we have to look
|
||||
* through all catalogs that could possibly contain a constraint for
|
||||
* this relation. We also keep a count of the number of constraints
|
||||
* removed.
|
||||
* Process child tables if requested.
|
||||
*/
|
||||
if (inh)
|
||||
{
|
||||
List *child,
|
||||
*children;
|
||||
|
||||
deleted = 0;
|
||||
/* This routine is actually in the planner */
|
||||
children = find_all_inheritors(myrelid);
|
||||
|
||||
/*
|
||||
* find_all_inheritors does the recursive search of the
|
||||
* inheritance hierarchy, so all we have to do is process all of
|
||||
* the relids in the list that it returns.
|
||||
*/
|
||||
foreach(child, children)
|
||||
{
|
||||
Oid childrelid = lfirsti(child);
|
||||
Relation inhrel;
|
||||
|
||||
if (childrelid == myrelid)
|
||||
continue;
|
||||
inhrel = heap_open(childrelid, AccessExclusiveLock);
|
||||
/* do NOT count child constraints in deleted. */
|
||||
RemoveRelConstraints(inhrel, constrName, behavior);
|
||||
heap_close(inhrel, NoLock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* First, we remove all CHECK constraints with the given name
|
||||
*/
|
||||
|
||||
deleted += RemoveCheckConstraint(rel, constrName, inh);
|
||||
|
||||
/*
|
||||
* Now we remove NULL, UNIQUE, PRIMARY KEY and FOREIGN KEY
|
||||
* constraints.
|
||||
*
|
||||
* Unimplemented.
|
||||
* Now do the thing on this relation.
|
||||
*/
|
||||
deleted += RemoveRelConstraints(rel, constrName, behavior);
|
||||
|
||||
/* Close the target relation */
|
||||
heap_close(rel, NoLock);
|
||||
@ -2797,6 +3227,8 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
|
||||
char toast_idxname[NAMEDATALEN];
|
||||
IndexInfo *indexInfo;
|
||||
Oid classObjectId[2];
|
||||
ObjectAddress baseobject,
|
||||
toastobject;
|
||||
|
||||
/*
|
||||
* Grab an exclusive lock on the target table, which we will NOT
|
||||
@ -2957,7 +3389,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
|
||||
|
||||
toast_idxid = index_create(toast_relid, toast_idxname, indexInfo,
|
||||
BTREE_AM_OID, classObjectId,
|
||||
true, true);
|
||||
true, false, true);
|
||||
|
||||
/*
|
||||
* Update toast rel's pg_class entry to show that it has an index. The
|
||||
@ -2981,6 +3413,19 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
|
||||
|
||||
heap_freetuple(reltup);
|
||||
|
||||
/*
|
||||
* Register dependency from the toast table to the master, so that
|
||||
* the toast table will be deleted if the master is.
|
||||
*/
|
||||
baseobject.classId = RelOid_pg_class;
|
||||
baseobject.objectId = relOid;
|
||||
baseobject.objectSubId = 0;
|
||||
toastobject.classId = RelOid_pg_class;
|
||||
toastobject.objectId = toast_relid;
|
||||
toastobject.objectSubId = 0;
|
||||
|
||||
recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
|
||||
|
||||
/*
|
||||
* Close relations and make changes visible
|
||||
*/
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.120 2002/06/20 20:29:27 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.121 2002/07/12 18:43:16 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,12 +17,12 @@
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catalog.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/indexing.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_language.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "catalog/pg_trigger.h"
|
||||
#include "commands/comment.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "executor/executor.h"
|
||||
#include "miscadmin.h"
|
||||
@ -50,8 +50,8 @@ static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
|
||||
MemoryContext per_tuple_context);
|
||||
|
||||
|
||||
void
|
||||
CreateTrigger(CreateTrigStmt *stmt)
|
||||
Oid
|
||||
CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
|
||||
{
|
||||
int16 tgtype;
|
||||
int16 tgattr[FUNC_MAX_ARGS];
|
||||
@ -69,11 +69,15 @@ CreateTrigger(CreateTrigStmt *stmt)
|
||||
Oid fargtypes[FUNC_MAX_ARGS];
|
||||
Oid funcoid;
|
||||
Oid funclang;
|
||||
Oid trigoid;
|
||||
int found = 0;
|
||||
int i;
|
||||
char constrtrigname[NAMEDATALEN];
|
||||
char *constrname = "";
|
||||
Oid constrrelid = InvalidOid;
|
||||
char *trigname;
|
||||
char *constrname;
|
||||
Oid constrrelid;
|
||||
ObjectAddress myself,
|
||||
referenced;
|
||||
|
||||
rel = heap_openrv(stmt->relation, AccessExclusiveLock);
|
||||
|
||||
@ -91,21 +95,28 @@ CreateTrigger(CreateTrigStmt *stmt)
|
||||
aclcheck_error(aclresult, RelationGetRelationName(rel));
|
||||
|
||||
/*
|
||||
* If trigger is an RI constraint, use trigger name as constraint name
|
||||
* and build a unique trigger name instead.
|
||||
* If trigger is an RI constraint, use specified trigger name as
|
||||
* constraint name and build a unique trigger name instead.
|
||||
* This is mainly for backwards compatibility with CREATE CONSTRAINT
|
||||
* TRIGGER commands.
|
||||
*/
|
||||
if (stmt->isconstraint)
|
||||
{
|
||||
constrname = stmt->trigname;
|
||||
snprintf(constrtrigname, sizeof(constrtrigname),
|
||||
"RI_ConstraintTrigger_%u", newoid());
|
||||
stmt->trigname = constrtrigname;
|
||||
|
||||
if (stmt->constrrel != NULL)
|
||||
constrrelid = RangeVarGetRelid(stmt->constrrel, false);
|
||||
else
|
||||
constrrelid = InvalidOid;
|
||||
trigname = constrtrigname;
|
||||
constrname = stmt->trigname;
|
||||
}
|
||||
else
|
||||
{
|
||||
trigname = stmt->trigname;
|
||||
constrname = "";
|
||||
}
|
||||
|
||||
if (stmt->constrrel != NULL)
|
||||
constrrelid = RangeVarGetRelid(stmt->constrrel, false);
|
||||
else
|
||||
constrrelid = InvalidOid;
|
||||
|
||||
TRIGGER_CLEAR_TYPE(tgtype);
|
||||
if (stmt->before)
|
||||
@ -160,9 +171,9 @@ CreateTrigger(CreateTrigStmt *stmt)
|
||||
{
|
||||
Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
|
||||
|
||||
if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0)
|
||||
if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)
|
||||
elog(ERROR, "CreateTrigger: trigger %s already defined on relation %s",
|
||||
stmt->trigname, stmt->relation->relname);
|
||||
trigname, stmt->relation->relname);
|
||||
found++;
|
||||
}
|
||||
systable_endscan(tgscan);
|
||||
@ -209,12 +220,13 @@ CreateTrigger(CreateTrigStmt *stmt)
|
||||
|
||||
values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
|
||||
values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,
|
||||
CStringGetDatum(stmt->trigname));
|
||||
CStringGetDatum(trigname));
|
||||
values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
|
||||
values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
|
||||
values[Anum_pg_trigger_tgenabled - 1] = BoolGetDatum(true);
|
||||
values[Anum_pg_trigger_tgisconstraint - 1] = BoolGetDatum(stmt->isconstraint);
|
||||
values[Anum_pg_trigger_tgconstrname - 1] = PointerGetDatum(constrname);
|
||||
values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein,
|
||||
CStringGetDatum(constrname));
|
||||
values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
|
||||
values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);
|
||||
values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);
|
||||
@ -270,10 +282,16 @@ CreateTrigger(CreateTrigStmt *stmt)
|
||||
/*
|
||||
* Insert tuple into pg_trigger.
|
||||
*/
|
||||
simple_heap_insert(tgrel, tuple);
|
||||
trigoid = simple_heap_insert(tgrel, tuple);
|
||||
|
||||
CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs);
|
||||
CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple);
|
||||
CatalogCloseIndices(Num_pg_trigger_indices, idescs);
|
||||
|
||||
myself.classId = RelationGetRelid(tgrel);
|
||||
myself.objectId = trigoid;
|
||||
myself.objectSubId = 0;
|
||||
|
||||
heap_freetuple(tuple);
|
||||
heap_close(tgrel, RowExclusiveLock);
|
||||
|
||||
@ -294,10 +312,13 @@ CreateTrigger(CreateTrigStmt *stmt)
|
||||
stmt->relation->relname);
|
||||
|
||||
((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1;
|
||||
|
||||
simple_heap_update(pgrel, &tuple->t_self, tuple);
|
||||
|
||||
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
|
||||
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
|
||||
CatalogCloseIndices(Num_pg_class_indices, ridescs);
|
||||
|
||||
heap_freetuple(tuple);
|
||||
heap_close(pgrel, RowExclusiveLock);
|
||||
|
||||
@ -307,25 +328,129 @@ CreateTrigger(CreateTrigStmt *stmt)
|
||||
* upcoming CommandCounterIncrement...
|
||||
*/
|
||||
|
||||
/*
|
||||
* Record dependencies for trigger. Always place a normal dependency
|
||||
* on the function. If we are doing this in response to an explicit
|
||||
* CREATE TRIGGER command, also make trigger be auto-dropped if its
|
||||
* relation is dropped or if the FK relation is dropped. (Auto drop
|
||||
* is compatible with our pre-7.3 behavior.) If the trigger is being
|
||||
* made for a constraint, we can skip the relation links; the dependency
|
||||
* on the constraint will indirectly depend on the relations.
|
||||
*/
|
||||
referenced.classId = RelOid_pg_proc;
|
||||
referenced.objectId = funcoid;
|
||||
referenced.objectSubId = 0;
|
||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
||||
|
||||
if (!forConstraint)
|
||||
{
|
||||
referenced.classId = RelOid_pg_class;
|
||||
referenced.objectId = RelationGetRelid(rel);
|
||||
referenced.objectSubId = 0;
|
||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
|
||||
if (constrrelid != InvalidOid)
|
||||
{
|
||||
referenced.classId = RelOid_pg_class;
|
||||
referenced.objectId = constrrelid;
|
||||
referenced.objectSubId = 0;
|
||||
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep lock on target rel until end of xact */
|
||||
heap_close(rel, NoLock);
|
||||
|
||||
return trigoid;
|
||||
}
|
||||
|
||||
/*
|
||||
* DropTrigger - drop an individual trigger by name
|
||||
*/
|
||||
void
|
||||
DropTrigger(Oid relid, const char *trigname)
|
||||
DropTrigger(Oid relid, const char *trigname, DropBehavior behavior)
|
||||
{
|
||||
Relation tgrel;
|
||||
ScanKeyData skey[2];
|
||||
SysScanDesc tgscan;
|
||||
HeapTuple tup;
|
||||
ObjectAddress object;
|
||||
|
||||
/*
|
||||
* Find the trigger, verify permissions, set up object address
|
||||
*/
|
||||
tgrel = heap_openr(TriggerRelationName, AccessShareLock);
|
||||
|
||||
ScanKeyEntryInitialize(&skey[0], 0x0,
|
||||
Anum_pg_trigger_tgrelid, F_OIDEQ,
|
||||
ObjectIdGetDatum(relid));
|
||||
|
||||
ScanKeyEntryInitialize(&skey[1], 0x0,
|
||||
Anum_pg_trigger_tgname, F_NAMEEQ,
|
||||
CStringGetDatum(trigname));
|
||||
|
||||
tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
|
||||
SnapshotNow, 2, skey);
|
||||
|
||||
tup = systable_getnext(tgscan);
|
||||
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "DropTrigger: there is no trigger %s on relation %s",
|
||||
trigname, get_rel_name(relid));
|
||||
|
||||
if (!pg_class_ownercheck(relid, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, get_rel_name(relid));
|
||||
|
||||
object.classId = RelationGetRelid(tgrel);
|
||||
object.objectId = tup->t_data->t_oid;
|
||||
object.objectSubId = 0;
|
||||
|
||||
systable_endscan(tgscan);
|
||||
heap_close(tgrel, AccessShareLock);
|
||||
|
||||
/*
|
||||
* Do the deletion
|
||||
*/
|
||||
performDeletion(&object, behavior);
|
||||
}
|
||||
|
||||
/*
|
||||
* Guts of trigger deletion.
|
||||
*/
|
||||
void
|
||||
RemoveTriggerById(Oid trigOid)
|
||||
{
|
||||
Relation rel;
|
||||
Relation tgrel;
|
||||
SysScanDesc tgscan;
|
||||
ScanKeyData key;
|
||||
ScanKeyData skey[1];
|
||||
HeapTuple tup;
|
||||
Oid relid;
|
||||
Relation rel;
|
||||
Relation pgrel;
|
||||
HeapTuple tuple;
|
||||
Form_pg_class classForm;
|
||||
Relation ridescs[Num_pg_class_indices];
|
||||
int remaining = 0;
|
||||
int found = 0;
|
||||
|
||||
tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
|
||||
|
||||
/*
|
||||
* Find the trigger to delete.
|
||||
*/
|
||||
ScanKeyEntryInitialize(&skey[0], 0x0,
|
||||
ObjectIdAttributeNumber, F_OIDEQ,
|
||||
ObjectIdGetDatum(trigOid));
|
||||
|
||||
tgscan = systable_beginscan(tgrel, TriggerOidIndex, true,
|
||||
SnapshotNow, 1, skey);
|
||||
|
||||
tup = systable_getnext(tgscan);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "RemoveTriggerById: Trigger %u does not exist",
|
||||
trigOid);
|
||||
|
||||
/*
|
||||
* Open and exclusive-lock the relation the trigger belongs to.
|
||||
*/
|
||||
relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid;
|
||||
|
||||
rel = heap_open(relid, AccessExclusiveLock);
|
||||
|
||||
@ -337,55 +462,22 @@ DropTrigger(Oid relid, const char *trigname)
|
||||
elog(ERROR, "DropTrigger: can't drop trigger for system relation %s",
|
||||
RelationGetRelationName(rel));
|
||||
|
||||
if (!pg_class_ownercheck(relid, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, RelationGetRelationName(rel));
|
||||
|
||||
/*
|
||||
* Search pg_trigger, delete target trigger, count remaining triggers
|
||||
* for relation. (Although we could fetch and delete the target
|
||||
* trigger directly, we'd still have to scan the remaining triggers,
|
||||
* so we may as well do both in one indexscan.)
|
||||
*
|
||||
* Note this is OK only because we have AccessExclusiveLock on the rel,
|
||||
* so no one else is creating/deleting triggers on this rel at the same
|
||||
* time.
|
||||
* Delete the pg_trigger tuple.
|
||||
*/
|
||||
tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
|
||||
ScanKeyEntryInitialize(&key, 0,
|
||||
Anum_pg_trigger_tgrelid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(relid));
|
||||
tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
|
||||
SnapshotNow, 1, &key);
|
||||
while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
|
||||
{
|
||||
Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
|
||||
simple_heap_delete(tgrel, &tup->t_self);
|
||||
|
||||
if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)
|
||||
{
|
||||
/* Delete any comments associated with this trigger */
|
||||
DeleteComments(tuple->t_data->t_oid, RelationGetRelid(tgrel));
|
||||
|
||||
simple_heap_delete(tgrel, &tuple->t_self);
|
||||
found++;
|
||||
}
|
||||
else
|
||||
remaining++;
|
||||
}
|
||||
systable_endscan(tgscan);
|
||||
heap_close(tgrel, RowExclusiveLock);
|
||||
|
||||
if (found == 0)
|
||||
elog(ERROR, "DropTrigger: there is no trigger %s on relation %s",
|
||||
trigname, RelationGetRelationName(rel));
|
||||
if (found > 1) /* shouldn't happen */
|
||||
elog(NOTICE, "DropTrigger: found (and deleted) %d triggers %s on relation %s",
|
||||
found, trigname, RelationGetRelationName(rel));
|
||||
|
||||
/*
|
||||
* Update relation's pg_class entry. Crucial side-effect: other
|
||||
* backends (and this one too!) are sent SI message to make them
|
||||
* rebuild relcache entries.
|
||||
*
|
||||
* Note this is OK only because we have AccessExclusiveLock on the rel,
|
||||
* so no one else is creating/deleting triggers on this rel at the same
|
||||
* time.
|
||||
*/
|
||||
pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
|
||||
tuple = SearchSysCacheCopy(RELOID,
|
||||
@ -394,115 +486,27 @@ DropTrigger(Oid relid, const char *trigname)
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "DropTrigger: relation %s not found in pg_class",
|
||||
RelationGetRelationName(rel));
|
||||
classForm = (Form_pg_class) GETSTRUCT(tuple);
|
||||
|
||||
if (classForm->reltriggers == 0)
|
||||
elog(ERROR, "DropTrigger: relation %s has reltriggers = 0",
|
||||
RelationGetRelationName(rel));
|
||||
classForm->reltriggers--;
|
||||
|
||||
((Form_pg_class) GETSTRUCT(tuple))->reltriggers = remaining;
|
||||
simple_heap_update(pgrel, &tuple->t_self, tuple);
|
||||
|
||||
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
|
||||
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tuple);
|
||||
CatalogCloseIndices(Num_pg_class_indices, ridescs);
|
||||
|
||||
heap_freetuple(tuple);
|
||||
|
||||
heap_close(pgrel, RowExclusiveLock);
|
||||
|
||||
/* Keep lock on target rel until end of xact */
|
||||
/* Keep lock on trigger's rel until end of xact */
|
||||
heap_close(rel, NoLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all triggers for a relation that's being deleted.
|
||||
*/
|
||||
void
|
||||
RelationRemoveTriggers(Relation rel)
|
||||
{
|
||||
Relation tgrel;
|
||||
SysScanDesc tgscan;
|
||||
ScanKeyData key;
|
||||
HeapTuple tup;
|
||||
bool found = false;
|
||||
|
||||
tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
|
||||
ScanKeyEntryInitialize(&key, 0,
|
||||
Anum_pg_trigger_tgrelid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(RelationGetRelid(rel)));
|
||||
tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
|
||||
SnapshotNow, 1, &key);
|
||||
|
||||
while (HeapTupleIsValid(tup = systable_getnext(tgscan)))
|
||||
{
|
||||
/* Delete any comments associated with this trigger */
|
||||
DeleteComments(tup->t_data->t_oid, RelationGetRelid(tgrel));
|
||||
|
||||
simple_heap_delete(tgrel, &tup->t_self);
|
||||
|
||||
found = true;
|
||||
}
|
||||
|
||||
systable_endscan(tgscan);
|
||||
|
||||
/*
|
||||
* If we deleted any triggers, must update pg_class entry and advance
|
||||
* command counter to make the updated entry visible. This is fairly
|
||||
* annoying, since we'e just going to drop the durn thing later, but
|
||||
* it's necessary to have a consistent state in case we do
|
||||
* CommandCounterIncrement() below --- if RelationBuildTriggers()
|
||||
* runs, it will complain otherwise. Perhaps RelationBuildTriggers()
|
||||
* shouldn't be so picky...
|
||||
*/
|
||||
if (found)
|
||||
{
|
||||
Relation pgrel;
|
||||
Relation ridescs[Num_pg_class_indices];
|
||||
|
||||
pgrel = heap_openr(RelationRelationName, RowExclusiveLock);
|
||||
tup = SearchSysCacheCopy(RELOID,
|
||||
ObjectIdGetDatum(RelationGetRelid(rel)),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "RelationRemoveTriggers: relation %u not found in pg_class",
|
||||
RelationGetRelid(rel));
|
||||
|
||||
((Form_pg_class) GETSTRUCT(tup))->reltriggers = 0;
|
||||
simple_heap_update(pgrel, &tup->t_self, tup);
|
||||
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
|
||||
CatalogIndexInsert(ridescs, Num_pg_class_indices, pgrel, tup);
|
||||
CatalogCloseIndices(Num_pg_class_indices, ridescs);
|
||||
heap_freetuple(tup);
|
||||
heap_close(pgrel, RowExclusiveLock);
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
|
||||
/*
|
||||
* Also drop all constraint triggers referencing this relation
|
||||
*/
|
||||
ScanKeyEntryInitialize(&key, 0,
|
||||
Anum_pg_trigger_tgconstrrelid,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(RelationGetRelid(rel)));
|
||||
tgscan = systable_beginscan(tgrel, TriggerConstrRelidIndex, true,
|
||||
SnapshotNow, 1, &key);
|
||||
|
||||
while (HeapTupleIsValid(tup = systable_getnext(tgscan)))
|
||||
{
|
||||
Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tup);
|
||||
|
||||
elog(NOTICE, "DROP TABLE implicitly drops referential integrity trigger from table \"%s\"",
|
||||
get_rel_name(pg_trigger->tgrelid));
|
||||
|
||||
DropTrigger(pg_trigger->tgrelid, NameStr(pg_trigger->tgname));
|
||||
|
||||
/*
|
||||
* Need to do a command counter increment here to show up new
|
||||
* pg_class.reltriggers in the next loop iteration (in case there
|
||||
* are multiple referential integrity action triggers for the same
|
||||
* FK table defined on the PK table).
|
||||
*/
|
||||
CommandCounterIncrement();
|
||||
}
|
||||
systable_endscan(tgscan);
|
||||
|
||||
heap_close(tgrel, RowExclusiveLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* renametrig - changes the name of a trigger on a relation
|
||||
*
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.4 2002/07/01 15:27:48 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.5 2002/07/12 18:43:16 tgl Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@ -33,10 +33,10 @@
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/comment.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "miscadmin.h"
|
||||
#include "parser/parse_func.h"
|
||||
@ -262,17 +262,14 @@ DefineType(List *names, List *parameters)
|
||||
/*
|
||||
* RemoveType
|
||||
* Removes a datatype.
|
||||
*
|
||||
* NOTE: since this tries to remove the associated array type too, it'll
|
||||
* only work on scalar types.
|
||||
*/
|
||||
void
|
||||
RemoveType(List *names, DropBehavior behavior)
|
||||
{
|
||||
TypeName *typename;
|
||||
Relation relation;
|
||||
Oid typeoid;
|
||||
HeapTuple tup;
|
||||
ObjectAddress object;
|
||||
|
||||
/* Make a TypeName so we can use standard type lookup machinery */
|
||||
typename = makeNode(TypeName);
|
||||
@ -280,8 +277,6 @@ RemoveType(List *names, DropBehavior behavior)
|
||||
typename->typmod = -1;
|
||||
typename->arrayBounds = NIL;
|
||||
|
||||
relation = heap_openr(TypeRelationName, RowExclusiveLock);
|
||||
|
||||
/* Use LookupTypeName here so that shell types can be removed. */
|
||||
typeoid = LookupTypeName(typename);
|
||||
if (!OidIsValid(typeoid))
|
||||
@ -301,30 +296,36 @@ RemoveType(List *names, DropBehavior behavior)
|
||||
GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
|
||||
|
||||
/* Delete any comments associated with this type */
|
||||
DeleteComments(typeoid, RelationGetRelid(relation));
|
||||
|
||||
/* Remove the type tuple from pg_type */
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
/* Now, delete the "array of" that type */
|
||||
typename->arrayBounds = makeList1(makeInteger(1));
|
||||
/*
|
||||
* Do the deletion
|
||||
*/
|
||||
object.classId = RelOid_pg_type;
|
||||
object.objectId = typeoid;
|
||||
object.objectSubId = 0;
|
||||
|
||||
typeoid = LookupTypeName(typename);
|
||||
if (!OidIsValid(typeoid))
|
||||
elog(ERROR, "Type \"%s\" does not exist",
|
||||
TypeNameToString(typename));
|
||||
performDeletion(&object, behavior);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Guts of type deletion.
|
||||
*/
|
||||
void
|
||||
RemoveTypeById(Oid typeOid)
|
||||
{
|
||||
Relation relation;
|
||||
HeapTuple tup;
|
||||
|
||||
relation = heap_openr(TypeRelationName, RowExclusiveLock);
|
||||
|
||||
tup = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(typeoid),
|
||||
ObjectIdGetDatum(typeOid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "Type \"%s\" does not exist",
|
||||
TypeNameToString(typename));
|
||||
|
||||
DeleteComments(typeoid, RelationGetRelid(relation));
|
||||
elog(ERROR, "RemoveTypeById: type %u not found",
|
||||
typeOid);
|
||||
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
|
||||
@ -365,6 +366,8 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
HeapTuple typeTup;
|
||||
List *schema = stmt->constraints;
|
||||
List *listptr;
|
||||
Oid basetypeoid;
|
||||
Form_pg_type baseType;
|
||||
|
||||
/* Convert list of names to a name and namespace */
|
||||
domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
|
||||
@ -389,40 +392,43 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
*/
|
||||
typeTup = typenameType(stmt->typename);
|
||||
|
||||
baseType = (Form_pg_type) GETSTRUCT(typeTup);
|
||||
basetypeoid = typeTup->t_data->t_oid;
|
||||
|
||||
/*
|
||||
* What we really don't want is domains of domains. This could cause all sorts
|
||||
* of neat issues if we allow that.
|
||||
*
|
||||
* With testing, we may determine complex types should be allowed
|
||||
*/
|
||||
typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype;
|
||||
typtype = baseType->typtype;
|
||||
if (typtype != 'b')
|
||||
elog(ERROR, "DefineDomain: %s is not a basetype",
|
||||
TypeNameToString(stmt->typename));
|
||||
|
||||
/* passed by value */
|
||||
byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval;
|
||||
byValue = baseType->typbyval;
|
||||
|
||||
/* Required Alignment */
|
||||
alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign;
|
||||
alignment = baseType->typalign;
|
||||
|
||||
/* TOAST Strategy */
|
||||
storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage;
|
||||
storage = baseType->typstorage;
|
||||
|
||||
/* Storage Length */
|
||||
internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen;
|
||||
internalLength = baseType->typlen;
|
||||
|
||||
/* External Length (unused) */
|
||||
externalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typprtlen;
|
||||
externalLength = baseType->typprtlen;
|
||||
|
||||
/* Array element Delimiter */
|
||||
delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim;
|
||||
delimiter = baseType->typdelim;
|
||||
|
||||
/* I/O Functions */
|
||||
inputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typinput;
|
||||
outputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
|
||||
receiveProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typreceive;
|
||||
sendProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typsend;
|
||||
inputProcedure = baseType->typinput;
|
||||
outputProcedure = baseType->typoutput;
|
||||
receiveProcedure = baseType->typreceive;
|
||||
sendProcedure = baseType->typsend;
|
||||
|
||||
/* Inherited default value */
|
||||
datum = SysCacheGetAttr(TYPEOID, typeTup,
|
||||
@ -441,7 +447,7 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
*
|
||||
* This is what enables us to make a domain of an array
|
||||
*/
|
||||
basetypelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
|
||||
basetypelem = baseType->typelem;
|
||||
|
||||
/*
|
||||
* Run through constraints manually to avoid the additional
|
||||
@ -474,7 +480,7 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
* Note: Name is strictly for error message
|
||||
*/
|
||||
expr = cookDefault(pstate, colDef->raw_expr,
|
||||
typeTup->t_data->t_oid,
|
||||
basetypeoid,
|
||||
stmt->typename->typmod,
|
||||
domainName);
|
||||
/*
|
||||
@ -540,7 +546,7 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
*/
|
||||
TypeCreate(domainName, /* type name */
|
||||
domainNamespace, /* namespace */
|
||||
InvalidOid, /* preassigned type oid (not done here) */
|
||||
InvalidOid, /* preassigned type oid (none here) */
|
||||
InvalidOid, /* relation oid (n/a here) */
|
||||
internalLength, /* internal size */
|
||||
externalLength, /* external size */
|
||||
@ -551,7 +557,7 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
receiveProcedure, /* receive procedure */
|
||||
sendProcedure, /* send procedure */
|
||||
basetypelem, /* element type ID */
|
||||
typeTup->t_data->t_oid, /* base type ID */
|
||||
basetypeoid, /* base type ID */
|
||||
defaultValue, /* default type value (text) */
|
||||
defaultValueBin, /* default type value (binary) */
|
||||
byValue, /* passed by value */
|
||||
@ -571,19 +577,17 @@ DefineDomain(CreateDomainStmt *stmt)
|
||||
/*
|
||||
* RemoveDomain
|
||||
* Removes a domain.
|
||||
*
|
||||
* This is identical to RemoveType except we insist it be a domain.
|
||||
*/
|
||||
void
|
||||
RemoveDomain(List *names, DropBehavior behavior)
|
||||
{
|
||||
TypeName *typename;
|
||||
Relation relation;
|
||||
Oid typeoid;
|
||||
HeapTuple tup;
|
||||
char typtype;
|
||||
|
||||
/* CASCADE unsupported */
|
||||
if (behavior == DROP_CASCADE)
|
||||
elog(ERROR, "DROP DOMAIN does not support the CASCADE keyword");
|
||||
ObjectAddress object;
|
||||
|
||||
/* Make a TypeName so we can use standard type lookup machinery */
|
||||
typename = makeNode(TypeName);
|
||||
@ -591,15 +595,17 @@ RemoveDomain(List *names, DropBehavior behavior)
|
||||
typename->typmod = -1;
|
||||
typename->arrayBounds = NIL;
|
||||
|
||||
relation = heap_openr(TypeRelationName, RowExclusiveLock);
|
||||
|
||||
typeoid = typenameTypeId(typename);
|
||||
/* Use LookupTypeName here so that shell types can be removed. */
|
||||
typeoid = LookupTypeName(typename);
|
||||
if (!OidIsValid(typeoid))
|
||||
elog(ERROR, "Type \"%s\" does not exist",
|
||||
TypeNameToString(typename));
|
||||
|
||||
tup = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(typeoid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "RemoveDomain: type '%s' does not exist",
|
||||
elog(ERROR, "RemoveDomain: type \"%s\" does not exist",
|
||||
TypeNameToString(typename));
|
||||
|
||||
/* Permission check: must own type or its namespace */
|
||||
@ -615,17 +621,16 @@ RemoveDomain(List *names, DropBehavior behavior)
|
||||
elog(ERROR, "%s is not a domain",
|
||||
TypeNameToString(typename));
|
||||
|
||||
/* Delete any comments associated with this type */
|
||||
DeleteComments(typeoid, RelationGetRelid(relation));
|
||||
|
||||
/* Remove the type tuple from pg_type */
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
/* At present, domains don't have associated array types */
|
||||
/*
|
||||
* Do the deletion
|
||||
*/
|
||||
object.classId = RelOid_pg_type;
|
||||
object.objectId = typeoid;
|
||||
object.objectSubId = 0;
|
||||
|
||||
heap_close(relation, RowExclusiveLock);
|
||||
performDeletion(&object, behavior);
|
||||
}
|
||||
|
||||
|
||||
|
@ -6,13 +6,14 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: view.c,v 1.65 2002/07/01 15:27:49 tgl Exp $
|
||||
* $Id: view.c,v 1.66 2002/07/12 18:43:16 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "access/xact.h"
|
||||
#include "catalog/dependency.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "commands/tablecmds.h"
|
||||
@ -252,16 +253,21 @@ DefineView(const RangeVar *view, Query *viewParse)
|
||||
* RemoveView
|
||||
*
|
||||
* Remove a view given its name
|
||||
*
|
||||
* We just have to drop the relation; the associated rules will be
|
||||
* cleaned up automatically.
|
||||
*/
|
||||
void
|
||||
RemoveView(const RangeVar *view, DropBehavior behavior)
|
||||
{
|
||||
Oid viewOid;
|
||||
ObjectAddress object;
|
||||
|
||||
viewOid = RangeVarGetRelid(view, false);
|
||||
/*
|
||||
* We just have to drop the relation; the associated rules will be
|
||||
* cleaned up automatically.
|
||||
*/
|
||||
heap_drop_with_catalog(viewOid, allowSystemTableMods);
|
||||
|
||||
object.classId = RelOid_pg_class;
|
||||
object.objectId = viewOid;
|
||||
object.objectSubId = 0;
|
||||
|
||||
performDeletion(&object, behavior);
|
||||
}
|
||||
|
Reference in New Issue
Block a user