Best way to change the owner of a PostgreSQL database and their tables? - postgresql

I am trying to change the owner of a PostgreSQL database (version > 8.2) and its tables.
I read this solution:
Modify OWNER on all tables simultaneously in PostgreSQL
But is this the best way to do it (for recent versions of PostgreSQL)?. It seems that there is a function REASSIGN OWNED which is better, but this one changes every database owned by the old_role, doesn't it? I only want it for one database.
Like this post:
REASSIGN OWNED BY for 1 specified database
I am not going to change the owner postgres, which is the best way nowadays?
Thank you in advance

According to the manual:
Because REASSIGN OWNED does not affect objects within other databases, it is usually necessary to execute this command in each database
which would seem to meet your requirements, although it also says the command would affect table spaces (which are not specific to the current database).
The second SO answer you linked applies to the special case of the postgres user, which owns the system catalogs. You cannot change the ownership of these.

The two methods that spring to mind for me are:
1) First alter the database name, and then perhaps right a quick script which changes the owner on the current tables to the old tables.
ALTER DATABASE <dbname> OWNER TO <newowner>;
\o altertable.sql
SELECT 'ALTER TABLE ' || table_name || ' OWNER TO <newowner>; ' FROM information_schema WHERE table_schema = <oldowner> and table_catalog = '<dbname>';
\o
\i altertable.sql
The above will generate your commands, then just pop them into a file and execute it.
2) The other option would be to use pg_dump to dump your database in plain text mode and then alter the appropriate strings using search and replace:
pg_dump -Fp -f dbbackup.dmp <dbname>
vi dbbackup.dmp
:%s/oldowner/newowner/g
save and exit
Hope this helps.

Related

Cannot drop a schema in PostgreSQL

I am trying to drop the schema masterdata from a postgres database, but it does not work. I am using PostgreSQL 9.5. I have 2 databses, one of them has the masterdata schema, which I want to drop. Here is the structure (in DBeaver):
My first attempt was just to execute the SQL statement DROP SCHEMA masterdata in DBeaver, but it tells me, that such a schema does not exist (but it does show it, as we can see in the picture!). Maybe, it does not know, in which of the 2 databses to look for this schema? If so, then how to specify it? I was looking for a way to specify the database, but did not find anything.
However, my second attempt was to use psql and to type the command
psql -U postgres -d bobd -h localhost
So here I concretely specify, which databse to use! psql does not answer anything, it just asks for the next command. And when I type \dn to view the current schemas, then the schema masterdata is still there! Also, the data in the tables is still there. I tried the same with other users instead of the user postgres (also with the owner of the schema to remove), but the result is the same.
Any ideas, what I am doing wrong?
Your DBeaver session was probably connected to the wrong database (postgres?).
Do it from the psql session, that is easiest.
Right after \dn showed you that there is indeed such a schema, enter
DROP SCHEMA masterdata;
It may complain that there are objects in that schema. Then you can use
DROP SCHEMA masterdata CASCADE;
Maybe your schema name contains characters that do not display or display differently (maybe capital letters) in DBeaver. I suggest you check names by running following query:
SELECT '~~' || nspname || '~~' FROM pg_catalog.pg_namespace;
Try adding quotes around schema name if you have added capital letters when creating schema.

how to restore a postgresql database to the exact same state?

I am trying to
create a snapshot of a PostgreSQL database (using pg_dump),
do some random tests, and
restore to the exact same state as the snapshot, and do some other random tests.
These can happen over many/different days. Also I am in a multi-user environment where I am not DB admin. In particular, I cannot create new DB.
However, when I restore db using
gunzip -c dump_file.gz | psql my_db
changes in step 2 above remain.
For example, if I make a copy of a table:
create table foo1 as (select * from foo);
and then restore, the copied table foo1 remains there.
Could some explain how can I restore to the exact same state as if step 2 never happened?
-- Update --
Following the comments #a_horse_with_no_name, I tried to to use
DROP OWNED BY my_db_user
to drop all my objects before restore, but I got an error associated with an extension that I cannot control, and my tables remain intact.
ERROR: cannot drop sequence bg_gid_seq because extension postgis_tiger_geocoder requires it
HINT: You can drop extension postgis_tiger_geocoder instead.
Any suggestions?
You have to remove everything that's there by dropping and recreating the database or something like that. pg_dump basically just makes an SQL script that, when applied, will ensure all the tables, stored procs, etc. exist and have their data. It doesn't remove anything.
You can use PostgreSQL Schemas.

Group many postgresql databases into separate schemas into same database

We have many postgresql databases with the same structure using only public shcema on each one.
How can I group all of them in a single database using separate schemas?
You can dump the database definition and data out, edit the output by putting the default schema as whatever you choose and run the scripts back into database.
Remember to make the dump in SQL format, pg_dump with default custom format won't work. The schema change will only need a change on a row like
SET search_path TO *whateverschema*
If you don't want to edit the dumps (maybe they're very large), you can of course also restore them one by one to the public schema, alter the tables into the desired schema and then repeat for the next one.
There is no special way to convert an existing database into a schema in another database unfortunately.
I forgot to post the answer afer all klin comment was the answer, this step was the solution,
Inside customer_x database:
alter schema public rename to customer_x;
And then take pg_dump customer_x:
pg_dump "customer_x" --schema "customer_x" -f customer_x.sql
Inside new conglomerated database:
DROP schema customer_x CASCADE;
create schema customer_x;
Then load the dump of customer_x:
psql "conglomerated_database" -f customer_x.sql

restoring database from pg_dump file creates strange tables

I have backup created like this:
pg_dump dbname > file
I am trying to restore the database (after drop database and create database) like this:
psql dbname < file
What I get is a database full of tables that are created with dbname.tablename instead of just tablename.
How do I restore a postgres database making sure the tables it creates has just tablename and not dbname.tablename?
Thanks to #Craig Ringer for pointing me in the right direction.
Yes, there was SET search_path on the database for the original DB. This created the table names with schema names prefixed to table names.
Removing or commenting those out of the backup script created tables without a schema prefix. Which was desirable. But the restore didn't result in complete restore, and many tables got left out.
So did the restore, with usual means. Tables are created with schema names prefixed. The sql query scripts broke because they were not specifying the schema names every time they queried the table. To fix this, I followed this - https://stackoverflow.com/a/2875705/1945517
ALTER ROLE <your_login_role> SET search_path TO dbname;
This fixed the broken queries.

Allow postgres user to only list his own database

I'm using a postgresql server and I want to forbid my users to see what other databases are on the same server.
Essentially a \l should only list his own database.
I'm pretty sure that there is a right which I need to revoke from the user but I can't find it in the docs.
This seems to work but might have unforeseen consequences. It requires tinkering with system catalogues, which isn't really a good idea!
First off, you have to permit superusers to update system catalogues by adding this to your postgresql config:
allow_system_table_mods = on
and restart.
Now, you can use DDL statements to modify system catalogues (you should be afraid). Connect to one of the user databases (a test one would be a good idea) and:
alter table pg_catalog.pg_database rename to pg_database_catalog;
create view pg_catalog.pg_database as
select oid, 1262::oid as tableoid, pg_database_catalog.*
from pg_catalog.pg_database_catalog
where has_database_privilege(pg_database_catalog.oid, 'connect');
grant select on pg_catalog.pg_database to public;
You should now find that if you connect to that database as a low-priv user, the \l command will just list the databases that that user can connect to.
The problem is you now need to guess which database the users connect to initially to fetch their database list from. If they connect to their own database initially, then you're probably done at this point. If they connect to postgres or template1 first, then you need to make this change on that database instead.
It seems to me that this should work, since the pg_database catalog is referred to by postgres backends directly by oid, rather than by name, so moving it out of the way and changing which rows are shown in it should be invisible to them. In particular, you can't stop the server distinguishing to the user between a database not existing and them not having connection privilege.
I'm not going to make any promises that this sort of change doesn't screw something else up down the line. If it breaks, you get to keep the pieces.
You probably want to make this change in a template database, and create user databases from that in future, and deactivate the allow_system_table_mods setting when you're done (which requires a server restart, remember).
Also, I tested this on 9.0: it seems to me it should work on some earlier versions too, caveat emptor.
There's no such setting in pgsql. There are settings to prevent users from connecting to databases that they shouldn't (grant / revoke connect). Being able to see there's a database is no big deal. Being able to connect / have edit rights etc. is.
I would imagine this might have negative repercussions for the user, such as not being able to connect to the database since the system does not have access to the sytem tables, not sure though. But as far as figuring out what table to revoke - this a good general way to see what the psql meta commands are doing:
To see what \l is doing you can also use the -E flag from the command line with psql.
~$ psql -E -c '\l'
********* QUERY **********
SELECT d.datname as "Name",
pg_catalog.pg_get_userbyid(d.datdba) as "Owner",
pg_catalog.pg_encoding_to_char(d.encoding) as "Encoding",
d.datcollate as "Collation",
d.datctype as "Ctype",
pg_catalog.array_to_string(d.datacl, E'\n') AS "Access privileges"
FROM pg_catalog.pg_database d
ORDER BY 1;
**************************
So if the user does not have access to pg_database they will not be able to use the \l command.
#araqnid 's answer above seems to be the way to go except for one problem: select oid, 1262::oid as tableoid, pg_database_catalog.* will have the oid column defined twice in its results, once as expicitly given via select oid and once taken from pg_database_catalog.*. At least on Postgresql 12 create view pg_catalog.pg_database will complain that the column oid is being defined twice and will abort.
Thus the corrected code would be:
alter table pg_catalog.pg_database rename to pg_database_catalog;
create view pg_catalog.pg_database as
select 1262::oid as tableoid, pg_database_catalog.*
from pg_catalog.pg_database_catalog
where has_database_privilege(pg_database_catalog.oid, 'connect');
grant select on pg_catalog.pg_database to public;
Please refer to the original answer for all other information.
I'd be glad if somebody could confirm that my findings here are correct (or refute them).