I have a Postgresql table:
CREATE TABLE IF NOT EXISTS acls1k (
pkey serial PRIMARY KEY,
user_name VARCHAR(50),
tenant_id VARCHAR(36),
CONSTRAINT user_name_unique1k UNIQUE (user_name)
);
There is a unique index on the user_name column. When I query the table using a constant, the index is used for the query:
explain analyze select * from acls1k where user_name = 'p1kuser1t1';
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
Index Scan using user_name_unique1k on acls1k (cost=0.28..8.29 rows=1 width=53) (actual time=0.071..0.073 rows=1 loops=1)
Index Cond: ((user_name)::text = 'p1kuser1t1'::text)
Planning Time: 0.240 ms
Execution Time: 0.094 ms
(4 rows)
but when I use the current_user variable a sequential scan is performed instead of index scan:
explain analyze select * from acls1k where user_name = current_user;
QUERY PLAN
--------------------------------------------------------------------------------------------------
Seq Scan on acls1k (cost=0.00..59.00 rows=1 width=53) (actual time=0.162..0.845 rows=1 loops=1)
Filter: ((user_name)::text = CURRENT_USER)
Rows Removed by Filter: 999
Planning Time: 0.097 ms
Execution Time: 0.861 ms
(5 rows)
Tried casting:
explain analyze select * from acls1k where user_name = CAST(current_user as varchar(50));
explain analyze select * from acls1k where user_name = CAST(current_user as text);
explain analyze select * from acls1k where user_name = current_user::text;
But still, a sequential scan is used, not really sure why I see this behavior, any way to make this query use an index scan?
Edit:
Can anyone answer why inline casting from name datatype to varchar fails?
@jimjonesbr gave an answer on how to use an index scan using prepared statements. I speculate the problem is with the query planner not handling the datatype conversion properly.
I have tried to use a function instead of a prepared statement but was unable to run explain analyze to check index scan is performed. what I was able to notice is that runtime is faster when passing current_user as a varchar parameter instead of using it inline which might suggest index scan is used.
create function get_acl(auser varchar(63))
returns varchar(36)
language plpgsql
as
$$
begin
return (select tenant_id from acls1k where user_name = auser);
end;
$$;
create function get_acl_inline()
returns varchar(36)
language plpgsql
as
$$
begin
return (select tenant_id from acls1k where user_name = current_user);
end;
and running with explain analyze produce this:
explain analyze select from get_acl(current_user::text);
QUERY PLAN
------------------------------------------------------------------------------------------------------
Function Scan on get_acl (cost=0.26..0.27 rows=1 width=0) (actual time=0.435..0.436 rows=1 loops=1)
Planning Time: 0.027 ms
Execution Time: 0.456 ms
(3 rows)
explain analyze select from get_acl_inline();
QUERY PLAN
-------------------------------------------------------------------------------------------------------------
Function Scan on get_acl_inline (cost=0.25..0.26 rows=1 width=0) (actual time=1.833..1.834 rows=1 loops=1)
Planning Time: 0.024 ms
Execution Time: 1.850 ms
the time difference might suggest index scan is preform in get_acls(current_user::text)