The following patch extends the COMMENT ON functionality to the
rest of the database objects beyond just tables, columns, and views. The
grammer of the COMMENT ON statement now looks like:

COMMENT ON [
  [ DATABASE | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ] <objname>
|

  COLUMN <relation>.<attribute> |
  AGGREGATE <aggname> <aggtype> |
  FUNCTION <funcname> (arg1, arg2, ...) |
  OPERATOR <op> (leftoperand_typ rightoperand_typ) |
  TRIGGER <triggername> ON relname>

Mike Mascari
(mascarim@yahoo.com)
This commit is contained in:
Bruce Momjian 1999-10-26 03:12:39 +00:00
parent 51f62d505e
commit 577e21b34f
15 changed files with 1042 additions and 241 deletions

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.104 1999/10/15 01:49:39 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.105 1999/10/26 03:12:33 momjian Exp $
*
*
* INTERFACE ROUTINES
@ -45,6 +45,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_relcheck.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/trigger.h"
#include "optimizer/clauses.h"
#include "optimizer/planmain.h"
@ -1276,148 +1277,20 @@ DeleteAttributeTuples(Relation rel)
Int16GetDatum(attnum),
0, 0)))
{
DeleteComments(tup->t_data->t_oid);
heap_delete(pg_attribute_desc, &tup->t_self, NULL);
pfree(tup);
/*** Delete any comments associated with this attribute ***/
DeleteComments(tup->t_data->t_oid);
heap_delete(pg_attribute_desc, &tup->t_self, NULL);
pfree(tup);
}
}
heap_close(pg_attribute_desc, RowExclusiveLock);
}
/* ----------------------------------------------------------
* CreateComments
*
* This routine is handed the oid and the command associated
* with that id and will insert, update, or delete (if the
* comment is an empty string or a NULL pointer) the associated
* comment from the system cataloge, pg_description.
*
* ----------------------------------------------------------
*/
void
CreateComments(Oid oid, char *comment)
{
Relation description;
TupleDesc tupDesc;
HeapScanDesc scan;
ScanKeyData entry;
HeapTuple desctuple, searchtuple;
Datum values[Natts_pg_description];
char nulls[Natts_pg_description];
char replaces[Natts_pg_description];
bool modified = false;
int i;
/*** Open pg_description, form a new tuple, if necessary ***/
description = heap_openr(DescriptionRelationName, RowExclusiveLock);
tupDesc = description->rd_att;
if ((comment != NULL) && (strlen(comment) > 0)) {
for (i = 0; i < Natts_pg_description; i++) {
nulls[i] = ' ';
replaces[i] = 'r';
values[i] = (Datum) NULL;
}
i = 0;
values[i++] = ObjectIdGetDatum(oid);
values[i++] = (Datum) fmgr(F_TEXTIN, comment);
}
/*** Now, open pg_description and attempt to find the old tuple ***/
ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_description_objoid, F_OIDEQ,
ObjectIdGetDatum(oid));
scan = heap_beginscan(description, false, SnapshotNow, 1, &entry);
searchtuple = heap_getnext(scan, 0);
/*** If a previous tuple exists, either delete it or prepare a replacement ***/
if (HeapTupleIsValid(searchtuple)) {
/*** If the comment is blank, call heap_delete, else heap_replace ***/
if ((comment == NULL) || (strlen(comment) == 0)) {
heap_delete(description, &searchtuple->t_self, NULL);
} else {
desctuple = heap_modifytuple(searchtuple, description, values, nulls, replaces);
setheapoverride(true);
heap_replace(description, &searchtuple->t_self, desctuple, NULL);
setheapoverride(false);
modified = TRUE;
}
} else {
desctuple = heap_formtuple(tupDesc, values, nulls);
heap_insert(description, desctuple);
modified = TRUE;
}
/*** Complete the scan, update indices, if necessary ***/
heap_endscan(scan);
if (modified) {
if (RelationGetForm(description)->relhasindex) {
Relation idescs[Num_pg_description_indices];
CatalogOpenIndices(Num_pg_description_indices, Name_pg_description_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_description_indices, description, desctuple);
CatalogCloseIndices(Num_pg_description_indices, idescs);
}
pfree(desctuple);
}
heap_close(description, RowExclusiveLock);
}
/* --------------------------------
* DeleteComments
*
* This routine is used to purge any comments
* associated with the Oid handed to this routine,
* regardless of the actual object type. It is
* called, for example, when a relation is destroyed.
* --------------------------------
*/
void
DeleteComments(Oid oid)
{
Relation description;
TupleDesc tupDesc;
ScanKeyData entry;
HeapScanDesc scan;
HeapTuple searchtuple;
description = heap_openr(DescriptionRelationName, RowExclusiveLock);
tupDesc = description->rd_att;
/*** Now, open pg_description and attempt to find the old tuple ***/
ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_description_objoid, F_OIDEQ,
ObjectIdGetDatum(oid));
scan = heap_beginscan(description, false, SnapshotNow, 1, &entry);
searchtuple = heap_getnext(scan, 0);
/*** If a previous tuple exists, delete it ***/
if (HeapTupleIsValid(searchtuple)) {
heap_delete(description, &searchtuple->t_self, NULL);
}
/*** Complete the scan, update indices, if necessary ***/
heap_endscan(scan);
heap_close(description, RowExclusiveLock);
}
/* --------------------------------
* DeleteTypeTuple
*
@ -1529,6 +1402,7 @@ DeleteTypeTuple(Relation rel)
* we release the read lock on pg_type. -mer 13 Aug 1991
* ----------------
*/
heap_delete(pg_type_desc, &tup->t_self, NULL);
heap_endscan(pg_type_scan);

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.91 1999/09/24 00:24:11 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.92 1999/10/26 03:12:33 momjian Exp $
*
*
* INTERFACE ROUTINES
@ -32,6 +32,7 @@
#include "catalog/pg_index.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "optimizer/clauses.h"
@ -1127,6 +1128,13 @@ index_destroy(Oid indexId)
if (IsTransactionBlock() && ! userindexRelation->rd_myxactonly)
elog(NOTICE, "Caution: DROP INDEX cannot be rolled back, so don't abort now");
/* ----------------
* fix DESCRIPTION relation
* ----------------
*/
DeleteComments(indexId);
/* ----------------
* fix RELATION relation
* ----------------

View File

@ -4,7 +4,7 @@
# Makefile for commands
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.18 1999/02/27 21:42:33 tgl Exp $
# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.19 1999/10/26 03:12:34 momjian Exp $
#
#-------------------------------------------------------------------------
@ -17,7 +17,7 @@ ifdef MULTIBYTE
CFLAGS+= $(MBFLAGS)
endif
OBJS = async.o creatinh.o command.o copy.o indexcmds.o define.o \
OBJS = async.o creatinh.o command.o comment.o copy.o indexcmds.o define.o \
remove.o rename.o vacuum.o view.o cluster.o \
explain.o sequence.o trigger.o user.o proclang.o \
dbcommands.o variable.o
@ -27,6 +27,12 @@ all: SUBSYS.o
SUBSYS.o: $(OBJS)
$(LD) -r -o SUBSYS.o $(OBJS)
# The following declares a hard-coded dependency on parse.h since,
# if compiled without make dep, comment.c would get compiled before
# the parser.
comment.o: ../parse.h
depend dep:
$(CC) -MM $(CFLAGS) *.c >depend

View File

@ -0,0 +1,796 @@
/*-------------------------------------------------------------------------
*
* comment.c
*
* PostgreSQL object comments utility code.
*
* Copyright (c) 1999, PostgreSQL Global Development Group
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/indexing.h"
#include "catalog/heap.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_database.h"
#include "catalog/pg_description.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_shadow.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "miscadmin.h"
#include "rewrite/rewriteRemove.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
#include "../backend/parser/parse.h"
/*------------------------------------------------------------------
* Static Function Prototypes --
*
* The following protoypes are declared static so as not to conflict
* with any other routines outside this module. These routines are
* called by the public function CommentObject() routine to create
* the appropriate comment for the specific object type.
*------------------------------------------------------------------
*/
static void CommentRelation(int objtype, char *relation, char *comment);
static void CommentAttribute(char *relation, char *attrib, char *comment);
static void CommentDatabase(char *database, char *comment);
static void CommentRewrite(char *rule, char *comment);
static void CommentType(char *type, char *comment);
static void CommentAggregate(char *aggregate, char *aggtype, char *comment);
static void CommentProc(char *function, List *arguments, char *comment);
static void CommentOperator(char *opname, List *arguments, char *comment);
static void CommentTrigger(char *trigger, char *relation, char *comments);
/*------------------------------------------------------------------
* CommentObject --
*
* This routine is used to add the associated comment into
* pg_description for the object specified by the paramters handed
* to this routine. If the routine cannot determine an Oid to
* associated with the parameters handed to this routine, an
* error is thrown. Otherwise the comment is added to pg_description
* by calling the CreateComments() routine. If the comments were
* empty, CreateComments() will drop any comments associated with
* the object.
*------------------------------------------------------------------
*/
void CommentObject(int objtype, char *objname, char *objproperty,
List *objlist, char *comment) {
switch (objtype) {
case (INDEX):
case (SEQUENCE):
case (TABLE):
case (VIEW):
CommentRelation(objtype, objname, comment);
break;
case (COLUMN):
CommentAttribute(objname, objproperty, comment);
break;
case (DATABASE):
CommentDatabase(objname, comment);
break;
case (RULE):
CommentRewrite(objname, comment);
break;
case (TYPE_P):
CommentType(objname, comment);
break;
case (AGGREGATE):
CommentAggregate(objname, objproperty, comment);
break;
case (FUNCTION):
CommentProc(objname, objlist, comment);
break;
case (OPERATOR):
CommentOperator(objname, objlist, comment);
break;
case (TRIGGER):
CommentTrigger(objname, objproperty, comment);
break;
default:
elog(ERROR, "An attempt was made to comment on a unkown type: %i",
objtype);
}
}
/*------------------------------------------------------------------
* CreateComments --
*
* This routine is handed the oid and the command associated
* with that id and will insert, update, or delete (if the
* comment is an empty string or a NULL pointer) the associated
* comment from the system cataloge, pg_description.
*
*------------------------------------------------------------------
*/
void CreateComments(Oid oid, char *comment) {
Relation description;
TupleDesc tupDesc;
HeapScanDesc scan;
ScanKeyData entry;
HeapTuple desctuple = NULL, searchtuple;
Datum values[Natts_pg_description];
char nulls[Natts_pg_description];
char replaces[Natts_pg_description];
bool modified = false;
int i;
/*** Open pg_description, form a new tuple, if necessary ***/
description = heap_openr(DescriptionRelationName, RowExclusiveLock);
tupDesc = description->rd_att;
if ((comment != NULL) && (strlen(comment) > 0)) {
for (i = 0; i < Natts_pg_description; i++) {
nulls[i] = ' ';
replaces[i] = 'r';
values[i] = (Datum) NULL;
}
i = 0;
values[i++] = ObjectIdGetDatum(oid);
values[i++] = (Datum) fmgr(F_TEXTIN, comment);
}
/*** Now, open pg_description and attempt to find the old tuple ***/
ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_description_objoid, F_OIDEQ,
ObjectIdGetDatum(oid));
scan = heap_beginscan(description, false, SnapshotNow, 1, &entry);
searchtuple = heap_getnext(scan, 0);
/*** If a previous tuple exists, either delete or prep replacement ***/
if (HeapTupleIsValid(searchtuple)) {
/*** If the comment is blank, call heap_delete, else heap_replace ***/
if ((comment == NULL) || (strlen(comment) == 0)) {
heap_delete(description, &searchtuple->t_self, NULL);
} else {
desctuple = heap_modifytuple(searchtuple, description, values,
nulls, replaces);
setheapoverride(true);
heap_replace(description, &searchtuple->t_self, desctuple, NULL);
setheapoverride(false);
modified = TRUE;
}
} else {
desctuple = heap_formtuple(tupDesc, values, nulls);
heap_insert(description, desctuple);
modified = TRUE;
}
/*** Complete the scan, update indices, if necessary ***/
heap_endscan(scan);
if (modified) {
if (RelationGetForm(description)->relhasindex) {
Relation idescs[Num_pg_description_indices];
CatalogOpenIndices(Num_pg_description_indices,
Name_pg_description_indices, idescs);
CatalogIndexInsert(idescs, Num_pg_description_indices, description,
desctuple);
CatalogCloseIndices(Num_pg_description_indices, idescs);
}
pfree(desctuple);
}
heap_close(description, RowExclusiveLock);
}
/*------------------------------------------------------------------
* DeleteComments --
*
* This routine is used to purge any comments
* associated with the Oid handed to this routine,
* regardless of the actual object type. It is
* called, for example, when a relation is destroyed.
*------------------------------------------------------------------
*/
void DeleteComments(Oid oid) {
Relation description;
TupleDesc tupDesc;
ScanKeyData entry;
HeapScanDesc scan;
HeapTuple searchtuple;
description = heap_openr(DescriptionRelationName, RowExclusiveLock);
tupDesc = description->rd_att;
/*** Now, open pg_description and attempt to find the old tuple ***/
ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_description_objoid, F_OIDEQ,
ObjectIdGetDatum(oid));
scan = heap_beginscan(description, false, SnapshotNow, 1, &entry);
searchtuple = heap_getnext(scan, 0);
/*** If a previous tuple exists, delete it ***/
if (HeapTupleIsValid(searchtuple)) {
heap_delete(description, &searchtuple->t_self, NULL);
}
/*** Complete the scan, update indices, if necessary ***/
heap_endscan(scan);
heap_close(description, RowExclusiveLock);
}
/*------------------------------------------------------------------
* CommentRelation --
*
* This routine is used to add/drop a comment from a relation, where
* a relation is a TABLE, SEQUENCE, VIEW or INDEX. The routine simply
* finds the relation name by searching the system cache, locating
* the appropriate tuple, and inserting a comment using that
* tuple's oid. Its parameters are the relation name and comments.
*------------------------------------------------------------------
*/
void CommentRelation(int reltype, char *relname, char *comment) {
HeapTuple reltuple;
Oid oid;
char relkind;
/*** First, check object security ***/
#ifndef NO_SECURITY
if (!pg_ownercheck(GetPgUserName(), relname, RELNAME)) {
elog(ERROR, "you are not permitted to comment on class '%s'", relname);
}
#endif
/*** Now, attempt to find the oid in the cached version of pg_class ***/
reltuple = SearchSysCacheTuple(RELNAME, PointerGetDatum(relname),
0, 0, 0);
if (!HeapTupleIsValid(reltuple)) {
elog(ERROR, "relation '%s' does not exist", relname);
}
oid = reltuple->t_data->t_oid;
/*** Next, verify that the relation type matches the intent ***/
relkind = ((Form_pg_class) GETSTRUCT(reltuple))->relkind;
switch (reltype) {
case (INDEX):
if (relkind != 'i') {
elog(ERROR, "relation '%s' is not an index", relname);
}
break;
case (TABLE):
if (relkind != 'r') {
elog(ERROR, "relation '%s' is not a table", relname);
}
break;
case (VIEW):
if (relkind != 'r') {
elog(ERROR, "relation '%s' is not a view", relname);
}
break;
case (SEQUENCE):
if (relkind != 'S') {
elog(ERROR, "relation '%s' is not a sequence", relname);
}
break;
}
/*** Create the comments using the tuple's oid ***/
CreateComments(oid, comment);
}
/*------------------------------------------------------------------
* CommentAttribute --
*
* This routine is used to add/drop a comment from an attribute
* such as a table's column. The routine will check security
* restrictions and then attempt to fetch the oid of the associated
* attribute. If successful, a comment is added/dropped, else an
* elog() exception is thrown. The parameters are the relation
* and attribute names, and the comments
*------------------------------------------------------------------
*/
void CommentAttribute(char *relname, char *attrname, char *comment) {
Relation relation;
HeapTuple attrtuple;
Oid oid;
/*** First, check object security ***/
#ifndef NO_SECURITY
if (!pg_ownercheck(GetPgUserName(), relname, RELNAME)) {
elog(ERROR, "you are not permitted to comment on class '%s\'", relname);
}
#endif
/*** Now, fetch the attribute oid from the system cache ***/
relation = heap_openr(relname, AccessShareLock);
attrtuple = SearchSysCacheTuple(ATTNAME, ObjectIdGetDatum(relation->rd_id),
PointerGetDatum(attrname), 0, 0);
if (!HeapTupleIsValid(attrtuple)) {
elog(ERROR, "'%s' is not an attribute of class '%s'",
attrname, relname);
}
oid = attrtuple->t_data->t_oid;
/*** Call CreateComments() to create/drop the comments ***/
CreateComments(oid, comment);
/*** Now, close the heap relation and return ***/
heap_close(relation, AccessShareLock);
}
/*------------------------------------------------------------------
* CommentDatabase --
*
* This routine is used to add/drop any user-comments a user might
* have regarding the specified database. The routine will check
* security for owner permissions, and, if succesful, will then
* attempt to find the oid of the database specified. Once found,
* a comment is added/dropped using the CreateComments() routine.
*------------------------------------------------------------------
*/
void CommentDatabase(char *database, char *comment) {
Relation pg_database;
HeapTuple dbtuple, usertuple;
ScanKeyData entry;
HeapScanDesc scan;
Oid oid;
bool superuser;
int4 dba, userid;
char *username;
/*** First find the tuple in pg_database for the database ***/
pg_database = heap_openr(DatabaseRelationName, AccessShareLock);
ScanKeyEntryInitialize(&entry, 0, Anum_pg_database_datname,
F_NAMEEQ, NameGetDatum(database));
scan = heap_beginscan(pg_database, 0, SnapshotNow, 1, &entry);
dbtuple = heap_getnext(scan, 0);
/*** Validate database exists, and fetch the dba id and oid ***/
if (!HeapTupleIsValid(dbtuple)) {
elog(ERROR, "database '%s' does not exist", database);
}
dba = ((Form_pg_database) GETSTRUCT(dbtuple))->datdba;
oid = dbtuple->t_data->t_oid;
/*** Now, fetch user information ***/
username = GetPgUserName();
usertuple = SearchSysCacheTuple(USENAME, PointerGetDatum(username),
0, 0, 0);
if (!HeapTupleIsValid(usertuple)) {
elog(ERROR, "current user '%s' does not exist", username);
}
userid = ((Form_pg_shadow) GETSTRUCT(usertuple))->usesysid;
superuser = ((Form_pg_shadow) GETSTRUCT(usertuple))->usesuper;
/*** Allow if the userid matches the database dba or is a superuser ***/
#ifndef NO_SECURITY
if (!(superuser || (userid == dba))) {
elog(ERROR, "you are not permitted to comment on database '%s'",
database);
}
#endif
/*** Create the comments with the pg_database oid ***/
CreateComments(oid, comment);
/*** Complete the scan and close any opened relations ***/
heap_endscan(scan);
heap_close(pg_database, AccessShareLock);
}
/*------------------------------------------------------------------
* CommentRewrite --
*
* This routine is used to add/drop any user-comments a user might
* have regarding a specified RULE. The rule is specified by name
* and, if found, and the user has appropriate permissions, a
* comment will be added/dropped using the CreateComments() routine.
*------------------------------------------------------------------
*/
void CommentRewrite(char *rule, char *comment) {
HeapTuple rewritetuple;
Oid oid;
char *user, *relation;
int aclcheck;
/*** First, validate user ***/
#ifndef NO_SECURITY
user = GetPgUserName();
relation = RewriteGetRuleEventRel(rule);
aclcheck = pg_aclcheck(relation, user, ACL_RU);
if (aclcheck != ACLCHECK_OK) {
elog(ERROR, "you are not permitted to comment on rule '%s'",
rule);
}
#endif
/*** Next, find the rule's oid ***/
rewritetuple = SearchSysCacheTuple(REWRITENAME, PointerGetDatum(rule),
0, 0, 0);
if (!HeapTupleIsValid(rewritetuple)) {
elog(ERROR, "rule '%s' does not exist", rule);
}
oid = rewritetuple->t_data->t_oid;
/*** Call CreateComments() to create/drop the comments ***/
CreateComments(oid, comment);
}
/*------------------------------------------------------------------
* CommentType --
*
* This routine is used to add/drop any user-comments a user might
* have regarding a TYPE. The type is specified by name
* and, if found, and the user has appropriate permissions, a
* comment will be added/dropped using the CreateComments() routine.
* The type's name and the comments are the paramters to this routine.
*------------------------------------------------------------------
*/
void CommentType(char *type, char *comment) {
HeapTuple typetuple;
Oid oid;
char *user;
/*** First, validate user ***/
#ifndef NO_SECURITY
user = GetPgUserName();
if (!pg_ownercheck(user, type, TYPNAME)) {
elog(ERROR, "you are not permitted to comment on type '%s'",
type);
}
#endif
/*** Next, find the type's oid ***/
typetuple = SearchSysCacheTuple(TYPNAME, PointerGetDatum(type),
0, 0, 0);
if (!HeapTupleIsValid(typetuple)) {
elog(ERROR, "type '%s' does not exist", type);
}
oid = typetuple->t_data->t_oid;
/*** Call CreateComments() to create/drop the comments ***/
CreateComments(oid, comment);
}
/*------------------------------------------------------------------
* CommentAggregate --
*
* This routine is used to allow a user to provide comments on an
* aggregate function. The aggregate function is determined by both
* its name and its argument type, which, with the comments are
* the three parameters handed to this routine.
*------------------------------------------------------------------
*/
void CommentAggregate(char *aggregate, char *argument, char *comment) {
HeapTuple aggtuple;
Oid baseoid, oid;
bool defined;
char *user;
/*** First, attempt to determine the base aggregate oid ***/
if (argument) {
baseoid = TypeGet(argument, &defined);
if (!OidIsValid(baseoid)) {
elog(ERROR, "aggregate type '%s' does not exist", argument);
}
} else {
baseoid = 0;
}
/*** Next, validate the user's attempt to comment ***/
#ifndef NO_SECURITY
user = GetPgUserName();
if (!pg_aggr_ownercheck(user, aggregate, baseoid)) {
if (argument) {
elog(ERROR, "you are not permitted to comment on aggregate '%s' %s '%s'",
aggregate, "with type", argument);
} else {
elog(ERROR, "you are not permitted to comment on aggregate '%s'",
aggregate);
}
}
#endif
/*** Now, attempt to find the actual tuple in pg_aggregate ***/
aggtuple = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggregate),
ObjectIdGetDatum(baseoid), 0, 0);
if (!HeapTupleIsValid(aggtuple)) {
if (argument) {
elog(ERROR, "aggregate type '%s' does not exist for aggregate '%s'",
argument, aggregate);
} else {
elog(ERROR, "aggregate '%s' does not exist", aggregate);
}
}
oid = aggtuple->t_data->t_oid;
/*** Call CreateComments() to create/drop the comments ***/
CreateComments(oid, comment);
}
/*------------------------------------------------------------------
* CommentProc --
*
* This routine is used to allow a user to provide comments on an
* procedure (function). The procedure is determined by both
* its name and its argument list. The argument list is expected to
* be a series of parsed nodes pointed to by a List object. If the
* comments string is empty, the associated comment is dropped.
*------------------------------------------------------------------
*/
void CommentProc(char *function, List *arguments, char *comment) {
HeapTuple argtuple, functuple;
Oid oid, argoids[8];
char *user, *argument;
int i, argcount;
/*** First, initialize function's argument list with their type oids ***/
argcount = length(arguments);
if (argcount > 0) {
MemSet(argoids, 0, 8 * sizeof(Oid));
for (i = 0; i < argcount; i++) {
argument = strVal(lfirst(arguments));
arguments = lnext(arguments);
if (strcmp(argument, "opaque") == 0) {
argoids[i] = 0;
} else {
argtuple = SearchSysCacheTuple(TYPNAME, PointerGetDatum(argument),
0, 0, 0);
if (!HeapTupleIsValid(argtuple)) {
elog(ERROR, "function argument type '%s' does not exist",
argument);
}
argoids[i] = argtuple->t_data->t_oid;
}
}
}
/*** Now, validate the user's ability to comment on this function ***/
#ifndef NO_SECURITY
user = GetPgUserName();
if (!pg_func_ownercheck(user, function, argcount, argoids)) {
elog(ERROR, "you are not permitted to comment on function '%s'",
function);
}
#endif
/*** Now, find the corresponding oid for this procedure ***/
functuple = SearchSysCacheTuple(PRONAME, PointerGetDatum(function),
Int32GetDatum(argcount),
PointerGetDatum(argoids), 0);
/*** Deallocate our argument oids and check the function tuple ***/
if (!HeapTupleIsValid(functuple)) {
elog(ERROR, "function '%s' with the supplied %s does not exist",
function, "argument list");
}
oid = functuple->t_data->t_oid;
/*** Call CreateComments() to create/drop the comments ***/
CreateComments(oid, comment);
}
/*------------------------------------------------------------------
* CommentOperator --
*
* This routine is used to allow a user to provide comments on an
* operator. The operator for commenting is determined by both
* its name and its argument list which defines the left and right
* hand types the operator will operate on. The argument list is
* expected to be a couple of parse nodes pointed to be a List
* object. If the comments string is empty, the associated comment
* is dropped.
*------------------------------------------------------------------
*/
void CommentOperator(char *opername, List *arguments, char *comment) {
HeapTuple optuple;
Oid oid, leftoid = InvalidOid, rightoid = InvalidOid;
bool defined;
char oprtype = 0, *user, *lefttype = NULL, *righttype = NULL;
/*** Initialize our left and right argument types ***/
if (lfirst(arguments) != NULL) {
lefttype = strVal(lfirst(arguments));
}
if (lsecond(arguments) != NULL) {
righttype = strVal(lsecond(arguments));
}
/*** Attempt to fetch the left oid, if specified ***/
if (lefttype != NULL) {
leftoid = TypeGet(lefttype, &defined);
if (!OidIsValid(leftoid)) {
elog(ERROR, "left type '%s' does not exist", lefttype);
}
}
/*** Attempt to fetch the right oid, if specified ***/
if (righttype != NULL) {
rightoid = TypeGet(righttype, &defined);
if (!OidIsValid(rightoid)) {
elog(ERROR, "right type '%s' does not exist", righttype);
}
}
/*** Determine operator type ***/
if (OidIsValid(leftoid) && (OidIsValid(rightoid))) oprtype = 'b';
else if (OidIsValid(leftoid)) oprtype = 'l';
else if (OidIsValid(rightoid)) oprtype = 'r';
else elog(ERROR, "operator '%s' is of an illegal type'", opername);
/*** Attempt to fetch the operator oid ***/
optuple = SearchSysCacheTupleCopy(OPRNAME, PointerGetDatum(opername),
ObjectIdGetDatum(leftoid),
ObjectIdGetDatum(rightoid),
CharGetDatum(oprtype));
if (!HeapTupleIsValid(optuple)) {
elog(ERROR, "operator '%s' does not exist", opername);
}
oid = optuple->t_data->t_oid;
/*** Valid user's ability to comment on this operator ***/
#ifndef NO_SECURITY
user = GetPgUserName();
if (!pg_ownercheck(user, (char *) ObjectIdGetDatum(oid), OPROID)) {
elog(ERROR, "you are not permitted to comment on operator '%s'",
opername);
}
#endif
/*** Call CreateComments() to create/drop the comments ***/
CreateComments(oid, comment);
}
/*------------------------------------------------------------------
* CommentTrigger --
*
* This routine is used to allow a user to provide comments on a
* trigger event. The trigger for commenting is determined by both
* its name and the relation to which it refers. The arguments to this
* function are the trigger name, the relation name, and the comments
* to add/drop.
*------------------------------------------------------------------
*/
void CommentTrigger(char *trigger, char *relname, char *comment) {
Form_pg_trigger data;
Relation pg_trigger, relation;
HeapTuple triggertuple;
HeapScanDesc scan;
ScanKeyData entry;
Oid oid = InvalidOid;
char *user;
/*** First, validate the user's action ***/
#ifndef NO_SECURITY
user = GetPgUserName();
if (!pg_ownercheck(user, relname, RELNAME)) {
elog(ERROR, "you are not permitted to comment on trigger '%s' %s '%s'",
trigger, "defined for relation", relname);
}
#endif
/*** Now, fetch the trigger oid from pg_trigger ***/
relation = heap_openr(relname, AccessShareLock);
pg_trigger = heap_openr(TriggerRelationName, AccessShareLock);
ScanKeyEntryInitialize(&entry, 0, Anum_pg_trigger_tgrelid,
F_OIDEQ, RelationGetRelid(relation));
scan = heap_beginscan(pg_trigger, 0, SnapshotNow, 1, &entry);
triggertuple = heap_getnext(scan, 0);
while (HeapTupleIsValid(triggertuple)) {
data = (Form_pg_trigger) GETSTRUCT(triggertuple);
if (namestrcmp(&(data->tgname), trigger) == 0) {
oid = triggertuple->t_data->t_oid;
break;
}
triggertuple = heap_getnext(scan, 0);
}
/*** If no trigger exists for the relation specified, notify user ***/
if (oid == InvalidOid) {
elog(ERROR, "trigger '%s' defined for relation '%s' does not exist",
trigger, relname);
}
/*** Create the comments with the pg_trigger oid ***/
CreateComments(oid, comment);
/*** Complete the scan and close any opened relations ***/
heap_endscan(scan);
heap_close(pg_trigger, AccessShareLock);
heap_close(relation, AccessShareLock);
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.49 1999/10/15 01:49:39 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.50 1999/10/26 03:12:34 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -234,52 +234,6 @@ TruncateRelation(char *name)
heap_truncate(name);
}
/*------------------------------------------------------------------
* CommentRelation --
* Adds a comment to pg_description for the associated
* relation or relation attribute.
*
* Note:
* The comment is dropped on the relation or attribute if
* the comment is an empty string.
*------------------------------------------------------------------
*/
void
CommentRelation(char *relname, char *attrname, char *comments)
{
Relation relation;
HeapTuple attrtuple;
Oid oid;
/*** First ensure relname is valid ***/
relation = heap_openr(relname, AccessShareLock);
/*** Now, if an attribute was specified, fetch its oid, else use relation's oid ***/
if (attrname != NULL) {
attrtuple = SearchSysCacheTuple(ATTNAME, ObjectIdGetDatum(relation->rd_id),
PointerGetDatum(attrname), 0, 0);
if (!HeapTupleIsValid(attrtuple)) {
elog(ERROR, "CommentRelation: attribute \"%s\" is not an attribute of relation \"%s\"",
attrname, relname);
}
oid = attrtuple->t_data->t_oid;
} else {
oid = RelationGetRelid(relation);
}
/*** Call CreateComments() to create/drop the comments ***/
CreateComments(oid, comments);
/*** Now, close the heap relation ***/
heap_close(relation, AccessShareLock);
}
/*
* MergeAttributes
* Returns new schema given initial schema and supers.

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.42 1999/10/25 03:07:43 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.43 1999/10/26 03:12:34 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -20,6 +20,7 @@
#include "catalog/catname.h"
#include "catalog/pg_database.h"
#include "catalog/pg_shadow.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
#include "miscadmin.h"
#include "storage/sinval.h"
@ -149,6 +150,10 @@ destroydb(char *dbname, CommandDest dest)
dbname, db_id);
}
/*** Delete any comments associated with the database ***/
DeleteComments(db_id);
/*
* Houston, we have launch commit...
*

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.37 1999/09/18 19:06:40 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.38 1999/10/26 03:12:34 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,6 +18,7 @@
#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 "parser/parse_func.h"
@ -93,7 +94,14 @@ RemoveOperator(char *operatorName, /* operator name */
elog(ERROR, "RemoveOperator: operator '%s': permission denied",
operatorName);
#endif
/*** Delete any comments associated with this operator ***/
DeleteComments(tup->t_data->t_oid);
heap_delete(relation, &tup->t_self, NULL);
}
else
{
@ -147,8 +155,17 @@ SingleOpOperatorRemove(Oid typeOid)
{
key[0].sk_attno = attnums[i];
scan = heap_beginscan(rel, 0, SnapshotNow, 1, key);
while (HeapTupleIsValid(tup = heap_getnext(scan, 0)))
heap_delete(rel, &tup->t_self, NULL);
while (HeapTupleIsValid(tup = heap_getnext(scan, 0))) {
/*** This is apparently a routine not in use, but remove ***/
/*** any comments anyways ***/
DeleteComments(tup->t_data->t_oid);
heap_delete(rel, &tup->t_self, NULL);
}
heap_endscan(scan);
}
heap_close(rel, RowExclusiveLock);
@ -259,6 +276,11 @@ RemoveType(char *typeName) /* type name to be removed */
}
typeOid = tup->t_data->t_oid;
/*** Delete any comments associated with this type ***/
DeleteComments(typeOid);
heap_delete(relation, &tup->t_self, NULL);
/* Now, Delete the "array of" that type */
@ -347,6 +369,10 @@ RemoveFunction(char *functionName, /* function name to be removed */
elog(ERROR, "RemoveFunction: function \"%s\" is built-in", functionName);
}
/*** Delete any comments associated with this function ***/
DeleteComments(tup->t_data->t_oid);
heap_delete(relation, &tup->t_self, NULL);
heap_close(relation, RowExclusiveLock);
@ -418,6 +444,11 @@ RemoveAggregate(char *aggName, char *aggType)
aggName);
}
}
/*** Remove any comments related to this aggregate ***/
DeleteComments(tup->t_data->t_oid);
heap_delete(relation, &tup->t_self, NULL);
heap_close(relation, RowExclusiveLock);

View File

@ -16,6 +16,7 @@
#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"
@ -297,8 +298,14 @@ DropTrigger(DropTrigStmt *stmt)
if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0)
{
heap_delete(tgrel, &tuple->t_self, NULL);
tgfound++;
/*** Delete any comments associated with this trigger ***/
DeleteComments(tuple->t_data->t_oid);
heap_delete(tgrel, &tuple->t_self, NULL);
tgfound++;
}
else
found++;
@ -355,8 +362,15 @@ RelationRemoveTriggers(Relation rel)
tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0)))
heap_delete(tgrel, &tup->t_self, NULL);
while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0))) {
/*** Delete any comments associated with this trigger ***/
DeleteComments(tup->t_data->t_oid);
heap_delete(tgrel, &tup->t_self, NULL);
}
heap_endscan(tgscan);

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.109 1999/10/15 01:49:41 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.110 1999/10/26 03:12:34 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -196,7 +196,8 @@ Oid param_type(int t); /* used in parse_expr.c */
%type <boolean> opt_cursor
%type <ival> copy_dirn, def_type, opt_direction, remove_type,
opt_column, event
opt_column, event, comment_type, comment_cl,
comment_ag, comment_fn, comment_op, comment_tg
%type <ival> fetch_how_many
@ -1542,28 +1543,101 @@ TruncateStmt: TRUNCATE TABLE relation_name
/*****************************************************************************
*
* QUERY:
* comment on [ table <relname> | column <relname>.<attribute> ]
* is 'text'
* The COMMENT ON statement can take different forms based upon the type of
* the object associated with the comment. The form of the statement is:
*
* COMMENT ON [ [ DATABASE | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ]
* <objname> | AGGREGATE <aggname> <aggtype> | FUNCTION
* <funcname> (arg1, arg2, ...) | OPERATOR <op>
* (leftoperand_typ rightoperand_typ) | TRIGGER <triggername> ON
* <relname> ] IS 'text'
*
*****************************************************************************/
CommentStmt: COMMENT ON COLUMN relation_name '.' attr_name IS Sconst
CommentStmt: COMMENT ON comment_type name IS Sconst
{
CommentStmt *n = makeNode(CommentStmt);
n->relname = $4;
n->attrname = $6;
n->comment = $8;
$$ = (Node *) n;
}
| COMMENT ON TABLE relation_name IS Sconst
{
CommentStmt *n = makeNode(CommentStmt);
n->relname = $4;
n->attrname = NULL;
n->objtype = $3;
n->objname = $4;
n->objproperty = NULL;
n->objlist = NULL;
n->comment = $6;
$$ = (Node *) n;
}
| COMMENT ON comment_cl relation_name '.' attr_name IS Sconst
{
CommentStmt *n = makeNode(CommentStmt);
n->objtype = $3;
n->objname = $4;
n->objproperty = $6;
n->objlist = NULL;
n->comment = $8;
$$ = (Node *) n;
}
| COMMENT ON comment_ag name aggr_argtype IS Sconst
{
CommentStmt *n = makeNode(CommentStmt);
n->objtype = $3;
n->objname = $4;
n->objproperty = $5;
n->objlist = NULL;
n->comment = $7;
$$ = (Node *) n;
}
| COMMENT ON comment_fn func_name func_args IS Sconst
{
CommentStmt *n = makeNode(CommentStmt);
n->objtype = $3;
n->objname = $4;
n->objproperty = NULL;
n->objlist = $5;
n->comment = $7;
$$ = (Node *) n;
}
| COMMENT ON comment_op all_Op '(' oper_argtypes ')' IS Sconst
{
CommentStmt *n = makeNode(CommentStmt);
n->objtype = $3;
n->objname = $4;
n->objproperty = NULL;
n->objlist = $6;
n->comment = $9;
$$ = (Node *) n;
}
| COMMENT ON comment_tg name ON relation_name IS Sconst
{
CommentStmt *n = makeNode(CommentStmt);
n->objtype = $3;
n->objname = $4;
n->objproperty = $6;
n->objlist = NULL;
n->comment = $8;
$$ = (Node *) n;
}
;
comment_type: DATABASE { $$ = DATABASE; }
| INDEX { $$ = INDEX; }
| RULE { $$ = RULE; }
| SEQUENCE { $$ = SEQUENCE; }
| TABLE { $$ = TABLE; }
| TYPE_P { $$ = TYPE_P; }
| VIEW { $$ = VIEW; }
;
comment_cl: COLUMN { $$ = COLUMN; }
;
comment_ag: AGGREGATE { $$ = AGGREGATE; }
;
comment_fn: FUNCTION { $$ = FUNCTION; }
;
comment_op: OPERATOR { $$ = OPERATOR; }
;
comment_tg: TRIGGER { $$ = TRIGGER; }
;
/*****************************************************************************

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.29 1999/09/18 19:07:19 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.30 1999/10/26 03:12:35 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "catalog/catname.h"
#include "catalog/pg_rewrite.h"
#include "commands/comment.h"
#include "rewrite/rewriteRemove.h"
#include "rewrite/rewriteSupport.h"
#include "utils/syscache.h"
@ -119,6 +120,13 @@ RemoveRewriteRule(char *ruleName)
*/
prs2_deleteFromRelation(eventRelationOid, ruleId);
/*
* Delete any comments associated with this rule
*
*/
DeleteComments(ruleId);
/*
* Now delete the tuple...
*/
@ -158,8 +166,15 @@ RelationRemoveRules(Oid relid)
scanDesc = heap_beginscan(RewriteRelation,
0, SnapshotNow, 1, &scanKeyData);
while (HeapTupleIsValid(tuple = heap_getnext(scanDesc, 0)))
heap_delete(RewriteRelation, &tuple->t_self, NULL);
while (HeapTupleIsValid(tuple = heap_getnext(scanDesc, 0))) {
/*** Delete any comments associated with this relation ***/
DeleteComments(tuple->t_data->t_oid);
heap_delete(RewriteRelation, &tuple->t_self, NULL);
}
heap_endscan(scanDesc);
heap_close(RewriteRelation, RowExclusiveLock);

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.70 1999/10/15 01:49:43 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.71 1999/10/26 03:12:36 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -21,6 +21,7 @@
#include "commands/async.h"
#include "commands/cluster.h"
#include "commands/command.h"
#include "commands/comment.h"
#include "commands/copy.h"
#include "commands/creatinh.h"
#include "commands/dbcommands.h"
@ -38,7 +39,6 @@
#include "rewrite/rewriteRemove.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/acl.h"
#include "utils/ps_status.h"
#include "utils/syscache.h"
@ -242,14 +242,9 @@ ProcessUtility(Node *parsetree,
PS_SET_STATUS(commandTag = "COMMENT");
CHECK_IF_ABORTED();
#ifndef NO_SECURITY
if (!pg_ownercheck(userName, statement->relname, RELNAME))
elog(ERROR, "you do not own class \"%s\"", statement->relname);
#endif
CommentRelation(statement->relname, statement->attrname,
statement->comment);
CommentObject(statement->objtype, statement->objname,
statement->objproperty, statement->objlist,
statement->comment);
}
break;

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: heap.h,v 1.23 1999/10/15 01:49:44 momjian Exp $
* $Id: heap.h,v 1.24 1999/10/26 03:12:37 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -21,23 +21,20 @@ typedef struct RawColumnDefault
Node *raw_default; /* default value (untransformed parse tree) */
} RawColumnDefault;
extern Oid RelnameFindRelid(char *relname);
extern Oid RelnameFindRelid(char *relname);
extern Relation heap_create(char *relname, TupleDesc att,
bool isnoname, bool istemp);
bool isnoname, bool istemp);
extern Oid heap_create_with_catalog(char *relname,
TupleDesc tupdesc, char relkind, bool istemp);
extern Oid heap_create_with_catalog(char *relname, TupleDesc tupdesc,
char relkind, bool istemp);
extern void heap_destroy_with_catalog(char *relname);
extern void heap_truncate(char *relname);
extern void heap_destroy(Relation rel);
extern void CreateComments(Oid object, char *comments);
extern void DeleteComments(Oid object);
extern void AddRelationRawConstraints(Relation rel,
List *rawColDefaults,
List *rawConstraints);
List *rawColDefaults,
List *rawConstraints);
extern void InitNoNameRelList(void);
extern void DestroyNoNameRels(void);

View File

@ -0,0 +1,31 @@
/*-------------------------------------------------------------------------
*
* comment.h
*
* Prototypes for functions in commands/comment.c
*
* Copyright (c) 1999, PostgreSQL Global Development Group
*
*-------------------------------------------------------------------------
*/
#ifndef COMMENT_H
#define COMMENT_H
/*------------------------------------------------------------------
* Function Prototypes --
*
* The following protoypes define the public functions of the comment
* related routines. CreateComments() is used to create/drop a comment
* for any object with a valid oid. DeleteComments() deletes, if any,
* the comments associated with the object. CommentObject() is used to
* create comments to be identified by the specific type.
*------------------------------------------------------------------
*/
void CreateComments(Oid oid, char *comment);
void DeleteComments(Oid oid);
void CommentObject(int objtype, char *objname, char *objproperty,
List *objlist, char *comment);
#endif /* COMMENT_H */

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: creatinh.h,v 1.11 1999/10/15 01:49:46 momjian Exp $
* $Id: creatinh.h,v 1.12 1999/10/26 03:12:38 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,6 +18,5 @@
extern void DefineRelation(CreateStmt *stmt, char relkind);
extern void RemoveRelation(char *name);
extern void TruncateRelation(char *name);
extern void CommentRelation(char *name, char *attr, char *comment);
#endif /* CREATINH_H */

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: parsenodes.h,v 1.85 1999/10/15 01:49:47 momjian Exp $
* $Id: parsenodes.h,v 1.86 1999/10/26 03:12:39 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -316,10 +316,12 @@ typedef struct TruncateStmt
*/
typedef struct CommentStmt
{
NodeTag type;
char *relname; /* relation to create/drop comment */
char *attrname; /* attribute to comment on */
char *comment; /* the actual comment */
NodeTag type;
int objtype; /* Object's type */
char *objname; /* Name of the object */
char *objproperty; /* Property Id (such as column) */
List *objlist; /* Arguments for VAL objects */
char *comment; /* The comment to insert */
} CommentStmt;
/* ----------------------