1999-10-26 05:12:39 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* comment.c
|
2000-01-10 18:14:46 +01:00
|
|
|
*
|
1999-10-26 05:12:39 +02:00
|
|
|
* PostgreSQL object comments utility code.
|
|
|
|
*
|
2001-06-13 23:44:41 +02:00
|
|
|
* Copyright (c) 1999-2001, PostgreSQL Global Development Group
|
1999-10-26 05:12:39 +02:00
|
|
|
*
|
2001-01-23 05:32:23 +01:00
|
|
|
* IDENTIFICATION
|
2001-08-10 20:57:42 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.32 2001/08/10 18:57:34 tgl Exp $
|
2001-01-23 05:32:23 +01:00
|
|
|
*
|
1999-10-26 05:12:39 +02:00
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
#include "access/genam.h"
|
1999-10-26 05:12:39 +02:00
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/catname.h"
|
|
|
|
#include "catalog/indexing.h"
|
|
|
|
#include "catalog/pg_database.h"
|
|
|
|
#include "catalog/pg_description.h"
|
2000-01-18 19:09:02 +01:00
|
|
|
#include "catalog/pg_operator.h"
|
1999-10-26 05:12:39 +02:00
|
|
|
#include "catalog/pg_trigger.h"
|
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "commands/comment.h"
|
|
|
|
#include "miscadmin.h"
|
2000-10-07 02:58:23 +02:00
|
|
|
#include "parser/parse_expr.h"
|
|
|
|
#include "parser/parse_func.h"
|
2001-06-25 23:11:45 +02:00
|
|
|
#include "parser/parse.h"
|
1999-10-26 05:12:39 +02:00
|
|
|
#include "rewrite/rewriteRemove.h"
|
|
|
|
#include "utils/acl.h"
|
2001-06-25 23:11:45 +02:00
|
|
|
#include "utils/builtins.h"
|
2000-05-28 19:56:29 +02:00
|
|
|
#include "utils/fmgroids.h"
|
2001-08-10 20:57:42 +02:00
|
|
|
#include "utils/lsyscache.h"
|
1999-10-26 05:12:39 +02:00
|
|
|
#include "utils/syscache.h"
|
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
* Static Function Prototypes --
|
2000-01-10 18:14:46 +01:00
|
|
|
*
|
|
|
|
* 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
|
1999-10-26 05:12:39 +02:00
|
|
|
* 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);
|
2000-10-07 02:58:23 +02:00
|
|
|
static void CommentAggregate(char *aggregate, List *arguments, char *comment);
|
1999-10-26 05:12:39 +02:00
|
|
|
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);
|
2001-08-10 20:57:42 +02:00
|
|
|
|
1999-10-26 05:12:39 +02:00
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
* CommentObject --
|
|
|
|
*
|
2000-01-10 18:14:46 +01:00
|
|
|
* 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
|
1999-10-26 05:12:39 +02:00
|
|
|
* error is thrown. Otherwise the comment is added to pg_description
|
2001-08-10 20:57:42 +02:00
|
|
|
* by calling the CreateComments() routine. If the comment string is
|
1999-10-26 05:12:39 +02:00
|
|
|
* empty, CreateComments() will drop any comments associated with
|
|
|
|
* the object.
|
|
|
|
*------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
void
|
|
|
|
CommentObject(int objtype, char *objname, char *objproperty,
|
|
|
|
List *objlist, char *comment)
|
|
|
|
{
|
|
|
|
switch (objtype)
|
|
|
|
{
|
2001-08-10 20:57:42 +02:00
|
|
|
case INDEX:
|
|
|
|
case SEQUENCE:
|
|
|
|
case TABLE:
|
|
|
|
case VIEW:
|
2000-04-12 19:17:23 +02:00
|
|
|
CommentRelation(objtype, objname, comment);
|
|
|
|
break;
|
2001-08-10 20:57:42 +02:00
|
|
|
case COLUMN:
|
2000-04-12 19:17:23 +02:00
|
|
|
CommentAttribute(objname, objproperty, comment);
|
|
|
|
break;
|
2001-08-10 20:57:42 +02:00
|
|
|
case DATABASE:
|
2000-04-12 19:17:23 +02:00
|
|
|
CommentDatabase(objname, comment);
|
|
|
|
break;
|
2001-08-10 20:57:42 +02:00
|
|
|
case RULE:
|
2000-04-12 19:17:23 +02:00
|
|
|
CommentRewrite(objname, comment);
|
|
|
|
break;
|
2001-08-10 20:57:42 +02:00
|
|
|
case TYPE_P:
|
2000-04-12 19:17:23 +02:00
|
|
|
CommentType(objname, comment);
|
|
|
|
break;
|
2001-08-10 20:57:42 +02:00
|
|
|
case AGGREGATE:
|
2000-10-07 02:58:23 +02:00
|
|
|
CommentAggregate(objname, objlist, comment);
|
2000-04-12 19:17:23 +02:00
|
|
|
break;
|
2001-08-10 20:57:42 +02:00
|
|
|
case FUNCTION:
|
2000-04-12 19:17:23 +02:00
|
|
|
CommentProc(objname, objlist, comment);
|
|
|
|
break;
|
2001-08-10 20:57:42 +02:00
|
|
|
case OPERATOR:
|
2000-04-12 19:17:23 +02:00
|
|
|
CommentOperator(objname, objlist, comment);
|
|
|
|
break;
|
2001-08-10 20:57:42 +02:00
|
|
|
case TRIGGER:
|
2000-04-12 19:17:23 +02:00
|
|
|
CommentTrigger(objname, objproperty, comment);
|
|
|
|
break;
|
|
|
|
default:
|
2001-08-10 20:57:42 +02:00
|
|
|
elog(ERROR, "An attempt was made to comment on a unknown type: %d",
|
2000-04-12 19:17:23 +02:00
|
|
|
objtype);
|
|
|
|
}
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
* CreateComments --
|
2000-01-10 18:14:46 +01:00
|
|
|
*
|
2001-08-10 20:57:42 +02:00
|
|
|
* Create a comment for the specified object descriptor. Inserts a new
|
|
|
|
* pg_description tuple, or replaces an existing one with the same key.
|
1999-10-26 05:12:39 +02:00
|
|
|
*
|
2001-08-10 20:57:42 +02:00
|
|
|
* If the comment given is null or an empty string, instead delete any
|
|
|
|
* existing comment for the specified key.
|
1999-10-26 05:12:39 +02:00
|
|
|
*------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
void
|
|
|
|
CreateComments(Oid oid, Oid classoid, int32 subid, char *comment)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
Relation description;
|
2001-08-10 20:57:42 +02:00
|
|
|
Relation descriptionindex;
|
|
|
|
ScanKeyData skey[3];
|
|
|
|
IndexScanDesc sd;
|
|
|
|
RetrieveIndexResult indexRes;
|
|
|
|
HeapTupleData oldtuple;
|
|
|
|
Buffer buffer;
|
|
|
|
HeapTuple newtuple = NULL;
|
2000-04-12 19:17:23 +02:00
|
|
|
Datum values[Natts_pg_description];
|
|
|
|
char nulls[Natts_pg_description];
|
|
|
|
char replaces[Natts_pg_description];
|
|
|
|
int i;
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Reduce empty-string to NULL case */
|
|
|
|
if (comment != NULL && strlen(comment) == 0)
|
|
|
|
comment = NULL;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Prepare to form or update a tuple, if necessary */
|
|
|
|
if (comment != NULL)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
for (i = 0; i < Natts_pg_description; i++)
|
|
|
|
{
|
|
|
|
nulls[i] = ' ';
|
|
|
|
replaces[i] = 'r';
|
|
|
|
}
|
|
|
|
i = 0;
|
|
|
|
values[i++] = ObjectIdGetDatum(oid);
|
2001-08-10 20:57:42 +02:00
|
|
|
values[i++] = ObjectIdGetDatum(classoid);
|
|
|
|
values[i++] = Int32GetDatum(subid);
|
2000-07-06 01:12:09 +02:00
|
|
|
values[i++] = DirectFunctionCall1(textin, CStringGetDatum(comment));
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Open pg_description and its index */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
description = heap_openr(DescriptionRelationName, RowExclusiveLock);
|
|
|
|
descriptionindex = index_openr(DescriptionObjIndex);
|
|
|
|
|
|
|
|
/* Use the index to search for a matching old tuple */
|
|
|
|
|
|
|
|
ScanKeyEntryInitialize(&skey[0],
|
|
|
|
(bits16) 0x0,
|
|
|
|
(AttrNumber) 1,
|
|
|
|
(RegProcedure) F_OIDEQ,
|
2000-04-12 19:17:23 +02:00
|
|
|
ObjectIdGetDatum(oid));
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
ScanKeyEntryInitialize(&skey[1],
|
|
|
|
(bits16) 0x0,
|
|
|
|
(AttrNumber) 2,
|
|
|
|
(RegProcedure) F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classoid));
|
|
|
|
|
|
|
|
ScanKeyEntryInitialize(&skey[2],
|
|
|
|
(bits16) 0x0,
|
|
|
|
(AttrNumber) 3,
|
|
|
|
(RegProcedure) F_INT4EQ,
|
|
|
|
Int32GetDatum(subid));
|
|
|
|
|
|
|
|
sd = index_beginscan(descriptionindex, false, 3, skey);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
oldtuple.t_datamcxt = CurrentMemoryContext;
|
|
|
|
oldtuple.t_data = NULL;
|
|
|
|
|
|
|
|
while ((indexRes = index_getnext(sd, ForwardScanDirection)))
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2001-08-10 20:57:42 +02:00
|
|
|
oldtuple.t_self = indexRes->heap_iptr;
|
|
|
|
heap_fetch(description, SnapshotNow, &oldtuple, &buffer, sd);
|
|
|
|
pfree(indexRes);
|
|
|
|
|
|
|
|
if (oldtuple.t_data == NULL)
|
|
|
|
continue; /* time qual failed */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Found the old tuple, so delete or update it */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
if (comment == NULL)
|
|
|
|
simple_heap_delete(description, &oldtuple.t_self);
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
|
|
|
{
|
2001-08-10 20:57:42 +02:00
|
|
|
newtuple = heap_modifytuple(&oldtuple, description, values,
|
|
|
|
nulls, replaces);
|
|
|
|
simple_heap_update(description, &oldtuple.t_self, newtuple);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
ReleaseBuffer(buffer);
|
|
|
|
break; /* Assume there can be only one match */
|
2000-01-20 16:13:19 +01:00
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
index_endscan(sd);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* If we didn't find an old tuple, insert a new one */
|
2000-01-20 16:13:19 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
if (oldtuple.t_data == NULL && comment != NULL)
|
|
|
|
{
|
|
|
|
newtuple = heap_formtuple(RelationGetDescr(description),
|
|
|
|
values, nulls);
|
|
|
|
heap_insert(description, newtuple);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Update indexes, if necessary */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
if (newtuple != NULL)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
if (RelationGetForm(description)->relhasindex)
|
|
|
|
{
|
|
|
|
Relation idescs[Num_pg_description_indices];
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
CatalogOpenIndices(Num_pg_description_indices,
|
|
|
|
Name_pg_description_indices, idescs);
|
|
|
|
CatalogIndexInsert(idescs, Num_pg_description_indices, description,
|
2001-08-10 20:57:42 +02:00
|
|
|
newtuple);
|
2000-04-12 19:17:23 +02:00
|
|
|
CatalogCloseIndices(Num_pg_description_indices, idescs);
|
|
|
|
}
|
2001-08-10 20:57:42 +02:00
|
|
|
heap_freetuple(newtuple);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Done */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
index_close(descriptionindex);
|
|
|
|
heap_close(description, NoLock);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
|
|
|
|
2000-01-10 18:14:46 +01:00
|
|
|
/*------------------------------------------------------------------
|
1999-10-26 05:12:39 +02:00
|
|
|
* DeleteComments --
|
|
|
|
*
|
2001-08-10 20:57:42 +02:00
|
|
|
* 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.
|
1999-10-26 05:12:39 +02:00
|
|
|
*------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
void
|
2001-08-10 20:57:42 +02:00
|
|
|
DeleteComments(Oid oid, Oid classoid)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
Relation description;
|
2001-08-10 20:57:42 +02:00
|
|
|
Relation descriptionindex;
|
|
|
|
ScanKeyData skey[2];
|
|
|
|
IndexScanDesc sd;
|
|
|
|
RetrieveIndexResult indexRes;
|
|
|
|
HeapTupleData oldtuple;
|
|
|
|
Buffer buffer;
|
|
|
|
|
|
|
|
/* Open pg_description and its index */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
description = heap_openr(DescriptionRelationName, RowExclusiveLock);
|
2001-08-10 20:57:42 +02:00
|
|
|
descriptionindex = index_openr(DescriptionObjIndex);
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Use the index to search for all matching old tuples */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
ScanKeyEntryInitialize(&skey[0],
|
|
|
|
(bits16) 0x0,
|
|
|
|
(AttrNumber) 1,
|
|
|
|
(RegProcedure) F_OIDEQ,
|
2000-04-12 19:17:23 +02:00
|
|
|
ObjectIdGetDatum(oid));
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
ScanKeyEntryInitialize(&skey[1],
|
|
|
|
(bits16) 0x0,
|
|
|
|
(AttrNumber) 2,
|
|
|
|
(RegProcedure) F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(classoid));
|
|
|
|
|
|
|
|
sd = index_beginscan(descriptionindex, false, 2, skey);
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
while ((indexRes = index_getnext(sd, ForwardScanDirection)))
|
|
|
|
{
|
|
|
|
oldtuple.t_self = indexRes->heap_iptr;
|
|
|
|
heap_fetch(description, SnapshotNow, &oldtuple, &buffer, sd);
|
|
|
|
pfree(indexRes);
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
if (oldtuple.t_data == NULL)
|
|
|
|
continue; /* time qual failed */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
simple_heap_delete(description, &oldtuple.t_self);
|
|
|
|
|
|
|
|
ReleaseBuffer(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Done */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
index_endscan(sd);
|
|
|
|
index_close(descriptionindex);
|
|
|
|
heap_close(description, NoLock);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
2000-01-10 18:14:46 +01:00
|
|
|
|
1999-10-26 05:12:39 +02:00
|
|
|
/*------------------------------------------------------------------
|
|
|
|
* CommentRelation --
|
|
|
|
*
|
2000-01-10 18:14:46 +01:00
|
|
|
* This routine is used to add/drop a comment from a relation, where
|
1999-10-26 05:12:39 +02:00
|
|
|
* a relation is a TABLE, SEQUENCE, VIEW or INDEX. The routine simply
|
2000-01-10 18:14:46 +01:00
|
|
|
* finds the relation name by searching the system cache, locating
|
|
|
|
* the appropriate tuple, and inserting a comment using that
|
1999-10-26 05:12:39 +02:00
|
|
|
* tuple's oid. Its parameters are the relation name and comments.
|
|
|
|
*------------------------------------------------------------------
|
2001-08-10 20:57:42 +02:00
|
|
|
*/
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
static void
|
|
|
|
CommentRelation(int reltype, char *relname, char *comment)
|
|
|
|
{
|
2001-08-10 20:57:42 +02:00
|
|
|
Relation relation;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* First, check object security */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-09-06 16:15:31 +02:00
|
|
|
if (!pg_ownercheck(GetUserId(), relname, RELNAME))
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "you are not permitted to comment on class '%s'", relname);
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/*
|
|
|
|
* Open the relation. We do this mainly to acquire a lock that ensures
|
|
|
|
* no one else drops the relation before we commit. (If they did, they'd
|
|
|
|
* fail to remove the entry we are about to make in pg_description.)
|
|
|
|
*
|
|
|
|
* heap_openr will complain if it's an index, so we must do this:
|
|
|
|
*/
|
|
|
|
if (reltype != INDEX)
|
|
|
|
relation = heap_openr(relname, AccessShareLock);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
relation = index_openr(relname);
|
|
|
|
LockRelation(relation, AccessShareLock);
|
|
|
|
}
|
2000-11-16 23:30:52 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Next, verify that the relation type matches the intent */
|
2000-11-16 23:30:52 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
switch (reltype)
|
|
|
|
{
|
2001-08-10 20:57:42 +02:00
|
|
|
case INDEX:
|
|
|
|
if (relation->rd_rel->relkind != RELKIND_INDEX)
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "relation '%s' is not an index", relname);
|
|
|
|
break;
|
2001-08-10 20:57:42 +02:00
|
|
|
case TABLE:
|
|
|
|
if (relation->rd_rel->relkind != RELKIND_RELATION)
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "relation '%s' is not a table", relname);
|
|
|
|
break;
|
2001-08-10 20:57:42 +02:00
|
|
|
case VIEW:
|
|
|
|
if (relation->rd_rel->relkind != RELKIND_VIEW)
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "relation '%s' is not a view", relname);
|
|
|
|
break;
|
2001-08-10 20:57:42 +02:00
|
|
|
case SEQUENCE:
|
|
|
|
if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "relation '%s' is not a sequence", relname);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Create the comment using the relation's oid */
|
|
|
|
|
|
|
|
CreateComments(RelationGetRelid(relation), RelOid_pg_class, 0, comment);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Done, but hold lock until commit */
|
|
|
|
|
|
|
|
if (reltype != INDEX)
|
|
|
|
heap_close(relation, NoLock);
|
|
|
|
else
|
|
|
|
index_close(relation);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
* CommentAttribute --
|
|
|
|
*
|
|
|
|
* This routine is used to add/drop a comment from an attribute
|
2000-01-10 18:14:46 +01:00
|
|
|
* such as a table's column. The routine will check security
|
2001-08-10 20:57:42 +02:00
|
|
|
* restrictions and then attempt to look up the specified
|
1999-10-26 05:12:39 +02:00
|
|
|
* attribute. If successful, a comment is added/dropped, else an
|
2000-04-12 19:17:23 +02:00
|
|
|
* elog() exception is thrown. The parameters are the relation
|
2000-01-10 18:14:46 +01:00
|
|
|
* and attribute names, and the comments
|
1999-10-26 05:12:39 +02:00
|
|
|
*------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
static void
|
|
|
|
CommentAttribute(char *relname, char *attrname, char *comment)
|
|
|
|
{
|
|
|
|
Relation relation;
|
2001-08-10 20:57:42 +02:00
|
|
|
AttrNumber attnum;
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* First, check object security */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2000-09-06 16:15:31 +02:00
|
|
|
if (!pg_ownercheck(GetUserId(), relname, RELNAME))
|
2001-08-10 20:57:42 +02:00
|
|
|
elog(ERROR, "you are not permitted to comment on class '%s'", relname);
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
/* Open the containing relation to ensure it won't go away meanwhile */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
relation = heap_openr(relname, AccessShareLock);
|
2000-11-16 23:30:52 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Now, fetch the attribute number from the system cache */
|
2000-11-16 23:30:52 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
attnum = get_attnum(RelationGetRelid(relation), attrname);
|
|
|
|
if (attnum == InvalidAttrNumber)
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "'%s' is not an attribute of class '%s'",
|
|
|
|
attrname, relname);
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Create the comment using the relation's oid */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
CreateComments(RelationGetRelid(relation), RelOid_pg_class,
|
|
|
|
(int32) attnum, comment);
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Done, but hold lock until commit */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2000-11-08 23:10:03 +01:00
|
|
|
heap_close(relation, NoLock);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
* CommentDatabase --
|
|
|
|
*
|
|
|
|
* This routine is used to add/drop any user-comments a user might
|
2000-01-10 18:14:46 +01:00
|
|
|
* 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.
|
1999-10-26 05:12:39 +02:00
|
|
|
*------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
static void
|
|
|
|
CommentDatabase(char *database, char *comment)
|
|
|
|
{
|
|
|
|
Relation pg_database;
|
|
|
|
ScanKeyData entry;
|
|
|
|
HeapScanDesc scan;
|
2001-06-13 23:44:41 +02:00
|
|
|
HeapTuple dbtuple;
|
2000-04-12 19:17:23 +02:00
|
|
|
Oid oid;
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* First find the tuple in pg_database for the database */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
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);
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Validate database exists, and fetch the db oid */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!HeapTupleIsValid(dbtuple))
|
|
|
|
elog(ERROR, "database '%s' does not exist", database);
|
|
|
|
oid = dbtuple->t_data->t_oid;
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Allow if the user matches the database dba or is a superuser */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2001-06-13 23:44:41 +02:00
|
|
|
if (!(superuser() || is_dbadmin(oid)))
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "you are not permitted to comment on database '%s'",
|
|
|
|
database);
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Create the comments with the pg_database oid */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
CreateComments(oid, RelOid_pg_database, 0, comment);
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Complete the scan and close any opened relations */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
heap_endscan(scan);
|
|
|
|
heap_close(pg_database, AccessShareLock);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
* 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
|
2000-01-10 18:14:46 +01:00
|
|
|
* and, if found, and the user has appropriate permissions, a
|
1999-10-26 05:12:39 +02:00
|
|
|
* comment will be added/dropped using the CreateComments() routine.
|
|
|
|
*------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
static void
|
|
|
|
CommentRewrite(char *rule, char *comment)
|
|
|
|
{
|
|
|
|
Oid oid;
|
2001-08-10 20:57:42 +02:00
|
|
|
Oid classoid;
|
2000-09-06 16:15:31 +02:00
|
|
|
char *relation;
|
2000-04-12 19:17:23 +02:00
|
|
|
int aclcheck;
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* First, validate user */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
relation = RewriteGetRuleEventRel(rule);
|
2001-05-27 11:59:30 +02:00
|
|
|
aclcheck = pg_aclcheck(relation, GetUserId(), ACL_RULE);
|
2000-04-12 19:17:23 +02:00
|
|
|
if (aclcheck != ACLCHECK_OK)
|
|
|
|
elog(ERROR, "you are not permitted to comment on rule '%s'",
|
|
|
|
rule);
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Next, find the rule's oid */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
oid = GetSysCacheOid(RULENAME,
|
|
|
|
PointerGetDatum(rule),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!OidIsValid(oid))
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "rule '%s' does not exist", rule);
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* pg_rewrite doesn't have a hard-coded OID, so must look it up */
|
|
|
|
|
|
|
|
classoid = GetSysCacheOid(RELNAME,
|
|
|
|
PointerGetDatum(RewriteRelationName),
|
|
|
|
0, 0, 0);
|
|
|
|
Assert(OidIsValid(classoid));
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Call CreateComments() to create/drop the comments */
|
|
|
|
|
|
|
|
CreateComments(oid, classoid, 0, comment);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
* CommentType --
|
|
|
|
*
|
|
|
|
* This routine is used to add/drop any user-comments a user might
|
|
|
|
* have regarding a TYPE. The type is specified by name
|
2000-01-10 18:14:46 +01:00
|
|
|
* and, if found, and the user has appropriate permissions, a
|
1999-10-26 05:12:39 +02:00
|
|
|
* comment will be added/dropped using the CreateComments() routine.
|
|
|
|
* The type's name and the comments are the paramters to this routine.
|
|
|
|
*------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
static void
|
|
|
|
CommentType(char *type, char *comment)
|
|
|
|
{
|
|
|
|
Oid oid;
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* First, validate user */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2000-09-06 16:15:31 +02:00
|
|
|
if (!pg_ownercheck(GetUserId(), type, TYPENAME))
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "you are not permitted to comment on type '%s'",
|
|
|
|
type);
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Next, find the type's oid */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
oid = GetSysCacheOid(TYPENAME,
|
|
|
|
PointerGetDatum(type),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!OidIsValid(oid))
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "type '%s' does not exist", type);
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Call CreateComments() to create/drop the comments */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
CreateComments(oid, RelOid_pg_type, 0, comment);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
* CommentAggregate --
|
|
|
|
*
|
2000-01-10 18:14:46 +01:00
|
|
|
* This routine is used to allow a user to provide comments on an
|
1999-10-26 05:12:39 +02:00
|
|
|
* aggregate function. The aggregate function is determined by both
|
2000-01-10 18:14:46 +01:00
|
|
|
* its name and its argument type, which, with the comments are
|
1999-10-26 05:12:39 +02:00
|
|
|
* the three parameters handed to this routine.
|
|
|
|
*------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
static void
|
2000-10-07 02:58:23 +02:00
|
|
|
CommentAggregate(char *aggregate, List *arguments, char *comment)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2000-10-07 02:58:23 +02:00
|
|
|
TypeName *aggtype = (TypeName *) lfirst(arguments);
|
|
|
|
char *aggtypename = NULL;
|
2000-04-12 19:17:23 +02:00
|
|
|
Oid baseoid,
|
|
|
|
oid;
|
2001-08-10 20:57:42 +02:00
|
|
|
Oid classoid;
|
2000-04-12 19:17:23 +02:00
|
|
|
bool defined;
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* First, attempt to determine the base aggregate oid */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-10-07 02:58:23 +02:00
|
|
|
if (aggtype)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2000-10-07 02:58:23 +02:00
|
|
|
aggtypename = TypeNameToInternalName(aggtype);
|
|
|
|
baseoid = TypeGet(aggtypename, &defined);
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!OidIsValid(baseoid))
|
2000-10-07 02:58:23 +02:00
|
|
|
elog(ERROR, "type '%s' does not exist", aggtypename);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
else
|
2001-08-10 20:57:42 +02:00
|
|
|
baseoid = InvalidOid;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Next, validate the user's attempt to comment */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-09-06 16:15:31 +02:00
|
|
|
if (!pg_aggr_ownercheck(GetUserId(), aggregate, baseoid))
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2000-10-07 02:58:23 +02:00
|
|
|
if (aggtypename)
|
2001-08-10 20:57:42 +02:00
|
|
|
elog(ERROR, "you are not permitted to comment on aggregate '%s' with type '%s'",
|
|
|
|
aggregate, aggtypename);
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
|
|
|
elog(ERROR, "you are not permitted to comment on aggregate '%s'",
|
|
|
|
aggregate);
|
|
|
|
}
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Now, attempt to find the actual tuple in pg_aggregate */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
oid = GetSysCacheOid(AGGNAME,
|
|
|
|
PointerGetDatum(aggregate),
|
|
|
|
ObjectIdGetDatum(baseoid),
|
|
|
|
0, 0);
|
|
|
|
if (!OidIsValid(oid))
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2000-10-07 02:58:23 +02:00
|
|
|
if (aggtypename)
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "aggregate type '%s' does not exist for aggregate '%s'",
|
2000-10-07 02:58:23 +02:00
|
|
|
aggtypename, aggregate);
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
|
|
|
elog(ERROR, "aggregate '%s' does not exist", aggregate);
|
|
|
|
}
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* pg_aggregate doesn't have a hard-coded OID, so must look it up */
|
|
|
|
|
|
|
|
classoid = GetSysCacheOid(RELNAME,
|
|
|
|
PointerGetDatum(AggregateRelationName),
|
|
|
|
0, 0, 0);
|
|
|
|
Assert(OidIsValid(classoid));
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Call CreateComments() to create/drop the comments */
|
|
|
|
|
|
|
|
CreateComments(oid, classoid, 0, comment);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
* CommentProc --
|
|
|
|
*
|
2000-01-10 18:14:46 +01:00
|
|
|
* This routine is used to allow a user to provide comments on an
|
1999-10-26 05:12:39 +02:00
|
|
|
* procedure (function). The procedure is determined by both
|
2000-01-10 18:14:46 +01:00
|
|
|
* its name and its argument list. The argument list is expected to
|
1999-10-26 05:12:39 +02:00
|
|
|
* be a series of parsed nodes pointed to by a List object. If the
|
|
|
|
* comments string is empty, the associated comment is dropped.
|
|
|
|
*------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
static void
|
|
|
|
CommentProc(char *function, List *arguments, char *comment)
|
2000-01-12 05:59:41 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
Oid oid,
|
|
|
|
argoids[FUNC_MAX_ARGS];
|
|
|
|
int i,
|
|
|
|
argcount;
|
2000-01-12 05:59:41 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* First, initialize function's argument list with their type oids */
|
2000-01-12 05:59:41 +01:00
|
|
|
|
|
|
|
MemSet(argoids, 0, FUNC_MAX_ARGS * sizeof(Oid));
|
|
|
|
argcount = length(arguments);
|
|
|
|
if (argcount > FUNC_MAX_ARGS)
|
|
|
|
elog(ERROR, "functions cannot have more than %d arguments",
|
|
|
|
FUNC_MAX_ARGS);
|
2000-04-12 19:17:23 +02:00
|
|
|
for (i = 0; i < argcount; i++)
|
|
|
|
{
|
2000-10-07 02:58:23 +02:00
|
|
|
TypeName *t = (TypeName *) lfirst(arguments);
|
|
|
|
char *typnam = TypeNameToInternalName(t);
|
|
|
|
|
2000-01-12 05:59:41 +01:00
|
|
|
arguments = lnext(arguments);
|
2000-10-07 02:58:23 +02:00
|
|
|
|
|
|
|
if (strcmp(typnam, "opaque") == 0)
|
|
|
|
argoids[i] = InvalidOid;
|
2000-01-12 05:59:41 +01:00
|
|
|
else
|
|
|
|
{
|
2000-11-16 23:30:52 +01:00
|
|
|
argoids[i] = GetSysCacheOid(TYPENAME,
|
|
|
|
PointerGetDatum(typnam),
|
|
|
|
0, 0, 0);
|
|
|
|
if (!OidIsValid(argoids[i]))
|
2000-10-07 02:58:23 +02:00
|
|
|
elog(ERROR, "CommentProc: type '%s' not found", typnam);
|
2000-01-12 05:59:41 +01:00
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Now, validate the user's ability to comment on this function */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2000-09-06 16:15:31 +02:00
|
|
|
if (!pg_func_ownercheck(GetUserId(), function, argcount, argoids))
|
2000-01-12 05:59:41 +01:00
|
|
|
elog(ERROR, "you are not permitted to comment on function '%s'",
|
|
|
|
function);
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Now, find the corresponding oid for this procedure */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
oid = GetSysCacheOid(PROCNAME,
|
|
|
|
PointerGetDatum(function),
|
|
|
|
Int32GetDatum(argcount),
|
|
|
|
PointerGetDatum(argoids),
|
|
|
|
0);
|
|
|
|
if (!OidIsValid(oid))
|
2000-10-07 02:58:23 +02:00
|
|
|
func_error("CommentProc", function, argcount, argoids, NULL);
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Call CreateComments() to create/drop the comments */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
CreateComments(oid, RelOid_pg_proc, 0, comment);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
2000-01-10 18:14:46 +01:00
|
|
|
|
1999-10-26 05:12:39 +02:00
|
|
|
/*------------------------------------------------------------------
|
|
|
|
* CommentOperator --
|
|
|
|
*
|
2000-01-10 18:14:46 +01:00
|
|
|
* This routine is used to allow a user to provide comments on an
|
1999-10-26 05:12:39 +02:00
|
|
|
* operator. The operator for commenting is determined by both
|
|
|
|
* its name and its argument list which defines the left and right
|
2000-01-10 18:14:46 +01:00
|
|
|
* 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
|
1999-10-26 05:12:39 +02:00
|
|
|
* is dropped.
|
2001-08-10 20:57:42 +02:00
|
|
|
*
|
|
|
|
* NOTE: we actually attach the comment to the procedure that underlies
|
|
|
|
* the operator. This is a feature, not a bug: we want the same comment
|
|
|
|
* to be visible for both operator and function.
|
1999-10-26 05:12:39 +02:00
|
|
|
*------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
static void
|
|
|
|
CommentOperator(char *opername, List *arguments, char *comment)
|
|
|
|
{
|
2000-10-07 02:58:23 +02:00
|
|
|
TypeName *typenode1 = (TypeName *) lfirst(arguments);
|
|
|
|
TypeName *typenode2 = (TypeName *) lsecond(arguments);
|
|
|
|
char oprtype = 0,
|
|
|
|
*lefttype = NULL,
|
|
|
|
*righttype = NULL;
|
2000-04-12 19:17:23 +02:00
|
|
|
Form_pg_operator data;
|
|
|
|
HeapTuple optuple;
|
|
|
|
Oid oid,
|
|
|
|
leftoid = InvalidOid,
|
|
|
|
rightoid = InvalidOid;
|
|
|
|
bool defined;
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Initialize our left and right argument types */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-10-07 02:58:23 +02:00
|
|
|
if (typenode1 != NULL)
|
|
|
|
lefttype = TypeNameToInternalName(typenode1);
|
|
|
|
if (typenode2 != NULL)
|
|
|
|
righttype = TypeNameToInternalName(typenode2);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Attempt to fetch the left oid, if specified */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
if (lefttype != NULL)
|
|
|
|
{
|
|
|
|
leftoid = TypeGet(lefttype, &defined);
|
|
|
|
if (!OidIsValid(leftoid))
|
|
|
|
elog(ERROR, "left type '%s' does not exist", lefttype);
|
|
|
|
}
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Attempt to fetch the right oid, if specified */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (righttype != NULL)
|
|
|
|
{
|
|
|
|
rightoid = TypeGet(righttype, &defined);
|
|
|
|
if (!OidIsValid(rightoid))
|
|
|
|
elog(ERROR, "right type '%s' does not exist", righttype);
|
|
|
|
}
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Determine operator type */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (OidIsValid(leftoid) && (OidIsValid(rightoid)))
|
|
|
|
oprtype = 'b';
|
|
|
|
else if (OidIsValid(leftoid))
|
|
|
|
oprtype = 'r';
|
2000-10-07 02:58:23 +02:00
|
|
|
else if (OidIsValid(rightoid))
|
|
|
|
oprtype = 'l';
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
|
|
|
elog(ERROR, "operator '%s' is of an illegal type'", opername);
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Attempt to fetch the operator oid */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
optuple = SearchSysCache(OPERNAME,
|
|
|
|
PointerGetDatum(opername),
|
|
|
|
ObjectIdGetDatum(leftoid),
|
|
|
|
ObjectIdGetDatum(rightoid),
|
|
|
|
CharGetDatum(oprtype));
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!HeapTupleIsValid(optuple))
|
|
|
|
elog(ERROR, "operator '%s' does not exist", opername);
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
oid = optuple->t_data->t_oid;
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Valid user's ability to comment on this operator */
|
2000-01-10 18:14:46 +01:00
|
|
|
|
2001-06-05 21:34:56 +02:00
|
|
|
if (!pg_oper_ownercheck(GetUserId(), oid))
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "you are not permitted to comment on operator '%s'",
|
|
|
|
opername);
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Get the procedure associated with the operator */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
data = (Form_pg_operator) GETSTRUCT(optuple);
|
2001-06-25 23:11:45 +02:00
|
|
|
oid = data->oprcode;
|
2000-04-12 19:17:23 +02:00
|
|
|
if (oid == InvalidOid)
|
|
|
|
elog(ERROR, "operator '%s' does not have an underlying function", opername);
|
2000-01-18 19:09:02 +01:00
|
|
|
|
2000-11-16 23:30:52 +01:00
|
|
|
ReleaseSysCache(optuple);
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Call CreateComments() to create/drop the comments */
|
1999-10-26 05:12:39 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
CreateComments(oid, RelOid_pg_proc, 0, comment);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
* 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.
|
|
|
|
*------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
static void
|
|
|
|
CommentTrigger(char *trigger, char *relname, char *comment)
|
|
|
|
{
|
|
|
|
Relation pg_trigger,
|
|
|
|
relation;
|
|
|
|
HeapTuple triggertuple;
|
|
|
|
HeapScanDesc scan;
|
2001-08-10 20:57:42 +02:00
|
|
|
ScanKeyData entry[2];
|
|
|
|
Oid oid;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* First, validate the user's action */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-09-06 16:15:31 +02:00
|
|
|
if (!pg_ownercheck(GetUserId(), relname, RELNAME))
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "you are not permitted to comment on trigger '%s' %s '%s'",
|
|
|
|
trigger, "defined for relation", relname);
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Now, fetch the trigger oid from pg_trigger */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
relation = heap_openr(relname, AccessShareLock);
|
|
|
|
pg_trigger = heap_openr(TriggerRelationName, AccessShareLock);
|
2001-08-10 20:57:42 +02:00
|
|
|
ScanKeyEntryInitialize(&entry[0], 0x0, Anum_pg_trigger_tgrelid,
|
|
|
|
F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(RelationGetRelid(relation)));
|
|
|
|
ScanKeyEntryInitialize(&entry[1], 0x0, Anum_pg_trigger_tgname,
|
|
|
|
F_NAMEEQ,
|
|
|
|
NameGetDatum(trigger));
|
|
|
|
scan = heap_beginscan(pg_trigger, 0, SnapshotNow, 2, entry);
|
2000-04-12 19:17:23 +02:00
|
|
|
triggertuple = heap_getnext(scan, 0);
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* If no trigger exists for the relation specified, notify user */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
if (!HeapTupleIsValid(triggertuple))
|
2000-04-12 19:17:23 +02:00
|
|
|
elog(ERROR, "trigger '%s' defined for relation '%s' does not exist",
|
|
|
|
trigger, relname);
|
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
oid = triggertuple->t_data->t_oid;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
heap_endscan(scan);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2001-08-10 20:57:42 +02:00
|
|
|
/* Create the comments with the pg_trigger oid */
|
|
|
|
|
|
|
|
CreateComments(oid, RelationGetRelid(pg_trigger), 0, comment);
|
|
|
|
|
|
|
|
/* Done, but hold lock on relation */
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
heap_close(pg_trigger, AccessShareLock);
|
2000-11-08 23:10:03 +01:00
|
|
|
heap_close(relation, NoLock);
|
1999-10-26 05:12:39 +02:00
|
|
|
}
|