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