mirror of
https://github.com/postgres/postgres.git
synced 2025-07-12 21:01:52 +03:00
COMMENT ON casts, conversions, languages, operator classes, and
large objects. Dump all these in pg_dump; also add code to pg_dump user-defined conversions. Make psql's large object code rely on the backend for inserting/deleting LOB comments, instead of trying to hack pg_description directly. Documentation and regression tests added. Christopher Kings-Lynne, code reviewed by Tom
This commit is contained in:
@ -7,7 +7,7 @@
|
||||
* Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.73 2003/11/12 21:15:49 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.74 2003/11/21 22:32:48 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -21,6 +21,7 @@
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_constraint.h"
|
||||
#include "catalog/pg_description.h"
|
||||
#include "catalog/pg_largeobject.h"
|
||||
#include "catalog/pg_operator.h"
|
||||
#include "catalog/pg_rewrite.h"
|
||||
#include "catalog/pg_trigger.h"
|
||||
@ -58,6 +59,11 @@ static void CommentProc(List *function, List *arguments, char *comment);
|
||||
static void CommentOperator(List *opername, List *arguments, char *comment);
|
||||
static void CommentTrigger(List *qualname, char *comment);
|
||||
static void CommentConstraint(List *qualname, char *comment);
|
||||
static void CommentConversion(List *qualname, char *comment);
|
||||
static void CommentLanguage(List *qualname, char *comment);
|
||||
static void CommentOpClass(List *qualname, List *arguments, char *comment);
|
||||
static void CommentLargeObject(List *qualname, char *comment);
|
||||
static void CommentCast(List *qualname, List *arguments, char *comment);
|
||||
|
||||
|
||||
/*
|
||||
@ -107,6 +113,21 @@ CommentObject(CommentStmt *stmt)
|
||||
case OBJECT_CONSTRAINT:
|
||||
CommentConstraint(stmt->objname, stmt->comment);
|
||||
break;
|
||||
case OBJECT_CONVERSION:
|
||||
CommentConversion(stmt->objname, stmt->comment);
|
||||
break;
|
||||
case OBJECT_LANGUAGE:
|
||||
CommentLanguage(stmt->objname, stmt->comment);
|
||||
break;
|
||||
case OBJECT_OPCLASS:
|
||||
CommentOpClass(stmt->objname, stmt->objargs, stmt->comment);
|
||||
break;
|
||||
case OBJECT_LARGEOBJECT:
|
||||
CommentLargeObject(stmt->objname, stmt->comment);
|
||||
break;
|
||||
case OBJECT_CAST:
|
||||
CommentCast(stmt->objname, stmt->objargs, stmt->comment);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized object type: %d",
|
||||
(int) stmt->objtype);
|
||||
@ -592,7 +613,10 @@ CommentRule(List *qualname, char *comment)
|
||||
PointerGetDatum(rulename),
|
||||
0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "cache lookup failed for rule \"%s\"", rulename);
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("rule \"%s\" for relation \"%s\" does not exist",
|
||||
rulename, RelationGetRelationName(relation))));
|
||||
Assert(reloid == ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class);
|
||||
ruleoid = HeapTupleGetOid(tuple);
|
||||
ReleaseSysCache(tuple);
|
||||
@ -910,3 +934,297 @@ CommentConstraint(List *qualname, char *comment)
|
||||
heap_close(pg_constraint, AccessShareLock);
|
||||
heap_close(relation, NoLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* CommentConversion --
|
||||
*
|
||||
* This routine is used to add/drop any user-comments a user might
|
||||
* have regarding a CONVERSION. The conversion is specified by name
|
||||
* and, if found, and the user has appropriate permissions, a
|
||||
* comment will be added/dropped using the CreateComments() routine.
|
||||
* The conversion's name and the comment are the parameters to this routine.
|
||||
*/
|
||||
static void
|
||||
CommentConversion(List *qualname, char *comment)
|
||||
{
|
||||
Oid conversionOid;
|
||||
Oid classoid;
|
||||
|
||||
conversionOid = FindConversionByName(qualname);
|
||||
if (!OidIsValid(conversionOid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("conversion \"%s\" does not exist",
|
||||
NameListToString(qualname))));
|
||||
|
||||
/* Check object security */
|
||||
if (!pg_conversion_ownercheck(conversionOid, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
|
||||
NameListToString(qualname));
|
||||
|
||||
/* pg_conversion doesn't have a hard-coded OID, so must look it up */
|
||||
classoid = get_system_catalog_relid(ConversionRelationName);
|
||||
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
CreateComments(conversionOid, classoid, 0, comment);
|
||||
}
|
||||
|
||||
/*
|
||||
* CommentLanguage --
|
||||
*
|
||||
* This routine is used to add/drop any user-comments a user might
|
||||
* have regarding a LANGUAGE. The language is specified by name
|
||||
* and, if found, and the user has appropriate permissions, a
|
||||
* comment will be added/dropped using the CreateComments() routine.
|
||||
* The language's name and the comment are the parameters to this routine.
|
||||
*/
|
||||
static void
|
||||
CommentLanguage(List *qualname, char *comment)
|
||||
{
|
||||
Oid oid;
|
||||
Oid classoid;
|
||||
char *language;
|
||||
|
||||
if (length(qualname) != 1)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("language name may not be qualified")));
|
||||
language = strVal(lfirst(qualname));
|
||||
|
||||
oid = GetSysCacheOid(LANGNAME,
|
||||
CStringGetDatum(language),
|
||||
0, 0, 0);
|
||||
if (!OidIsValid(oid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_SCHEMA),
|
||||
errmsg("language \"%s\" does not exist", language)));
|
||||
|
||||
/* Check object security */
|
||||
if (!superuser())
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be superuser to comment on procedural language")));
|
||||
|
||||
/* pg_language doesn't have a hard-coded OID, so must look it up */
|
||||
classoid = get_system_catalog_relid(LanguageRelationName);
|
||||
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
CreateComments(oid, classoid, 0, comment);
|
||||
}
|
||||
|
||||
/*
|
||||
* CommentOpClass --
|
||||
*
|
||||
* This routine is used to allow a user to provide comments on an
|
||||
* operator class. The operator class for commenting is determined by both
|
||||
* its name and its argument list which defines the index method
|
||||
* the operator class is used for. The argument list is expected to contain
|
||||
* a single name (represented as a string Value node).
|
||||
*/
|
||||
static void
|
||||
CommentOpClass(List *qualname, List *arguments, char *comment)
|
||||
{
|
||||
char *amname;
|
||||
char *schemaname;
|
||||
char *opcname;
|
||||
Oid amID;
|
||||
Oid opcID;
|
||||
Oid classoid;
|
||||
HeapTuple tuple;
|
||||
|
||||
Assert(length(arguments) == 1);
|
||||
amname = strVal(lfirst(arguments));
|
||||
|
||||
/*
|
||||
* Get the access method's OID.
|
||||
*/
|
||||
amID = GetSysCacheOid(AMNAME,
|
||||
CStringGetDatum(amname),
|
||||
0, 0, 0);
|
||||
if (!OidIsValid(amID))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("access method \"%s\" does not exist",
|
||||
amname)));
|
||||
|
||||
/*
|
||||
* Look up the opclass.
|
||||
*/
|
||||
|
||||
/* deconstruct the name list */
|
||||
DeconstructQualifiedName(qualname, &schemaname, &opcname);
|
||||
|
||||
if (schemaname)
|
||||
{
|
||||
/* Look in specific schema only */
|
||||
Oid namespaceId;
|
||||
|
||||
namespaceId = LookupExplicitNamespace(schemaname);
|
||||
tuple = SearchSysCache(CLAAMNAMENSP,
|
||||
ObjectIdGetDatum(amID),
|
||||
PointerGetDatum(opcname),
|
||||
ObjectIdGetDatum(namespaceId),
|
||||
0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Unqualified opclass name, so search the search path */
|
||||
opcID = OpclassnameGetOpcid(amID, opcname);
|
||||
if (!OidIsValid(opcID))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("operator class \"%s\" does not exist for access method \"%s\"",
|
||||
opcname, amname)));
|
||||
tuple = SearchSysCache(CLAOID,
|
||||
ObjectIdGetDatum(opcID),
|
||||
0, 0, 0);
|
||||
}
|
||||
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("operator class \"%s\" does not exist for access method \"%s\"",
|
||||
NameListToString(qualname), amname)));
|
||||
|
||||
opcID = HeapTupleGetOid(tuple);
|
||||
|
||||
/* Permission check: must own opclass */
|
||||
if (!pg_opclass_ownercheck(opcID, GetUserId()))
|
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
|
||||
NameListToString(qualname));
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/* pg_opclass doesn't have a hard-coded OID, so must look it up */
|
||||
classoid = get_system_catalog_relid(OperatorClassRelationName);
|
||||
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
CreateComments(opcID, classoid, 0, comment);
|
||||
}
|
||||
|
||||
/*
|
||||
* CommentLargeObject --
|
||||
*
|
||||
* This routine is used to add/drop any user-comments a user might
|
||||
* have regarding a LARGE OBJECT. The large object is specified by OID
|
||||
* and, if found, and the user has appropriate permissions, a
|
||||
* comment will be added/dropped using the CreateComments() routine.
|
||||
* The large object's OID and the comment are the parameters to this routine.
|
||||
*/
|
||||
static void
|
||||
CommentLargeObject(List *qualname, char *comment)
|
||||
{
|
||||
Oid loid;
|
||||
Oid classoid;
|
||||
Node *node;
|
||||
|
||||
Assert(length(qualname) == 1);
|
||||
node = (Node *) lfirst(qualname);
|
||||
|
||||
switch (nodeTag(node))
|
||||
{
|
||||
case T_Integer:
|
||||
loid = intVal(node);
|
||||
break;
|
||||
case T_Float:
|
||||
/*
|
||||
* Values too large for int4 will be represented as Float
|
||||
* constants by the lexer. Accept these if they are valid
|
||||
* OID strings.
|
||||
*/
|
||||
loid = DatumGetObjectId(DirectFunctionCall1(oidin,
|
||||
CStringGetDatum(strVal(node))));
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d",
|
||||
(int) nodeTag(node));
|
||||
/* keep compiler quiet */
|
||||
loid = InvalidOid;
|
||||
}
|
||||
|
||||
/* check that the large object exists */
|
||||
if (!LargeObjectExists(loid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("large object %u does not exist", loid)));
|
||||
|
||||
/* pg_largeobject doesn't have a hard-coded OID, so must look it up */
|
||||
classoid = get_system_catalog_relid(LargeObjectRelationName);
|
||||
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
CreateComments(loid, classoid, 0, comment);
|
||||
}
|
||||
|
||||
/*
|
||||
* CommentCast --
|
||||
*
|
||||
* This routine is used to add/drop any user-comments a user might
|
||||
* have regarding a CAST. The cast is specified by source and destination types
|
||||
* and, if found, and the user has appropriate permissions, a
|
||||
* comment will be added/dropped using the CreateComments() routine.
|
||||
* The cast's source type is passed as the "name", the destination type
|
||||
* as the "arguments".
|
||||
*/
|
||||
static void
|
||||
CommentCast(List *qualname, List *arguments, char *comment)
|
||||
{
|
||||
TypeName *sourcetype;
|
||||
TypeName *targettype;
|
||||
Oid sourcetypeid;
|
||||
Oid targettypeid;
|
||||
HeapTuple tuple;
|
||||
Oid castOid;
|
||||
Oid classoid;
|
||||
|
||||
Assert(length(qualname) == 1);
|
||||
sourcetype = (TypeName *) lfirst(qualname);
|
||||
Assert(IsA(sourcetype, TypeName));
|
||||
Assert(length(arguments) == 1);
|
||||
targettype = (TypeName *) lfirst(arguments);
|
||||
Assert(IsA(targettype, TypeName));
|
||||
|
||||
sourcetypeid = typenameTypeId(sourcetype);
|
||||
if (!OidIsValid(sourcetypeid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("source data type %s does not exist",
|
||||
TypeNameToString(sourcetype))));
|
||||
|
||||
targettypeid = typenameTypeId(targettype);
|
||||
if (!OidIsValid(targettypeid))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("target data type %s does not exist",
|
||||
TypeNameToString(targettype))));
|
||||
|
||||
tuple = SearchSysCache(CASTSOURCETARGET,
|
||||
ObjectIdGetDatum(sourcetypeid),
|
||||
ObjectIdGetDatum(targettypeid),
|
||||
0, 0);
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
errmsg("cast from type %s to type %s does not exist",
|
||||
TypeNameToString(sourcetype),
|
||||
TypeNameToString(targettype))));
|
||||
|
||||
/* Get the OID of the cast */
|
||||
castOid = HeapTupleGetOid(tuple);
|
||||
|
||||
/* Permission check */
|
||||
if (!pg_type_ownercheck(sourcetypeid, GetUserId())
|
||||
&& !pg_type_ownercheck(targettypeid, GetUserId()))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
||||
errmsg("must be owner of type %s or type %s",
|
||||
TypeNameToString(sourcetype),
|
||||
TypeNameToString(targettype))));
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/* pg_cast doesn't have a hard-coded OID, so must look it up */
|
||||
classoid = get_system_catalog_relid(CastRelationName);
|
||||
|
||||
/* Call CreateComments() to create/drop the comments */
|
||||
CreateComments(castOid, classoid, 0, comment);
|
||||
}
|
||||
|
Reference in New Issue
Block a user