aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/alter.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-02-08 16:08:41 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2011-02-08 16:13:22 -0500
commitd9572c4e3b474031060189050e14ef384b94e001 (patch)
tree07646762f4086b94a69b9fc215734d2bccade5db /src/backend/commands/alter.c
parent414c5a2ea65cbd38d79ffdf9b1fde7cc75c134e0 (diff)
downloadpostgresql-d9572c4e3b474031060189050e14ef384b94e001.tar.gz
postgresql-d9572c4e3b474031060189050e14ef384b94e001.zip
Core support for "extensions", which are packages of SQL objects.
This patch adds the server infrastructure to support extensions. There is still one significant loose end, namely how to make it play nice with pg_upgrade, so I am not yet committing the changes that would make all the contrib modules depend on this feature. In passing, fix a disturbingly large amount of breakage in AlterObjectNamespace() and callers. Dimitri Fontaine, reviewed by Anssi Kääriäinen, Itagaki Takahiro, Tom Lane, and numerous others
Diffstat (limited to 'src/backend/commands/alter.c')
-rw-r--r--src/backend/commands/alter.c196
1 files changed, 160 insertions, 36 deletions
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 1c6ae0243e4..2c9340accf1 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -23,6 +23,7 @@
#include "commands/conversioncmds.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
+#include "commands/extension.h"
#include "commands/proclang.h"
#include "commands/schemacmds.h"
#include "commands/tablecmds.h"
@@ -188,6 +189,10 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
AlterConversionNamespace(stmt->object, stmt->newschema);
break;
+ case OBJECT_EXTENSION:
+ AlterExtensionNamespace(stmt->object, stmt->newschema);
+ break;
+
case OBJECT_FUNCTION:
AlterFunctionNamespace(stmt->object, stmt->objarg, false,
stmt->newschema);
@@ -242,87 +247,204 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt)
}
/*
+ * Change an object's namespace given its classOid and object Oid.
+ *
+ * Objects that don't have a namespace should be ignored.
+ *
+ * This function is currently used only by ALTER EXTENSION SET SCHEMA,
+ * so it only needs to cover object types that can be members of an
+ * extension, and it doesn't have to deal with certain special cases
+ * such as not wanting to process array types --- those should never
+ * be direct members of an extension anyway.
+ *
+ * Returns the OID of the object's previous namespace, or InvalidOid if
+ * object doesn't have a schema.
+ */
+Oid
+AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid)
+{
+ Oid oldNspOid = InvalidOid;
+ ObjectAddress dep;
+
+ dep.classId = classId;
+ dep.objectId = objid;
+ dep.objectSubId = 0;
+
+ switch (getObjectClass(&dep))
+ {
+ case OCLASS_CLASS:
+ {
+ Relation rel;
+ Relation classRel;
+
+ rel = relation_open(objid, AccessExclusiveLock);
+ oldNspOid = RelationGetNamespace(rel);
+
+ classRel = heap_open(RelationRelationId, RowExclusiveLock);
+
+ AlterRelationNamespaceInternal(classRel,
+ objid,
+ oldNspOid,
+ nspOid,
+ true);
+
+ heap_close(classRel, RowExclusiveLock);
+
+ relation_close(rel, NoLock);
+ break;
+ }
+
+ case OCLASS_PROC:
+ oldNspOid = AlterFunctionNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TYPE:
+ oldNspOid = AlterTypeNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_CONVERSION:
+ oldNspOid = AlterConversionNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_OPERATOR:
+ oldNspOid = AlterOperatorNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_OPCLASS:
+ oldNspOid = AlterOpClassNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_OPFAMILY:
+ oldNspOid = AlterOpFamilyNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TSPARSER:
+ oldNspOid = AlterTSParserNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TSDICT:
+ oldNspOid = AlterTSDictionaryNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TSTEMPLATE:
+ oldNspOid = AlterTSTemplateNamespace_oid(objid, nspOid);
+ break;
+
+ case OCLASS_TSCONFIG:
+ oldNspOid = AlterTSConfigurationNamespace_oid(objid, nspOid);
+ break;
+
+ default:
+ break;
+ }
+
+ return oldNspOid;
+}
+
+/*
* Generic function to change the namespace of a given object, for simple
- * cases (won't work for tables or functions, objects which have more than 2
- * key-attributes to use when searching for their syscache entries --- we
- * don't want nor need to get this generic here).
+ * cases (won't work for tables, nor other cases where we need to do more
+ * than change the namespace column of a single catalog entry).
*
* The AlterFooNamespace() calls just above will call a function whose job
* is to lookup the arguments for the generic function here.
*
- * Relation must already by open, it's the responsibility of the caller to
- * close it.
+ * rel: catalog relation containing object (RowExclusiveLock'd by caller)
+ * oidCacheId: syscache that indexes this catalog by OID
+ * nameCacheId: syscache that indexes this catalog by name and namespace
+ * (pass -1 if there is none)
+ * objid: OID of object to change the namespace of
+ * nspOid: OID of new namespace
+ * Anum_name: column number of catalog's name column
+ * Anum_namespace: column number of catalog's namespace column
+ * Anum_owner: column number of catalog's owner column, or -1 if none
+ * acl_kind: ACL type for object, or -1 if none assigned
+ *
+ * If the object does not have an owner or permissions, pass -1 for
+ * Anum_owner and acl_kind. In this case the calling user must be superuser.
+ *
+ * Returns the OID of the object's previous namespace.
*/
-void
-AlterObjectNamespace(Relation rel, int cacheId,
- Oid classId, Oid objid, Oid nspOid,
+Oid
+AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId,
+ Oid objid, Oid nspOid,
int Anum_name, int Anum_namespace, int Anum_owner,
- AclObjectKind acl_kind,
- bool superuser_only)
+ AclObjectKind acl_kind)
{
+ Oid classId = RelationGetRelid(rel);
Oid oldNspOid;
Datum name, namespace;
bool isnull;
- HeapTuple tup, newtup = NULL;
+ HeapTuple tup, newtup;
Datum *values;
bool *nulls;
bool *replaces;
- tup = SearchSysCacheCopy1(cacheId, ObjectIdGetDatum(objid));
+ tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid));
if (!HeapTupleIsValid(tup)) /* should not happen */
- elog(ERROR, "cache lookup failed for object %u: %s",
- objid, getObjectDescriptionOids(classId, objid));
+ elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"",
+ objid, RelationGetRelationName(rel));
- name = heap_getattr(tup, Anum_name, rel->rd_att, &isnull);
- namespace = heap_getattr(tup, Anum_namespace, rel->rd_att, &isnull);
+ name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull);
+ Assert(!isnull);
+ namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel), &isnull);
+ Assert(!isnull);
oldNspOid = DatumGetObjectId(namespace);
/* Check basic namespace related issues */
CheckSetNamespace(oldNspOid, nspOid, classId, objid);
- /* check for duplicate name (more friendly than unique-index failure) */
- if (SearchSysCacheExists2(cacheId, name, ObjectIdGetDatum(nspOid)))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("%s already exists in schema \"%s\"",
- getObjectDescriptionOids(classId, objid),
- get_namespace_name(nspOid))));
-
- /* Superusers can always do it */
+ /* Permission checks ... superusers can always do it */
if (!superuser())
{
Datum owner;
Oid ownerId;
AclResult aclresult;
- if (superuser_only)
+ /* Fail if object does not have an explicit owner */
+ if (Anum_owner <= 0)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
(errmsg("must be superuser to SET SCHEMA of %s",
getObjectDescriptionOids(classId, objid)))));
/* Otherwise, must be owner of the existing object */
- owner = heap_getattr(tup, Anum_owner, rel->rd_att, &isnull);
+ owner = heap_getattr(tup, Anum_owner, RelationGetDescr(rel), &isnull);
+ Assert(!isnull);
ownerId = DatumGetObjectId(owner);
if (!has_privs_of_role(GetUserId(), ownerId))
aclcheck_error(ACLCHECK_NOT_OWNER, acl_kind,
NameStr(*(DatumGetName(name))));
- /* owner must have CREATE privilege on namespace */
- aclresult = pg_namespace_aclcheck(oldNspOid, GetUserId(), ACL_CREATE);
+ /* User must have CREATE privilege on new namespace */
+ aclresult = pg_namespace_aclcheck(nspOid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
- get_namespace_name(oldNspOid));
+ get_namespace_name(nspOid));
}
- /* Prepare to update tuple */
- values = palloc0(rel->rd_att->natts * sizeof(Datum));
- nulls = palloc0(rel->rd_att->natts * sizeof(bool));
- replaces = palloc0(rel->rd_att->natts * sizeof(bool));
- values[Anum_namespace - 1] = nspOid;
+ /*
+ * Check for duplicate name (more friendly than unique-index failure).
+ * Since this is just a friendliness check, we can just skip it in cases
+ * where there isn't a suitable syscache available.
+ */
+ if (nameCacheId >= 0 &&
+ SearchSysCacheExists2(nameCacheId, name, ObjectIdGetDatum(nspOid)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("%s already exists in schema \"%s\"",
+ getObjectDescriptionOids(classId, objid),
+ get_namespace_name(nspOid))));
+
+ /* Build modified tuple */
+ values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum));
+ nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
+ replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool));
+ values[Anum_namespace - 1] = ObjectIdGetDatum(nspOid);
replaces[Anum_namespace - 1] = true;
- newtup = heap_modify_tuple(tup, rel->rd_att, values, nulls, replaces);
+ newtup = heap_modify_tuple(tup, RelationGetDescr(rel),
+ values, nulls, replaces);
/* Perform actual update */
simple_heap_update(rel, &tup->t_self, newtup);
@@ -336,6 +458,8 @@ AlterObjectNamespace(Relation rel, int cacheId,
/* update dependencies to point to the new schema */
changeDependencyFor(classId, objid,
NamespaceRelationId, oldNspOid, nspOid);
+
+ return oldNspOid;
}