patternsqlMinor
Evaluate comparison operator from table column
Viewed 0 times
operatorcolumncomparisonevaluatefromtable
Problem
Is it possible to expand a field value into a comparison operator? Something like this:
PS: I know that it is possible to solve this use case by means of
create table math (
value1 int,
value2 int,
operator text
);
insert into math values(1,2,'>=');
select * from math where value1 operator value2;PS: I know that it is possible to solve this use case by means of
case when, but want to know if there is an alternative solution.Solution
Yes, possible. You need dynamic SQL to evaluate the expression.
Unsafe
A simple, naive approach. A bit simpler and faster than the safe approach.
This is open to SQL injection. If your table source is safe, it's good enough. Is a table source ever safe, though?
Safe
I suggest this safe approach instead:
Call for your example:
db<>fiddle here
The cast to the object identifier type
See:
Unsafe
A simple, naive approach. A bit simpler and faster than the safe approach.
CREATE OR REPLACE FUNCTION eval_unsafe(_operand1 int, _operand2 int, _operator text, OUT _result bool)
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('SELECT $1 %s $2', _operator)
USING _operand1, _operand2
INTO _result;
END
$func$;This is open to SQL injection. If your table source is safe, it's good enough. Is a table source ever safe, though?
Safe
I suggest this safe approach instead:
CREATE OR REPLACE FUNCTION eval_safe(_operand1 int, _operand2 int, _operator text, OUT _result bool)
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('SELECT $1 OPERATOR (%s) $2', (_operator || '(integer, integer)')::regoperator::regoper)
USING _operand1, _operand2
INTO _result;
END
$func$;Call for your example:
SELECT *
FROM math m
WHERE eval_safe(m.value1, m.value2, m.operator);db<>fiddle here
The cast to the object identifier type
pg_operator enforces a valid operator (effectively checks against valid entries in the system catalog pg_operator). The next cast to pg_oper is just a convenient way to get valid format for the OPERATOR construct.See:
- SQL injection in Postgres functions vs prepared queries
Code Snippets
CREATE OR REPLACE FUNCTION eval_unsafe(_operand1 int, _operand2 int, _operator text, OUT _result bool)
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('SELECT $1 %s $2', _operator)
USING _operand1, _operand2
INTO _result;
END
$func$;CREATE OR REPLACE FUNCTION eval_safe(_operand1 int, _operand2 int, _operator text, OUT _result bool)
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('SELECT $1 OPERATOR (%s) $2', (_operator || '(integer, integer)')::regoperator::regoper)
USING _operand1, _operand2
INTO _result;
END
$func$;SELECT *
FROM math m
WHERE eval_safe(m.value1, m.value2, m.operator);Context
StackExchange Database Administrators Q#305382, answer score: 3
Revisions (0)
No revisions yet.