4

Parameter table is initially created and one row is added in Postgres.

This table should have always one row, otherwise SQL queries using this table will produce incorrect results. DELETE or INSERT to this table are disallowed, only UPDATE is allowed.

How to add single row constraint to this table?
Maybe DELETE and INSERT triggers can raise an exception or is there simpler way?

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Andrus
  • 26,339
  • 60
  • 204
  • 378

3 Answers3

7

The following will create a table where you can only insert one single row. Any update of the id column will result in an error, as will any insert with a different value than 42. The actual id value doesn't matter actually (unless there is some special meaning that you need).

create table singleton
(
   id integer not null primary key default 42,
   parameter_1 text,
   parameter_2 text,
   constraint only_one_row check (id = 42)
);

insert into singleton values (default);

To prevent deletes you can use a rule:

create or replace rule ignore_delete
  AS on delete to singleton
     do instead nothing;

You could also use a rule to make insert do nothing as well if you want to make an insert "fail" silently. Without the rule, an insert would generate an error. If you want a delete to generate an error as well, you would need to create a trigger that simply raises an exception.


Edit

If you want an error to be thrown for inserts or deletes, you need a trigger for that:

create table singleton
(
   id integer not null primary key,
   parameter_1  text,
   parameter_2  text
);

insert into singleton (id) values (42);

create or replace function raise_error()
  returns trigger
as
$body$
begin
  RAISE EXCEPTION 'No changes allowed';
end;
$body$
language plpgsql;


create trigger singleton_trg 
  before insert or delete on singleton
  for each statement execute procedure raise_error();

Note that you have to insert the single row before you create the trigger, otherwise you can't insert that row.

This will only partially work for a superuser or the owner of the table. Both have the privilege to drop or disable the trigger. But that is the nature of a superuser - he can do anything.

  • Primary key can changed at runtime. Any delete and insert commands on this table should cause exception. How to change answer so that those conditions are met ? – Andrus Apr 03 '15 at 09:22
  • @Andrus: if there is only one row in the table then why would you ever want to change the value of the primary key? Given those requirements you would need an insert and delete trigger that simply raise an exception. –  Apr 03 '15 at 09:23
  • Company name is used as primary key. Company name can change. How to create such constraint triggers ? – Andrus Apr 03 '15 at 09:35
  • 1
    If you only have one company, why do you need to store its value? – David Aldridge Apr 03 '15 at 11:52
  • Well, there may be only one company per instance of the database, but there could be many instances of the database out there. But that said, why does company name have to be the primary key? If there's only one record, a primary key serves no useful purpose. When you read the table you don't need a WHERE clause. Without it you'll get the 1 record. And BTW, any sort of name usually makes a bad primary key. Too long, usually not guaranteed to be unique, and often subject to ambiguity in spelling, etc. – Jay Apr 03 '15 at 21:38
1

To make any table a singleton just add this column:

just_me bool NOT NULL DEFAULT TRUE UNIQUE CHECK (just_me)

This allows exactly one row. Plus add the trigger @a_horse provided.

But I would rather use a function instead of the table for this purpose. Simpler and cheaper.

CREATE OR REPLACE FUNCTION one_row()
  RETURNS TABLE (company_id int, company text) LANGUAGE sql IMMUTABLE AS
$$SELECT 123, 'The Company'$$
ALTER FUNCTION one_row() OWNER TO postgres;

Set the owner to the user that should be allowed to change it.

Nobody else change it - except superusers of course. Superusers can do anything.

You can use this function just like you would use the table:

SELECT * FROM one_row();

If you need a "table", create a view (which is actually a special table internally):

CREATE VIEW one_row AS SELECT * FROM one_row();
Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • I was thinking about a view that just does a `select 42, 'foo'` as well, but that contradicts the requirement that "updates should be allowed" –  Apr 03 '15 at 11:58
  • "Update" might be replaced with `CREATE OR REPLACE VIEW` or `CREATE OR REPLACE FUNCTION`. Maybe the OP just wasn't aware of the alternatives. – Erwin Brandstetter Apr 03 '15 at 14:42
0

I guess you will not use the PostgreSQL root user in your application so you could simply limit the permissions of your application user on UPDATE for this table.

An INSERT or DELETE will then cause an Insufficient privilege exception.

das Keks
  • 3,723
  • 5
  • 35
  • 57
  • This should for for superuser also. Any delete and insert commands on this table should cause exception in superuser also. How to implement this ? – Andrus Apr 03 '15 at 09:22