#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "commands/dbcommands.h"
+#include "commands/event_trigger.h"
#include "commands/proclang.h"
#include "commands/tablespace.h"
#include "foreign/foreign.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
+#include "utils/aclchk.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/tqual.h"
-/*
- * The information about one Grant/Revoke statement, in internal format: object
- * and grantees names have been turned into Oids, the privilege list is an
- * AclMode bitmask. If 'privileges' is ACL_NO_RIGHTS (the 0 value) and
- * all_privs is true, 'privileges' will be internally set to the right kind of
- * ACL_ALL_RIGHTS_*, depending on the object type (NB - this will modify the
- * InternalGrant struct!)
- *
- * Note: 'all_privs' and 'privileges' represent object-level privileges only.
- * There might also be column-level privilege specifications, which are
- * represented in col_privs (this is a list of untransformed AccessPriv nodes).
- * Column privileges are only valid for objtype ACL_OBJECT_RELATION.
- */
-typedef struct
-{
- bool is_grant;
- GrantObjectType objtype;
- List *objects;
- bool all_privs;
- AclMode privileges;
- List *col_privs;
- List *grantees;
- bool grant_option;
- DropBehavior behavior;
-} InternalGrant;
-
/*
* Internal format used by ALTER DEFAULT PRIVILEGES.
*/
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) istmt->objtype);
}
+
+ /*
+ * Pass the info to event triggers about the just-executed GRANT. Note
+ * that we prefer to do it after actually executing it, because that gives
+ * the functions a chance to adjust the istmt with privileges actually
+ * granted.
+ */
+ EventTriggerStashGrant(istmt);
}
/*
currentEventTriggerState->curcmd = NULL;
}
+/*
+ * EventTriggerStashGrant
+ * Save data about a GRANT/REVOKE command being executed
+ *
+ * This function creates a copy of the InternalGrant, as the original might
+ * not have the right lifetime.
+ */
+void
+EventTriggerStashGrant(InternalGrant *istmt)
+{
+ MemoryContext oldcxt;
+ StashedCommand *stashed;
+ InternalGrant *icopy;
+ ListCell *cell;
+
+ oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+
+ /*
+ * copying the node is moderately challenging ... XXX should we consider
+ * changing InternalGrant into a full-fledged node instead?
+ */
+ icopy = palloc(sizeof(InternalGrant));
+ memcpy(icopy, istmt, sizeof(InternalGrant));
+ icopy->objects = list_copy(istmt->objects);
+ icopy->grantees = list_copy(istmt->grantees);
+ icopy->col_privs = NIL;
+ foreach(cell, istmt->col_privs)
+ icopy->col_privs = lappend(icopy->col_privs, copyObject(lfirst(cell)));
+
+ stashed = palloc(sizeof(StashedCommand));
+ stashed->type = SCT_Grant;
+ stashed->in_extension = currentEventTriggerState->in_extension;
+
+ stashed->d.grant.istmt = icopy;
+ stashed->parsetree = NULL;
+
+ currentEventTriggerState->stash = lappend(currentEventTriggerState->stash,
+ stashed);
+
+ MemoryContextSwitchTo(oldcxt);
+}
+
Datum
pg_event_trigger_get_creation_commands(PG_FUNCTION_ARGS)
{
/* command */
values[i++] = CStringGetTextDatum(command);
}
+ else
+ {
+ Assert(cmd->type == SCT_Grant);
+
+ /* classid */
+ nulls[i++] = true;
+ /* objid */
+ nulls[i++] = true;
+ /* objsubid */
+ nulls[i++] = true;
+ /* command tag */
+ values[i++] = CStringGetTextDatum("GRANT"); /* XXX maybe REVOKE or something else */
+ /* object_type */
+ values[i++] = CStringGetTextDatum("TABLE"); /* XXX maybe something else */
+ /* schema */
+ nulls[i++] = true;
+ /* identity */
+ nulls[i++] = true;
+ /* in_extension */
+ values[i++] = BoolGetDatum(cmd->in_extension);
+ /* command */
+ values[i++] = CStringGetTextDatum(command);
+ }
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
}
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_aggregate.h"
+#include "catalog/pg_authid.h"
#include "catalog/pg_class.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_extension.h"
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_language.h"
+#include "catalog/pg_largeobject.h"
+#include "catalog/pg_namespace.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_opfamily.h"
return command;
}
+static char *
+deparse_GrantStmt(StashedCommand *cmd)
+{
+ InternalGrant *istmt;
+ ObjTree *grantStmt;
+ char *command;
+ char *fmt;
+ char *objtype;
+ List *list;
+ ListCell *cell;
+ Oid classId;
+ ObjTree *tmp;
+
+ istmt = cmd->d.grant.istmt;
+
+ switch (istmt->objtype)
+ {
+ case ACL_OBJECT_COLUMN:
+ case ACL_OBJECT_RELATION:
+ objtype = "TABLE";
+ classId = RelationRelationId;
+ break;
+ case ACL_OBJECT_SEQUENCE:
+ objtype = "SEQUENCE";
+ classId = RelationRelationId;
+ break;
+ case ACL_OBJECT_DOMAIN:
+ objtype = "DOMAIN";
+ classId = TypeRelationId;
+ break;
+ case ACL_OBJECT_FDW:
+ objtype = "FOREIGN DATA WRAPPER";
+ classId = ForeignDataWrapperRelationId;
+ break;
+ case ACL_OBJECT_FOREIGN_SERVER:
+ objtype = "SERVER";
+ classId = ForeignServerRelationId;
+ break;
+ case ACL_OBJECT_FUNCTION:
+ objtype = "FUNCTION";
+ classId = ProcedureRelationId;
+ break;
+ case ACL_OBJECT_LANGUAGE:
+ objtype = "LANGUAGE";
+ classId = LanguageRelationId;
+ break;
+ case ACL_OBJECT_LARGEOBJECT:
+ objtype = "LARGE OBJECT";
+ classId = LargeObjectRelationId;
+ break;
+ case ACL_OBJECT_NAMESPACE:
+ objtype = "SCHEMA";
+ classId = NamespaceRelationId;
+ break;
+ case ACL_OBJECT_TYPE:
+ objtype = "TYPE";
+ classId = TypeRelationId;
+ break;
+ case ACL_OBJECT_DATABASE:
+ case ACL_OBJECT_TABLESPACE:
+ objtype = "";
+ classId = InvalidOid;
+ elog(ERROR, "global objects not supported");
+ default:
+ elog(ERROR, "invalid ACL_OBJECT value %d", istmt->objtype);
+ }
+
+ /* GRANT TO or REVOKE FROM */
+ if (istmt->is_grant)
+ fmt = psprintf("GRANT %%{privileges:, }s ON %s %%{privtarget:, }s "
+ "TO %%{grantees:, }s %%{grant_option}s",
+ objtype);
+ else
+ fmt = psprintf("REVOKE %%{grant_option}s %%{privileges:, }s ON %s %%{privtarget:, }s "
+ "FROM %%{grantees:, }s %%{cascade}s",
+ objtype);
+
+ grantStmt = new_objtree_VA(fmt, 0);
+
+ /* build list of privileges to grant/revoke */
+ if (istmt->all_privs)
+ {
+ tmp = new_objtree_VA("ALL PRIVILEGES", 0);
+ list = list_make1(new_object_object(NULL, tmp));
+ }
+ else
+ {
+ list = NIL;
+
+ if (istmt->privileges & ACL_INSERT)
+ list = lappend(list, new_string_object(NULL, "INSERT"));
+ if (istmt->privileges & ACL_SELECT)
+ list = lappend(list, new_string_object(NULL, "SELECT"));
+ if (istmt->privileges & ACL_UPDATE)
+ list = lappend(list, new_string_object(NULL, "UPDATE"));
+ if (istmt->privileges & ACL_DELETE)
+ list = lappend(list, new_string_object(NULL, "DELETE"));
+ if (istmt->privileges & ACL_TRUNCATE)
+ list = lappend(list, new_string_object(NULL, "TRUNCATE"));
+ if (istmt->privileges & ACL_REFERENCES)
+ list = lappend(list, new_string_object(NULL, "REFERENCES"));
+ if (istmt->privileges & ACL_TRIGGER)
+ list = lappend(list, new_string_object(NULL, "TRIGGER"));
+ if (istmt->privileges & ACL_EXECUTE)
+ list = lappend(list, new_string_object(NULL, "EXECUTE"));
+ if (istmt->privileges & ACL_USAGE)
+ list = lappend(list, new_string_object(NULL, "USAGE"));
+ if (istmt->privileges & ACL_CREATE)
+ list = lappend(list, new_string_object(NULL, "CREATE"));
+ if (istmt->privileges & ACL_CREATE_TEMP)
+ list = lappend(list, new_string_object(NULL, "TEMPORARY"));
+ if (istmt->privileges & ACL_CONNECT)
+ list = lappend(list, new_string_object(NULL, "CONNECT"));
+
+ if (istmt->col_privs != NIL)
+ {
+ ListCell *ocell;
+
+ foreach(ocell, istmt->col_privs)
+ {
+ AccessPriv *priv = lfirst(ocell);
+ List *cols = NIL;
+
+ tmp = new_objtree_VA("%{priv}s (%{cols:, }I)", 0);
+ foreach(cell, priv->cols)
+ {
+ Value *colname = lfirst(cell);
+
+ cols = lappend(cols,
+ new_string_object(NULL,
+ strVal(colname)));
+ }
+ append_array_object(tmp, "cols", cols);
+ if (priv->priv_name == NULL)
+ append_string_object(tmp, "priv", "ALL PRIVILEGES");
+ else
+ append_string_object(tmp, "priv", priv->priv_name);
+
+ list = lappend(list, new_object_object(NULL, tmp));
+ }
+ }
+ }
+ append_array_object(grantStmt, "privileges", list);
+
+ /* target objects. We use object identities here */
+ list = NIL;
+ foreach(cell, istmt->objects)
+ {
+ Oid objid = lfirst_oid(cell);
+ ObjectAddress addr;
+
+ addr.classId = classId;
+ addr.objectId = objid;
+ addr.objectSubId = 0;
+
+ tmp = new_objtree_VA("%{identity}s", 0);
+ append_string_object(tmp, "identity",
+ getObjectIdentity(&addr));
+ list = lappend(list, new_object_object(NULL, tmp));
+ }
+ append_array_object(grantStmt, "privtarget", list);
+
+ /* list of grantees */
+ list = NIL;
+ foreach(cell, istmt->grantees)
+ {
+ Oid grantee = lfirst_oid(cell);
+
+ if (grantee == ACL_ID_PUBLIC)
+ tmp = new_objtree_VA("PUBLIC", 0);
+ else
+ {
+ HeapTuple roltup;
+ char *rolname;
+
+ roltup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(grantee));
+ if (!HeapTupleIsValid(roltup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("role with OID %u does not exist", grantee)));
+
+ tmp = new_objtree_VA("%{name}I", 0);
+ rolname = NameStr(((Form_pg_authid) GETSTRUCT(roltup))->rolname);
+ append_string_object(tmp, "name", pstrdup(rolname));
+ ReleaseSysCache(roltup);
+ }
+ list = lappend(list, new_object_object(NULL, tmp));
+ }
+ append_array_object(grantStmt, "grantees", list);
+
+ /* the wording of the grant option is variable ... */
+ if (istmt->is_grant)
+ append_string_object(grantStmt, "grant_option",
+ istmt->grant_option ? "WITH GRANT OPTION" : "");
+ else
+ append_string_object(grantStmt, "grant_option",
+ istmt->grant_option ? "GRANT OPTION FOR" : "");
+
+ if (!istmt->is_grant)
+ {
+ if (istmt->behavior == DROP_CASCADE)
+ append_string_object(grantStmt, "cascade", "CASCADE");
+ else
+ append_string_object(grantStmt, "cascade", "");
+ }
+
+ command = jsonize_objtree(grantStmt);
+ free_objtree(grantStmt);
+
+ return command;
+}
+
static char *
deparse_AlterTableStmt(StashedCommand *cmd)
{
command = deparse_AlterOwnerStmt(objectId, parsetree);
break;
+ case T_GrantStmt:
+ elog(ERROR, "unexpected node type T_GrantStmt");
+ break;
+
default:
command = NULL;
elog(LOG, "unrecognized node type: %d",
case SCT_AlterTable:
command = deparse_parsenode_cmd(cmd);
break;
+ case SCT_Grant:
+ command = deparse_GrantStmt(cmd);
+ break;
default:
elog(ERROR, "unexpected deparse node type %d", cmd->type);
}