Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Abuhujair Javed
Postgres FD Implementation
Commits
c10575ff
Commit
c10575ff
authored
Aug 27, 2010
by
Robert Haas
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Rewrite comment code for better modularity, and add necessary locking.
Review by Alvaro Herrera, KaiGai Kohei, and Tom Lane.
parent
73b3bd55
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
829 additions
and
824 deletions
+829
-824
src/backend/catalog/Makefile
src/backend/catalog/Makefile
+5
-4
src/backend/catalog/objectaddress.c
src/backend/catalog/objectaddress.c
+654
-0
src/backend/commands/comment.c
src/backend/commands/comment.c
+135
-808
src/include/catalog/dependency.h
src/include/catalog/dependency.h
+2
-12
src/include/catalog/objectaddress.h
src/include/catalog/objectaddress.h
+33
-0
No files found.
src/backend/catalog/Makefile
View file @
c10575ff
...
...
@@ -2,7 +2,7 @@
#
# Makefile for backend/catalog
#
# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.7
8 2010/05/13 11:49:48 petere
Exp $
# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.7
9 2010/08/27 11:47:41 rhaas
Exp $
#
#-------------------------------------------------------------------------
...
...
@@ -11,9 +11,10 @@ top_builddir = ../../..
include
$(top_builddir)/src/Makefile.global
OBJS
=
catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o
\
pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o
\
pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o
\
pg_db_role_setting.o pg_shdepend.o pg_type.o storage.o toasting.o
objectaddress.o pg_aggregate.o pg_constraint.o pg_conversion.o
\
pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o
\
pg_operator.o pg_proc.o pg_db_role_setting.o pg_shdepend.o pg_type.o
\
storage.o toasting.o
BKIFILES
=
postgres.bki postgres.description postgres.shdescription
...
...
src/backend/catalog/objectaddress.c
0 → 100644
View file @
c10575ff
/*-------------------------------------------------------------------------
*
* objectaddress.c
* functions for working with ObjectAddresses
*
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/objectaddress.c,v 1.1 2010/08/27 11:47:41 rhaas Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/heapam.h"
#include "access/sysattr.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaddress.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_class.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/proclang.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
#include "rewrite/rewriteSupport.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/rel.h"
#include "utils/tqual.h"
static
ObjectAddress
get_object_address_unqualified
(
ObjectType
objtype
,
List
*
qualname
);
static
Relation
get_relation_by_qualified_name
(
ObjectType
objtype
,
List
*
objname
,
LOCKMODE
lockmode
);
static
ObjectAddress
get_object_address_relobject
(
ObjectType
objtype
,
List
*
objname
,
Relation
*
relp
);
static
ObjectAddress
get_object_address_attribute
(
ObjectType
objtype
,
List
*
objname
,
Relation
*
relp
,
LOCKMODE
lockmode
);
static
ObjectAddress
get_object_address_opcf
(
ObjectType
objtype
,
List
*
objname
,
List
*
objargs
);
static
bool
object_exists
(
ObjectAddress
address
);
/*
* Translate an object name and arguments (as passed by the parser) to an
* ObjectAddress.
*
* The returned object will be locked using the specified lockmode. If a
* sub-object is looked up, the parent object will be locked instead.
*
* If the object is a relation or a child object of a relation (e.g. an
* attribute or contraint), the relation is also opened and *relp receives
* the open relcache entry pointer; otherwise, *relp is set to NULL. This
* is a bit grotty but it makes life simpler, since the caller will
* typically need the relcache entry too. Caller must close the relcache
* entry when done with it. The relation is locked with the specified lockmode
* if the target object is the relation itself or an attribute, but for other
* child objects, only AccessShareLock is acquired on the relation.
*
* We don't currently provide a function to release the locks acquired here;
* typically, the lock must be held until commit to guard against a concurrent
* drop operation.
*/
ObjectAddress
get_object_address
(
ObjectType
objtype
,
List
*
objname
,
List
*
objargs
,
Relation
*
relp
,
LOCKMODE
lockmode
)
{
ObjectAddress
address
;
Relation
relation
=
NULL
;
/* Some kind of lock must be taken. */
Assert
(
lockmode
!=
NoLock
);
switch
(
objtype
)
{
case
OBJECT_INDEX
:
case
OBJECT_SEQUENCE
:
case
OBJECT_TABLE
:
case
OBJECT_VIEW
:
relation
=
get_relation_by_qualified_name
(
objtype
,
objname
,
lockmode
);
address
.
classId
=
RelationRelationId
;
address
.
objectId
=
RelationGetRelid
(
relation
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_COLUMN
:
address
=
get_object_address_attribute
(
objtype
,
objname
,
&
relation
,
lockmode
);
break
;
case
OBJECT_RULE
:
case
OBJECT_TRIGGER
:
case
OBJECT_CONSTRAINT
:
address
=
get_object_address_relobject
(
objtype
,
objname
,
&
relation
);
break
;
case
OBJECT_DATABASE
:
case
OBJECT_TABLESPACE
:
case
OBJECT_ROLE
:
case
OBJECT_SCHEMA
:
case
OBJECT_LANGUAGE
:
address
=
get_object_address_unqualified
(
objtype
,
objname
);
break
;
case
OBJECT_TYPE
:
address
.
classId
=
TypeRelationId
;
address
.
objectId
=
typenameTypeId
(
NULL
,
makeTypeNameFromNameList
(
objname
),
NULL
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_AGGREGATE
:
address
.
classId
=
ProcedureRelationId
;
address
.
objectId
=
LookupAggNameTypeNames
(
objname
,
objargs
,
false
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_FUNCTION
:
address
.
classId
=
ProcedureRelationId
;
address
.
objectId
=
LookupFuncNameTypeNames
(
objname
,
objargs
,
false
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_OPERATOR
:
Assert
(
list_length
(
objargs
)
==
2
);
address
.
classId
=
OperatorRelationId
;
address
.
objectId
=
LookupOperNameTypeNames
(
NULL
,
objname
,
(
TypeName
*
)
linitial
(
objargs
),
(
TypeName
*
)
lsecond
(
objargs
),
false
,
-
1
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_CONVERSION
:
address
.
classId
=
ConversionRelationId
;
address
.
objectId
=
get_conversion_oid
(
objname
,
false
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_OPCLASS
:
case
OBJECT_OPFAMILY
:
address
=
get_object_address_opcf
(
objtype
,
objname
,
objargs
);
break
;
case
OBJECT_LARGEOBJECT
:
Assert
(
list_length
(
objname
)
==
1
);
address
.
classId
=
LargeObjectRelationId
;
address
.
objectId
=
oidparse
(
linitial
(
objname
));
address
.
objectSubId
=
0
;
if
(
!
LargeObjectExists
(
address
.
objectId
))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_OBJECT
),
errmsg
(
"large object %u does not exist"
,
address
.
objectId
)));
break
;
case
OBJECT_CAST
:
{
TypeName
*
sourcetype
=
(
TypeName
*
)
linitial
(
objname
);
TypeName
*
targettype
=
(
TypeName
*
)
linitial
(
objargs
);
Oid
sourcetypeid
=
typenameTypeId
(
NULL
,
sourcetype
,
NULL
);
Oid
targettypeid
=
typenameTypeId
(
NULL
,
targettype
,
NULL
);
address
.
classId
=
CastRelationId
;
address
.
objectId
=
get_cast_oid
(
sourcetypeid
,
targettypeid
,
false
);
address
.
objectSubId
=
0
;
}
break
;
case
OBJECT_TSPARSER
:
address
.
classId
=
TSParserRelationId
;
address
.
objectId
=
get_ts_parser_oid
(
objname
,
false
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_TSDICTIONARY
:
address
.
classId
=
TSDictionaryRelationId
;
address
.
objectId
=
get_ts_dict_oid
(
objname
,
false
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_TSTEMPLATE
:
address
.
classId
=
TSTemplateRelationId
;
address
.
objectId
=
get_ts_template_oid
(
objname
,
false
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_TSCONFIGURATION
:
address
.
classId
=
TSConfigRelationId
;
address
.
objectId
=
get_ts_config_oid
(
objname
,
false
);
address
.
objectSubId
=
0
;
break
;
default:
elog
(
ERROR
,
"unrecognized objtype: %d"
,
(
int
)
objtype
);
}
/*
* If we're dealing with a relation or attribute, then the relation is
* already locked. If we're dealing with any other type of object, we need
* to lock it and then verify that it still exists.
*/
if
(
address
.
classId
!=
RelationRelationId
)
{
if
(
IsSharedRelation
(
address
.
classId
))
LockSharedObject
(
address
.
classId
,
address
.
objectId
,
0
,
lockmode
);
else
LockDatabaseObject
(
address
.
classId
,
address
.
objectId
,
0
,
lockmode
);
/* Did it go away while we were waiting for the lock? */
if
(
!
object_exists
(
address
))
elog
(
ERROR
,
"cache lookup failed for class %u object %u subobj %d"
,
address
.
classId
,
address
.
objectId
,
address
.
objectSubId
);
}
/* Return the object address and the relation. */
*
relp
=
relation
;
return
address
;
}
/*
* Find an ObjectAddress for a type of object that is identified by an
* unqualified name.
*/
static
ObjectAddress
get_object_address_unqualified
(
ObjectType
objtype
,
List
*
qualname
)
{
const
char
*
name
;
ObjectAddress
address
;
/*
* The types of names handled by this function are not permitted to be
* schema-qualified or catalog-qualified.
*/
if
(
list_length
(
qualname
)
!=
1
)
{
const
char
*
msg
;
switch
(
objtype
)
{
case
OBJECT_DATABASE
:
msg
=
gettext_noop
(
"database name cannot be qualified"
);
break
;
case
OBJECT_TABLESPACE
:
msg
=
gettext_noop
(
"tablespace name cannot be qualified"
);
break
;
case
OBJECT_ROLE
:
msg
=
gettext_noop
(
"role name cannot be qualified"
);
break
;
case
OBJECT_SCHEMA
:
msg
=
gettext_noop
(
"schema name cannot be qualified"
);
break
;
case
OBJECT_LANGUAGE
:
msg
=
gettext_noop
(
"language name cannot be qualified"
);
break
;
default:
elog
(
ERROR
,
"unrecognized objtype: %d"
,
(
int
)
objtype
);
msg
=
NULL
;
/* placate compiler */
}
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"%s"
,
_
(
msg
))));
}
/* Format is valid, extract the actual name. */
name
=
strVal
(
linitial
(
qualname
));
/* Translate name to OID. */
switch
(
objtype
)
{
case
OBJECT_DATABASE
:
address
.
classId
=
DatabaseRelationId
;
address
.
objectId
=
get_database_oid
(
name
,
false
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_TABLESPACE
:
address
.
classId
=
TableSpaceRelationId
;
address
.
objectId
=
get_tablespace_oid
(
name
,
false
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_ROLE
:
address
.
classId
=
AuthIdRelationId
;
address
.
objectId
=
get_role_oid
(
name
,
false
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_SCHEMA
:
address
.
classId
=
NamespaceRelationId
;
address
.
objectId
=
get_namespace_oid
(
name
,
false
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_LANGUAGE
:
address
.
classId
=
LanguageRelationId
;
address
.
objectId
=
get_language_oid
(
name
,
false
);
address
.
objectSubId
=
0
;
break
;
default:
elog
(
ERROR
,
"unrecognized objtype: %d"
,
(
int
)
objtype
);
/* placate compiler, which doesn't know elog won't return */
address
.
classId
=
InvalidOid
;
address
.
objectId
=
InvalidOid
;
address
.
objectSubId
=
0
;
}
return
address
;
}
/*
* Locate a relation by qualified name.
*/
static
Relation
get_relation_by_qualified_name
(
ObjectType
objtype
,
List
*
objname
,
LOCKMODE
lockmode
)
{
Relation
relation
;
relation
=
relation_openrv
(
makeRangeVarFromNameList
(
objname
),
lockmode
);
switch
(
objtype
)
{
case
OBJECT_INDEX
:
if
(
relation
->
rd_rel
->
relkind
!=
RELKIND_INDEX
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"
\"
%s
\"
is not an index"
,
RelationGetRelationName
(
relation
))));
break
;
case
OBJECT_SEQUENCE
:
if
(
relation
->
rd_rel
->
relkind
!=
RELKIND_SEQUENCE
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"
\"
%s
\"
is not a sequence"
,
RelationGetRelationName
(
relation
))));
break
;
case
OBJECT_TABLE
:
if
(
relation
->
rd_rel
->
relkind
!=
RELKIND_RELATION
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"
\"
%s
\"
is not a table"
,
RelationGetRelationName
(
relation
))));
break
;
case
OBJECT_VIEW
:
if
(
relation
->
rd_rel
->
relkind
!=
RELKIND_VIEW
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"
\"
%s
\"
is not a view"
,
RelationGetRelationName
(
relation
))));
break
;
default:
elog
(
ERROR
,
"unrecognized objtype: %d"
,
(
int
)
objtype
);
break
;
}
return
relation
;
}
/*
* Find object address for an object that is attached to a relation.
*
* Note that we take only an AccessShareLock on the relation. We need not
* pass down the LOCKMODE from get_object_address(), because that is the lock
* mode for the object itself, not the relation to which it is attached.
*/
static
ObjectAddress
get_object_address_relobject
(
ObjectType
objtype
,
List
*
objname
,
Relation
*
relp
)
{
ObjectAddress
address
;
Relation
relation
=
NULL
;
int
nnames
;
const
char
*
depname
;
/* Extract name of dependent object. */
depname
=
strVal
(
lfirst
(
list_tail
(
objname
)));
/* Separate relation name from dependent object name. */
nnames
=
list_length
(
objname
);
if
(
nnames
<
2
)
{
Oid
reloid
;
/*
* For compatibility with very old releases, we sometimes allow users
* to attempt to specify a rule without mentioning the relation name.
* If there's only rule by that name in the entire database, this will
* work. But objects other than rules don't get this special
* treatment.
*/
if
(
objtype
!=
OBJECT_RULE
)
elog
(
ERROR
,
"must specify relation and object name"
);
address
.
classId
=
RewriteRelationId
;
address
.
objectId
=
get_rewrite_oid_without_relid
(
depname
,
&
reloid
);
address
.
objectSubId
=
0
;
relation
=
heap_open
(
reloid
,
AccessShareLock
);
}
else
{
List
*
relname
;
Oid
reloid
;
/* Extract relation name and open relation. */
relname
=
list_truncate
(
list_copy
(
objname
),
nnames
-
1
);
relation
=
heap_openrv
(
makeRangeVarFromNameList
(
relname
),
AccessShareLock
);
reloid
=
RelationGetRelid
(
relation
);
switch
(
objtype
)
{
case
OBJECT_RULE
:
address
.
classId
=
RewriteRelationId
;
address
.
objectId
=
get_rewrite_oid
(
reloid
,
depname
,
false
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_TRIGGER
:
address
.
classId
=
TriggerRelationId
;
address
.
objectId
=
get_trigger_oid
(
reloid
,
depname
,
false
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_CONSTRAINT
:
address
.
classId
=
ConstraintRelationId
;
address
.
objectId
=
get_constraint_oid
(
reloid
,
depname
,
false
);
address
.
objectSubId
=
0
;
break
;
default:
elog
(
ERROR
,
"unrecognized objtype: %d"
,
(
int
)
objtype
);
/* placate compiler, which doesn't know elog won't return */
address
.
classId
=
InvalidOid
;
address
.
objectId
=
InvalidOid
;
address
.
objectSubId
=
0
;
}
}
/* Done. */
*
relp
=
relation
;
return
address
;
}
/*
* Find the ObjectAddress for an attribute.
*/
static
ObjectAddress
get_object_address_attribute
(
ObjectType
objtype
,
List
*
objname
,
Relation
*
relp
,
LOCKMODE
lockmode
)
{
ObjectAddress
address
;
List
*
relname
;
Oid
reloid
;
Relation
relation
;
const
char
*
attname
;
/* Extract relation name and open relation. */
attname
=
strVal
(
lfirst
(
list_tail
(
objname
)));
relname
=
list_truncate
(
list_copy
(
objname
),
list_length
(
objname
)
-
1
);
relation
=
heap_openrv
(
makeRangeVarFromNameList
(
relname
),
lockmode
);
reloid
=
RelationGetRelid
(
relation
);
/* Look up attribute and construct return value. */
address
.
classId
=
RelationRelationId
;
address
.
objectId
=
reloid
;
address
.
objectSubId
=
get_attnum
(
reloid
,
attname
);
if
(
address
.
objectSubId
==
InvalidAttrNumber
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_COLUMN
),
errmsg
(
"column
\"
%s
\"
of relation
\"
%s
\"
does not exist"
,
attname
,
RelationGetRelationName
(
relation
))));
*
relp
=
relation
;
return
address
;
}
/*
* Find the ObjectAddress for an opclass or opfamily.
*/
static
ObjectAddress
get_object_address_opcf
(
ObjectType
objtype
,
List
*
objname
,
List
*
objargs
)
{
Oid
amoid
;
ObjectAddress
address
;
Assert
(
list_length
(
objargs
)
==
1
);
amoid
=
get_am_oid
(
strVal
(
linitial
(
objargs
)),
false
);
switch
(
objtype
)
{
case
OBJECT_OPCLASS
:
address
.
classId
=
OperatorClassRelationId
;
address
.
objectId
=
get_opclass_oid
(
amoid
,
objname
,
false
);
address
.
objectSubId
=
0
;
break
;
case
OBJECT_OPFAMILY
:
address
.
classId
=
OperatorFamilyRelationId
;
address
.
objectId
=
get_opfamily_oid
(
amoid
,
objname
,
false
);
address
.
objectSubId
=
0
;
break
;
default:
elog
(
ERROR
,
"unrecognized objtype: %d"
,
(
int
)
objtype
);
/* placate compiler, which doesn't know elog won't return */
address
.
classId
=
InvalidOid
;
address
.
objectId
=
InvalidOid
;
address
.
objectSubId
=
0
;
}
return
address
;
}
/*
* Test whether an object exists.
*/
static
bool
object_exists
(
ObjectAddress
address
)
{
int
cache
=
-
1
;
Oid
indexoid
=
InvalidOid
;
Relation
rel
;
ScanKeyData
skey
[
1
];
SysScanDesc
sd
;
bool
found
;
/* Sub-objects require special treatment. */
if
(
address
.
objectSubId
!=
0
)
{
HeapTuple
atttup
;
/* Currently, attributes are the only sub-objects. */
Assert
(
address
.
classId
==
RelationRelationId
);
atttup
=
SearchSysCache2
(
ATTNUM
,
ObjectIdGetDatum
(
address
.
objectId
),
Int16GetDatum
(
address
.
objectSubId
));
if
(
!
HeapTupleIsValid
(
atttup
))
found
=
false
;
else
{
found
=
((
Form_pg_attribute
)
GETSTRUCT
(
atttup
))
->
attisdropped
;
ReleaseSysCache
(
atttup
);
}
return
found
;
}
/*
* For object types that have a relevant syscache, we use it; for
* everything else, we'll have to do an index-scan. This switch
* sets either the cache to be used for the syscache lookup, or the
* index to be used for the index scan.
*/
switch
(
address
.
classId
)
{
case
RelationRelationId
:
cache
=
RELOID
;
break
;
case
RewriteRelationId
:
indexoid
=
RewriteOidIndexId
;
break
;
case
TriggerRelationId
:
indexoid
=
TriggerOidIndexId
;
break
;
case
ConstraintRelationId
:
cache
=
CONSTROID
;
break
;
case
DatabaseRelationId
:
cache
=
DATABASEOID
;
break
;
case
TableSpaceRelationId
:
cache
=
TABLESPACEOID
;
break
;
case
AuthIdRelationId
:
cache
=
AUTHOID
;
break
;
case
NamespaceRelationId
:
cache
=
NAMESPACEOID
;
break
;
case
LanguageRelationId
:
cache
=
LANGOID
;
break
;
case
TypeRelationId
:
cache
=
TYPEOID
;
break
;
case
ProcedureRelationId
:
cache
=
PROCOID
;
break
;
case
OperatorRelationId
:
cache
=
OPEROID
;
break
;
case
ConversionRelationId
:
cache
=
CONVOID
;
break
;
case
OperatorClassRelationId
:
cache
=
CLAOID
;
break
;
case
OperatorFamilyRelationId
:
cache
=
OPFAMILYOID
;
break
;
case
LargeObjectRelationId
:
/*
* Weird backward compatibility hack: ObjectAddress notation uses
* LargeObjectRelationId for large objects, but since PostgreSQL
* 9.0, the relevant catalog is actually
* LargeObjectMetadataRelationId.
*/
address
.
classId
=
LargeObjectMetadataRelationId
;
indexoid
=
LargeObjectMetadataOidIndexId
;
break
;
case
CastRelationId
:
indexoid
=
CastOidIndexId
;
break
;
case
TSParserRelationId
:
cache
=
TSPARSEROID
;
break
;
case
TSDictionaryRelationId
:
cache
=
TSDICTOID
;
break
;
case
TSTemplateRelationId
:
cache
=
TSTEMPLATEOID
;
break
;
case
TSConfigRelationId
:
cache
=
TSCONFIGOID
;
break
;
default:
elog
(
ERROR
,
"unrecognized classid: %u"
,
address
.
classId
);
}
/* Found a syscache? */
if
(
cache
!=
-
1
)
return
SearchSysCacheExists1
(
cache
,
ObjectIdGetDatum
(
address
.
objectId
));
/* No syscache, so examine the table directly. */
Assert
(
OidIsValid
(
indexoid
));
ScanKeyInit
(
&
skey
[
0
],
ObjectIdAttributeNumber
,
BTEqualStrategyNumber
,
F_OIDEQ
,
ObjectIdGetDatum
(
address
.
objectId
));
rel
=
heap_open
(
address
.
classId
,
AccessShareLock
);
sd
=
systable_beginscan
(
rel
,
indexoid
,
true
,
SnapshotNow
,
1
,
skey
);
found
=
HeapTupleIsValid
(
systable_getnext
(
sd
));
systable_endscan
(
sd
);
heap_close
(
rel
,
AccessShareLock
);
return
found
;
}
src/backend/commands/comment.c
View file @
c10575ff
...
...
@@ -7,7 +7,7 @@
* Copyright (c) 1996-2010, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.11
7 2010/08/05 15:25:35
rhaas Exp $
* $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.11
8 2010/08/27 11:47:41
rhaas Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -17,83 +17,30 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/indexing.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/objectaddress.h"
#include "catalog/pg_description.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/proclang.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "libpq/be-fsstubs.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
#include "rewrite/rewriteSupport.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
/*
* Static Function Prototypes --
*
* The following prototypes 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.
* For most object types, the permissions-checking logic is simple enough
* that it makes sense to just include it in CommentObject(). However, a few
* object types require something more complex; for those, we define helper
* functions.
*/
static
void
CommentRelation
(
int
objtype
,
List
*
relname
,
char
*
comment
);
static
void
CommentAttribute
(
List
*
qualname
,
char
*
comment
);
static
void
CommentDatabase
(
List
*
qualname
,
char
*
comment
);
static
void
CommentNamespace
(
List
*
qualname
,
char
*
comment
);
static
void
CommentRule
(
List
*
qualname
,
char
*
comment
);
static
void
CommentType
(
List
*
typename
,
char
*
comment
);
static
void
CommentAggregate
(
List
*
aggregate
,
List
*
arguments
,
char
*
comment
);
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
CommentOpFamily
(
List
*
qualname
,
List
*
arguments
,
char
*
comment
);
static
void
CommentLargeObject
(
List
*
qualname
,
char
*
comment
);
static
void
CommentCast
(
List
*
qualname
,
List
*
arguments
,
char
*
comment
);
static
void
CommentTablespace
(
List
*
qualname
,
char
*
comment
);
static
void
CommentRole
(
List
*
qualname
,
char
*
comment
);
static
void
CommentTSParser
(
List
*
qualname
,
char
*
comment
);
static
void
CommentTSDictionary
(
List
*
qualname
,
char
*
comment
);
static
void
CommentTSTemplate
(
List
*
qualname
,
char
*
comment
);
static
void
CommentTSConfiguration
(
List
*
qualname
,
char
*
comment
);
static
void
CheckRelationComment
(
int
objtype
,
Relation
relation
);
static
void
CheckAttributeComment
(
Relation
relation
);
static
void
CheckCastComment
(
List
*
qualname
,
List
*
arguments
);
/*
...
...
@@ -105,84 +52,175 @@ static void CommentTSConfiguration(List *qualname, char *comment);
void
CommentObject
(
CommentStmt
*
stmt
)
{
ObjectAddress
address
;
Relation
relation
;
/*
* When loading a dump, we may see a COMMENT ON DATABASE for the old name
* of the database. Erroring out would prevent pg_restore from completing
* (which is really pg_restore's fault, but for now we will work around
* the problem here). Consensus is that the best fix is to treat wrong
* database name as a WARNING not an ERROR; hence, the following special
* case. (If the length of stmt->objname is not 1, get_object_address will
* throw an error below; that's OK.)
*/
if
(
stmt
->
objtype
==
OBJECT_DATABASE
&&
list_length
(
stmt
->
objname
)
==
1
)
{
char
*
database
=
strVal
(
linitial
(
stmt
->
objname
));
if
(
!
OidIsValid
(
get_database_oid
(
database
,
true
)))
{
ereport
(
WARNING
,
(
errcode
(
ERRCODE_UNDEFINED_DATABASE
),
errmsg
(
"database
\"
%s
\"
does not exist"
,
database
)));
return
;
}
}
/*
* Translate the parser representation which identifies this object into
* an ObjectAddress. get_object_address() will throw an error if the
* object does not exist, and will also acquire a lock on the target
* to guard against concurrent DROP operations.
*/
address
=
get_object_address
(
stmt
->
objtype
,
stmt
->
objname
,
stmt
->
objargs
,
&
relation
,
ShareUpdateExclusiveLock
);
/* Privilege and integrity checks. */
switch
(
stmt
->
objtype
)
{
case
OBJECT_INDEX
:
case
OBJECT_SEQUENCE
:
case
OBJECT_TABLE
:
case
OBJECT_VIEW
:
C
ommentRelation
(
stmt
->
objtype
,
stmt
->
objname
,
stmt
->
comment
);
C
heckRelationComment
(
stmt
->
objtype
,
relation
);
break
;
case
OBJECT_COLUMN
:
C
ommentAttribute
(
stmt
->
objname
,
stmt
->
comment
);
C
heckAttributeComment
(
relation
);
break
;
case
OBJECT_DATABASE
:
CommentDatabase
(
stmt
->
objname
,
stmt
->
comment
);
break
;
case
OBJECT_RULE
:
CommentRule
(
stmt
->
objname
,
stmt
->
comment
);
if
(
!
pg_database_ownercheck
(
address
.
objectId
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_DATABASE
,
strVal
(
linitial
(
stmt
->
objname
)));
break
;
case
OBJECT_TYPE
:
CommentType
(
stmt
->
objname
,
stmt
->
comment
);
if
(
!
pg_type_ownercheck
(
address
.
objectId
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_TYPE
,
format_type_be
(
address
.
objectId
));
break
;
case
OBJECT_AGGREGATE
:
CommentAggregate
(
stmt
->
objname
,
stmt
->
objargs
,
stmt
->
comment
);
break
;
case
OBJECT_FUNCTION
:
CommentProc
(
stmt
->
objname
,
stmt
->
objargs
,
stmt
->
comment
);
if
(
!
pg_proc_ownercheck
(
address
.
objectId
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_PROC
,
NameListToString
(
stmt
->
objname
));
break
;
case
OBJECT_OPERATOR
:
CommentOperator
(
stmt
->
objname
,
stmt
->
objargs
,
stmt
->
comment
);
if
(
!
pg_oper_ownercheck
(
address
.
objectId
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_OPER
,
NameListToString
(
stmt
->
objname
));
break
;
case
OBJECT_RULE
:
case
OBJECT_TRIGGER
:
CommentTrigger
(
stmt
->
objname
,
stmt
->
comment
);
case
OBJECT_CONSTRAINT
:
if
(
!
pg_class_ownercheck
(
RelationGetRelid
(
relation
),
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_CLASS
,
RelationGetRelationName
(
relation
));
break
;
case
OBJECT_SCHEMA
:
CommentNamespace
(
stmt
->
objname
,
stmt
->
comment
);
break
;
case
OBJECT_CONSTRAINT
:
CommentConstraint
(
stmt
->
objname
,
stmt
->
comment
);
if
(
!
pg_namespace_ownercheck
(
address
.
objectId
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_NAMESPACE
,
strVal
(
linitial
(
stmt
->
objname
)));
break
;
case
OBJECT_CONVERSION
:
CommentConversion
(
stmt
->
objname
,
stmt
->
comment
);
if
(
!
pg_conversion_ownercheck
(
address
.
objectId
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_CONVERSION
,
NameListToString
(
stmt
->
objname
));
break
;
case
OBJECT_LANGUAGE
:
CommentLanguage
(
stmt
->
objname
,
stmt
->
comment
);
if
(
!
superuser
())
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INSUFFICIENT_PRIVILEGE
),
errmsg
(
"must be superuser to comment on procedural language"
)));
break
;
case
OBJECT_OPCLASS
:
CommentOpClass
(
stmt
->
objname
,
stmt
->
objargs
,
stmt
->
comment
);
if
(
!
pg_opclass_ownercheck
(
address
.
objectId
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_OPCLASS
,
NameListToString
(
stmt
->
objname
));
break
;
case
OBJECT_OPFAMILY
:
CommentOpFamily
(
stmt
->
objname
,
stmt
->
objargs
,
stmt
->
comment
);
if
(
!
pg_opfamily_ownercheck
(
address
.
objectId
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_OPFAMILY
,
NameListToString
(
stmt
->
objname
));
break
;
case
OBJECT_LARGEOBJECT
:
CommentLargeObject
(
stmt
->
objname
,
stmt
->
comment
);
if
(
!
lo_compat_privileges
&&
!
pg_largeobject_ownercheck
(
address
.
objectId
,
GetUserId
()))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INSUFFICIENT_PRIVILEGE
),
errmsg
(
"must be owner of large object %u"
,
address
.
objectId
)));
break
;
case
OBJECT_CAST
:
C
ommentCast
(
stmt
->
objname
,
stmt
->
objargs
,
stmt
->
comment
);
C
heckCastComment
(
stmt
->
objname
,
stmt
->
objargs
);
break
;
case
OBJECT_TABLESPACE
:
CommentTablespace
(
stmt
->
objname
,
stmt
->
comment
);
if
(
!
pg_tablespace_ownercheck
(
address
.
objectId
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_TABLESPACE
,
strVal
(
linitial
(
stmt
->
objname
)));
break
;
case
OBJECT_ROLE
:
CommentRole
(
stmt
->
objname
,
stmt
->
comment
);
if
(
!
has_privs_of_role
(
GetUserId
(),
address
.
objectId
))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INSUFFICIENT_PRIVILEGE
),
errmsg
(
"must be member of role
\"
%s
\"
to comment upon it"
,
strVal
(
linitial
(
stmt
->
objname
)))));
break
;
case
OBJECT_TSPARSER
:
CommentTSParser
(
stmt
->
objname
,
stmt
->
comment
);
if
(
!
superuser
())
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INSUFFICIENT_PRIVILEGE
),
errmsg
(
"must be superuser to comment on text search parser"
)));
break
;
case
OBJECT_TSDICTIONARY
:
CommentTSDictionary
(
stmt
->
objname
,
stmt
->
comment
);
if
(
!
pg_ts_dict_ownercheck
(
address
.
objectId
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_TSDICTIONARY
,
NameListToString
(
stmt
->
objname
));
break
;
case
OBJECT_TSTEMPLATE
:
CommentTSTemplate
(
stmt
->
objname
,
stmt
->
comment
);
if
(
!
superuser
())
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INSUFFICIENT_PRIVILEGE
),
errmsg
(
"must be superuser to comment on text search template"
)));
break
;
case
OBJECT_TSCONFIGURATION
:
CommentTSConfiguration
(
stmt
->
objname
,
stmt
->
comment
);
if
(
!
pg_ts_config_ownercheck
(
address
.
objectId
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_TSCONFIGURATION
,
NameListToString
(
stmt
->
objname
));
break
;
default:
elog
(
ERROR
,
"unrecognized object type: %d"
,
(
int
)
stmt
->
objtype
);
}
/*
* Databases, tablespaces, and roles are cluster-wide objects, so any
* comments on those objects are recorded in the shared pg_shdescription
* catalog. Comments on all other objects are recorded in pg_description.
*/
if
(
stmt
->
objtype
==
OBJECT_DATABASE
||
stmt
->
objtype
==
OBJECT_TABLESPACE
||
stmt
->
objtype
==
OBJECT_ROLE
)
CreateSharedComments
(
address
.
objectId
,
address
.
classId
,
stmt
->
comment
);
else
CreateComments
(
address
.
objectId
,
address
.
classId
,
address
.
objectSubId
,
stmt
->
comment
);
/*
* If get_object_address() opened the relation for us, we close it to keep
* the reference count correct - but we retain any locks acquired by
* get_object_address() until commit time, to guard against concurrent
* activity.
*/
if
(
relation
!=
NULL
)
relation_close
(
relation
,
NoLock
);
}
/*
...
...
@@ -524,36 +562,17 @@ GetComment(Oid oid, Oid classoid, int32 subid)
}
/*
* 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.
* Check whether the user is allowed to comment on this relation.
*/
static
void
C
ommentRelation
(
int
objtype
,
List
*
relname
,
char
*
comment
)
C
heckRelationComment
(
int
objtype
,
Relation
relation
)
{
Relation
relation
;
RangeVar
*
tgtrel
;
tgtrel
=
makeRangeVarFromNameList
(
relname
);
/*
* 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.)
*/
relation
=
relation_openrv
(
tgtrel
,
AccessShareLock
);
/* Check object security */
if
(
!
pg_class_ownercheck
(
RelationGetRelid
(
relation
),
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_CLASS
,
RelationGetRelationName
(
relation
));
/* Next, verify that the relation type matches the intent */
switch
(
objtype
)
{
case
OBJECT_INDEX
:
...
...
@@ -585,48 +604,15 @@ CommentRelation(int objtype, List *relname, char *comment)
RelationGetRelationName
(
relation
))));
break
;
}
/* Create the comment using the relation's oid */
CreateComments
(
RelationGetRelid
(
relation
),
RelationRelationId
,
0
,
comment
);
/* Done, but hold lock until commit */
relation_close
(
relation
,
NoLock
);
}
/*
* 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 look up the specified
* attribute. If successful, a comment is added/dropped, else an
* ereport() exception is thrown. The parameters are the relation
* and attribute names, and the comment
* Check whether the user is allowed to comment on an attribute of the
* specified relation.
*/
static
void
C
ommentAttribute
(
List
*
qualname
,
char
*
comment
)
C
heckAttributeComment
(
Relation
relation
)
{
int
nnames
;
List
*
relname
;
char
*
attrname
;
RangeVar
*
rel
;
Relation
relation
;
AttrNumber
attnum
;
/* Separate relname and attr name */
nnames
=
list_length
(
qualname
);
if
(
nnames
<
2
)
/* parser messed up */
elog
(
ERROR
,
"must specify relation and attribute"
);
relname
=
list_truncate
(
list_copy
(
qualname
),
nnames
-
1
);
attrname
=
strVal
(
lfirst
(
list_tail
(
qualname
)));
/* Open the containing relation to ensure it won't go away meanwhile */
rel
=
makeRangeVarFromNameList
(
relname
);
relation
=
relation_openrv
(
rel
,
AccessShareLock
);
/* Check object security */
if
(
!
pg_class_ownercheck
(
RelationGetRelid
(
relation
),
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_CLASS
,
RelationGetRelationName
(
relation
));
...
...
@@ -645,613 +631,18 @@ CommentAttribute(List *qualname, char *comment)
(
errcode
(
ERRCODE_WRONG_OBJECT_TYPE
),
errmsg
(
"
\"
%s
\"
is not a table, view, or composite type"
,
RelationGetRelationName
(
relation
))));
/* Now, fetch the attribute number from the system cache */
attnum
=
get_attnum
(
RelationGetRelid
(
relation
),
attrname
);
if
(
attnum
==
InvalidAttrNumber
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_COLUMN
),
errmsg
(
"column
\"
%s
\"
of relation
\"
%s
\"
does not exist"
,
attrname
,
RelationGetRelationName
(
relation
))));
/* Create the comment using the relation's oid */
CreateComments
(
RelationGetRelid
(
relation
),
RelationRelationId
,
(
int32
)
attnum
,
comment
);
/* Done, but hold lock until commit */
relation_close
(
relation
,
NoLock
);
}
/*
* 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 successful, will then
* attempt to find the oid of the database specified. Once found,
* a comment is added/dropped using the CreateSharedComments() routine.
*/
static
void
CommentDatabase
(
List
*
qualname
,
char
*
comment
)
{
char
*
database
;
Oid
oid
;
if
(
list_length
(
qualname
)
!=
1
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"database name cannot be qualified"
)));
database
=
strVal
(
linitial
(
qualname
));
/*
* When loading a dump, we may see a COMMENT ON DATABASE for the old name
* of the database. Erroring out would prevent pg_restore from completing
* (which is really pg_restore's fault, but for now we will work around
* the problem here). Consensus is that the best fix is to treat wrong
* database name as a WARNING not an ERROR (thus, we tell get_database_oid
* to ignore the error so that we can handle it differently here).
*/
oid
=
get_database_oid
(
database
,
true
);
if
(
!
OidIsValid
(
oid
))
{
ereport
(
WARNING
,
(
errcode
(
ERRCODE_UNDEFINED_DATABASE
),
errmsg
(
"database
\"
%s
\"
does not exist"
,
database
)));
return
;
}
/* Check object security */
if
(
!
pg_database_ownercheck
(
oid
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_DATABASE
,
database
);
/* Call CreateSharedComments() to create/drop the comments */
CreateSharedComments
(
oid
,
DatabaseRelationId
,
comment
);
}
/*
* CommentTablespace --
*
* This routine is used to add/drop any user-comments a user might
* have regarding a tablespace. The tablepace is specified by name
* and, if found, and the user has appropriate permissions, a
* comment will be added/dropped using the CreateSharedComments() routine.
*
* Check whether the user is allowed to comment on the specified cast.
*/
static
void
CommentTablespace
(
List
*
qualname
,
char
*
comment
)
{
char
*
tablespace
;
Oid
oid
;
if
(
list_length
(
qualname
)
!=
1
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"tablespace name cannot be qualified"
)));
tablespace
=
strVal
(
linitial
(
qualname
));
oid
=
get_tablespace_oid
(
tablespace
,
false
);
/* Check object security */
if
(
!
pg_tablespace_ownercheck
(
oid
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_TABLESPACE
,
tablespace
);
/* Call CreateSharedComments() to create/drop the comments */
CreateSharedComments
(
oid
,
TableSpaceRelationId
,
comment
);
}
/*
* CommentRole --
*
* This routine is used to add/drop any user-comments a user might
* have regarding a role. The role is specified by name
* and, if found, and the user has appropriate permissions, a
* comment will be added/dropped using the CreateSharedComments() routine.
*/
static
void
CommentRole
(
List
*
qualname
,
char
*
comment
)
{
char
*
role
;
Oid
oid
;
if
(
list_length
(
qualname
)
!=
1
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"role name cannot be qualified"
)));
role
=
strVal
(
linitial
(
qualname
));
oid
=
get_role_oid
(
role
,
false
);
/* Check object security */
if
(
!
has_privs_of_role
(
GetUserId
(),
oid
))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INSUFFICIENT_PRIVILEGE
),
errmsg
(
"must be member of role
\"
%s
\"
to comment upon it"
,
role
)));
/* Call CreateSharedComments() to create/drop the comments */
CreateSharedComments
(
oid
,
AuthIdRelationId
,
comment
);
}
/*
* CommentNamespace --
*
* This routine is used to add/drop any user-comments a user might
* have regarding the specified namespace. The routine will check
* security for owner permissions, and, if successful, will then
* attempt to find the oid of the namespace specified. Once found,
* a comment is added/dropped using the CreateComments() routine.
*/
static
void
CommentNamespace
(
List
*
qualname
,
char
*
comment
)
{
Oid
oid
;
char
*
namespace
;
if
(
list_length
(
qualname
)
!=
1
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"schema name cannot be qualified"
)));
namespace
=
strVal
(
linitial
(
qualname
));
oid
=
get_namespace_oid
(
namespace
,
false
);
/* Check object security */
if
(
!
pg_namespace_ownercheck
(
oid
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_NAMESPACE
,
namespace
);
/* Call CreateComments() to create/drop the comments */
CreateComments
(
oid
,
NamespaceRelationId
,
0
,
comment
);
}
/*
* CommentRule --
*
* This routine is used to add/drop any user-comments a user might
* have regarding a specified RULE. The rule for commenting is determined by
* both its name and the relation to which it refers. The arguments to this
* function are the rule name and relation name (merged into a qualified
* name), and the comment to add/drop.
*
* Before PG 7.3, rules had unique names across the whole database, and so
* the syntax was just COMMENT ON RULE rulename, with no relation name.
* For purposes of backwards compatibility, we support that as long as there
* is only one rule by the specified name in the database.
*/
static
void
CommentRule
(
List
*
qualname
,
char
*
comment
)
{
int
nnames
;
List
*
relname
;
char
*
rulename
;
RangeVar
*
rel
;
Relation
relation
;
Oid
reloid
;
Oid
ruleoid
;
/* Separate relname and trig name */
nnames
=
list_length
(
qualname
);
if
(
nnames
==
1
)
{
rulename
=
strVal
(
linitial
(
qualname
));
ruleoid
=
get_rewrite_oid_without_relid
(
rulename
,
&
reloid
);
/* Open the owning relation to ensure it won't go away meanwhile */
relation
=
heap_open
(
reloid
,
AccessShareLock
);
}
else
{
/* New-style: rule and relname both provided */
Assert
(
nnames
>=
2
);
relname
=
list_truncate
(
list_copy
(
qualname
),
nnames
-
1
);
rulename
=
strVal
(
lfirst
(
list_tail
(
qualname
)));
/* Open the owning relation to ensure it won't go away meanwhile */
rel
=
makeRangeVarFromNameList
(
relname
);
relation
=
heap_openrv
(
rel
,
AccessShareLock
);
reloid
=
RelationGetRelid
(
relation
);
/* Find the rule's pg_rewrite tuple, get its OID */
ruleoid
=
get_rewrite_oid
(
reloid
,
rulename
,
false
);
}
/* Check object security */
if
(
!
pg_class_ownercheck
(
reloid
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_CLASS
,
get_rel_name
(
reloid
));
/* Call CreateComments() to create/drop the comments */
CreateComments
(
ruleoid
,
RewriteRelationId
,
0
,
comment
);
heap_close
(
relation
,
NoLock
);
}
/*
* 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 parameters to this routine.
*/
static
void
CommentType
(
List
*
typename
,
char
*
comment
)
{
TypeName
*
tname
;
Oid
oid
;
/* XXX a bit of a crock; should accept TypeName in COMMENT syntax */
tname
=
makeTypeNameFromNameList
(
typename
);
/* Find the type's oid */
oid
=
typenameTypeId
(
NULL
,
tname
,
NULL
);
/* Check object security */
if
(
!
pg_type_ownercheck
(
oid
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_TYPE
,
format_type_be
(
oid
));
/* Call CreateComments() to create/drop the comments */
CreateComments
(
oid
,
TypeRelationId
,
0
,
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(s).
*/
static
void
CommentAggregate
(
List
*
aggregate
,
List
*
arguments
,
char
*
comment
)
{
Oid
oid
;
/* Look up function and make sure it's an aggregate */
oid
=
LookupAggNameTypeNames
(
aggregate
,
arguments
,
false
);
/* Next, validate the user's attempt to comment */
if
(
!
pg_proc_ownercheck
(
oid
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_PROC
,
NameListToString
(
aggregate
));
/* Call CreateComments() to create/drop the comments */
CreateComments
(
oid
,
ProcedureRelationId
,
0
,
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.
*/
static
void
CommentProc
(
List
*
function
,
List
*
arguments
,
char
*
comment
)
{
Oid
oid
;
/* Look up the procedure */
oid
=
LookupFuncNameTypeNames
(
function
,
arguments
,
false
);
/* Now, validate the user's ability to comment on this function */
if
(
!
pg_proc_ownercheck
(
oid
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_PROC
,
NameListToString
(
function
));
/* Call CreateComments() to create/drop the comments */
CreateComments
(
oid
,
ProcedureRelationId
,
0
,
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.
*/
static
void
CommentOperator
(
List
*
opername
,
List
*
arguments
,
char
*
comment
)
{
TypeName
*
typenode1
=
(
TypeName
*
)
linitial
(
arguments
);
TypeName
*
typenode2
=
(
TypeName
*
)
lsecond
(
arguments
);
Oid
oid
;
/* Look up the operator */
oid
=
LookupOperNameTypeNames
(
NULL
,
opername
,
typenode1
,
typenode2
,
false
,
-
1
);
/* Check user's privilege to comment on this operator */
if
(
!
pg_oper_ownercheck
(
oid
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_OPER
,
NameListToString
(
opername
));
/* Call CreateComments() to create/drop the comments */
CreateComments
(
oid
,
OperatorRelationId
,
0
,
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 and relation name (merged into a qualified
* name), and the comment to add/drop.
*/
static
void
CommentTrigger
(
List
*
qualname
,
char
*
comment
)
{
int
nnames
;
List
*
relname
;
char
*
trigname
;
RangeVar
*
rel
;
Relation
relation
;
Oid
oid
;
/* Separate relname and trig name */
nnames
=
list_length
(
qualname
);
if
(
nnames
<
2
)
/* parser messed up */
elog
(
ERROR
,
"must specify relation and trigger"
);
relname
=
list_truncate
(
list_copy
(
qualname
),
nnames
-
1
);
trigname
=
strVal
(
lfirst
(
list_tail
(
qualname
)));
/* Open the owning relation to ensure it won't go away meanwhile */
rel
=
makeRangeVarFromNameList
(
relname
);
relation
=
heap_openrv
(
rel
,
AccessShareLock
);
/* Check object security */
if
(
!
pg_class_ownercheck
(
RelationGetRelid
(
relation
),
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_CLASS
,
RelationGetRelationName
(
relation
));
oid
=
get_trigger_oid
(
RelationGetRelid
(
relation
),
trigname
,
false
);
/* Call CreateComments() to create/drop the comments */
CreateComments
(
oid
,
TriggerRelationId
,
0
,
comment
);
/* Done, but hold lock on relation */
heap_close
(
relation
,
NoLock
);
}
/*
* CommentConstraint --
*
* Enable commenting on constraints held within the pg_constraint
* table. A qualified name is required as constraint names are
* unique per relation.
*/
static
void
CommentConstraint
(
List
*
qualname
,
char
*
comment
)
{
int
nnames
;
List
*
relName
;
char
*
conName
;
RangeVar
*
rel
;
Relation
relation
;
Oid
conOid
;
/* Separate relname and constraint name */
nnames
=
list_length
(
qualname
);
if
(
nnames
<
2
)
/* parser messed up */
elog
(
ERROR
,
"must specify relation and constraint"
);
relName
=
list_truncate
(
list_copy
(
qualname
),
nnames
-
1
);
conName
=
strVal
(
lfirst
(
list_tail
(
qualname
)));
/* Open the owning relation to ensure it won't go away meanwhile */
rel
=
makeRangeVarFromNameList
(
relName
);
relation
=
heap_openrv
(
rel
,
AccessShareLock
);
/* Check object security */
if
(
!
pg_class_ownercheck
(
RelationGetRelid
(
relation
),
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_CLASS
,
RelationGetRelationName
(
relation
));
conOid
=
get_constraint_oid
(
RelationGetRelid
(
relation
),
conName
,
false
);
/* Call CreateComments() to create/drop the comments */
CreateComments
(
conOid
,
ConstraintRelationId
,
0
,
comment
);
/* Done, but hold lock on relation */
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
;
conversionOid
=
get_conversion_oid
(
qualname
,
false
);
/* Check object security */
if
(
!
pg_conversion_ownercheck
(
conversionOid
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_CONVERSION
,
NameListToString
(
qualname
));
/* Call CreateComments() to create/drop the comments */
CreateComments
(
conversionOid
,
ConversionRelationId
,
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
;
char
*
language
;
if
(
list_length
(
qualname
)
!=
1
)
ereport
(
ERROR
,
(
errcode
(
ERRCODE_SYNTAX_ERROR
),
errmsg
(
"language name cannot be qualified"
)));
language
=
strVal
(
linitial
(
qualname
));
oid
=
get_language_oid
(
language
,
false
);
/* Check object security */
if
(
!
superuser
())
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INSUFFICIENT_PRIVILEGE
),
errmsg
(
"must be superuser to comment on procedural language"
)));
/* Call CreateComments() to create/drop the comments */
CreateComments
(
oid
,
LanguageRelationId
,
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
;
Oid
amID
;
Oid
opcID
;
Assert
(
list_length
(
arguments
)
==
1
);
amname
=
strVal
(
linitial
(
arguments
));
/*
* Get the operator class OID.
*/
amID
=
get_am_oid
(
amname
,
false
);
opcID
=
get_opclass_oid
(
amID
,
qualname
,
false
);
/* Permission check: must own opclass */
if
(
!
pg_opclass_ownercheck
(
opcID
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_OPCLASS
,
NameListToString
(
qualname
));
/* Call CreateComments() to create/drop the comments */
CreateComments
(
opcID
,
OperatorClassRelationId
,
0
,
comment
);
}
/*
* CommentOpFamily --
*
* This routine is used to allow a user to provide comments on an
* operator family. The operator family for commenting is determined by both
* its name and its argument list which defines the index method
* the operator family is used for. The argument list is expected to contain
* a single name (represented as a string Value node).
*/
static
void
CommentOpFamily
(
List
*
qualname
,
List
*
arguments
,
char
*
comment
)
{
char
*
amname
;
Oid
amID
;
Oid
opfID
;
Assert
(
list_length
(
arguments
)
==
1
);
amname
=
strVal
(
linitial
(
arguments
));
/* Get the opfamily OID. */
amID
=
get_am_oid
(
amname
,
false
);
opfID
=
get_opfamily_oid
(
amID
,
qualname
,
false
);
/* Permission check: must own opfamily */
if
(
!
pg_opfamily_ownercheck
(
opfID
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_OPFAMILY
,
NameListToString
(
qualname
));
/* Call CreateComments() to create/drop the comments */
CreateComments
(
opfID
,
OperatorFamilyRelationId
,
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
;
Assert
(
list_length
(
qualname
)
==
1
);
loid
=
oidparse
((
Node
*
)
linitial
(
qualname
));
/* check that the large object exists */
if
(
!
LargeObjectExists
(
loid
))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_UNDEFINED_OBJECT
),
errmsg
(
"large object %u does not exist"
,
loid
)));
/* Permission checks */
if
(
!
lo_compat_privileges
&&
!
pg_largeobject_ownercheck
(
loid
,
GetUserId
()))
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INSUFFICIENT_PRIVILEGE
),
errmsg
(
"must be owner of large object %u"
,
loid
)));
/*
* Call CreateComments() to create/drop the comments
*
* See the comment in the inv_create() which describes the reason why
* LargeObjectRelationId is used instead of LargeObjectMetadataRelationId.
*/
CreateComments
(
loid
,
LargeObjectRelationId
,
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
)
CheckCastComment
(
List
*
qualname
,
List
*
arguments
)
{
TypeName
*
sourcetype
;
TypeName
*
targettype
;
Oid
sourcetypeid
;
Oid
targettypeid
;
Oid
castOid
;
Assert
(
list_length
(
qualname
)
==
1
);
sourcetype
=
(
TypeName
*
)
linitial
(
qualname
);
...
...
@@ -1263,9 +654,6 @@ CommentCast(List *qualname, List *arguments, char *comment)
sourcetypeid
=
typenameTypeId
(
NULL
,
sourcetype
,
NULL
);
targettypeid
=
typenameTypeId
(
NULL
,
targettype
,
NULL
);
/* Get the OID of the cast */
castOid
=
get_cast_oid
(
sourcetypeid
,
targettypeid
,
false
);
/* Permission check */
if
(
!
pg_type_ownercheck
(
sourcetypeid
,
GetUserId
())
&&
!
pg_type_ownercheck
(
targettypeid
,
GetUserId
()))
...
...
@@ -1274,65 +662,4 @@ CommentCast(List *qualname, List *arguments, char *comment)
errmsg
(
"must be owner of type %s or type %s"
,
format_type_be
(
sourcetypeid
),
format_type_be
(
targettypeid
))));
/* Call CreateComments() to create/drop the comments */
CreateComments
(
castOid
,
CastRelationId
,
0
,
comment
);
}
static
void
CommentTSParser
(
List
*
qualname
,
char
*
comment
)
{
Oid
prsId
;
prsId
=
get_ts_parser_oid
(
qualname
,
false
);
if
(
!
superuser
())
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INSUFFICIENT_PRIVILEGE
),
errmsg
(
"must be superuser to comment on text search parser"
)));
CreateComments
(
prsId
,
TSParserRelationId
,
0
,
comment
);
}
static
void
CommentTSDictionary
(
List
*
qualname
,
char
*
comment
)
{
Oid
dictId
;
dictId
=
get_ts_dict_oid
(
qualname
,
false
);
if
(
!
pg_ts_dict_ownercheck
(
dictId
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_TSDICTIONARY
,
NameListToString
(
qualname
));
CreateComments
(
dictId
,
TSDictionaryRelationId
,
0
,
comment
);
}
static
void
CommentTSTemplate
(
List
*
qualname
,
char
*
comment
)
{
Oid
tmplId
;
tmplId
=
get_ts_template_oid
(
qualname
,
false
);
if
(
!
superuser
())
ereport
(
ERROR
,
(
errcode
(
ERRCODE_INSUFFICIENT_PRIVILEGE
),
errmsg
(
"must be superuser to comment on text search template"
)));
CreateComments
(
tmplId
,
TSTemplateRelationId
,
0
,
comment
);
}
static
void
CommentTSConfiguration
(
List
*
qualname
,
char
*
comment
)
{
Oid
cfgId
;
cfgId
=
get_ts_config_oid
(
qualname
,
false
);
if
(
!
pg_ts_config_ownercheck
(
cfgId
,
GetUserId
()))
aclcheck_error
(
ACLCHECK_NOT_OWNER
,
ACL_KIND_TSCONFIGURATION
,
NameListToString
(
qualname
));
CreateComments
(
cfgId
,
TSConfigRelationId
,
0
,
comment
);
}
src/include/catalog/dependency.h
View file @
c10575ff
...
...
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.4
5 2010/04/05 01:09:53 tgl
Exp $
* $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.4
6 2010/08/27 11:47:41 rhaas
Exp $
*
*-------------------------------------------------------------------------
*/
...
...
@@ -15,6 +15,7 @@
#define DEPENDENCY_H
#include "nodes/parsenodes.h"
/* for DropBehavior */
#include "catalog/objectaddress.h"
/*
...
...
@@ -100,17 +101,6 @@ typedef enum SharedDependencyType
SHARED_DEPENDENCY_INVALID
=
0
}
SharedDependencyType
;
/*
* The two objects related by a dependency are identified by ObjectAddresses.
*/
typedef
struct
ObjectAddress
{
Oid
classId
;
/* Class Id from pg_class */
Oid
objectId
;
/* OID of the object */
int32
objectSubId
;
/* Subitem within object (eg column), or 0 */
}
ObjectAddress
;
/* expansible list of ObjectAddresses (private in dependency.c) */
typedef
struct
ObjectAddresses
ObjectAddresses
;
...
...
src/include/catalog/objectaddress.h
0 → 100644
View file @
c10575ff
/*-------------------------------------------------------------------------
*
* objectaddress.h
* functions for working with object addresses
*
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/objectaddress.h,v 1.1 2010/08/27 11:47:41 rhaas Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef OBJECTADDRESS_H
#define OBJECTADDRESS_H
#include "nodes/parsenodes.h"
#include "storage/lock.h"
#include "utils/rel.h"
/*
* An ObjectAddress represents a database object of any type.
*/
typedef
struct
ObjectAddress
{
Oid
classId
;
/* Class Id from pg_class */
Oid
objectId
;
/* OID of the object */
int32
objectSubId
;
/* Subitem within object (eg column), or 0 */
}
ObjectAddress
;
ObjectAddress
get_object_address
(
ObjectType
objtype
,
List
*
objname
,
List
*
objargs
,
Relation
*
relp
,
LOCKMODE
lockmode
);
#endif
/* PARSE_OBJECT_H */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment