I have seen that it is possible to convert all tables to case insensitive names using the following commands in psql:
\o /tmp/go_to_lower
select 'ALTER TABLE '||'"'||tablename||'"'||' RENAME TO ' ||
lower(tablename)||';' from pg_tables where schemaname = 'public';
psql -U username database < /tmp/go_to_lower
I have been unable to unearth a command to convert all columns to case insensitive in the same way. How can this be achieved?
EDIT: Apparently the above code only converts table names to lower case. I am aware that this code ALTER TABLE "YourTableName" RENAME TO YourTableName; will convert to case insensitive for a table name. Is there a way to do a similar function on mass for column names?
Along the same lines as the original, then, you should be able to do the following. This renames all columns that are not already in lower case, by extracting them from the information_schema, generating SQL for the changes, storing it to a file then executing the SQL again.
\t on
select 'ALTER TABLE '||'"'||table_name||'"'||' RENAME COLUMN '||'"'||column_name||'"'||' TO ' || lower(column_name)||';'
from information_schema.columns
where table_schema = 'public' and lower(column_name) != column_name
\g /tmp/go_to_lower
\i /tmp/go_to_lower
By default, all you identifiers are case insensitive, and internally PostgreSQL stores them in lowercase. In case you need to have:
case sensitive
non-ASCII characters
special characters
within your identifiers, you should use double quotes (") around your identifiers.
Please, check this bit of the PostgreSQL documentation.
EDIT: After your clarification, you can use:
SELECT 'ALTER TABLE '||quote_ident(t.relname)||' RENAME TO '||t.relname||';'
FROM pg_class t, pg_namespace s
WHERE s.oid = t.relnamespace AND s.nspname = 'public'
AND t.relkind='r' AND t.relname != lower(t.relname)
ORDER BY 1;
and for columns:
SELECT 'ALTER TABLE '||quote_ident(t.relname)||
' RENAME COLUMN '||quote_ident(a.attname)||
' TO '||a.attname||';'
FROM pg_class t, pg_namespace s, pg_attribute a
WHERE s.oid = t.relnamespace AND s.nspname = 'public'
AND t.relkind='r'
AND a.attrelid = t.oid AND NOT a.attisdropped AND a.attnum > 0
AND a.attname != lower(a.attname)
ORDER BY 1;
Then copy-paste the output into your client.
If you're using psql, you can use \t to enable rows-only mode, \o <full_file_path> to save output into the temporary file and, finally, \i <full_file_path> to execute actual statements.
I created a SQL query on Database Administrators that does just this.
Converts all identifiers to lower case
Converts spaces ' ' to '_'
Does this for all schema, table, and column names
For more information see,
How do I adopt the PostgreSQL naming convention in legacy database?
do language plpgsql $$
declare
r record;
begin
for r in
select relname, attname
from pg_attribute a
inner join pg_class c on a.attrelid = c.oid
inner join pg_namespace n on c.relnamespace = n.oid
where
n.nspname = 'public'
and
attname != lower(attname)
and
not attisdropped
loop
execute format('
alter table %1$I rename column %2$I to %3$s
', r.relname, r.attname, lower(r.attname));
end loop;
end;
$$;
Issue a begin; before trying this. Check if it is correct. Only then issue a commit;. If you are using a namespace then substitute it in the where clause.
Let me add a step by step guide using PgAdmin for beginners like myself and those who are not used to command line tools like psql:
Execute the following query in PgAdmin:
SELECT 'ALTER TABLE ' || quote_ident(c.table_schema) || '.'
|| quote_ident(c.table_name) || ' RENAME "' || c.column_name || '" TO ' || quote_ident(lower(c.column_name)) || ';' As ddlsql
FROM information_schema.columns As c
WHERE c.table_schema NOT IN('information_schema', 'pg_catalog')
AND c.column_name <> lower(c.column_name)
ORDER BY c.table_schema, c.table_name, c.column_name;
*Source: https://www.postgresonline.com/article_pfriendly/141.html. Note that you do not need to change anything here.
Now export the result to a textfile.
Open the .csv file with Excel, Notepad or any texteditor of your choice. Copy all lines except the first one ("ddlsql") and past them into a new query in PgAdmin. Make sure to remove doubled text quotations. Run it and done.
Related
I am trying to follow this guide from AWS docs: http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.html#Appendix.PostgreSQL.CommonDBATasks.PostGIS
On "Step 2: Load the PostGIS extensions", in the docs it shows (4 rows) but there are three rows there. Running the same commands myself up to that point, I see four rows, and the row that was missing from the docs is tiger_data. In Step 3, should ownership of tiger_data be given to rds_superuser as well?
In "Step 4: Transfer ownership of the objects to the rds_superuser role", I'm getting a syntax error from using the query provided in the docs and I don't know what to do about this:
postgres=> CREATE FUNCTION exec(text) returns text language plpgsql volatile AS $f$ BEGIN EXECUTE $1; RETURN $1; END; $f$;
CREATE FUNCTION
postgres=> SELECT exec('ALTER TABLE ' || quote_ident(s.nspname) || '.' || quote_ident(s.relname) || ' OWNER TO rds_superuser,')
postgres-> FROM (
postgres(> SELECT nspname, relname
postgres(> FROM pg_class c JOIN pg_namespace n ON (c.relnamespace = n.oid)
postgres(> WHERE nspname in ('tiger','topology') AND
postgres(> relkind IN ('r','S','v') ORDER BY relkind = 'S')
postgres-> s;
ERROR: syntax error at end of input
LINE 1: ALTER TABLE tiger.loader_variables OWNER TO rds_superuser,
^
QUERY: ALTER TABLE tiger.loader_variables OWNER TO rds_superuser,
CONTEXT: PL/pgSQL function exec(text) line 1 at EXECUTE statement
That looks to me like a typo in the docs - there is a , where there should be a ;. The query being constructed is:
ALTER TABLE tiger.loader_variables OWNER TO rds_superuser,
But should be:
ALTER TABLE tiger.loader_variables OWNER TO rds_superuser;
So change the SELECT line to:
SELECT exec('ALTER TABLE ' || quote_ident(s.nspname) || '.' || quote_ident(s.relname) || ' OWNER TO rds_superuser;')
FROM ...
I have few tables in my database. They all have the same columns (id, name) but differ in the table name. Those tables have names that start with letter 'h'.
Not a very interesting schema design but I have to follow it.
I need to search for id in all those tables.
I tried something similar to:
select id from (select table_name
FROM information_schema.tables
where table_name like 'h%') as t;
I got error:
ERROR: column "id" does not exist.
I understand the error now but I still do not know how to do the query?
You need dynamic SQL to do that since you cannot use values as identifiers in plain SQL. Write a PL/pgSQL function with EXECUTE:
CREATE FUNCTION f_all_tables()
RETURNS TABLE (id int) AS
$func$
DECLARE
_tbl regclass;
BEGIN
FOR _tbl IN
SELECT c.oid::regclass
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
AND c.relname LIKE 'h%'
AND n.nspname = 'public' -- your schema name here
LOOP
RETURN QUERY EXECUTE '
SELECT id FROM ' || _tbl;
END LOOP;
END
$func$ LANGUAGE plpgsql;
I am using a variable of the object identifier type regclass to prevent SQL injection effectively. More about that in this related answer:
Table name as a PostgreSQL function parameter
I want to loop through all my tables to count rows in each of them. The following query gets me an error:
DO $$
DECLARE
tables CURSOR FOR
SELECT tablename FROM pg_tables
WHERE tablename NOT LIKE 'pg_%'
ORDER BY tablename;
tablename varchar(100);
nbRow int;
BEGIN
FOR tablename IN tables LOOP
EXECUTE 'SELECT count(*) FROM ' || tablename INTO nbRow;
-- Do something with nbRow
END LOOP;
END$$;
Errors:
ERROR: syntax error at or near ")"
LINE 1: SELECT count(*) FROM (sql_features)
^
QUERY: SELECT count(*) FROM (sql_features)
CONTEXT: PL/pgSQL function inline_code_block line 8 at EXECUTE statement
sql_features is a table's name in my DB. I already tried to use quote_ident() but to no avail.
I can't remember the last time I actually needed to use an explicit cursor for looping in PL/pgSQL.
Use the implicit cursor of a FOR loop, that's much cleaner:
DO
$$
DECLARE
rec record;
nbrow bigint;
BEGIN
FOR rec IN
SELECT *
FROM pg_tables
WHERE tablename NOT LIKE 'pg\_%'
ORDER BY tablename
LOOP
EXECUTE 'SELECT count(*) FROM '
|| quote_ident(rec.schemaname) || '.'
|| quote_ident(rec.tablename)
INTO nbrow;
-- Do something with nbrow
END LOOP;
END
$$;
You need to include the schema name to make this work for all schemas (including those not in your search_path).
Also, you actually need to use quote_ident() or format() with %I or a regclass variable to safeguard against SQL injection. A table name can be almost anything inside double quotes. See:
Table name as a PostgreSQL function parameter
Minor detail: escape the underscore (_) in the LIKE pattern to make it a literal underscore: tablename NOT LIKE 'pg\_%'
How I might do it:
DO
$$
DECLARE
tbl regclass;
nbrow bigint;
BEGIN
FOR tbl IN
SELECT c.oid
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
AND n.nspname NOT LIKE 'pg\_%' -- system schema(s)
AND n.nspname <> 'information_schema' -- information schema
ORDER BY n.nspname, c.relname
LOOP
EXECUTE 'SELECT count(*) FROM ' || tbl INTO nbrow;
-- raise notice '%: % rows', tbl, nbrow;
END LOOP;
END
$$;
Query pg_catalog.pg_class instead of tablename, it provides the OID of the table.
The object identifier type regclass is handy to simplify. n particular, table names are double-quoted and schema-qualified where necessary automatically (also prevents SQL injection).
This query also excludes temporary tables (temp schema is named pg_temp% internally).
To only include tables from a given schema:
AND n.nspname = 'public' -- schema name here, case-sensitive
The cursor returns a record, not a scalar value, so "tablename" is not a string variable.
The concatenation turns the record into a string that looks like this (sql_features). If you had selected e.g. the schemaname with the tablename, the text representation of the record would have been (public,sql_features).
So you need to access the column inside the record to create your SQL statement:
DO $$
DECLARE
tables CURSOR FOR
SELECT tablename
FROM pg_tables
WHERE tablename NOT LIKE 'pg_%'
ORDER BY tablename;
nbRow int;
BEGIN
FOR table_record IN tables LOOP
EXECUTE 'SELECT count(*) FROM ' || table_record.tablename INTO nbRow;
-- Do something with nbRow
END LOOP;
END$$;
You might want to use WHERE schemaname = 'public' instead of not like 'pg_%' to exclude the Postgres system tables.
How I can Delete All User Defined Views From PostgreSQL using a Query?
Like we can delete All functions using query :
SELECT 'DROP FUNCTION ' || ns.nspname || '.' || proname
|| '(' || oidvectortypes(proargtypes) || ');'
FROM pg_proc INNER JOIN pg_namespace ns ON (pg_proc.pronamespace = ns.oid)
WHERE ns.nspname = 'my_messed_up_schema' order by proname;
Script for deleting all views in a certain schema:
SELECT 'DROP VIEW ' || t.oid::regclass || ';' -- CASCADE?
FROM pg_class t
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE t.relkind = 'v'
AND n.nspname = 'my_messed_up_schema -- select by schema(s)
ORDER BY 1;
The cast to regclass (t.oid::regclass) prevents SQLi, because otherwise illegal names are quoted automatically. You could also use quote_ident().
Your example is inherently unsafe.
Do it right away:
DO
$$
DECLARE
sql text;
BEGIN
SELECT INTO sql
string_agg('DROP VIEW ' || t.oid::regclass || ';', ' ') -- CASCADE?
FROM pg_class t
JOIN pg_namespace n ON n.oid = t.relnamespace
WHERE t.relkind = 'v'
AND n.nspname = 'my_messed_up_schema';
IF sql IS NOT NULL THEN
-- RAISE NOTICE '%', sql; -- to debug
EXECUTE sql;
ELSE
RAISE NOTICE 'No views found. Nothing dropped.';
END IF;
END
$$
DO requires PostgreSQL 9.0 or later.
The IF construct avoids an exception if no views are found.
If you have views referencing other views, you'll have to add the keyword CASCADE or drop views in their hierarchical order from top to bottom.
Always check what you are going to drop before you do it, or you might nuke yourself. If you are unsure, start a transaction, drop the bomb, check if all is good and then either commit or roll back.
BEGIN;
DO$$
...
$$;
-- check ..
ROLLBACK; -- if something wrong
COMMIT; -- else
Note that you cannot COMMIT or ROLLBACK inside the plpgsql block. Only outside.
Use table pg_class.
You need relkind = 'v'
It seems a pretty simple problem but I can't find an answer to it!
How can you delete views in bulk from the postgreSQL console? I have got 10,000 views that I made just to test something and now I can't get rid of them!
you can select the views from the meta tables, like this, (the actual select may differ if you use older version, see here e.g. http://www.alberton.info/postgresql_meta_info.html)
SELECT 'DROP VIEW ' || table_name || ';'
FROM information_schema.views
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
AND table_name !~ '^pg_';
So you fix this select according your actual version, run it, save the results into a .sql file, and run the .sql file.
I add this answer which references a comment by #greg which in turn references an answer, which seems to be gone, from a user named justbob. This comment helped me a lot but might easily be overlooked by others.
Although it doesn't answer samachs question, because his views were already made, it might help others like me who found this question after a google search for a way to delete all views in a bulk operation.
I have a few views which are dependent on another and are generated by an ever-evolving script. So there will be some views added or removed from the script. To limit the places where I have to make changes when adding or removing views I want to drop them all at the beginning of the script before I create them again.
The solution I use now is to create all views in a separate schema which holds nothing but the views.
At the beginning of my script, I have the following statements before creating my views:
DROP SCHEMA IF EXISTS dbview_schema CASCADE;
CREATE SCHEMA dbview_schema;
After that, I recreate all my views in their own schema:
CREATE VIEW dbview_schema.my_view AS
SELECT * FROM my_table
ORDER BY my_table.my_column;
Test this out and see if it works. I'm pulling this out of memory so there may be some syntax issues.
BEGIN TRANSACTION;
DO $$DECLARE r record;
DECLARE s TEXT;
BEGIN
FOR r IN select table_schema,table_name
from information_schema.views
where table_schema = 'public'
LOOP
s := 'DROP VIEW ' || quote_ident(r.table_schema) || '.' || quote_ident(r.table_name) || ';';
EXECUTE s;
RAISE NOTICE 's = % ',s;
END LOOP;
END$$;
ROLLBACK TRANSACTION;
When I drop particular views first I get a backup with the script below;
WITH dump_options AS (
-- feel free to add any dump options
SELECT '--file=/tmp/create_views.sql --host=... my_db_name' AS dump_options
)
SELECT
format(e'pg_dump --schema-only --format=plain \\\n%s \\\n%s',
string_agg(
format('--table="%s.%s"', v.schemaname, v.viewname),
e' \\\n'
),
d.dump_options
) create_views
FROM pg_views v, dump_options d
WHERE
v.schemaname NOT IN (
'pg_catalog',
'information_schema',
'any_other_schema'
) AND
v.viewname NOT IN (
'pg_stat_statements'
) AND
(v.schemaname, v.viewname) NOT IN (
('a_particular_schema', 'a_particular_view')
)
-- AND ...
GROUP BY d.dump_options
The script creates a dump command which will get backup of all views with owners and privileges when we run it;
pg_dump --schema-only --format=plain \
--table="a_schema.a_view" \
--table="another_schema.another_view" \
... \
--file=/tmp/create_views.sql --host=... my_db_name
Finally, I drop views with the command below (psql recommended);
SELECT
format('DROP VIEW IF EXISTS %I.%I;', v.schemaname, v.viewname) drop_views
FROM pg_views v
WHERE -- the very same where clause used above
v.schemaname NOT IN (
'pg_catalog',
'information_schema',
'any_other_schema'
) AND
v.viewname NOT IN (
'pg_stat_statements'
) AND
(v.schemaname, v.viewname) NOT IN (
('a_particular_schema', 'a_particular_view')
)
-- AND ...
; -- \gexec
DROP ALL VIEW (2022/03/12 version)
shorter version command:
SELECT 'DROP VIEW ' || string_agg (table_name, ', ') || ' cascade;'
FROM information_schema.views
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
AND table_name !~ '^pg_';
output
DROP VIEW v_mytable, v_my_view cascade;
DROP ALL MATERIALIZED VIEW
SELECT 'DROP MATERIALIZED VIEW ' || string_agg(oid::regclass::text, ', ') || ' cascade;'
FROM pg_class
WHERE relkind = 'm';