Given this table and data:
test# create table t (c int);
CREATE TABLE
test# insert into t (c) values (1), (3), (2);
INSERT 0 3
If I try to query the table with an invalid order by expression in a statement I get an error:
test# select c from t order by 'c' desc;
ERROR: non-integer constant in ORDER BY
LINE 1: select c from t order by 'c' desc;
which is logged:
2024-02-14 11:26:31.734 GMT test user [5047]LOG: statement: select c from t order by 'c' desc;
2024-02-14 11:26:31.734 GMT test user [5047]ERROR: non-integer constant in ORDER BY at character 26
2024-02-14 11:26:31.734 GMT test user [5047]STATEMENT: select c from t order by 'c' desc;
However if I bind the invalid value to a prepared statement, there is no error and the rows are returned unordered:
test# prepare q (varchar) as select c from t order by $1;
PREPARE
test# execute q ('c');
c
═══
1
3
2
(3 rows)
No error is logged:
2024-02-14 15:18:17.626 GMT test user [5047]LOG: statement: prepare q (varchar) as select c from t order by $1;
2024-02-14 15:18:29.834 GMT test user [5047]LOG: statement: execute q ('c');
2024-02-14 15:18:29.834 GMT test user [5047]DETAIL: prepare: prepare q (varchar) as select c from t order by $1;
Using the bind
meta-command likewise does not error and produces the same result:
test# select c from t order by $1 desc bind 'c' g
c
═══
1
3
2
(3 rows)
Logs:
2024-02-14 15:22:31.123 GMT test user [5047]LOG: execute <unnamed>: select c from t order by $1 desc
2024-02-14 15:22:31.123 GMT test user [5047]DETAIL: parameters: $1 = 'c'
Why doesn’t the prepared statement fail with the same error as in the first statement?
Background context: I noticed in this Python psycopg3 question, originally tagged as psycopg2*, the statement cursor.execute("SELECT * FROM cars ORDER BY %s DESC;", (k,))
raises the above error when executed by psycopg2
, which binds parameters client-side, but succeeds, returning an unordered response, using psycopg3
, which lets the server perform the binding.
* psycopg2
and psycopg3
are Python connection packages (drivers) for PostgreSQL. Both are wrappers around libpq
as I understand it.
2
Answers
The parameter should not be used as column name or table name ever. If you want to do it, you should to use dynamic SQL. The clause
ORDER BY
is difficult, because (naturally) supports column names, but it can supports any expressions too – but the order is based on the result on expression, and result of expression cannot be used as name of sorted column.ORDER BY
supports integer values there, and then it means sort by column on position.You can check
EXPLAIN
of prepared statement:There is not
sort
node.The reason, why the error is not raised in this case is probably effect of some heuristic for that separates processing for constants and expressions. In this concept the constant is some different from immutable expression. I think it depends on implementation of SQL parser (based on bison). When you use
$1
– query parameter, it is expression (it is allowed), but because it is immutable expression, then it is ignored.Order by clause need
sort_expression
.A
sort_expression
can also be the column label or number of an output column.Some examples:
In this query,
c
is output column name, output sorted by column cIn this query,
1
is number of output column, output sorted by output column 1In this query,
'c'
not is output column name, therefore must be number of output columnNext example.
'c'||'a'
is sort_expression. There no error and, no ordering 🙂 because expression is constant.There
2-1
issort_expression
For prepared statement
$1
isonly sort_expression
, not column name, not output column number. By design, this isconstant
expression – no sortingFor conditional sorting you can