All,

Attached is a patch with minor updates/corrections.

-Adam

-- 
Adam Brightwell - adam.brightw...@crunchydatasolutions.com
Database Engineer - www.crunchydatasolutions.com
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
new file mode 100644
index b257b02..8cdc5cb
*** a/src/backend/catalog/Makefile
--- b/src/backend/catalog/Makefile
*************** POSTGRES_BKI_SRCS = $(addprefix $(top_sr
*** 41,47 ****
  	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 \
      )
  
  # location of Catalog.pm
--- 41,47 ----
  	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 \
! 	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
new file mode 100644
index d30612c..3717bf5
*** a/src/backend/catalog/aclchk.c
--- b/src/backend/catalog/aclchk.c
***************
*** 30,35 ****
--- 30,36 ----
  #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,53 ****
--- 49,55 ----
  #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"
*************** ExecGrant_Type(InternalGrant *istmt)
*** 3183,3188 ****
--- 3185,3374 ----
  	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)
*************** static const char *const no_priv_msg[MAX
*** 3307,3312 ****
--- 3493,3500 ----
  	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] =
*************** static const char *const not_owner_msg[M
*** 3353,3358 ****
--- 3541,3548 ----
  	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"),
  };
  
  
*************** pg_foreign_server_aclmask(Oid srv_oid, O
*** 4194,4199 ****
--- 4384,4445 ----
  }
  
  /*
+  * 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
*************** pg_type_aclcheck(Oid type_oid, Oid rolei
*** 4510,4515 ****
--- 4756,4773 ----
  		return ACLCHECK_OK;
  	else
  		return ACLCHECK_NO_PRIV;
+ }
+ 
+ /*
+  * 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;
  }
  
  /*
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
new file mode 100644
index 256486c..b056559
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 33,38 ****
--- 33,39 ----
  #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,61 ****
--- 57,63 ----
  #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"
*************** doDeletion(const ObjectAddress *object,
*** 1255,1260 ****
--- 1257,1266 ----
  			RemovePolicyById(object->objectId);
  			break;
  
+ 		case OCLASS_DIRALIAS:
+ 			RemoveDirAliasById(object->objectId);
+ 			break;
+ 
  		default:
  			elog(ERROR, "unrecognized object class: %u",
  				 object->classId);
*************** getObjectClass(const ObjectAddress *obje
*** 2325,2330 ****
--- 2331,2339 ----
  
  		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
new file mode 100644
index b69b75b..872d233
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
***************
*** 26,31 ****
--- 26,32 ----
  #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,59 ****
--- 55,61 ----
  #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"
*************** static const ObjectPropertyType ObjectPr
*** 358,363 ****
--- 360,377 ----
  		false
  	},
  	{
+ 		DirAliasRelationId,
+ 		DirAliasOidIndexId,
+ 		DIRALIASOID,
+ 		-1,
+ 		Anum_pg_diralias_dirname,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		InvalidAttrNumber,
+ 		-1,
+ 		false
+ 	},
+ 	{
  		EventTriggerRelationId,
  		EventTriggerOidIndexId,
  		EVENTTRIGGEROID,
*************** get_object_address(ObjectType objtype, L
*** 536,541 ****
--- 550,556 ----
  													   &relation, missing_ok);
  				break;
  			case OBJECT_DATABASE:
+ 			case OBJECT_DIRALIAS:
  			case OBJECT_EXTENSION:
  			case OBJECT_TABLESPACE:
  			case OBJECT_ROLE:
*************** get_object_address_unqualified(ObjectTyp
*** 746,751 ****
--- 761,769 ----
  			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;
*************** get_object_address_unqualified(ObjectTyp
*** 790,795 ****
--- 808,818 ----
  			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);
*************** check_object_ownership(Oid roleid, Objec
*** 1318,1323 ****
--- 1341,1347 ----
  			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,
*************** getObjectDescription(const ObjectAddress
*** 2224,2229 ****
--- 2248,2265 ----
  				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
new file mode 100644
index b1ac704..36a897c
*** a/src/backend/commands/Makefile
--- b/src/backend/commands/Makefile
*************** include $(top_builddir)/src/Makefile.glo
*** 14,20 ****
  
  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 \
  	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 \
--- 14,20 ----
  
  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 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
new file mode 100644
index c9a9baf..47f8d49
*** a/src/backend/commands/alter.c
--- b/src/backend/commands/alter.c
***************
*** 41,46 ****
--- 41,47 ----
  #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"
*************** ExecRenameStmt(RenameStmt *stmt)
*** 349,354 ****
--- 350,356 ----
  		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
new file mode 100644
index 6b83576..3a9562b
*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
***************
*** 28,33 ****
--- 28,34 ----
  #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"
*************** DoCopy(const CopyStmt *stmt, const char
*** 788,796 ****
  	Oid			relid;
  	Node	   *query = NULL;
  
- 	/* Disallow COPY to/from file or program except to superusers. */
  	if (!pipe && !superuser())
  	{
  		if (stmt->is_program)
  			ereport(ERROR,
  					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
--- 789,801 ----
  	Oid			relid;
  	Node	   *query = NULL;
  
  	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),
*************** DoCopy(const CopyStmt *stmt, const char
*** 798,808 ****
  					 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.")));
  	}
  
  	if (stmt->relation)
--- 803,845 ----
  					 errhint("Anyone can COPY to stdout or from stdin. "
  						   "psql's \\copy command also works for anyone.")));
  		else
! 		{
! 			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 ...9ccd77e
*** a/src/backend/commands/diralias.c
--- b/src/backend/commands/diralias.c
***************
*** 0 ****
--- 1,375 ----
+ /*-------------------------------------------------------------------------
+  *
+  * diralias.c
+  *	  Commands for manipulating directory aliases.
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/backend/commands/diralias.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
new file mode 100644
index 8583581..8cae12e
*** a/src/backend/commands/dropcmds.c
--- b/src/backend/commands/dropcmds.c
*************** does_not_exist_skipping(ObjectType objty
*** 380,385 ****
--- 380,389 ----
  												  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
new file mode 100644
index 1b8c94b..b70c322
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
*************** static event_trigger_support_data event_
*** 73,78 ****
--- 73,79 ----
  	{"COLLATION", true},
  	{"CONVERSION", true},
  	{"DATABASE", false},
+ 	{"DIRALIAS", true},
  	{"DOMAIN", true},
  	{"EXTENSION", true},
  	{"EVENT TRIGGER", false},
*************** EventTriggerSupportsObjectType(ObjectTyp
*** 924,929 ****
--- 925,931 ----
  		case OBJECT_CONSTRAINT:
  		case OBJECT_COLLATION:
  		case OBJECT_CONVERSION:
+ 		case OBJECT_DIRALIAS:
  		case OBJECT_DOMAIN:
  		case OBJECT_EXTENSION:
  		case OBJECT_FDW:
*************** EventTriggerSupportsObjectClass(ObjectCl
*** 998,1003 ****
--- 1000,1006 ----
  		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
new file mode 100644
index 21b070a..8941fa2
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyGrantRoleStmt(const GrantRoleStmt *
*** 2702,2707 ****
--- 2702,2721 ----
  	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)
  {
*************** _copyAlterPolicyStmt(const AlterPolicySt
*** 3879,3884 ****
--- 3893,3920 ----
  	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
   * ****************************************************************
*************** copyObject(const void *from)
*** 4318,4323 ****
--- 4354,4362 ----
  		case T_GrantStmt:
  			retval = _copyGrantStmt(from);
  			break;
+ 		case T_GrantDirAliasStmt:
+ 			retval = _copyGrantDirAliasStmt(from);
+ 			break;
  		case T_GrantRoleStmt:
  			retval = _copyGrantRoleStmt(from);
  			break;
*************** copyObject(const void *from)
*** 4597,4602 ****
--- 4636,4647 ----
  		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
new file mode 100644
index 358395f..e266ad8
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalGrantRoleStmt(const GrantRoleStmt
*** 1046,1051 ****
--- 1046,1063 ----
  }
  
  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);
*************** _equalAlterPolicyStmt(const AlterPolicyS
*** 2034,2039 ****
--- 2046,2069 ----
  }
  
  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);
*************** equal(const void *a, const void *b)
*** 2778,2783 ****
--- 2808,2816 ----
  		case T_GrantStmt:
  			retval = _equalGrantStmt(a, b);
  			break;
+ 		case T_GrantDirAliasStmt:
+ 			retval = _equalGrantDirAliasStmt(a, b);
+ 			break;
  		case T_GrantRoleStmt:
  			retval = _equalGrantRoleStmt(a, b);
  			break;
*************** equal(const void *a, const void *b)
*** 3057,3062 ****
--- 3090,3101 ----
  		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
new file mode 100644
index 0de9584..24d5eb5
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
*************** static Node *makeRecursiveViewSelect(cha
*** 258,264 ****
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
--- 258,265 ----
  		DeallocateStmt PrepareStmt ExecuteStmt
  		DropOwnedStmt ReassignOwnedStmt
  		AlterTSConfigurationStmt AlterTSDictionaryStmt
! 		CreateMatViewStmt RefreshMatViewStmt GrantDirStmt RevokeDirStmt
! 		CreateDirAliasStmt AlterDirAliasStmt
  
  %type <node>	select_no_parens select_with_parens select_clause
  				simple_select values_clause
*************** static Node *makeRecursiveViewSelect(cha
*** 324,329 ****
--- 325,333 ----
  %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
*************** static Node *makeRecursiveViewSelect(cha
*** 559,565 ****
  
  	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
  
  	EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
  	EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
--- 563,570 ----
  
  	DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
  	DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DESC
! 	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
*************** stmt :
*** 734,739 ****
--- 739,745 ----
  			| AlterDatabaseStmt
  			| AlterDatabaseSetStmt
  			| AlterDefaultPrivilegesStmt
+ 			| AlterDirAliasStmt
  			| AlterDomainStmt
  			| AlterEnumStmt
  			| AlterExtensionStmt
*************** stmt :
*** 769,774 ****
--- 775,781 ----
  			| CreateAssertStmt
  			| CreateCastStmt
  			| CreateConversionStmt
+ 			| CreateDirAliasStmt
  			| CreateDomainStmt
  			| CreateExtensionStmt
  			| CreateFdwStmt
*************** stmt :
*** 820,825 ****
--- 827,833 ----
  			| ExplainStmt
  			| FetchStmt
  			| GrantStmt
+ 			| GrantDirStmt
  			| GrantRoleStmt
  			| ImportForeignSchemaStmt
  			| IndexStmt
*************** stmt :
*** 837,842 ****
--- 845,851 ----
  			| RemoveOperStmt
  			| RenameStmt
  			| RevokeStmt
+ 			| RevokeDirStmt
  			| RevokeRoleStmt
  			| RuleStmt
  			| SecLabelStmt
*************** row_security_cmd:
*** 4606,4611 ****
--- 4615,4648 ----
  
  /*****************************************************************************
   *
+  *		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 ...
*************** drop_type:	TABLE									{ $$ = OBJECT_T
*** 5481,5486 ****
--- 5518,5524 ----
  			| 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; }
*************** opt_granted_by: GRANTED BY RoleId						{
*** 6314,6319 ****
--- 6352,6418 ----
  
  /*****************************************************************************
   *
+  *		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
   *
   *****************************************************************************/
*************** RenameStmt: ALTER AGGREGATE func_name ag
*** 7273,7278 ****
--- 7372,7386 ----
  					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);
*************** unreserved_keyword:
*** 13051,13056 ****
--- 13159,13165 ----
  			| DELIMITER
  			| DELIMITERS
  			| DICTIONARY
+ 			| DIRALIAS
  			| DISABLE_P
  			| DISCARD
  			| DOCUMENT_P
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
new file mode 100644
index 4a2a339..4980016
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 33,38 ****
--- 33,39 ----
  #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"
*************** check_xact_readonly(Node *parsetree)
*** 185,190 ****
--- 186,192 ----
  		case T_DropRoleStmt:
  		case T_GrantStmt:
  		case T_GrantRoleStmt:
+ 		case T_GrantDirAliasStmt:
  		case T_AlterDefaultPrivilegesStmt:
  		case T_TruncateStmt:
  		case T_DropOwnedStmt:
*************** standard_ProcessUtility(Node *parsetree,
*** 557,562 ****
--- 559,568 ----
  			GrantRole((GrantRoleStmt *) parsetree);
  			break;
  
+ 		case T_GrantDirAliasStmt:
+ 			ExecuteGrantDirAliasStmt((GrantDirAliasStmt *) parsetree);
+ 			break;
+ 
  		case T_CreatedbStmt:
  			/* no event triggers for global objects */
  			PreventTransactionChain(isTopLevel, "CREATE DATABASE");
*************** ProcessUtilitySlow(Node *parsetree,
*** 1329,1334 ****
--- 1335,1348 ----
  				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));
*************** AlterObjectTypeCommandTag(ObjectType obj
*** 1596,1601 ****
--- 1610,1618 ----
  		case OBJECT_DATABASE:
  			tag = "ALTER DATABASE";
  			break;
+ 		case OBJECT_DIRALIAS:
+ 			tag = "ALTER DIRALIAS";
+ 			break;
  		case OBJECT_DOMAIN:
  			tag = "ALTER DOMAIN";
  			break;
*************** CreateCommandTag(Node *parsetree)
*** 1959,1964 ****
--- 1976,1984 ----
  				case OBJECT_POLICY:
  					tag = "DROP POLICY";
  					break;
+ 				case OBJECT_DIRALIAS:
+ 					tag = "DROP DIRALIAS";
+ 					break;
  				default:
  					tag = "???";
  			}
*************** CreateCommandTag(Node *parsetree)
*** 2016,2021 ****
--- 2036,2049 ----
  			}
  			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;
*************** CreateCommandTag(Node *parsetree)
*** 2310,2315 ****
--- 2338,2351 ----
  			tag = "ALTER POLICY";
  			break;
  
+ 		case T_CreateDirAliasStmt:
+ 			tag = "CREATE DIRALIAS";
+ 			break;
+ 
+ 		case T_AlterDirAliasStmt:
+ 			tag = "ALTER DIRALIAS";
+ 			break;
+ 
  		case T_PrepareStmt:
  			tag = "PREPARE";
  			break;
*************** GetCommandLogLevel(Node *parsetree)
*** 2862,2867 ****
--- 2898,2911 ----
  			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
new file mode 100644
index dc6eb2c..7cc00ef
*** a/src/backend/utils/adt/acl.c
--- b/src/backend/utils/adt/acl.c
*************** acldefault(GrantObjectType objtype, Oid
*** 790,795 ****
--- 790,799 ----
  			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
new file mode 100644
index 3a0957f..58dd1dc
*** a/src/backend/utils/adt/genfile.c
--- b/src/backend/utils/adt/genfile.c
***************
*** 22,32 ****
--- 22,34 ----
  
  #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"
*************** typedef struct
*** 37,82 ****
  	DIR		   *dirdesc;
  } directory_fctx;
  
- 
  /*
!  * Convert a "text" filename argument to C string, and check it's allowable.
   *
!  * Filename may be absolute or relative to the DataDir, but we only allow
!  * absolute paths that match DataDir or Log_directory.
   */
! static char *
! convert_and_check_filename(text *arg)
  {
! 	char	   *filename;
  
! 	filename = text_to_cstring(arg);
! 	canonicalize_path(filename);	/* filename can change length here */
  
! 	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))
  		ereport(ERROR,
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("path must be in or below the current directory"))));
  
! 	return filename;
  }
  
  
--- 39,82 ----
  	DIR		   *dirdesc;
  } directory_fctx;
  
  /*
!  * Check the directory permissions for the provided filename/path.
   *
!  * The filename must be an absolute path to the file.
   */
! static void
! check_directory_permissions(char *directory)
  {
! 	Oid			diralias_id;
! 	AclResult	aclresult;
  
! 	/* Do not allow relative paths */
! 	if (!is_absolute_path(directory))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 errmsg("relative path not allowed")));
  
! 	/* 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("directory alias entry for \"%s\" does not exist",
! 						directory)));
  
! 	/* Check directory alias entry permissions */
! 	aclresult = pg_diralias_aclcheck(diralias_id, GetUserId(), ACL_SELECT);
! 
! 	/* 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")));
  }
  
  
*************** pg_read_file(PG_FUNCTION_ARGS)
*** 173,185 ****
  	int64		seek_offset = PG_GETARG_INT64(1);
  	int64		bytes_to_read = PG_GETARG_INT64(2);
  	char	   *filename;
  
! 	if (!superuser())
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser to read files"))));
  
! 	filename = convert_and_check_filename(filename_t);
  
  	if (bytes_to_read < 0)
  		ereport(ERROR,
--- 173,191 ----
  	int64		seek_offset = PG_GETARG_INT64(1);
  	int64		bytes_to_read = PG_GETARG_INT64(2);
  	char	   *filename;
+ 	char	   *directory;
  
! 	/* Convert and cleanup the filename */
! 	filename = text_to_cstring(filename_t);
! 	canonicalize_path(filename);
  
! 	/* 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,
*************** pg_read_file_all(PG_FUNCTION_ARGS)
*** 197,209 ****
  {
  	text	   *filename_t = PG_GETARG_TEXT_P(0);
  	char	   *filename;
  
! 	if (!superuser())
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser to read files"))));
  
! 	filename = convert_and_check_filename(filename_t);
  
  	PG_RETURN_TEXT_P(read_text_file(filename, 0, -1));
  }
--- 203,221 ----
  {
  	text	   *filename_t = PG_GETARG_TEXT_P(0);
  	char	   *filename;
+ 	char	   *directory;
  
! 	/* Convert and cleanup the filename */
! 	filename = text_to_cstring(filename_t);
! 	canonicalize_path(filename);
  
! 	/* 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));
  }
*************** pg_read_binary_file(PG_FUNCTION_ARGS)
*** 218,230 ****
  	int64		seek_offset = PG_GETARG_INT64(1);
  	int64		bytes_to_read = PG_GETARG_INT64(2);
  	char	   *filename;
  
! 	if (!superuser())
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser to read files"))));
  
! 	filename = convert_and_check_filename(filename_t);
  
  	if (bytes_to_read < 0)
  		ereport(ERROR,
--- 230,248 ----
  	int64		seek_offset = PG_GETARG_INT64(1);
  	int64		bytes_to_read = PG_GETARG_INT64(2);
  	char	   *filename;
+ 	char	   *directory;
  
! 	/* Convert and cleanup the filename */
! 	filename = text_to_cstring(filename_t);
! 	canonicalize_path(filename);
  
! 	/* 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,
*************** pg_read_binary_file_all(PG_FUNCTION_ARGS
*** 242,254 ****
  {
  	text	   *filename_t = PG_GETARG_TEXT_P(0);
  	char	   *filename;
  
! 	if (!superuser())
! 		ereport(ERROR,
! 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
! 				 (errmsg("must be superuser to read files"))));
  
! 	filename = convert_and_check_filename(filename_t);
  
  	PG_RETURN_BYTEA_P(read_binary_file(filename, 0, -1));
  }
--- 260,278 ----
  {
  	text	   *filename_t = PG_GETARG_TEXT_P(0);
  	char	   *filename;
+ 	char	   *directory;
  
! 	/* Convert and cleanup the filename */
! 	filename = text_to_cstring(filename_t);
! 	canonicalize_path(filename);
  
! 	/* 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));
  }
*************** pg_stat_file(PG_FUNCTION_ARGS)
*** 261,278 ****
  {
  	text	   *filename_t = PG_GETARG_TEXT_P(0);
  	char	   *filename;
  	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 = convert_and_check_filename(filename_t);
  
  	if (stat(filename, &fst) < 0)
  		ereport(ERROR,
--- 285,307 ----
  {
  	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;
  
! 	filename = text_to_cstring(filename_t);
! 	canonicalize_path(filename);
  
! 	/* 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,
*************** pg_ls_dir(PG_FUNCTION_ARGS)
*** 331,341 ****
  	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;
--- 360,365 ----
*************** pg_ls_dir(PG_FUNCTION_ARGS)
*** 344,350 ****
  		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
  
  		fctx = palloc(sizeof(directory_fctx));
! 		fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0));
  
  		fctx->dirdesc = AllocateDir(fctx->location);
  
--- 368,379 ----
  		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
  
  		fctx = palloc(sizeof(directory_fctx));
! 		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
new file mode 100644
index 94d951c..383e1c9
*** a/src/backend/utils/cache/syscache.c
--- b/src/backend/utils/cache/syscache.c
***************
*** 37,42 ****
--- 37,43 ----
  #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"
*************** static const struct cachedesc cacheinfo[
*** 366,371 ****
--- 367,383 ----
  			0
  		},
  		8
+ 	},
+ 	{DirAliasRelationId,		/* DIRALIASOID */
+ 		DirAliasOidIndexId,
+ 		1,
+ 		{
+ 			ObjectIdAttributeNumber,
+ 			0,
+ 			0,
+ 			0
+ 		},
+ 		8
  	},
  	{EnumRelationId,			/* ENUMOID */
  		EnumOidIndexId,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
new file mode 100644
index 8bfc604..da46c9f
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
*************** getSchemaData(Archive *fout, DumpOptions
*** 103,108 ****
--- 103,109 ----
  	int			numForeignServers;
  	int			numDefaultACLs;
  	int			numEventTriggers;
+ 	int			numDirectoryAliases;
  
  	if (g_verbose)
  		write_msg(NULL, "reading schemas\n");
*************** getSchemaData(Archive *fout, DumpOptions
*** 251,256 ****
--- 252,261 ----
  		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
new file mode 100644
index 259c472..08761e3
*** a/src/bin/pg_dump/dumputils.c
--- b/src/bin/pg_dump/dumputils.c
***************
*** 19,24 ****
--- 19,25 ----
  #include "dumputils.h"
  
  #include "parser/keywords.h"
+ #include "pg_backup_utils.h"
  
  
  /* Globals from keywords.c */
*************** buildACLCommands(const char *name, const
*** 545,554 ****
  	 * 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);
  
  	/*
  	 * We still need some hacking though to cover the case where new default
--- 546,561 ----
  	 * wire-in knowledge about the default public privileges for different
  	 * kinds of objects.
  	 */
! 	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
*************** buildACLCommands(const char *name, const
*** 593,608 ****
  					? 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));
  					if (privs->len > 0)
! 						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",
--- 600,633 ----
  					? strcmp(privswgo->data, "ALL") != 0
  					: strcmp(privs->data, "ALL") != 0)
  				{
! 					/* 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)
! 					{
! 						/* 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",
*************** buildACLCommands(const char *name, const
*** 622,629 ****
  
  				if (privs->len > 0)
  				{
! 					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 ",
--- 647,660 ----
  
  				if (privs->len > 0)
  				{
! 					/* 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 ",
*************** buildACLCommands(const char *name, const
*** 660,670 ****
  	 */
  	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));
  	}
  
  	destroyPQExpBuffer(grantee);
--- 691,708 ----
  	 */
  	if (!found_owner_privs && 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);
*************** do { \
*** 873,878 ****
--- 911,921 ----
  		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
new file mode 100644
index ed28d36..0449b87
*** a/src/bin/pg_dump/pg_backup_archiver.c
--- b/src/bin/pg_dump/pg_backup_archiver.c
*************** _getObjectDescription(PQExpBuffer buf, T
*** 3106,3112 ****
  		strcmp(type, "SCHEMA") == 0 ||
  		strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
  		strcmp(type, "SERVER") == 0 ||
! 		strcmp(type, "USER MAPPING") == 0)
  	{
  		/* We already know that search_path was set properly */
  		appendPQExpBuffer(buf, "%s %s", type, fmtId(te->tag));
--- 3106,3113 ----
  		strcmp(type, "SCHEMA") == 0 ||
  		strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
  		strcmp(type, "SERVER") == 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));
*************** _printTocEntry(ArchiveHandle *AH, TocEnt
*** 3307,3313 ****
  			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)
  		{
  			PQExpBuffer temp = createPQExpBuffer();
  
--- 3308,3315 ----
  			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, "DIRALIAS") == 0)
  		{
  			PQExpBuffer temp = createPQExpBuffer();
  
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
new file mode 100644
index 1e8f089..2c154c0
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** static void dumpUserMappings(Archive *fo
*** 191,196 ****
--- 191,197 ----
  				 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,
*************** dumpRowSecurity(Archive *fout, DumpOptio
*** 2987,2992 ****
--- 2988,3086 ----
  	destroyPQExpBuffer(delqry);
  }
  
+ void
+ getDirectoryAliases(Archive *fout, int *numDirectoryAliases)
+ {
+ 	PQExpBuffer				query;
+ 	PGresult			   *res;
+ 	DirectoryAliasInfo	   *dirinfo;
+ 	int				i_tableoid;
+ 	int				i_oid;
+ 	int				i_dirname;
+ 	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, dirname, dirpath, diracl, "
+ 					  "(%s (SELECT relowner FROM pg_catalog.pg_class WHERE oid = tableoid)) AS rolname "
+ 					  "FROM pg_catalog.pg_diralias",
+ 					  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_dirname = PQfnumber(res, "dirname");
+ 	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_dirname));
+ 		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		   *dirname;
+ 
+ 	if (!dirinfo->dobj.dump || dopt->dataOnly)
+ 		return;
+ 
+ 	create_query = createPQExpBuffer();
+ 	delete_query = createPQExpBuffer();
+ 
+ 	dirname = pg_strdup(fmtId(dirinfo->dobj.name));
+ 
+ 	appendPQExpBuffer(delete_query, "DROP DIRALIAS %s;\n", dirname);
+ 
+ 	appendPQExpBuffer(create_query, "CREATE DIRALIAS %s AS \'%s\';\n",
+ 					  dirname, 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", dirname, 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,
*************** dumpDumpableObject(Archive *fout, DumpOp
*** 8218,8223 ****
--- 8312,8320 ----
  		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 */
*************** addBoundaryDependencies(DumpableObject *
*** 15615,15620 ****
--- 15712,15718 ----
  			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
new file mode 100644
index a7eb2fd..14a712c
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef enum
*** 76,82 ****
  	DO_POST_DATA_BOUNDARY,
  	DO_EVENT_TRIGGER,
  	DO_REFRESH_MATVIEW,
! 	DO_ROW_SECURITY
  } DumpableObjectType;
  
  typedef struct _dumpableObject
--- 76,83 ----
  	DO_POST_DATA_BOUNDARY,
  	DO_EVENT_TRIGGER,
  	DO_REFRESH_MATVIEW,
! 	DO_ROW_SECURITY,
! 	DO_DIRALIAS
  } DumpableObjectType;
  
  typedef struct _dumpableObject
*************** typedef struct _rowSecurityInfo
*** 469,474 ****
--- 470,484 ----
  	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 */
*************** extern void getExtensionMembership(Archi
*** 550,554 ****
--- 560,565 ----
  					   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
new file mode 100644
index 030bccc..5cece21
*** a/src/bin/pg_dump/pg_dump_sort.c
--- b/src/bin/pg_dump/pg_dump_sort.c
*************** static const int oldObjectTypePriority[]
*** 73,79 ****
  	13,							/* DO_POST_DATA_BOUNDARY */
  	20,							/* DO_EVENT_TRIGGER */
  	15,							/* DO_REFRESH_MATVIEW */
! 	21							/* DO_ROW_SECURITY */
  };
  
  /*
--- 73,80 ----
  	13,							/* DO_POST_DATA_BOUNDARY */
  	20,							/* DO_EVENT_TRIGGER */
  	15,							/* DO_REFRESH_MATVIEW */
! 	21,							/* DO_ROW_SECURITY */
! 	22,							/* DO_DIRALIAS */
  };
  
  /*
*************** static const int newObjectTypePriority[]
*** 122,128 ****
  	25,							/* DO_POST_DATA_BOUNDARY */
  	32,							/* DO_EVENT_TRIGGER */
  	33,							/* DO_REFRESH_MATVIEW */
! 	34							/* DO_ROW_SECURITY */
  };
  
  static DumpId preDataBoundId;
--- 123,130 ----
  	25,							/* DO_POST_DATA_BOUNDARY */
  	32,							/* DO_EVENT_TRIGGER */
  	33,							/* DO_REFRESH_MATVIEW */
! 	34,							/* DO_ROW_SECURITY */
! 	35,							/* DO_DIRALIAS */
  };
  
  static DumpId preDataBoundId;
*************** describeDumpableObject(DumpableObject *o
*** 1443,1448 ****
--- 1445,1455 ----
  					 "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
new file mode 100644
index 6a4913a..1779bc0
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
*************** typedef enum ObjectClass
*** 148,153 ****
--- 148,154 ----
  	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
new file mode 100644
index 870692c..ce7464a
*** a/src/include/catalog/indexing.h
--- b/src/include/catalog/indexing.h
*************** DECLARE_UNIQUE_INDEX(pg_extension_name_i
*** 299,304 ****
--- 299,310 ----
  DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
  #define RangeTypidIndexId					3542
  
+ DECLARE_UNIQUE_INDEX(pg_diralias_oid_index, 6101, on pg_diralias using btree(oid oid_ops));
+ #define DirAliasOidIndexId					6101
+ 
+ DECLARE_UNIQUE_INDEX(pg_diralias_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 ...e58f047
*** a/src/include/catalog/pg_diralias.h
--- b/src/include/catalog/pg_diralias.h
***************
*** 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 ...fc2cf23
*** a/src/include/commands/diralias.h
--- b/src/include/commands/diralias.h
***************
*** 0 ****
--- 1,29 ----
+ /*-------------------------------------------------------------------------
+  *
+  * diralias.h
+  *	  prototypes for diralias.c.
+  *
+  *
+  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * src/include/commands/diralias.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
new file mode 100644
index 154d943..6b293aa
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 278,283 ****
--- 278,284 ----
  	T_AlterDomainStmt,
  	T_SetOperationStmt,
  	T_GrantStmt,
+ 	T_GrantDirAliasStmt,
  	T_GrantRoleStmt,
  	T_AlterDefaultPrivilegesStmt,
  	T_ClosePortalStmt,
*************** typedef enum NodeTag
*** 368,373 ****
--- 369,376 ----
  	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
new file mode 100644
index cef9544..4b7197c
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
*************** typedef enum ObjectType
*** 1212,1217 ****
--- 1212,1218 ----
  	OBJECT_COLLATION,
  	OBJECT_CONVERSION,
  	OBJECT_DATABASE,
+ 	OBJECT_DIRALIAS,
  	OBJECT_DOMAIN,
  	OBJECT_EVENT_TRIGGER,
  	OBJECT_EXTENSION,
*************** typedef enum GrantObjectType
*** 1412,1417 ****
--- 1413,1419 ----
  	ACL_OBJECT_LARGEOBJECT,		/* largeobject */
  	ACL_OBJECT_NAMESPACE,		/* namespace */
  	ACL_OBJECT_TABLESPACE,		/* tablespace */
+ 	ACL_OBJECT_DIRALIAS,		/* directory alias */
  	ACL_OBJECT_TYPE				/* type */
  } GrantObjectType;
  
*************** typedef struct GrantRoleStmt
*** 1483,1488 ****
--- 1485,1504 ----
  } 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
   * ----------------------
   */
*************** typedef struct AlterPolicyStmt
*** 1890,1895 ****
--- 1906,1933 ----
  } 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
new file mode 100644
index e14dc9a..eb31cf7
*** a/src/include/parser/kwlist.h
--- b/src/include/parser/kwlist.h
*************** PG_KEYWORD("delimiter", DELIMITER, UNRES
*** 125,130 ****
--- 125,131 ----
  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
new file mode 100644
index a8e3164..cb75b1a
*** a/src/include/utils/acl.h
--- b/src/include/utils/acl.h
*************** typedef ArrayType Acl;
*** 156,161 ****
--- 156,162 ----
  #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
*************** typedef enum AclObjectKind
*** 197,202 ****
--- 198,204 ----
  	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;
  
*************** extern Datum aclexplode(PG_FUNCTION_ARGS
*** 255,260 ****
--- 257,263 ----
   */
  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);
*************** extern AclMode pg_foreign_server_aclmask
*** 281,286 ****
--- 284,291 ----
  						  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);
*************** extern AclResult pg_tablespace_aclcheck(
*** 297,302 ****
--- 302,308 ----
  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
new file mode 100644
index f97229f..c4b822e
*** a/src/include/utils/syscache.h
--- b/src/include/utils/syscache.h
*************** enum SysCacheIdentifier
*** 54,59 ****
--- 54,60 ----
  	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
new file mode 100644
index 2c8ec11..334f648
*** a/src/test/regress/expected/sanity_check.out
--- b/src/test/regress/expected/sanity_check.out
*************** pg_db_role_setting|t
*** 102,107 ****
--- 102,108 ----
  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 (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to