I have the following table
CREATE TABLE test(id bigint primary key, path varchar);
with the additional index on path column
CREATE INDEX test_path_idx ON test(path varchar_pattern_ops);
With the query like this
SELECT * FROM test WHERE path LIKE 'prefix%';
the index on path is used. It's still used with
SELECT * FROM test WHERE path LIKE 'prefix' || '%';
Index Only Scan using test_path_idx on test (cost=0.41..8.43 rows=1 width=511)
Index Cond: ((path ~>=~ 'prefix'::text) AND (path ~<~ 'prefiy'::text))
Filter: ((path)::text ~~ 'prefix%'::text)
However, if the prefix is a result of a subquery
SELECT * FROM test WHERE path LIKE (SELECT 'prefix')::varchar || '%';
Seq Scan on test (cost=0.01..359.93 rows=20 width=511)
Filter: ((path)::text ~~ ((($0)::character varying)::text || '%'::text))
InitPlan 1 (returns $0)
-> Result (cost=0.00..0.01 rows=1 width=32)
the index is not used. Of course, in reality the subquery is more complicated but the above suffices to make the optimizer "ignore" the index.
I assume it happens because the optimizer cannot be sure there are no wildcards in the subquery result so it takes the "worst case" path. Is there a way to optimize this? Unfortunately, executing the inner select in a separate query and plugging in the result is not an option.
P.S. I have tested this on 9.6.