强制执行数据库约束:代码vs sql

时间:2009-02-04 15:46:56

标签: php sql postgresql

这是this question的后续行动。

这是我的架构

CREATE TABLE A(
     id serial NOT NULL,
     date timestamp without time zone,
     type text,
     sub_type text,
     filename text,
     filepath text,
     filesize integer,
     lock_status int
 );

在此数据库中,只要未设置“lock_status”,用户就可以更新类型,子类型,文件名,文件路径,文件大小。

因此,在网页代码(php)中,我可以在更新项目之前检查lock_status。

然而,可能存在另一个用户在第一个用户的检查和时间之间的时间内更新了锁定状态的情况。更新。

那么,在更新行之前,SQL是否有办法检查锁定状态?

  • 网页代码在php
  • 数据库是PostgreSQL

编辑在上面的可编辑字段列表中添加了类型,子类型

5 个答案:

答案 0 :(得分:5)

当然,请使用UPDATE ... WHERE lock_status = 0。或者,您可以尝试使用stored procedures

答案 1 :(得分:1)

正如Potato Head先生所说,最简洁的方法是简单地使用WHERE子句来仅影响lock_status = 0的行。因为这是一个单独的SQL语句,所以它保证是原子的。然后你可以看到是否有任何行受到影响(例如,使用@@ rowcount)并做出相应的反应,无论是通过无限期再次尝试,还是通过显示错误消息等等。

分两步检查和更新的问题是,除非将它们包装在显式事务中(例如“BEGIN TRANSACTION ... COMMIT TRANSACTION”),否则它们保证是原子的,所以理论上你可以得到多个进程,他们认为锁已关闭并继续进行。我说“理论上”,因为随着这些语句执行的速度,除非你有一个巨大的并发环境,同时有大量(数千?)用户敲击这个东西,否则它几乎不可能发生。这就是为什么像这样的错误经常被忽视,但随后会出现奇怪的无法解释的错误。

要了解有关此类问题的更多信息,您可能需要阅读有关并发编程的书籍,例如:Concurrent Programming by Gregory Andrews

答案 2 :(得分:0)

在数据库端只有一个带有触发器和PL / Pgsql函数的示例过程:

CREATE OR REPLACE FUNCTION trgfn_ensure_unlocked() RETURNS TRIGGER AS $trig$
  DECLARE
  BEGIN
    IF (OLD.lock_status <> 0) THEN
      RAISE EXCEPTION 'Row is locked';
    END IF;
    RETURN NEW;
  END;
$trig$ LANGUAGE plpgsql;

CREATE TRIGGER trg_check_unlocked BEFORE UPDATE ON table_name
      FOR EACH ROW EXECUTE PROCEDURE trgfn_ensure_unlocked();

基本上,你是一个plpgsql函数,它将检查在行的旧版本(更新前)版本中,lock_status为0.否则会抛出异常。

此函数由触发器调用,在此表上执行SQL UPDATE时将自动调用该函数。

希望这会有所帮助......

答案 3 :(得分:0)

您可能希望查看行级锁定。看起来Postgres有它。

答案 4 :(得分:0)

我建议同时检查并设置lock_status位。

发出UPDATE A SET lock_status = 1 WHERE id = ... AND lock_status = 0。该查询是原子的,无需显式事务。如果没有返回1个对象更新的计数,则无法应用锁定。然后,您只需确认您的主键仍然存在。如果您要从多个位置和/或多个表调用它,可能需要考虑将其移动到存储过程。

伪PHP:

$result = pg_query_params($conn, "UPDATE A SET lock_status = 1 WHERE id = $1 AND lock_status = 0", $id);
$tuples = pg_affected_rows($result);

if ($tuples < 1) {
    // couldn't lock
} else {
    // lock applied
}