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Инъекция сейчас.
И мы даже не коснулись осложнений с другими данными типов , пока.
Короче: возможно , но вам нужноточно знать, что вы делаете, чтобы справиться с различными видами возможных осложнений. Как правило, существует более простой способ реализации ваших требований.