我有一张桌子:
CREATE EXTENSION btree_gist;
CREATE TABLE excllock (
id BIGSERIAL PRIMARY KEY,
myrange INT8RANGE NOT NULL,
key UUID NOT NULL,
isread BOOLEAN NOT NULL,
EXCLUDE USING gist (
myrange WITH &&,
key WITH =
)
);
我希望它有这样的行为:
myrange
上重叠 key
的多行,只要它们全部都是 isread
。isread=FALSE
的新行,则会发生以下三种情况之一:
isread
行,因此约束应该失败(并发读取访问已经存在,无法写入)。isread=FALSE
行,因此约束失败(并发写访问已经存在,无法写入)。因此实际上一次只能有多个
isread=TRUE
或一个 isread=FALSE
(或者根本没有匹配 (key, myrange)
的记录),本质上是在键范围对上创建一个读/写锁。
示例:
-- Insert multiple read locks (allowed)
INSERT INTO excllock (myrange, key, isread) VALUES ('[1,10)', '00000000-0000-0000-0000-000000000001', TRUE);
INSERT INTO excllock (myrange, key, isread) VALUES ('[5,15)', '00000000-0000-0000-0000-000000000001', TRUE);
-- Try inserting a write lock (should fail due to read locks)
INSERT INTO excllock (myrange, key, isread) VALUES ('[1,10)', '00000000-0000-0000-0000-000000000001', FALSE);
-- Insert a write lock on a free range
INSERT INTO excllock (myrange, key, isread) VALUES ('[100,1000)', '00000000-0000-0000-0000-000000000001', FALSE);
-- Try to insert a read lock inside the write range (should fail)
INSERT INTO excllock (myrange, key, isread) VALUES ('[105,900)', '00000000-0000-0000-0000-000000000001', TRUE);
我想过使用
where (NOT isread)
,但它会完全忽略isread
行。
有没有一种巧妙的方法来创建这种约束?
这可以通过在排除约束中添加一个附加参数来表示锁的读/写状态来实现。我已经对一个读锁进行了建模,其范围仅包含该行的 id,保证该行的唯一性并且永远不会与任何其他行的 id 重叠。然后,我使用无限范围来表示写锁,该写锁将与所有其他范围重叠,无论它们表示读锁还是写锁。
CREATE TABLE excllock (
id BIGSERIAL PRIMARY KEY,
myrange INT8RANGE NOT NULL,
key UUID NOT NULL,
isread BOOLEAN NOT NULL,
EXCLUDE USING gist (
myrange WITH &&,
key WITH =,
(CASE WHEN isread THEN int8range(id, id, '[]')
ELSE int8range(null, null) END) WITH &&
)
);