5

I have three tables -

file (
  file_id int primary key
  filename text not null
  etc...
)
product (
  product_id int primary key
  etc....
)
product_attachment (
  product_id references product
  file_id references file
)

I want to ensure that when these are natural-joined, product_id + filename is unique. The best solution I have so far involves adding filename to the product_attachment table, but I'm wondering if there's a way to avoid that.

2 Answers 2

11

If the filename column is not unique you can add a custom constraint on your product_attachment table. Note that this will execute the query below on every insert and update, which is not ideal performance wise.

CREATE OR REPLACE FUNCTION check_filename(product_id integer, file_id integer)
RETURNS boolean AS
$$
    LOCK product_attachment IN SHARE MODE;
    SELECT (COUNT(*) = 0)
    FROM product_attachment pa
    JOIN file f1 ON f1.file_id = pa.file_id
    JOIN file f2 ON f1.filename = f2.filename
    WHERE pa.product_id = $1 AND f2.file_id = $2
$$
LANGUAGE 'plpgsql'

ALTER TABLE product_attachment
ADD CONSTRAINT check_filename CHECK
(check_filename(product_id, file_id))
Sign up to request clarification or add additional context in comments.

7 Comments

I'm not clear why you have a "for update" there, and I think the test should be count(*) = 0 (or using a sub-query with "exists" might yield better performance), but this is exactly the approach I was looking for, thank you!
@qcodepeter you're right, it should be count(*) = 0, for update locks the selected rows in order to prevent a race condition
If I understand correctly, the query will either lock and return 0 rows - in which case the lock does nothing - or the constraint will fail - in which case the lock does nothing. Maybe adding a "lock product_attachment in share mode" to the start of the query will do the trick?
@qcodepeter if i understood the docs, for update acquires a table level lock but it's probably clearer and safer to explicitly lock the table as you've mentioned. btw if it's possible to update filename you may need a similar constraint on your file table
you're right to say that it acquires a table level lock, but it's only a row share lock, which I don't think will be enough. As it happens, we aren't currently allowing filename updates, so I'm not going to worry about that, but yes, that would otherwise be a consideration.
|
0

Why not just add a unique constraint to product_attachment?

create unique index idx_product_attachment_2 on product_attachment(product_id, file_id);

This assumes that the file name is unique, which you can ensure by defining the file name to be unique in that table.

1 Comment

File name is not unique across the entire file table, unfortunately that's a requirement of the system.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.