0

I use Postgres 14. I know about ALTER TABLE DROP COLUMN. But this option doesn't really work in my case.

Are these two queries equal:

ALTER TABLE <some_table_1>
    DROP COLUMN IF EXISTS <column_1>,
    DROP COLUMN IF EXISTS <column_2>;
ALTER TABLE <some_table_2>
    DROP COLUMN IF EXISTS <column_1>,
    DROP COLUMN IF EXISTS <column_2>;

And

DELETE FROM information_schema.columns
WHERE table_name IN (<some_table_1>, <some_table_2>)
AND column_name IN (<column_1>, <column_2>)

Or ALTER TABLE does some extra work? I want to use DELETE FROM because I really need filters like WHERE in columns deletion.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Prosto_Oleg
  • 322
  • 3
  • 13
  • What happens when you try it? – Bohemian May 03 '23 at 00:26
  • I have error `Views that do not select from a single table or view are not automatically updatable.` – Prosto_Oleg May 03 '23 at 00:28
  • Right, so your question is moot; it's impossible to execute the code you're asking about the effects of. Besides, you should never even think about hacking the catalog tables. Use the API provided for the task - ie the `alter` statement. – Bohemian May 03 '23 at 00:40
  • What kind of filters do you need for dropping columns? Instead of just declaring that a specific option doesn't work for your needs, make an effort to explain the requirements and the reasons why the option is unacceptable. Had the `DELETE`statement been executable, it would have been functionally identical to the pair of `ALTER TABLE` statements, so nothing in the question demonstrates a rational for rejecting the `ALTER TABLE` statements. – JohnH May 03 '23 at 01:33

1 Answers1

4

You cannot DELETE rows from any view in the information schema. That would be nonsense for multiple reasons. For the record, views in the information schema are not part of the system catalogs. But you don't mess with system catalogs directly, either - even if that might be possible. One false move and you can break your database (cluster). Use dedicated DDL commands.

You are looking for dynamic SQL - which can be based on either: information schema or system catalogs. Each has pros and cons. See:

My implementation uses system catalogs:

CREATE OR REPLACE PROCEDURE public.my_column_drop(_tbls text[]
                                                , _cols text[]
                                                , _schema text = 'public')
  LANGUAGE plpgsql AS
$proc$
DECLARE
   _tbl regclass;
   _drops text;
BEGIN
   FOR _tbl IN
      SELECT c.oid
      FROM   pg_catalog.pg_class c
      WHERE  c.relkind = 'r'  -- only plain tables (?)
      AND    c.relnamespace = _schema::regnamespace
      AND    c.relname = ANY (_tbls)
   -- more filters HERE
   LOOP
   -- RAISE NOTICE '%', _tbl;
      SELECT INTO _drops
             string_agg(format('DROP COLUMN IF EXISTS %I', a.attname), ', ')
      FROM   pg_catalog.pg_attribute a
      WHERE  a.attrelid = _tbl
      AND    a.attname = ANY (_cols)
      AND    NOT a.attisdropped
      AND    a.attnum > 0
   -- more filters HERE
      ;

      IF _drops IS NOT NULL THEN
      -- RAISE NOTICE '%',  concat_ws(' ', 'ALTER TABLE', _tbl, _drops);
         EXECUTE concat_ws(' ', 'ALTER TABLE', _tbl, _drops);
      ELSE
         RAISE NOTICE 'Table % has no candidate columns', _tbl;
      END IF;
   END LOOP;
END
$proc$;

Call:

CALL public.my_column_drop ('{some_table_1,some_table_2}', '{column_1, column_2}');

The attempt to drop columns fails if there are any dependencies. My simple function does not check for those. You would have to define what to check for, and what to do in case of dependencies ...

Procedures were added with Postgres 11. You can do the same with a function in older versions. Or with a DO command for one-off use in any version. See:

Basics for this kind of dynamic SQL:

Adding IF EXISTS seems like overkill after we checked that the column exists. Only makes sense if multiple transactions might manipulate columns concurrently, which seems like an extremely odd case.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228