I am setting up a postgres server with what is going to be a (mostly) read-only workload.
I would like to allow myself and colleagues to create/drop indices as we see fit, without allowing ourselves to drop tables or mutate the tables.
I spent time reading the grant/revoke documentation as well as role documentation pages, but it seems the table owner roles get both powers.
One thing I found was that, supposedly, grant create on tablespace allows index creation, but it didn't work ( note, I did not create any other tablespaces).
to be specific, I tried:
mydb=# grant create on tablespace pg_default to bob;
GRANT
… then switched over to bob:
mydb=> create index on foo(a);
ERROR: must be owner of relation foo
I also tried making bob an owner, but then bob gets the right to drop tables, and there is seemingly no way to revoke that privilege. So I'm stuck with both or none.
Any ideas?
Best Answer
At the SQL level you can't, since all those tasks are governed by table ownership.
The
CREATE
on a tablespace is required but not sufficient to create an index on a table and store the index in that tablespace. If you don't have theCREATE
right on the tablespace you want to put the index in then you cannotCREATE INDEX
that index. However, having that right is not enough; otherwise anybody could create indexes on any table if they had the right to create anything in any tablespace, and we don't want that. Indexes have a performance cost, take heavy locks during creation, and perhaps most importantly an expression index can leak data about the table via a malicious function or operator. So you must also own the table the index is to be created on.Support for a separate
INDEX
right on a table could be added to PostgreSQL, but has not been, and might not get accepted if submitted. For now, you're stuck with having to own the table.You could write a C extension that installs a
ProcessUtility_hook
that checks what operations are being performed and the current user identity, then rejects or permits them as appropriate. You can find examples ofProcessUtility_hook
use incontrib/sepgsql
and externally in thebdr_commandfilter.c
file in the BDR project source code. You have to compile the extension, install it into the file system, then add it toshared_preload_libraries
to install it, so you need full filesystem level access to the server, and usually root access.A more practical approach is to use a
SECURITY DEFINER
function as a wrapper. Write a PL/PgSQL function that runs as the table owner and accepts as arguments the table to index, the column(s) to index, etc. Have it create theCREATE INDEX
expression usingformat(...)
then pass it toEXECUTE
. Do not allow the user to pass arbitrary SQL expressions as arguments, or you're basically giving them full access via SQL injection. Want multiple columns? You'll have to acceptcolname text[]
as an argument andquote_ident
each one. And so on. Search for "dynamic SQL plpgsql" to learn more about this approach.