применение ограничений базы данных: код против sql - PullRequest
0 голосов
/ 04 февраля 2009

Это продолжение до этого вопроса .

Вот моя схема

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

edit добавлен тип, подтип в списке редактируемых полей выше

Ответы [ 5 ]

5 голосов
/ 04 февраля 2009

Конечно, используйте UPDATE ... WHERE lock_status = 0. В качестве альтернативы вы можете попробовать использовать хранимые процедуры .

1 голос
/ 04 февраля 2009

Самый чистый способ, как сказал г-н Потато Хэд, состоит в том, чтобы просто использовать предложение WHERE, чтобы воздействовать только на строки, где lock_status = 0. Поскольку это один оператор SQL, он гарантированно будет атомарным. Затем вы можете увидеть, были ли затронуты какие-либо строки (например, с помощью @@ rowcount), и соответственно отреагировать, либо повторив попытку бесконечно, либо показав сообщение об ошибке и т. Д.

Проблема с проверкой и обновлением в два этапа заключается в том, что если вы не заключите их в явную транзакцию (например, «BEGIN TRANSACTION ... COMMIT TRANSACTION»), они не гарантированно будут атомарными, поэтому теоретически вы можете получить более одного процесса, который думает, что блокировка отключена и продолжается. Я говорю «теоретически», потому что с той скоростью, с которой выполняются эти операторы, невероятно маловероятно, что это когда-либо произойдет, если у вас не будет чрезвычайно параллельной среды с сотней (тысяч?) Пользователей, одновременно стучащих в эту штуку. Вот почему ошибки, подобные этой, часто остаются незамеченными, но позже возникают неожиданные необъяснимые ошибки.

Чтобы узнать больше об этой проблеме, вы можете прочитать книгу о параллельном программировании, такую ​​как эта: Параллельное программирование Грегори Эндрюса .

0 голосов
/ 04 февраля 2009

только один пример процедуры на стороне базы данных с триггером и функцией 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 для этой таблицы.

Надеюсь, это поможет ...

0 голосов
/ 04 февраля 2009

Я бы рекомендовал проверить и установить бит 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
}
0 голосов
/ 04 февраля 2009

Возможно, вы захотите изучить блокировку на уровне строк. Похоже, Postgres есть.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...