You can use a DO
statement to run a single dynamic command:
DO
$do$
BEGIN
RAISE NOTICE '%', (
-- EXECUTE (
SELECT string_agg(format('ALTER TABLE %s ALTER COLUMN %I TYPE text'
, a.attrelid::regclass, a.attname), E';\n')
FROM pg_attribute a
JOIN pg_class c ON c.oid = a.attrelid
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE a.attname = 'description'
AND a.atttypid = 'varchar'::regtype
AND NOT a.attisdropped -- no dropped columns
AND a.attnum > 0 -- no system columns (redundant check)
AND format_type(a.atttypid, a.atttypmod) = 'character varying(255)'
AND n.nspname NOT LIKE ALL ('{pg_%, information_schema}'::text[])
);
END
$do$;
Since the command is potentially hazardous I commented the EXECUTE
and put a RAISE NOTICE
there instead. After confirming the commands are sane, switch the comment characters --
to actually execute the DDL commands.
This changes the type for all columns description varchar(255)
, except for those in system catalogs, temporary tables (both starting with 'pg_') and the information schema. I build command from the system catalogs. @a_horse demonstrates the other good option@a_horse demonstrates the other good option to use the information schema instead.
In your particular case, there can only be one column per table. If there can be multiple, it would be substantially cheaper to execute all type changes in a single ALTER TABLE
statement per table. Commands of the form:
ALTER TABLE foo
ALTER COLUMN col1 TYPE text
, ALTER COLUMN col2 TYPE text
, ALTER COLUMN col3 TYPE text;
More details in this related answer:
Dynamic UPDATE fails due to unwanted parenthesis around string in plpgsqlDynamic UPDATE fails due to unwanted parenthesis around string in plpgsql