Peter,
You patch is missing the files src/include/catalog/pg_diralias.h,
> src/include/commands/diralias.h, and src/backend/commands/diralias.c.
>
> (Hint: git add -N)
>
Yikes, sorry about that, not sure how that happened. Attached is an
updated patch.
-Adam
--
Adam Brightwell - [email protected]
Database Engineer - www.crunchydatasolutions.com
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index b257b02..8cdc5cb 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -41,7 +41,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_foreign_table.h pg_rowsecurity.h \
pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h pg_range.h \
- toasting.h indexing.h \
+ pg_diralias.h toasting.h indexing.h \
)
# location of Catalog.pm
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index d30612c..3717bf5 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -30,6 +30,7 @@
#include "catalog/pg_collation.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_diralias.h"
#include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_extension.h"
@@ -48,6 +49,7 @@
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "commands/dbcommands.h"
+#include "commands/diralias.h"
#include "commands/proclang.h"
#include "commands/tablespace.h"
#include "foreign/foreign.h"
@@ -3183,6 +3185,190 @@ ExecGrant_Type(InternalGrant *istmt)
heap_close(relation, RowExclusiveLock);
}
+/*
+ * ExecuteGrantDirAliasStmt
+ * handles the execution of the GRANT/REVOKE ON DIRALIAS command.
+ *
+ * stmt - the GrantDirAliasStmt that describes the directory aliases and
+ * permissions to be granted/revoked.
+ */
+void
+ExecuteGrantDirAliasStmt(GrantDirAliasStmt *stmt)
+{
+ Relation pg_diralias_rel;
+ Oid grantor;
+ List *grantee_ids = NIL;
+ AclMode permissions;
+ ListCell *item;
+
+ /* Must be superuser to grant directory alias permissions */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to grant directory alias permissions")));
+
+ /*
+ * Grantor is optional. If it is not provided then set it to the current
+ * user.
+ */
+ if (stmt->grantor)
+ grantor = get_role_oid(stmt->grantor, false);
+ else
+ grantor = GetUserId();
+
+ /* Convert grantee names to oids */
+ foreach(item, stmt->grantees)
+ {
+ PrivGrantee *grantee = (PrivGrantee *) lfirst(item);
+
+ if (grantee->rolname == NULL)
+ grantee_ids = lappend_oid(grantee_ids, ACL_ID_PUBLIC);
+ else
+ {
+ Oid roleid = get_role_oid(grantee->rolname, false);
+ grantee_ids = lappend_oid(grantee_ids, roleid);
+ }
+ }
+
+ permissions = ACL_NO_RIGHTS;
+
+ /* If ALL was provided then set permissions to ACL_ALL_RIGHTS_DIRALIAS */
+ if (stmt->permissions == NIL)
+ permissions = ACL_ALL_RIGHTS_DIRALIAS;
+ else
+ {
+ /* Condense all permissions */
+ foreach(item, stmt->permissions)
+ {
+ AccessPriv *priv = (AccessPriv *) lfirst(item);
+ permissions |= string_to_privilege(priv->priv_name);
+ }
+ }
+
+
+ /*
+ * Though it shouldn't be possible to provide permissions other than READ
+ * and WRITE, check to make sure no others have been set. If they have,
+ * then warn the user and correct the permissions.
+ */
+ if (permissions & !((AclMode) ACL_ALL_RIGHTS_DIRALIAS))
+ {
+ ereport(WARNING,
+ (errcode(ERRCODE_INVALID_GRANT_OPERATION),
+ errmsg("directory aliases only support READ and WRITE permissions")));
+
+ permissions &= ACL_ALL_RIGHTS_DIRALIAS;
+ }
+
+ pg_diralias_rel = heap_open(DirAliasRelationId, RowExclusiveLock);
+
+ /* Grant/Revoke permissions on directory aliases */
+ foreach(item, stmt->directories)
+ {
+ Datum values[Natts_pg_diralias];
+ bool replaces[Natts_pg_diralias];
+ bool nulls[Natts_pg_diralias];
+ ScanKeyData skey[1];
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ HeapTuple new_tuple;
+ Datum datum;
+ Oid owner_id;
+ Acl *dir_acl;
+ Acl *new_acl;
+ bool is_null;
+ int num_old_members;
+ int num_new_members;
+ Oid *old_members;
+ Oid *new_members;
+ Oid diralias_id;
+ char *name;
+
+ name = strVal(lfirst(item));
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_diralias_dirname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(name));
+
+ scandesc = heap_beginscan_catalog(pg_diralias_rel, 1, skey);
+
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "could not find tuple for directory alias \"%s\"", name);
+
+ /*
+ * Get directory alias owner id. Since all superusers are considered
+ * to be owners of a directory alias, it is safe to assume that the
+ * current user is an owner, given the superuser check above.
+ */
+ owner_id = GetUserId();
+
+ /* Get directory alias ACL */
+ datum = heap_getattr(tuple, Anum_pg_diralias_diracl,
+ RelationGetDescr(pg_diralias_rel), &is_null);
+
+ /* Get the directory alias oid */
+ diralias_id = HeapTupleGetOid(tuple);
+
+ /*
+ * If there are currently no permissions granted on the directory alias,
+ * then add default permissions, which should include the permssions
+ * granted to the owner of the table. Directory aliases are owned by
+ * all superusers.
+ */
+ if (is_null)
+ {
+ dir_acl = acldefault(ACL_OBJECT_DIRALIAS, owner_id);
+ num_old_members = 0;
+ old_members = NULL;
+ }
+ else
+ {
+ dir_acl = DatumGetAclPCopy(datum);
+
+ /* Get the roles in the current ACL */
+ num_old_members = aclmembers(dir_acl, &old_members);
+ }
+
+ /* Merge new ACL with current ACL */
+ new_acl = merge_acl_with_grant(dir_acl, stmt->is_grant, false,
+ DROP_CASCADE, grantee_ids, permissions,
+ grantor, owner_id);
+
+ num_new_members = aclmembers(new_acl, &new_members);
+
+ /* Insert new ACL value */
+ memset(values, 0, sizeof(values));
+ memset(nulls, 0, sizeof(nulls));
+ memset(replaces, 0, sizeof(replaces));
+
+ values[Anum_pg_diralias_diracl - 1] = PointerGetDatum(new_acl);
+ replaces[Anum_pg_diralias_diracl - 1] = true;
+
+ new_tuple = heap_modify_tuple(tuple, RelationGetDescr(pg_diralias_rel),
+ values, nulls, replaces);
+
+ simple_heap_update(pg_diralias_rel, &new_tuple->t_self, new_tuple);
+
+ /* Update Indexes */
+ CatalogUpdateIndexes(pg_diralias_rel, new_tuple);
+
+ /* Update shared dependency ACL information */
+ updateAclDependencies(DirAliasRelationId, diralias_id, 0,
+ owner_id,
+ num_old_members, old_members,
+ num_new_members, new_members);
+
+ /* Clean Up */
+ pfree(new_acl);
+ heap_endscan(scandesc);
+ }
+
+ heap_close(pg_diralias_rel, RowExclusiveLock);
+}
+
static AclMode
string_to_privilege(const char *privname)
@@ -3307,6 +3493,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
gettext_noop("permission denied for event trigger %s"),
/* ACL_KIND_EXTENSION */
gettext_noop("permission denied for extension %s"),
+ /* ACL_KIND_DIRALIAS */
+ gettext_noop("permission denied for directory alias %s"),
};
static const char *const not_owner_msg[MAX_ACL_KIND] =
@@ -3353,6 +3541,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] =
gettext_noop("must be owner of event trigger %s"),
/* ACL_KIND_EXTENSION */
gettext_noop("must be owner of extension %s"),
+ /* ACL_KIND_DIRALIAS */
+ gettext_noop("must be owner of directory alias %s"),
};
@@ -4194,6 +4384,62 @@ pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
}
/*
+ * Exported routine for examining a user's permissions for a directory alias.
+ */
+AclMode
+pg_diralias_aclmask(Oid dir_oid, Oid roleid, AclMode mask, AclMaskHow how)
+{
+ AclMode result;
+ HeapTuple tuple;
+ Datum aclDatum;
+ bool isNull;
+ Acl *acl;
+
+ /* Bypass permission checks for superusers */
+ if (superuser_arg(roleid))
+ return mask;
+
+ /* Must get the directory alias's tuple from pg_diralias */
+ tuple = SearchSysCache1(DIRALIASOID, ObjectIdGetDatum(dir_oid));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("directory alias with OID %u does not exist", dir_oid)));
+
+ aclDatum = SysCacheGetAttr(DIRALIASOID, tuple,
+ Anum_pg_diralias_diracl, &isNull);
+ if (isNull)
+ {
+ /* No ACL, so build default ACL */
+ acl = acldefault(ACL_OBJECT_DIRALIAS, roleid);
+ aclDatum = (Datum) 0;
+ }
+ else
+ {
+ /* detoast rel's ACL if necessary */
+ acl = DatumGetAclP(aclDatum);
+ }
+
+ /*
+ * We use InvalidOid as the ownerid for determining the aclmask. This is
+ * because directory aliases belong to all superusers. aclmask() uses the
+ * ownerid to determine grant options by implying that owners always have
+ * all grant options. If roleid, is not a superuser and therefore an owner
+ * (which it couldn't be at this point), then this check in aclmask() must
+ * be false. Therefore, by using InvalidOid we are guaranteed this behavior.
+ */
+ result = aclmask(acl, roleid, InvalidOid, mask, how);
+
+ /* if we have a detoasted copy, free it */
+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum))
+ pfree(acl);
+
+ ReleaseSysCache(tuple);
+
+ return result;
+}
+
+/*
* Exported routine for examining a user's privileges for a type.
*/
AclMode
@@ -4513,6 +4759,18 @@ pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode)
}
/*
+ * Exported routine for checking a user's access permissions to a directory alias
+ */
+AclResult
+pg_diralias_aclcheck(Oid dir_oid, Oid roleid, AclMode mode)
+{
+ if (pg_diralias_aclmask(dir_oid, roleid, mode, ACLMASK_ANY) != 0)
+ return ACLCHECK_OK;
+ else
+ return ACLCHECK_NO_PRIV;
+}
+
+/*
* Ownership check for a relation (specified by OID).
*/
bool
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 256486c..b056559 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -33,6 +33,7 @@
#include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h"
#include "catalog/pg_depend.h"
+#include "catalog/pg_diralias.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h"
@@ -56,6 +57,7 @@
#include "catalog/pg_user_mapping.h"
#include "commands/comment.h"
#include "commands/defrem.h"
+#include "commands/diralias.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
#include "commands/policy.h"
@@ -1255,6 +1257,10 @@ doDeletion(const ObjectAddress *object, int flags)
RemovePolicyById(object->objectId);
break;
+ case OCLASS_DIRALIAS:
+ RemoveDirAliasById(object->objectId);
+ break;
+
default:
elog(ERROR, "unrecognized object class: %u",
object->classId);
@@ -2325,6 +2331,9 @@ getObjectClass(const ObjectAddress *object)
case RowSecurityRelationId:
return OCLASS_ROWSECURITY;
+
+ case DirAliasRelationId:
+ return OCLASS_DIRALIAS;
}
/* shouldn't get here */
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index b69b75b..872d233 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -26,6 +26,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_default_acl.h"
+#include "catalog/pg_diralias.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
@@ -54,6 +55,7 @@
#include "catalog/pg_user_mapping.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
+#include "commands/diralias.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
#include "commands/policy.h"
@@ -358,6 +360,18 @@ static const ObjectPropertyType ObjectProperty[] =
false
},
{
+ DirAliasRelationId,
+ DirAliasOidIndexId,
+ DIRALIASOID,
+ -1,
+ Anum_pg_diralias_dirname,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ InvalidAttrNumber,
+ -1,
+ false
+ },
+ {
EventTriggerRelationId,
EventTriggerOidIndexId,
EVENTTRIGGEROID,
@@ -536,6 +550,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
&relation, missing_ok);
break;
case OBJECT_DATABASE:
+ case OBJECT_DIRALIAS:
case OBJECT_EXTENSION:
case OBJECT_TABLESPACE:
case OBJECT_ROLE:
@@ -746,6 +761,9 @@ get_object_address_unqualified(ObjectType objtype,
case OBJECT_DATABASE:
msg = gettext_noop("database name cannot be qualified");
break;
+ case OBJECT_DIRALIAS:
+ msg = gettext_noop("directory alias cannot be qualified");
+ break;
case OBJECT_EXTENSION:
msg = gettext_noop("extension name cannot be qualified");
break;
@@ -790,6 +808,11 @@ get_object_address_unqualified(ObjectType objtype,
address.objectId = get_database_oid(name, missing_ok);
address.objectSubId = 0;
break;
+ case OBJECT_DIRALIAS:
+ address.classId = DirAliasRelationId;
+ address.objectId = get_diralias_oid(name, missing_ok);
+ address.objectSubId = 0;
+ break;
case OBJECT_EXTENSION:
address.classId = ExtensionRelationId;
address.objectId = get_extension_oid(name, missing_ok);
@@ -1318,6 +1341,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
break;
case OBJECT_TSPARSER:
case OBJECT_TSTEMPLATE:
+ case OBJECT_DIRALIAS:
/* We treat these object types as being owned by superusers */
if (!superuser_arg(roleid))
ereport(ERROR,
@@ -2224,6 +2248,18 @@ getObjectDescription(const ObjectAddress *object)
break;
}
+ case OCLASS_DIRALIAS:
+ {
+ char *diralias_name;
+
+ diralias_name = get_diralias_name(object->objectId);
+ if (!diralias_name)
+ elog(ERROR, "cache lookup failed for directory alias %u",
+ object->objectId);
+ appendStringInfo(&buffer, _("directory alias %s"), diralias_name);
+ break;
+ }
+
default:
appendStringInfo(&buffer, "unrecognized object %u %u %d",
object->classId,
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index b1ac704..36a897c 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -14,7 +14,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
collationcmds.o constraint.o conversioncmds.o copy.o createas.o \
- dbcommands.o define.o discard.o dropcmds.o \
+ dbcommands.o define.o diralias.o discard.o dropcmds.o \
event_trigger.o explain.o extension.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o matview.o operatorcmds.o opclasscmds.o \
policy.o portalcmds.o prepare.o proclang.o \
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index c9a9baf..47f8d49 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -41,6 +41,7 @@
#include "commands/conversioncmds.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
+#include "commands/diralias.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
#include "commands/policy.h"
@@ -349,6 +350,7 @@ ExecRenameStmt(RenameStmt *stmt)
case OBJECT_AGGREGATE:
case OBJECT_COLLATION:
case OBJECT_CONVERSION:
+ case OBJECT_DIRALIAS:
case OBJECT_EVENT_TRIGGER:
case OBJECT_FDW:
case OBJECT_FOREIGN_SERVER:
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 6b83576..3a9562b 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -28,6 +28,7 @@
#include "catalog/pg_type.h"
#include "commands/copy.h"
#include "commands/defrem.h"
+#include "commands/diralias.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "libpq/libpq.h"
@@ -788,9 +789,13 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
Oid relid;
Node *query = NULL;
- /* Disallow COPY to/from file or program except to superusers. */
if (!pipe && !superuser())
{
+ /*
+ * Disallow COPY to/from program except to superusers. If COPY is to/from
+ * a file then diallow unless the current user is either superuser or has
+ * been granted the appropriate permissions on the target parent directory.
+ */
if (stmt->is_program)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -798,11 +803,43 @@ DoCopy(const CopyStmt *stmt, const char *queryString, uint64 *processed)
errhint("Anyone can COPY to stdout or from stdin. "
"psql's \\copy command also works for anyone.")));
else
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- errmsg("must be superuser to COPY to or from a file"),
- errhint("Anyone can COPY to stdout or from stdin. "
- "psql's \\copy command also works for anyone.")));
+ {
+ char *path;
+ Oid diralias_id;
+ AclResult aclresult;
+
+ /* Get the parent directory */
+ path = pstrdup(stmt->filename);
+ canonicalize_path(path);
+ get_parent_directory(path);
+
+ /* Search for directory in pg_diralias */
+ diralias_id = get_diralias_oid_by_path(path);
+
+ /*
+ * If an entry does not exist for the path in pg_diralias then raise
+ * an error.
+ */
+ if (!OidIsValid(diralias_id))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("a directory alias entry for \"%s\" does not exist.",
+ path)));
+
+ /* Check directory alias entry permissions */
+ if (stmt->is_from)
+ aclresult = pg_diralias_aclcheck(diralias_id, GetUserId(), ACL_SELECT);
+ else
+ aclresult = pg_diralias_aclcheck(diralias_id, GetUserId(), ACL_UPDATE);
+
+ /* If the current user has insufficient privileges then raise an error. */
+ if (aclresult != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must have permissions to COPY to or from \"%s\"", path),
+ errhint("Anyone can COPY to stdout or from stdin. "
+ "psql's \\copy command also works for anyone.")));
+ }
}
if (stmt->relation)
diff --git a/src/backend/commands/diralias.c b/src/backend/commands/diralias.c
new file mode 100644
index 0000000..f269624
--- /dev/null
+++ b/src/backend/commands/diralias.c
@@ -0,0 +1,375 @@
+/*-------------------------------------------------------------------------
+ *
+ * directory.c
+ * Commands for manipulating directories.
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/backend/commands/directory.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/sysattr.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_authid.h"
+#include "catalog/pg_diralias.h"
+#include "commands/diralias.h"
+#include "commands/user.h"
+#include "miscadmin.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/guc.h"
+#include "utils/fmgroids.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+
+/*
+ * RemoveDirAliasById
+ * remove a directory alias by its OID. If a directory does not exist with
+ * the provided oid, then an error is raised.
+ *
+ * diralias_id - the oid of the directory alias.
+ */
+void
+RemoveDirAliasById(Oid diralias_id)
+{
+ Relation pg_diralias_rel;
+ HeapTuple tuple;
+
+ pg_diralias_rel = heap_open(DirAliasRelationId, RowExclusiveLock);
+
+ /*
+ * Find the directory alias to delete.
+ */
+ tuple = SearchSysCache1(DIRALIASOID, ObjectIdGetDatum(diralias_id));
+
+ /* If the directory alias exists, then remove it, otherwise raise an error. */
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "could not find tuple for directory alias %u", diralias_id);
+
+ simple_heap_delete(pg_diralias_rel, &tuple->t_self);
+
+ ReleaseSysCache(tuple);
+ heap_close(pg_diralias_rel, RowExclusiveLock);
+}
+
+/*
+ * CreateDirAlias
+ * handles the execution of the CREATE DIRALIAS command.
+ *
+ * stmt - the CreateDirAliasStmt that describes the directory alias entry to
+ * create.
+ */
+void
+CreateDirAlias(CreateDirAliasStmt *stmt)
+{
+ Relation pg_diralias_rel;
+ Datum values[Natts_pg_diralias];
+ bool nulls[Natts_pg_diralias];
+ ScanKeyData skey[1];
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ Oid diralias_id;
+ char *path;
+
+ /* Must be superuser to create a directory alias entry. */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to create directory alias")));
+
+ /* Unix-ify the path, and strip any trailing slashes */
+ path = pstrdup(stmt->path);
+ canonicalize_path(path);
+
+ /* Disallow quotes */
+ if (strchr(path, '\''))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("directory path cannot contain single quotes")));
+
+ /*
+ * Allowing relative paths seems risky and really a bad idea. Therefore,
+ * if a relative path is provided then an error is raised.
+ *
+ * This also helps us ensure that directory path is not empty or whitespace.
+ */
+ if (!is_absolute_path(path))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("directory path must be an absolute path")));
+
+ /* Open pg_diralias catalog */
+ pg_diralias_rel = heap_open(DirAliasRelationId, RowExclusiveLock);
+
+ /*
+ * Make sure a duplicate does not already exist. Need to check both the name
+ * and the path. If either exists, then raise an error.
+ */
+
+ /* Check alias name does not already exist */
+ ScanKeyInit(&skey[0],
+ Anum_pg_diralias_dirname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(stmt->name));
+
+ /*
+ * We use a heapscan here even though there is an index on alias and path.
+ * We do this on the theory that pg_diralias will usually have a
+ * relatively small number of entries and therefore it is safe to assume
+ * an index scan would be wasted effort.
+ */
+ scandesc = heap_beginscan_catalog(pg_diralias_rel, 1, skey);
+
+ if (HeapTupleIsValid(heap_getnext(scandesc, ForwardScanDirection)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("directory alias \"%s\" already exists", stmt->name)));
+
+ heap_endscan(scandesc);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_diralias_dirpath,
+ BTEqualStrategyNumber, F_TEXTEQ,
+ CStringGetTextDatum(path));
+
+ scandesc = heap_beginscan_catalog(pg_diralias_rel, 1, skey);
+
+ /* Check that path does not already exist. */
+ if (HeapTupleIsValid(heap_getnext(scandesc, ForwardScanDirection)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("directory alias with path \"%s\" already exists", path)));
+
+ heap_endscan(scandesc);
+
+ /*
+ * All is well and safe to insert.
+ */
+
+ /* zero-clear */
+ memset(values, 0, sizeof(values));
+ memset(nulls, 0, sizeof(nulls));
+
+ values[Anum_pg_diralias_dirname - 1] = CStringGetDatum(stmt->name);
+ values[Anum_pg_diralias_dirpath - 1] = CStringGetTextDatum(path);
+
+ /* No ACL items are set on the directory by default */
+ nulls[Anum_pg_diralias_diracl - 1] = true;
+
+ tuple = heap_form_tuple(RelationGetDescr(pg_diralias_rel), values, nulls);
+
+ diralias_id = simple_heap_insert(pg_diralias_rel, tuple);
+
+ /* Update Indexes */
+ CatalogUpdateIndexes(pg_diralias_rel, tuple);
+
+ /* Post creation hook for new directory alias */
+ InvokeObjectPostCreateHook(DirAliasRelationId, diralias_id, 0);
+
+ /* Clean up */
+ heap_close(pg_diralias_rel, RowExclusiveLock);
+}
+
+/*
+ * AlterDirAlias
+ * handles the execution of the ALTER DIRALIAS command.
+ *
+ * stmt - the AlterDirAliasStmt that describes the directory alias entry to alter.
+ */
+void
+AlterDirAlias(AlterDirAliasStmt *stmt)
+{
+ Relation pg_diralias_rel;
+ ScanKeyData skey[1];
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ Datum values[Natts_pg_diralias];
+ bool nulls[Natts_pg_diralias];
+ bool replaces[Natts_pg_diralias];
+ HeapTuple new_tuple;
+ char *path;
+
+ /* Must be superuser to alter directory alias */
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to alter directory alias")));
+
+ /* Unix-ify the new path, and strip any trailing slashes */
+ path = pstrdup(stmt->path);
+ canonicalize_path(path);
+
+ /* Disallow quotes */
+ if (strchr(path, '\''))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("directory path cannot contain single quotes")));
+
+ /* Open pg_diralias catalog */
+ pg_diralias_rel = heap_open(DirAliasRelationId, RowExclusiveLock);
+
+ /* Search for directory alias by name */
+ ScanKeyInit(&skey[0],
+ Anum_pg_diralias_dirname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(stmt->name));
+
+ /*
+ * We use a heapscan here even though there is an index on alias and path.
+ * We do this on the theory that pg_diralias will usually have a
+ * relatively small number of entries and therefore it is safe to assume
+ * an index scan would be wasted effort.
+ */
+ scandesc = heap_beginscan_catalog(pg_diralias_rel, 1, skey);
+
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ /* If directory alias does not exist then raise an error */
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("directory alias \"%s\" does not exist", stmt->name)));
+
+ /* Build new tuple and update pg_diralias */
+ memset(nulls, 0, sizeof(nulls));
+ memset(replaces, 0, sizeof(replaces));
+ memset(values, 0, sizeof(values));
+
+ values[Anum_pg_diralias_dirpath - 1] = CStringGetTextDatum(path);
+ replaces[Anum_pg_diralias_dirpath - 1] = true;
+
+ new_tuple = heap_modify_tuple(tuple, RelationGetDescr(pg_diralias_rel),
+ values, nulls, replaces);
+
+ simple_heap_update(pg_diralias_rel, &new_tuple->t_self, new_tuple);
+
+ /* Update Indexes */
+ CatalogUpdateIndexes(pg_diralias_rel, new_tuple);
+
+ /* Post alter hook for directory alias */
+ InvokeObjectPostAlterHook(DirAliasRelationId, HeapTupleGetOid(tuple), 0);
+
+ /* Clean Up */
+ heap_freetuple(new_tuple);
+ heap_endscan(scandesc);
+ heap_close(pg_diralias_rel, RowExclusiveLock);
+}
+
+/*
+ * get_diralias_name
+ * given a directory alias OID, look up the name. If the directory does not
+ * exist then NULL is returned.
+ *
+ * diralias_id - the OID of the directory alias entry in pg_diralias.
+ */
+char *
+get_diralias_name(Oid diralias_id)
+{
+ char *name = NULL;
+ HeapTuple tuple;
+
+ tuple = SearchSysCache1(DIRALIASOID, ObjectIdGetDatum(diralias_id));
+ if (HeapTupleIsValid(tuple))
+ {
+ name = pstrdup(NameStr(((Form_pg_diralias) GETSTRUCT(tuple))->dirname));
+ ReleaseSysCache(tuple);
+ }
+
+ return name;
+}
+
+/*
+ * get_directory_oid_by_path
+ * given a directory path, look up the OID. If the directory does not exist
+ * this InvalidOid is returned.
+ *
+ * path - the path of the directory
+ */
+Oid
+get_diralias_oid_by_path(const char *path)
+{
+ Oid dir_id = InvalidOid;
+ Relation pg_diralias_rel;
+ HeapScanDesc scandesc;
+ HeapTuple tuple;
+ ScanKeyData skey[1];
+
+ /*
+ * Search pg_diralias. We use a heapscan here even though there is an index
+ * on alias. We do this on the theory that pg_diralias will usually have a
+ * relatively small number of entries and therefore it is safe to assume
+ * an index scan would be wasted effort.
+ */
+ pg_diralias_rel = heap_open(DirAliasRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_diralias_dirpath,
+ BTEqualStrategyNumber, F_TEXTEQ,
+ CStringGetTextDatum(path));
+
+ scandesc = heap_beginscan_catalog(pg_diralias_rel, 1, skey);
+ tuple = heap_getnext(scandesc, ForwardScanDirection);
+
+ if (HeapTupleIsValid(tuple))
+ dir_id = HeapTupleGetOid(tuple);
+
+ heap_endscan(scandesc);
+ heap_close(pg_diralias_rel, AccessShareLock);
+
+ return dir_id;
+}
+
+/*
+ * get_directory_oid
+ * given a directory alias name, look up the OID. If a directory alias does
+ * not exist for the given name then raise an error. However, if missing_ok
+ * is true, then return InvalidOid.
+ *
+ * name - the name of the directory alias
+ * missing_ok - false if an error should be raised if the directory alias does
+ * not exist.
+ */
+Oid
+get_diralias_oid(const char *name, bool missing_ok)
+{
+ Oid dir_id;
+ Relation pg_diralias_rel;
+ ScanKeyData skey[1];
+ SysScanDesc sscan;
+ HeapTuple tuple;
+
+ /* Search pg_diralias for a directory alias entry with provided name */
+ pg_diralias_rel = heap_open(DirAliasRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_diralias_dirname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(name));
+
+ sscan = systable_beginscan(pg_diralias_rel, DirAliasNameIndexId,
+ true, NULL, 1, skey);
+
+ tuple = systable_getnext(sscan);
+
+ if (HeapTupleIsValid(tuple))
+ dir_id = HeapTupleGetOid(tuple);
+ else
+ dir_id = InvalidOid;
+
+ systable_endscan(sscan);
+ heap_close(pg_diralias_rel, AccessShareLock);
+
+ if (!OidIsValid(dir_id) && !missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("directory alias \"%s\" does not exist", name)));
+
+ return dir_id;
+}
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 8583581..8cae12e 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -380,6 +380,10 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs)
list_length(objname) - 1));
}
break;
+ case OBJECT_DIRALIAS:
+ msg = gettext_noop("directory alias \"%s\" does not exist, skipping");
+ name = NameListToString(objname);
+ break;
case OBJECT_EVENT_TRIGGER:
msg = gettext_noop("event trigger \"%s\" does not exist, skipping");
name = NameListToString(objname);
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 1b8c94b..b70c322 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -73,6 +73,7 @@ static event_trigger_support_data event_trigger_support[] = {
{"COLLATION", true},
{"CONVERSION", true},
{"DATABASE", false},
+ {"DIRALIAS", true},
{"DOMAIN", true},
{"EXTENSION", true},
{"EVENT TRIGGER", false},
@@ -924,6 +925,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
case OBJECT_CONSTRAINT:
case OBJECT_COLLATION:
case OBJECT_CONVERSION:
+ case OBJECT_DIRALIAS:
case OBJECT_DOMAIN:
case OBJECT_EXTENSION:
case OBJECT_FDW:
@@ -998,6 +1000,7 @@ EventTriggerSupportsObjectClass(ObjectClass objclass)
case OCLASS_DEFACL:
case OCLASS_EXTENSION:
case OCLASS_ROWSECURITY:
+ case OCLASS_DIRALIAS:
return true;
case MAX_OCLASS:
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 21b070a..8941fa2 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2702,6 +2702,20 @@ _copyGrantRoleStmt(const GrantRoleStmt *from)
return newnode;
}
+static GrantDirAliasStmt *
+_copyGrantDirAliasStmt(const GrantDirAliasStmt *from)
+{
+ GrantDirAliasStmt *newnode = makeNode(GrantDirAliasStmt);
+
+ COPY_NODE_FIELD(directories);
+ COPY_NODE_FIELD(permissions);
+ COPY_NODE_FIELD(grantees);
+ COPY_SCALAR_FIELD(is_grant);
+ COPY_STRING_FIELD(grantor);
+
+ return newnode;
+}
+
static AlterDefaultPrivilegesStmt *
_copyAlterDefaultPrivilegesStmt(const AlterDefaultPrivilegesStmt *from)
{
@@ -3879,6 +3893,28 @@ _copyAlterPolicyStmt(const AlterPolicyStmt *from)
return newnode;
}
+static CreateDirAliasStmt *
+_copyCreateDirAliasStmt(const CreateDirAliasStmt *from)
+{
+ CreateDirAliasStmt *newnode = makeNode(CreateDirAliasStmt);
+
+ COPY_STRING_FIELD(name);
+ COPY_STRING_FIELD(path);
+
+ return newnode;
+}
+
+static AlterDirAliasStmt *
+_copyAlterDirAliasStmt(const AlterDirAliasStmt *from)
+{
+ AlterDirAliasStmt *newnode = makeNode(AlterDirAliasStmt);
+
+ COPY_STRING_FIELD(name);
+ COPY_STRING_FIELD(path);
+
+ return newnode;
+}
+
/* ****************************************************************
* pg_list.h copy functions
* ****************************************************************
@@ -4318,6 +4354,9 @@ copyObject(const void *from)
case T_GrantStmt:
retval = _copyGrantStmt(from);
break;
+ case T_GrantDirAliasStmt:
+ retval = _copyGrantDirAliasStmt(from);
+ break;
case T_GrantRoleStmt:
retval = _copyGrantRoleStmt(from);
break;
@@ -4597,6 +4636,12 @@ copyObject(const void *from)
case T_AlterPolicyStmt:
retval = _copyAlterPolicyStmt(from);
break;
+ case T_CreateDirAliasStmt:
+ retval = _copyCreateDirAliasStmt(from);
+ break;
+ case T_AlterDirAliasStmt:
+ retval = _copyAlterDirAliasStmt(from);
+ break;
case T_A_Expr:
retval = _copyAExpr(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 358395f..e266ad8 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1046,6 +1046,18 @@ _equalGrantRoleStmt(const GrantRoleStmt *a, const GrantRoleStmt *b)
}
static bool
+_equalGrantDirAliasStmt(const GrantDirAliasStmt *a, const GrantDirAliasStmt *b)
+{
+ COMPARE_NODE_FIELD(directories);
+ COMPARE_NODE_FIELD(permissions);
+ COMPARE_NODE_FIELD(grantees);
+ COMPARE_SCALAR_FIELD(is_grant);
+ COMPARE_STRING_FIELD(grantor);
+
+ return true;
+}
+
+static bool
_equalAlterDefaultPrivilegesStmt(const AlterDefaultPrivilegesStmt *a, const AlterDefaultPrivilegesStmt *b)
{
COMPARE_NODE_FIELD(options);
@@ -2034,6 +2046,24 @@ _equalAlterPolicyStmt(const AlterPolicyStmt *a, const AlterPolicyStmt *b)
}
static bool
+_equalCreateDirAliasStmt(const CreateDirAliasStmt *a, const CreateDirAliasStmt *b)
+{
+ COMPARE_STRING_FIELD(name);
+ COMPARE_STRING_FIELD(path);
+
+ return true;
+}
+
+static bool
+_equalAlterDirAliasStmt(const AlterDirAliasStmt *a, const AlterDirAliasStmt *b)
+{
+ COMPARE_STRING_FIELD(name);
+ COMPARE_STRING_FIELD(path);
+
+ return true;
+}
+
+static bool
_equalAExpr(const A_Expr *a, const A_Expr *b)
{
COMPARE_SCALAR_FIELD(kind);
@@ -2778,6 +2808,9 @@ equal(const void *a, const void *b)
case T_GrantStmt:
retval = _equalGrantStmt(a, b);
break;
+ case T_GrantDirAliasStmt:
+ retval = _equalGrantDirAliasStmt(a, b);
+ break;
case T_GrantRoleStmt:
retval = _equalGrantRoleStmt(a, b);
break;
@@ -3057,6 +3090,12 @@ equal(const void *a, const void *b)
case T_AlterPolicyStmt:
retval = _equalAlterPolicyStmt(a, b);
break;
+ case T_CreateDirAliasStmt:
+ retval = _equalCreateDirAliasStmt(a, b);
+ break;
+ case T_AlterDirAliasStmt:
+ retval = _equalAlterDirAliasStmt(a, b);
+ break;
case T_A_Expr:
retval = _equalAExpr(a, b);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 0de9584..24d5eb5 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -258,7 +258,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DeallocateStmt PrepareStmt ExecuteStmt
DropOwnedStmt ReassignOwnedStmt
AlterTSConfigurationStmt AlterTSDictionaryStmt
- CreateMatViewStmt RefreshMatViewStmt
+ CreateMatViewStmt RefreshMatViewStmt GrantDirStmt RevokeDirStmt
+ CreateDirAliasStmt AlterDirAliasStmt
%type <node> select_no_parens select_with_parens select_clause
simple_select values_clause
@@ -324,6 +325,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> RowSecurityOptionalWithCheck RowSecurityOptionalExpr
%type <list> RowSecurityDefaultToRole RowSecurityOptionalToRole
+%type <node> dir_perm_opts
+%type <list> dir_permissions dir_perm_list
+
%type <str> iso_level opt_encoding
%type <node> grantee
%type <list> grantee_list
@@ -559,7 +563,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DESC
- DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
+ DICTIONARY DIRALIAS DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
+ DOUBLE_P DROP
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
@@ -734,6 +739,7 @@ stmt :
| AlterDatabaseStmt
| AlterDatabaseSetStmt
| AlterDefaultPrivilegesStmt
+ | AlterDirAliasStmt
| AlterDomainStmt
| AlterEnumStmt
| AlterExtensionStmt
@@ -769,6 +775,7 @@ stmt :
| CreateAssertStmt
| CreateCastStmt
| CreateConversionStmt
+ | CreateDirAliasStmt
| CreateDomainStmt
| CreateExtensionStmt
| CreateFdwStmt
@@ -820,6 +827,7 @@ stmt :
| ExplainStmt
| FetchStmt
| GrantStmt
+ | GrantDirStmt
| GrantRoleStmt
| ImportForeignSchemaStmt
| IndexStmt
@@ -837,6 +845,7 @@ stmt :
| RemoveOperStmt
| RenameStmt
| RevokeStmt
+ | RevokeDirStmt
| RevokeRoleStmt
| RuleStmt
| SecLabelStmt
@@ -4606,6 +4615,34 @@ row_security_cmd:
/*****************************************************************************
*
+ * QUERIES:
+ * CREATE DIRALIAS <name> AS <path>
+ * ALTER DIRALIAS <name> AS <path>
+ *
+ *****************************************************************************/
+
+CreateDirAliasStmt:
+ CREATE DIRALIAS name AS Sconst
+ {
+ CreateDirAliasStmt *n = makeNode(CreateDirAliasStmt);
+ n->name = $3;
+ n->path = $5;
+ $$ = (Node *) n;
+ }
+ ;
+
+AlterDirAliasStmt:
+ ALTER DIRALIAS name AS Sconst
+ {
+ AlterDirAliasStmt *n = makeNode(AlterDirAliasStmt);
+ n->name = $3;
+ n->path = $5;
+ $$ = (Node *) n;
+ }
+ ;
+
+/*****************************************************************************
+ *
* QUERIES :
* CREATE TRIGGER ...
* DROP TRIGGER ...
@@ -5481,6 +5518,7 @@ drop_type: TABLE { $$ = OBJECT_TABLE; }
| CONVERSION_P { $$ = OBJECT_CONVERSION; }
| SCHEMA { $$ = OBJECT_SCHEMA; }
| EXTENSION { $$ = OBJECT_EXTENSION; }
+ | DIRALIAS { $$ = OBJECT_DIRALIAS; }
| TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; }
| TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; }
| TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; }
@@ -6314,6 +6352,67 @@ opt_granted_by: GRANTED BY RoleId { $$ = $3; }
/*****************************************************************************
*
+ * QUERIES:
+ * GRANT ON DIRALIAS <alias> <permissions> TO <roles>
+ * REVOKE ON DIRALIAS <alias> <permsissions> FROM <roles>
+ *
+ *****************************************************************************/
+GrantDirStmt:
+ GRANT ON DIRALIAS name_list dir_permissions TO grantee_list
+ opt_granted_by
+ {
+ GrantDirAliasStmt *n = makeNode(GrantDirAliasStmt);
+ n->is_grant = true;
+ n->directories = $4;
+ n->permissions = $5;
+ n->grantees = $7;
+ n->grantor = $8;
+ $$ = (Node*)n;
+ }
+ ;
+
+RevokeDirStmt:
+ REVOKE ON DIRALIAS name_list dir_permissions FROM grantee_list
+ {
+ GrantDirAliasStmt *n = makeNode(GrantDirAliasStmt);
+ n->is_grant = false;
+ n->directories = $4;
+ n->permissions = $5;
+ n->grantees = $7;
+ $$ = (Node*)n;
+ }
+ ;
+
+/* either ALL or a list of individual permissions */
+dir_permissions: dir_perm_list
+ { $$ = $1; }
+ | ALL { $$ = NIL; }
+ ;
+
+
+dir_perm_list: dir_perm_opts { $$ = list_make1($1); }
+ | dir_perm_list ',' dir_perm_opts { $$ = lappend($1, $3); }
+ ;
+
+dir_perm_opts:
+ READ
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("select");
+ n->cols = NIL;
+ $$ = (Node*)n;
+ }
+ | WRITE
+ {
+ AccessPriv *n = makeNode(AccessPriv);
+ n->priv_name = pstrdup("update");
+ n->cols = NIL;
+ $$ = (Node*)n;
+ }
+ ;
+
+/*****************************************************************************
+ *
* ALTER DEFAULT PRIVILEGES statement
*
*****************************************************************************/
@@ -7273,6 +7372,15 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
n->missing_ok = false;
$$ = (Node *)n;
}
+ | ALTER DIRALIAS name RENAME TO name
+ {
+ RenameStmt *n = makeNode(RenameStmt);
+ n->renameType = OBJECT_DIRALIAS;
+ n->object = list_make1(makeString($3));
+ n->newname = $6;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
| ALTER DOMAIN_P any_name RENAME TO name
{
RenameStmt *n = makeNode(RenameStmt);
@@ -13051,6 +13159,7 @@ unreserved_keyword:
| DELIMITER
| DELIMITERS
| DICTIONARY
+ | DIRALIAS
| DISABLE_P
| DISCARD
| DOCUMENT_P
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4a2a339..4980016 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -33,6 +33,7 @@
#include "commands/createas.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
+#include "commands/diralias.h"
#include "commands/discard.h"
#include "commands/event_trigger.h"
#include "commands/explain.h"
@@ -185,6 +186,7 @@ check_xact_readonly(Node *parsetree)
case T_DropRoleStmt:
case T_GrantStmt:
case T_GrantRoleStmt:
+ case T_GrantDirAliasStmt:
case T_AlterDefaultPrivilegesStmt:
case T_TruncateStmt:
case T_DropOwnedStmt:
@@ -557,6 +559,10 @@ standard_ProcessUtility(Node *parsetree,
GrantRole((GrantRoleStmt *) parsetree);
break;
+ case T_GrantDirAliasStmt:
+ ExecuteGrantDirAliasStmt((GrantDirAliasStmt *) parsetree);
+ break;
+
case T_CreatedbStmt:
/* no event triggers for global objects */
PreventTransactionChain(isTopLevel, "CREATE DATABASE");
@@ -1329,6 +1335,14 @@ ProcessUtilitySlow(Node *parsetree,
AlterPolicy((AlterPolicyStmt *) parsetree);
break;
+ case T_CreateDirAliasStmt: /* CREATE DIRALIAS */
+ CreateDirAlias((CreateDirAliasStmt *) parsetree);
+ break;
+
+ case T_AlterDirAliasStmt: /* ALTER DIRALIAS */
+ AlterDirAlias((AlterDirAliasStmt *) parsetree);
+ break;
+
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(parsetree));
@@ -1596,6 +1610,9 @@ AlterObjectTypeCommandTag(ObjectType objtype)
case OBJECT_DATABASE:
tag = "ALTER DATABASE";
break;
+ case OBJECT_DIRALIAS:
+ tag = "ALTER DIRALIAS";
+ break;
case OBJECT_DOMAIN:
tag = "ALTER DOMAIN";
break;
@@ -1959,6 +1976,9 @@ CreateCommandTag(Node *parsetree)
case OBJECT_POLICY:
tag = "DROP POLICY";
break;
+ case OBJECT_DIRALIAS:
+ tag = "DROP DIRALIAS";
+ break;
default:
tag = "???";
}
@@ -2016,6 +2036,14 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_GrantDirAliasStmt:
+ {
+ GrantDirAliasStmt *stmt = (GrantDirAliasStmt *) parsetree;
+
+ tag = (stmt->is_grant ? "GRANT ON DIRALIAS" : "REVOKE ON DIRALIAS");
+ }
+ break;
+
case T_GrantRoleStmt:
{
GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree;
@@ -2310,6 +2338,14 @@ CreateCommandTag(Node *parsetree)
tag = "ALTER POLICY";
break;
+ case T_CreateDirAliasStmt:
+ tag = "CREATE DIRALIAS";
+ break;
+
+ case T_AlterDirAliasStmt:
+ tag = "ALTER DIRALIAS";
+ break;
+
case T_PrepareStmt:
tag = "PREPARE";
break;
@@ -2862,6 +2898,14 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
+ case T_CreateDirAliasStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
+ case T_AlterDirAliasStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_AlterTSDictionaryStmt:
lev = LOGSTMT_DDL;
break;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index dc6eb2c..7cc00ef 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -790,6 +790,10 @@ acldefault(GrantObjectType objtype, Oid ownerId)
world_default = ACL_USAGE;
owner_default = ACL_ALL_RIGHTS_TYPE;
break;
+ case ACL_OBJECT_DIRALIAS:
+ world_default = ACL_NO_RIGHTS;
+ owner_default = ACL_ALL_RIGHTS_DIRALIAS;
+ break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index 3a0957f..58dd1dc 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -22,11 +22,13 @@
#include "access/htup_details.h"
#include "catalog/pg_type.h"
+#include "commands/diralias.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "postmaster/syslogger.h"
#include "storage/fd.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
@@ -37,46 +39,44 @@ typedef struct
DIR *dirdesc;
} directory_fctx;
-
/*
- * Convert a "text" filename argument to C string, and check it's allowable.
+ * Check the directory permissions for the provided filename/path.
*
- * Filename may be absolute or relative to the DataDir, but we only allow
- * absolute paths that match DataDir or Log_directory.
+ * The filename must be an absolute path to the file.
*/
-static char *
-convert_and_check_filename(text *arg)
+static void
+check_directory_permissions(char *directory)
{
- char *filename;
+ Oid diralias_id;
+ AclResult aclresult;
- filename = text_to_cstring(arg);
- canonicalize_path(filename); /* filename can change length here */
+ /* Do not allow relative paths */
+ if (!is_absolute_path(directory))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("relative path not allowed")));
- if (is_absolute_path(filename))
- {
- /* Disallow '/a/b/data/..' */
- if (path_contains_parent_reference(filename))
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("reference to parent directory (\"..\") not allowed"))));
-
- /*
- * Allow absolute paths if within DataDir or Log_directory, even
- * though Log_directory might be outside DataDir.
- */
- if (!path_is_prefix_of_path(DataDir, filename) &&
- (!is_absolute_path(Log_directory) ||
- !path_is_prefix_of_path(Log_directory, filename)))
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("absolute path not allowed"))));
- }
- else if (!path_is_relative_and_below_cwd(filename))
+ /* Search for directory in pg_diralias */
+ diralias_id = get_diralias_oid_by_path(directory);
+
+ /*
+ * If an entry does not exist for the path in pg_diralias then raise
+ * an error.
+ */
+ if (!OidIsValid(diralias_id))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("path must be in or below the current directory"))));
+ errmsg("directory alias entry for \"%s\" does not exist",
+ directory)));
+
+ /* Check directory alias entry permissions */
+ aclresult = pg_diralias_aclcheck(diralias_id, GetUserId(), ACL_SELECT);
- return filename;
+ /* If the current user has insufficient privileges then raise an error */
+ if (aclresult != ACLCHECK_OK)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must have read permissions on directory")));
}
@@ -173,13 +173,19 @@ pg_read_file(PG_FUNCTION_ARGS)
int64 seek_offset = PG_GETARG_INT64(1);
int64 bytes_to_read = PG_GETARG_INT64(2);
char *filename;
+ char *directory;
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to read files"))));
+ /* Convert and cleanup the filename */
+ filename = text_to_cstring(filename_t);
+ canonicalize_path(filename);
- filename = convert_and_check_filename(filename_t);
+ /* Superuser is always allowed to bypass directory permissions */
+ if (!superuser())
+ {
+ directory = pstrdup(filename);
+ get_parent_directory(directory);
+ check_directory_permissions(directory);
+ }
if (bytes_to_read < 0)
ereport(ERROR,
@@ -197,13 +203,19 @@ pg_read_file_all(PG_FUNCTION_ARGS)
{
text *filename_t = PG_GETARG_TEXT_P(0);
char *filename;
+ char *directory;
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to read files"))));
+ /* Convert and cleanup the filename */
+ filename = text_to_cstring(filename_t);
+ canonicalize_path(filename);
- filename = convert_and_check_filename(filename_t);
+ /* Superuser is always allowed to bypass directory permissions */
+ if (!superuser())
+ {
+ directory = pstrdup(filename);
+ get_parent_directory(directory);
+ check_directory_permissions(directory);
+ }
PG_RETURN_TEXT_P(read_text_file(filename, 0, -1));
}
@@ -218,13 +230,19 @@ pg_read_binary_file(PG_FUNCTION_ARGS)
int64 seek_offset = PG_GETARG_INT64(1);
int64 bytes_to_read = PG_GETARG_INT64(2);
char *filename;
+ char *directory;
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to read files"))));
+ /* Convert and cleanup the filename */
+ filename = text_to_cstring(filename_t);
+ canonicalize_path(filename);
- filename = convert_and_check_filename(filename_t);
+ /* Superuser is always allowed to bypass directory permissions */
+ if (!superuser())
+ {
+ directory = pstrdup(filename);
+ get_parent_directory(directory);
+ check_directory_permissions(directory);
+ }
if (bytes_to_read < 0)
ereport(ERROR,
@@ -242,13 +260,19 @@ pg_read_binary_file_all(PG_FUNCTION_ARGS)
{
text *filename_t = PG_GETARG_TEXT_P(0);
char *filename;
+ char *directory;
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to read files"))));
+ /* Convert and cleanup the filename */
+ filename = text_to_cstring(filename_t);
+ canonicalize_path(filename);
- filename = convert_and_check_filename(filename_t);
+ /* Superuser is always allowed to bypass directory permissions */
+ if (!superuser())
+ {
+ directory = pstrdup(filename);
+ get_parent_directory(directory);
+ check_directory_permissions(directory);
+ }
PG_RETURN_BYTEA_P(read_binary_file(filename, 0, -1));
}
@@ -261,18 +285,23 @@ pg_stat_file(PG_FUNCTION_ARGS)
{
text *filename_t = PG_GETARG_TEXT_P(0);
char *filename;
+ char *directory;
struct stat fst;
Datum values[6];
bool isnull[6];
HeapTuple tuple;
TupleDesc tupdesc;
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to get file information"))));
+ filename = text_to_cstring(filename_t);
+ canonicalize_path(filename);
- filename = convert_and_check_filename(filename_t);
+ /* Superuser is always allowed to bypass directory permissions */
+ if (!superuser())
+ {
+ directory = pstrdup(filename);
+ get_parent_directory(directory);
+ check_directory_permissions(directory);
+ }
if (stat(filename, &fst) < 0)
ereport(ERROR,
@@ -331,11 +360,6 @@ pg_ls_dir(PG_FUNCTION_ARGS)
struct dirent *de;
directory_fctx *fctx;
- if (!superuser())
- ereport(ERROR,
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
- (errmsg("must be superuser to get directory listings"))));
-
if (SRF_IS_FIRSTCALL())
{
MemoryContext oldcontext;
@@ -344,7 +368,12 @@ pg_ls_dir(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
fctx = palloc(sizeof(directory_fctx));
- fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0));
+ fctx->location = text_to_cstring(PG_GETARG_TEXT_P(0));
+ canonicalize_path(fctx->location);
+
+ /* Superuser is always allowed to bypass directory permissions */
+ if (!superuser())
+ check_directory_permissions(fctx->location);
fctx->dirdesc = AllocateDir(fctx->location);
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 94d951c..383e1c9 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -37,6 +37,7 @@
#include "catalog/pg_default_acl.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_description.h"
+#include "catalog/pg_diralias.h"
#include "catalog/pg_enum.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_foreign_data_wrapper.h"
@@ -367,6 +368,17 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {DirAliasRelationId, /* DIRALIASOID */
+ DirAliasOidIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 8
+ },
{EnumRelationId, /* ENUMOID */
EnumOidIndexId,
1,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 8bfc604..da46c9f 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -103,6 +103,7 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
int numForeignServers;
int numDefaultACLs;
int numEventTriggers;
+ int numDirectoryAliases;
if (g_verbose)
write_msg(NULL, "reading schemas\n");
@@ -251,6 +252,10 @@ getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
write_msg(NULL, "reading row-security policies\n");
getRowSecurity(fout, tblinfo, numTables);
+ if (g_verbose)
+ write_msg(NULL, "reading directory aliases\n");
+ getDirectoryAliases(fout, &numDirectoryAliases);
+
*numTablesPtr = numTables;
return tblinfo;
}
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 259c472..08761e3 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -19,6 +19,7 @@
#include "dumputils.h"
#include "parser/keywords.h"
+#include "pg_backup_utils.h"
/* Globals from keywords.c */
@@ -545,10 +546,16 @@ buildACLCommands(const char *name, const char *subname,
* wire-in knowledge about the default public privileges for different
* kinds of objects.
*/
- appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
- if (subname)
- appendPQExpBuffer(firstsql, "(%s)", subname);
- appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name);
+ if (strcmp(type, "DIRALIAS") == 0)
+ appendPQExpBuffer(firstsql, "REVOKE ON DIRALIAS %s ALL FROM PUBLIC;\n",
+ name);
+ else
+ {
+ appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
+ if (subname)
+ appendPQExpBuffer(firstsql, "(%s)", subname);
+ appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name);
+ }
/*
* We still need some hacking though to cover the case where new default
@@ -593,16 +600,34 @@ buildACLCommands(const char *name, const char *subname,
? strcmp(privswgo->data, "ALL") != 0
: strcmp(privs->data, "ALL") != 0)
{
- appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
- if (subname)
- appendPQExpBuffer(firstsql, "(%s)", subname);
- appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
- type, name, fmtId(grantee->data));
+ /* Handle special GRANT syntax for DIRALIAS */
+ if (strcmp(type, "DIRALIAS") == 0)
+ appendPQExpBuffer(firstsql, "REVOKE ON DIRALIAS %s ALL FROM %s;\n",
+ name, fmtId(grantee->data));
+ else
+ {
+ appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
+ if (subname)
+ appendPQExpBuffer(firstsql, "(%s)", subname);
+ appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
+ type, name, fmtId(grantee->data));
+ }
+
if (privs->len > 0)
- appendPQExpBuffer(firstsql,
- "%sGRANT %s ON %s %s TO %s;\n",
- prefix, privs->data, type, name,
- fmtId(grantee->data));
+ {
+ /* Handle special GRANT syntax for DIRALIAS */
+ if (strcmp(type, "DIRALIAS") == 0)
+ appendPQExpBuffer(firstsql,
+ "%sGRANT ON DIRALIAS %s %s TO %s;\n",
+ prefix, name, privs->data,
+ fmtId(grantee->data));
+ else
+ appendPQExpBuffer(firstsql,
+ "%sGRANT %s ON %s %s TO %s;\n",
+ prefix, privs->data, type, name,
+ fmtId(grantee->data));
+ }
+
if (privswgo->len > 0)
appendPQExpBuffer(firstsql,
"%sGRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
@@ -622,8 +647,14 @@ buildACLCommands(const char *name, const char *subname,
if (privs->len > 0)
{
- appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
- prefix, privs->data, type, name);
+ /* Handle special GRANT syntax for DIRALIAS */
+ if (strcmp(type, "DIRALIAS") == 0)
+ appendPQExpBuffer(secondsql, "%sGRANT ON DIRALIAS %s %s TO ",
+ prefix, name, privs->data);
+ else
+ appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
+ prefix, privs->data, type, name);
+
if (grantee->len == 0)
appendPQExpBufferStr(secondsql, "PUBLIC;\n");
else if (strncmp(grantee->data, "group ",
@@ -660,11 +691,18 @@ buildACLCommands(const char *name, const char *subname,
*/
if (!found_owner_privs && owner)
{
- appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
- if (subname)
- appendPQExpBuffer(firstsql, "(%s)", subname);
- appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
- type, name, fmtId(owner));
+ /* Handle special GRANT syntax for DIRALIAS */
+ if (strcmp(type, "DIRALIAS") == 0)
+ appendPQExpBuffer(firstsql, "REVOKE ON DIRALIAS %s ALL FROM %s",
+ name, fmtId(owner));
+ else
+ {
+ appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
+ if (subname)
+ appendPQExpBuffer(firstsql, "(%s)", subname);
+ appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
+ type, name, fmtId(owner));
+ }
}
destroyPQExpBuffer(grantee);
@@ -873,6 +911,11 @@ do { \
CONVERT_PRIV('r', "SELECT");
CONVERT_PRIV('w', "UPDATE");
}
+ else if (strcmp(type, "DIRALIAS") == 0)
+ {
+ CONVERT_PRIV('r', "READ");
+ CONVERT_PRIV('w', "WRITE");
+ }
else
abort();
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index ed28d36..0449b87 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -3106,7 +3106,8 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
strcmp(type, "SCHEMA") == 0 ||
strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
strcmp(type, "SERVER") == 0 ||
- strcmp(type, "USER MAPPING") == 0)
+ strcmp(type, "USER MAPPING") == 0 ||
+ strcmp(type, "DIRALIAS") == 0)
{
/* We already know that search_path was set properly */
appendPQExpBuffer(buf, "%s %s", type, fmtId(te->tag));
@@ -3307,7 +3308,8 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
strcmp(te->desc, "TEXT SEARCH DICTIONARY") == 0 ||
strcmp(te->desc, "TEXT SEARCH CONFIGURATION") == 0 ||
strcmp(te->desc, "FOREIGN DATA WRAPPER") == 0 ||
- strcmp(te->desc, "SERVER") == 0)
+ strcmp(te->desc, "SERVER") == 0 ||
+ strcmp(te->desc, "DIRALIAS") == 0)
{
PQExpBuffer temp = createPQExpBuffer();
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 1e8f089..c1c8c01 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -191,6 +191,7 @@ static void dumpUserMappings(Archive *fout,
const char *servername, const char *namespace,
const char *owner, CatalogId catalogId, DumpId dumpId);
static void dumpDefaultACL(Archive *fout, DumpOptions *dopt, DefaultACLInfo *daclinfo);
+static void dumpDirectoryAlias(Archive *fout, DumpOptions *dopt, DirectoryAliasInfo *dirinfo);
static void dumpACL(Archive *fout, DumpOptions *dopt, CatalogId objCatId, DumpId objDumpId,
const char *type, const char *name, const char *subname,
@@ -2987,6 +2988,99 @@ dumpRowSecurity(Archive *fout, DumpOptions *dopt, RowSecurityInfo *rsinfo)
destroyPQExpBuffer(delqry);
}
+void
+getDirectoryAliases(Archive *fout, int *numDirectoryAliases)
+{
+ PQExpBuffer query;
+ PGresult *res;
+ DirectoryAliasInfo *dirinfo;
+ int i_tableoid;
+ int i_oid;
+ int i_diralias;
+ int i_dirpath;
+ int i_rolname;
+ int i_diracl;
+ int i, ntups;
+
+ if (fout->remoteVersion < 90500)
+ return;
+
+ query = createPQExpBuffer();
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ appendPQExpBuffer(query, "SELECT tableoid, oid, diralias, dirpath, "
+ "(%s dirowner) AS rolname, diracl "
+ "FROM pg_catalog.pg_directory",
+ username_subquery);
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ dirinfo = (DirectoryAliasInfo *) pg_malloc(ntups * sizeof(DirectoryAliasInfo));
+
+ i_tableoid = PQfnumber(res, "tableoid");
+ i_oid = PQfnumber(res, "oid");
+ i_diralias = PQfnumber(res, "diralias");
+ i_dirpath = PQfnumber(res, "dirpath");
+ i_rolname = PQfnumber(res, "rolname");
+ i_diracl = PQfnumber(res, "diracl");
+
+ for (i = 0; i < ntups; i++)
+ {
+ dirinfo[i].dobj.objType = DO_DIRALIAS;
+ dirinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ dirinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&dirinfo[i].dobj);
+ dirinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_diralias));
+ dirinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
+ dirinfo[i].dirpath = pg_strdup(PQgetvalue(res, i, i_dirpath));
+ dirinfo[i].diracl = pg_strdup(PQgetvalue(res, i, i_diracl));
+ }
+}
+
+static void
+dumpDirectoryAlias(Archive *fout, DumpOptions *dopt, DirectoryAliasInfo *dirinfo)
+{
+ PQExpBuffer create_query;
+ PQExpBuffer delete_query;
+ char *diralias;
+
+ if (!dirinfo->dobj.dump || dopt->dataOnly)
+ return;
+
+ create_query = createPQExpBuffer();
+ delete_query = createPQExpBuffer();
+
+ diralias = pg_strdup(fmtId(dirinfo->dobj.name));
+
+ appendPQExpBuffer(delete_query, "DROP DIRALIAS %s;\n", diralias);
+
+ appendPQExpBuffer(create_query, "CREATE DIRALIAS %s AS \'%s\';\n",
+ diralias, dirinfo->dirpath);
+
+ ArchiveEntry(fout, dirinfo->dobj.catId, dirinfo->dobj.dumpId,
+ dirinfo->dobj.name,
+ NULL, NULL,
+ dirinfo->rolname, false,
+ "DIRALIAS", SECTION_POST_DATA,
+ create_query->data, delete_query->data, NULL,
+ NULL, 0,
+ NULL, NULL);
+
+ /* Dump ACL - because of the special GRANT syntax, we cannot use dumpACL */
+ if (dirinfo->diracl)
+ dumpACL(fout, dopt, dirinfo->dobj.catId, dirinfo->dobj.dumpId,
+ "DIRALIAS", diralias, NULL, dirinfo->dobj.name,
+ NULL, dirinfo->rolname,
+ dirinfo->diracl);
+
+ destroyPQExpBuffer(create_query);
+ destroyPQExpBuffer(delete_query);
+}
+
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
@@ -8218,6 +8312,9 @@ dumpDumpableObject(Archive *fout, DumpOptions *dopt, DumpableObject *dobj)
case DO_ROW_SECURITY:
dumpRowSecurity(fout, dopt, (RowSecurityInfo *) dobj);
break;
+ case DO_DIRALIAS:
+ dumpDirectoryAlias(fout, dopt, (DirectoryAliasInfo *) dobj);
+ break;
case DO_PRE_DATA_BOUNDARY:
case DO_POST_DATA_BOUNDARY:
/* never dumped, nothing to do */
@@ -15615,6 +15712,7 @@ addBoundaryDependencies(DumpableObject **dobjs, int numObjs,
case DO_EVENT_TRIGGER:
case DO_DEFAULT_ACL:
case DO_ROW_SECURITY:
+ case DO_DIRALIAS:
/* Post-data objects: must come after the post-data boundary */
addObjectDependency(dobj, postDataBound->dumpId);
break;
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index a7eb2fd..14a712c 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -76,7 +76,8 @@ typedef enum
DO_POST_DATA_BOUNDARY,
DO_EVENT_TRIGGER,
DO_REFRESH_MATVIEW,
- DO_ROW_SECURITY
+ DO_ROW_SECURITY,
+ DO_DIRALIAS
} DumpableObjectType;
typedef struct _dumpableObject
@@ -469,6 +470,15 @@ typedef struct _rowSecurityInfo
char *rsecwithcheck;
} RowSecurityInfo;
+typedef struct _directoryAliasInfo
+{
+ DumpableObject dobj;
+ char *diralias;
+ char *diracl;
+ char *dirpath;
+ char *rolname; /* name of owner */
+} DirectoryAliasInfo;
+
/* global decls */
extern bool force_quotes; /* double-quotes for identifiers flag */
extern bool g_verbose; /* verbose flag */
@@ -550,5 +560,6 @@ extern void getExtensionMembership(Archive *fout, DumpOptions *dopt, ExtensionIn
int numExtensions);
extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
extern void getRowSecurity(Archive *fout, TableInfo tblinfo[], int numTables);
+extern void getDirectoryAliases(Archive *fout, int *numDirectoryAliases);
#endif /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 030bccc..5cece21 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -73,7 +73,8 @@ static const int oldObjectTypePriority[] =
13, /* DO_POST_DATA_BOUNDARY */
20, /* DO_EVENT_TRIGGER */
15, /* DO_REFRESH_MATVIEW */
- 21 /* DO_ROW_SECURITY */
+ 21, /* DO_ROW_SECURITY */
+ 22, /* DO_DIRALIAS */
};
/*
@@ -122,7 +123,8 @@ static const int newObjectTypePriority[] =
25, /* DO_POST_DATA_BOUNDARY */
32, /* DO_EVENT_TRIGGER */
33, /* DO_REFRESH_MATVIEW */
- 34 /* DO_ROW_SECURITY */
+ 34, /* DO_ROW_SECURITY */
+ 35, /* DO_DIRALIAS */
};
static DumpId preDataBoundId;
@@ -1443,6 +1445,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
"ROW-SECURITY POLICY (ID %d OID %u)",
obj->dumpId, obj->catId.oid);
return;
+ case DO_DIRALIAS:
+ snprintf(buf, bufsize,
+ "DIRECTORY ALIAS (ID %d OID %u)",
+ obj->dumpId, obj->catId.oid);
+ return;
case DO_PRE_DATA_BOUNDARY:
snprintf(buf, bufsize,
"PRE-DATA BOUNDARY (ID %d)",
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 6a4913a..1779bc0 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -148,6 +148,7 @@ typedef enum ObjectClass
OCLASS_EXTENSION, /* pg_extension */
OCLASS_EVENT_TRIGGER, /* pg_event_trigger */
OCLASS_ROWSECURITY, /* pg_rowsecurity */
+ OCLASS_DIRALIAS, /* pg_diralias */
MAX_OCLASS /* MUST BE LAST */
} ObjectClass;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 870692c..e8a195d 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -299,6 +299,12 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
+DECLARE_UNIQUE_INDEX(pg_directory_oid_index, 6101, on pg_diralias using btree(oid oid_ops));
+#define DirAliasOidIndexId 6101
+
+DECLARE_UNIQUE_INDEX(pg_directory_name_index, 6102, on pg_diralias using btree(dirname name_ops));
+#define DirAliasNameIndexId 6102
+
DECLARE_UNIQUE_INDEX(pg_rowsecurity_oid_index, 3257, on pg_rowsecurity using btree(oid oid_ops));
#define RowSecurityOidIndexId 3257
diff --git a/src/include/catalog/pg_diralias.h b/src/include/catalog/pg_diralias.h
new file mode 100644
index 0000000..e58f047
--- /dev/null
+++ b/src/include/catalog/pg_diralias.h
@@ -0,0 +1,46 @@
+/*
+ * pg_diralias.h
+ * definition of the system catalog for directory permissions (pg_diralias)
+ *
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ */
+#ifndef PG_DIRALIAS_H
+#define PG_DIRALIAS_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_diralias definition. cpp turns this into
+ * typedef struct FormData_pg_diralias
+ * ----------------
+ */
+#define DirAliasRelationId 6100
+
+CATALOG(pg_diralias,6100)
+{
+ NameData dirname; /* directory alias name */
+ text dirpath; /* directory path */
+#ifdef CATALOG_VARLEN
+ aclitem diracl[1]; /* directory permissions */
+#endif
+} FormData_pg_diralias;
+
+/* ----------------
+ * Form_pg_diralias corresponds to a pointer to a row with
+ * the format of pg_diralias relation.
+ * ----------------
+ */
+typedef FormData_pg_diralias *Form_pg_diralias;
+
+/* ----------------
+ * compiler constants for pg_diralias
+ * ----------------
+ */
+#define Natts_pg_diralias 3
+#define Anum_pg_diralias_dirname 1
+#define Anum_pg_diralias_dirpath 2
+#define Anum_pg_diralias_diracl 3
+
+#endif /* PG_DIRALIAS_H */
\ No newline at end of file
diff --git a/src/include/commands/diralias.h b/src/include/commands/diralias.h
new file mode 100644
index 0000000..851d321
--- /dev/null
+++ b/src/include/commands/diralias.h
@@ -0,0 +1,29 @@
+/*-------------------------------------------------------------------------
+ *
+ * directory.h
+ * prototypes for directory.c.
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/commands/directory.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef DIRECTORY_H
+#define DIRECTORY_H
+
+#include "nodes/parsenodes.h"
+
+extern void RemoveDirAliasById(Oid dir_id);
+extern void CreateDirAlias(CreateDirAliasStmt *stmt);
+extern void AlterDirAlias(AlterDirAliasStmt *stmt);
+
+extern char *get_diralias_name(Oid dir_id);
+extern Oid get_diralias_oid(const char *name, bool missing_ok);
+extern Oid get_diralias_owner(Oid dir_id);
+extern Oid get_diralias_oid_by_path(const char *path);
+
+#endif /* DIRECTORY_H */
\ No newline at end of file
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 154d943..6b293aa 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -278,6 +278,7 @@ typedef enum NodeTag
T_AlterDomainStmt,
T_SetOperationStmt,
T_GrantStmt,
+ T_GrantDirAliasStmt,
T_GrantRoleStmt,
T_AlterDefaultPrivilegesStmt,
T_ClosePortalStmt,
@@ -368,6 +369,8 @@ typedef enum NodeTag
T_AlterSystemStmt,
T_CreatePolicyStmt,
T_AlterPolicyStmt,
+ T_CreateDirAliasStmt,
+ T_AlterDirAliasStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index cef9544..4b7197c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1212,6 +1212,7 @@ typedef enum ObjectType
OBJECT_COLLATION,
OBJECT_CONVERSION,
OBJECT_DATABASE,
+ OBJECT_DIRALIAS,
OBJECT_DOMAIN,
OBJECT_EVENT_TRIGGER,
OBJECT_EXTENSION,
@@ -1412,6 +1413,7 @@ typedef enum GrantObjectType
ACL_OBJECT_LARGEOBJECT, /* largeobject */
ACL_OBJECT_NAMESPACE, /* namespace */
ACL_OBJECT_TABLESPACE, /* tablespace */
+ ACL_OBJECT_DIRALIAS, /* directory alias */
ACL_OBJECT_TYPE /* type */
} GrantObjectType;
@@ -1483,6 +1485,20 @@ typedef struct GrantRoleStmt
} GrantRoleStmt;
/* ----------------------
+ * Grant/Revoke Directory Permission Statement
+ * ----------------------
+ */
+typedef struct GrantDirAliasStmt
+{
+ NodeTag type;
+ List *directories; /* the directory alias/name */
+ List *permissions; /* list of permission to be granted/revoked */
+ List *grantees; /* list of roles to be granted/revoked permission */
+ bool is_grant; /* true = GRANT, false = REVOKE */
+ char *grantor; /* set grantor to other than current role */
+} GrantDirAliasStmt;
+
+/* ----------------------
* Alter Default Privileges Statement
* ----------------------
*/
@@ -1890,6 +1906,28 @@ typedef struct AlterPolicyStmt
} AlterPolicyStmt;
/* ----------------------
+ * Create DIRALIAS Statement
+ * ----------------------
+ */
+typedef struct CreateDirAliasStmt
+{
+ NodeTag type;
+ char *name;
+ char *path;
+} CreateDirAliasStmt;
+
+/* ----------------------
+ * Alter DIRALIAS Statement
+ * ----------------------
+ */
+typedef struct AlterDirAliasStmt
+{
+ NodeTag type;
+ char *name;
+ char *path;
+} AlterDirAliasStmt;
+
+/* ----------------------
* Create TRIGGER Statement
* ----------------------
*/
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index e14dc9a..eb31cf7 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -125,6 +125,7 @@ PG_KEYWORD("delimiter", DELIMITER, UNRESERVED_KEYWORD)
PG_KEYWORD("delimiters", DELIMITERS, UNRESERVED_KEYWORD)
PG_KEYWORD("desc", DESC, RESERVED_KEYWORD)
PG_KEYWORD("dictionary", DICTIONARY, UNRESERVED_KEYWORD)
+PG_KEYWORD("diralias", DIRALIAS, UNRESERVED_KEYWORD)
PG_KEYWORD("disable", DISABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("discard", DISCARD, UNRESERVED_KEYWORD)
PG_KEYWORD("distinct", DISTINCT, RESERVED_KEYWORD)
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index a8e3164..cb75b1a 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -156,6 +156,7 @@ typedef ArrayType Acl;
#define ACL_ALL_RIGHTS_NAMESPACE (ACL_USAGE|ACL_CREATE)
#define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE)
#define ACL_ALL_RIGHTS_TYPE (ACL_USAGE)
+#define ACL_ALL_RIGHTS_DIRALIAS (ACL_SELECT|ACL_UPDATE)
/* operation codes for pg_*_aclmask */
typedef enum
@@ -197,6 +198,7 @@ typedef enum AclObjectKind
ACL_KIND_FOREIGN_SERVER, /* pg_foreign_server */
ACL_KIND_EVENT_TRIGGER, /* pg_event_trigger */
ACL_KIND_EXTENSION, /* pg_extension */
+ ACL_KIND_DIRALIAS, /* pg_diralias */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
@@ -255,6 +257,7 @@ extern Datum aclexplode(PG_FUNCTION_ARGS);
*/
extern void ExecuteGrantStmt(GrantStmt *stmt);
extern void ExecAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *stmt);
+extern void ExecuteGrantDirAliasStmt(GrantDirAliasStmt *stmt);
extern void RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid);
extern void RemoveDefaultACLById(Oid defaclOid);
@@ -281,6 +284,8 @@ extern AclMode pg_foreign_server_aclmask(Oid srv_oid, Oid roleid,
AclMode mask, AclMaskHow how);
extern AclMode pg_type_aclmask(Oid type_oid, Oid roleid,
AclMode mask, AclMaskHow how);
+extern AclMode pg_diralias_aclmask(Oid dir_oid, Oid roleid, AclMode mask,
+ AclMaskHow how);
extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,
Oid roleid, AclMode mode);
@@ -297,6 +302,7 @@ extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode);
extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode);
extern AclResult pg_type_aclcheck(Oid type_oid, Oid roleid, AclMode mode);
+extern AclResult pg_diralias_aclcheck(Oid diroid, Oid roleid, AclMode mode);
extern void aclcheck_error(AclResult aclerr, AclObjectKind objectkind,
const char *objectname);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f97229f..c4b822e 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -54,6 +54,7 @@ enum SysCacheIdentifier
CONVOID,
DATABASEOID,
DEFACLROLENSPOBJ,
+ DIRALIASOID,
ENUMOID,
ENUMTYPOIDNAME,
EVENTTRIGGERNAME,
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 2c8ec11..334f648 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -102,6 +102,7 @@ pg_db_role_setting|t
pg_default_acl|t
pg_depend|t
pg_description|t
+pg_diralias|t
pg_enum|t
pg_event_trigger|t
pg_extension|t
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers