Требование параметров в предложении SQL где? - PullRequest
2 голосов
/ 22 октября 2010

Представьте, что у вас есть следующая таблица (примечание: это надуманный / упрощенный пример):

CREATE TABLE foo (    
  book_id number,
  page number,
  -- [a bunch of other columns describing a single page in a book]
);

ALTER TABLE foo
ADD (CONSTRAINT foo_pk PRIMARY KEY(book_id, page));

Хотя пары (book_id, page) уникальны, один и тот же номер страницы будет повторяться между книгами (у многих книг будет страница 1).Поэтому, если SQL-запрос не указывает book_id, неправильные страницы могут быть выбраны / обновлены / удалены.Все наши запросы должны обрабатывать только одну книгу за раз, но я видел пару ошибок, в которых параметр book_id был случайно пропущен.

Существует ли программный способ обеспечить, чтобы каждый запрос на выбор, вставку, обновление и т. Д. Указывал book_id в предложении where?

Мы генерируем код SQL длядинамически запрашивает и выполняет их, используя Spring JdbcTemplate.База данных Oracle.Использовать автоматические тесты для проверки того, что множество возможных запросов (плюс новые, которые будут добавлены в будущем!) Не будут отключены дублирующимися page_ids, сложно.Я мог бы переопределить код JdbcTemplate, чтобы гарантировать, что запросы sql всегда включают параметр book_id, но это включает ручной анализ кода SQL (особенно сложно с подзапросами) и кажется хакерским.Есть ли более надежное решение для обеспечения этого?Какой-то триггер, хранимая процедура, ограничение?

Ответы [ 4 ]

3 голосов
/ 22 октября 2010

Обычный способ защитить вашу базу данных от ошибок программиста - требовать, чтобы приложения использовали хранимые процедуры. (Иногда это можно сделать с помощью разрешений.)

Гораздо проще проверять ваши процессы на соответствие, чем специальные запросы.

2 голосов
/ 22 октября 2010

Вы можете использовать функцию или хранимую процедуру вместо прямого обновления.Процедура принимает 2 параметра и выдает ошибку, если любой из них равен нулю.

Другой вариант - убедиться, что генерируемые вами запросы всегда имеют ограничение book_id.Я надеюсь, что вы не создаете весь оператор SQL как строку и используете параметризованные запросы.Если это не так, то использование параметризованных запросов - это хороший способ убедиться, что вы всегда передаете book_id (если оставить параметр неустановленным, запрос не будет выполняться).Кроме того, вы не подвергаетесь риску, если не выполняете очистку входных данных при использовании параметризованных запросов.

1 голос
/ 22 октября 2010

Прежде всего, это действительно проблема тестирования - ошибки совершают не пользователи, а разработчики, и их ошибки должны быть зафиксированы за до запуска приложения.

Сказав это, вы можете перехватывать такие обновления с помощью комбинации триггеров:

  • Триггер BEFORE уровня оператора для инициализации переменной пакета g_book_id в null
  • Триггер уровня строки для (a) проверки того, что обновляемый book_id совпадает с этим в переменной пакета (если не ноль), и (b) инициализирует переменную пакета, если она равна нулю.

Простой пример:

SQL> create table t1 (id int, col2 int);

Table created.

SQL> insert into t1 values(1, null);

1 row created.

SQL> insert into t1 values(2, null);

1 row created.

SQL> create package p1 is g_id integer; end;
  2  /

Package created.

SQL> create trigger t1_bus
  2  before update on t1
  3  begin
  4    p1.g_id := null;
  5* end;
SQL> /

Trigger created.

SQL> create trigger t1_bir
  2  before update on t1
  3  for each row
  4  begin
  5     if :new.id != p1.g_id then
  6       raise_application_error(-20000,'You can only update 1 ID at a time');
  7     end if;
  8     p1.g_id := :new.id;
  9  end;
 10  /

Trigger created.

SQL> update t1 set col2=1 where id=1;

1 row updated.

SQL> update t1 set col2=2 where id=2;

1 row updated.

SQL> update t1 set col2=3; -- ID not specified
update t1 set col2=3
       *
ERROR at line 1:
ORA-20000: You can only update 1 ID at a time
0 голосов
/ 22 октября 2010

Единственный способ сделать это - заменить столбцы book_id и страницы в таблице одним столбцом, в котором хранятся обе части информации - что-то вроде (book_id * 10000 + page), если вы хотите в целочисленном столбце или book_id-page для строкового столбца.

Это плохая идея с точки зрения правильности (два атрибута хранятся в одном столбце), но заставит ваших программистов использовать оба атрибута для взаимодействия с таблицей. Если это достаточно большое беспокойство для вас, вы можете рассмотреть это.

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