Оператор предложения WHERE, основанный на значении столбца - PullRequest
0 голосов
/ 03 октября 2018

Я хотел бы иметь таблицу, которая содержит значения для сравнения с используемыми операторами и (=, !=, ~, !~ и т. Д.).Например:

CREATE TABLE rule (
    value1 varchar NOT NULL,
    op1 varchar NOT NULL,
    value2 varchar NOT NULL,
    op2 varchar NOT NULL,
    ...
);

То, что я хочу, можно описать с помощью этого псевдокода:

SELECT * FROM rule WHERE value1 op1 ?;

В этом примере я сохранил оператор в отдельном столбце, но яоткрыт для других решений.

Ответы [ 3 ]

0 голосов
/ 03 октября 2018

Вы можете попытаться использовать OR и AND в состоянии.

SELECT * 
FROM rule 
WHERE 
    (op1 = '=' AND value1 = ?) 
OR
    (op1 = '!=' AND value1 != ?)
0 голосов
/ 03 октября 2018

SQL - это статический язык, который не позволяет параметризировать что-либо, кроме значений .Вам потребуется некоторая форма динамического SQL - которая сгорает до конкатенации строки запроса, которая выполняется по очереди.

Вы можете сделать весь оператор SELECT динамическим с помощью сервера-боковая функция PL / pgSQL.Или с любой клиентской логикой, которая влечет за собой дополнительную обратную передачу на сервер.

Или просто сделайте оценку динамической, инкапсулировав эту часть в функцию:

CREATE TABLE the_rule (
   value1 text NOT NULL
 , op1    text NOT NULL
 , value2 text NOT NULL
 , op2    text NOT NULL
);

INSERT INTO the_rule VALUES
   ('foo','=','bar','<')
 , ('baz','<','bam','>');


CREATE FUNCTION rule_eval(_val text, _opr text, _arg text
                        , OUT _pass bool) AS
$func$
BEGIN
   EXECUTE format('SELECT %L %s %L', _val, _opr, _arg)
   INTO _pass;
END
$func$  LANGUAGE plpgsql;

SELECT * FROM the_rule
WHERE  rule_eval(value1, op1, 'foo')
AND    rule_eval(value2, op2, 'aaa');

db <> fiddle здесь

Однако , этот вид запутывания в значительной степени запрещает оптимизированные для исполнения планы выполнения.Такие функции являются черными ящиками для планировщика запросов Postgres, например, индексы использовать нельзя.

И вы открыты для SQL-инъекции ._val и _arg правильно указаны в приведенном выше примере, что делает внедрение SQL невозможным.Но оператор нельзя заключать в кавычки.Вы можете использовать тип идентификатора объекта regoperator, чтобы гарантировать действительные операторы - и привести к regoper и объединиться с конструкцией OPERATOR(), чтобы получить действительныйсинтаксис.Например:

CREATE TABLE the_rule (
   value1 text NOT NULL
 , op1    regoperator NOT NULL
 , value2 text NOT NULL
 , op2    regoperator NOT NULL
);

INSERT INTO the_rule VALUES
   ('foo', '=(text,text)', 'bar', '<(text,text)')
 , ('baz', '<(text,text)', 'bam', '>(text,text)');

CREATE FUNCTION rule_eval(_val text, _opr regoperator, _arg text
                        , OUT _pass bool) AS
$func$
BEGIN
   EXECUTE format('SELECT %L OPERATOR(%s) %L', _val, _opr::regoper, _arg)
   INTO _pass;
END
$func$  LANGUAGE plpgsql;

-- Same query as above

db <> fiddle здесь

Теперь внедрение SQL невозможно.Но мы ввели еще больше сложности.И я не уверен, что reoperator остается действительным в течение циклов дампа / восстановления или обновлений основных версий.(Вероятно, лучше все-таки сохранить представление text.)

или , если вы разрешаете только предопределенный набор операторов - с ограничением FK для таблицы безопасного просмотра или enum Введите или просто CHECK ограничения для руки, полной разрешенных операторов.Например:

CREATE TABLE the_rule (
   value1 text NOT NULL
 , op1    text NOT NULL CHECK (op1 = ANY ('{>,>=,=,<=,<}'))
 , value2 text NOT NULL
 , op2    text NOT NULL CHECK (op2 = ANY ('{>,>=,=,<=,<}'))
);

INSERT INTO the_rule VALUES
   ('foo', '=', 'bar', '<')
 , ('baz', '<', 'bam', '>');

CREATE FUNCTION rule_eval(_val text, _opr text, _arg text
                        , OUT _pass bool) AS
$func$
BEGIN
   EXECUTE format('SELECT %L %s %L', _val, _opr, _arg)
   INTO _pass;
END
$func$  LANGUAGE plpgsql;

SELECT * FROM the_rule
WHERE  rule_eval(value1, op1, 'foo')
AND    rule_eval(value2, op2, 'aaa');

db <> fiddle здесь

Ввод из таблицы безопасен, но сама функция является точкой входа для SQLИнъекция сейчас.

И мы даже не коснулись осложнений с другими данными типов , пока.

Короче: возможно , но вам нужноточно знать, что вы делаете, чтобы справиться с различными видами возможных осложнений. Как правило, существует более простой способ реализации ваших требований.

0 голосов
/ 03 октября 2018

Вот явная логика для решения этого:

where (op1 = '=' and value1 = ?) or
      (op1 = '<' and value < ?) or
      . . .
...