Question

I'll try to inject and array of ids to a complex select query

CREATE FUNCTION permission_cache_update(
  IN affected_user_list INT4[]
)
  RETURNS TABLE(user_id INT4, permission_id INT4)
AS
  $BODY$
  BEGIN
    RETURN QUERY
    WITH
    affected_user AS (
      SELECT unnest(affected_user_list) AS user_id
    ), 
    permission_summary AS
    (
      SELECT affected_user.user_id, role_permission.permission_id
      FROM user_role, role_permission, affected_user
      WHERE role_permission.role_id = user_role.role_id AND user_role.user_id = affected_user.user_id
      UNION
      SELECT affected_user.user_id, user_permission.permission_id
      FROM user_permission, affected_user
      WHERE user_permission.user_id = affected_user.user_id
    )
    SELECT user_id, permission_id
    FROM permission_summary
    GROUP BY user_id, permission_id;
  END;
  $BODY$
LANGUAGE plpgsql VOLATILE;

The problem is with this part:

    affected_user AS (
      SELECT unnest(affected_user_list) AS user_id
    ), 

But I don't know how to fix it. The error message is meaningless.

[42601] ERROR: syntax error at or near "$2"

I want to use the user_id, permission_id pairs to update the permission cache which contains the same values. So it will be faster to ask the user permissions because they will be cached in there. I want to use the db for storing and checking session permissions. I don't know whether it is a wise thing or not ...

Was it helpful?

Solution

I derive from your previous question that you are using PostgreSQL 8.4, which is pretty outdated by now.
You need to provide this information with every question!

In your function header you declare

RETURNS TABLE(user_id INT4, permission_id INT4)

The OUT parameters user_id and permission_id are visible everywhere in the function body in PL/pgSQL. To avoid naming conflicts you need to table-qualify columns of the same name. Your function would not work properly with modern Postgres either, since the reference in your final SELECT would return NULL values:

SELECT user_id, permission_id

Postgres 8.4 had more problems with that and errors out at

SELECT unnest(affected_user_list) AS user_id

The naming conflict is unacceptable to the old version. Newer versions have resolved this.

sql function

You can use this SQL function with a largely simplified query instead:

CREATE FUNCTION permission_cache_update(affected_user_list int[])
  RETURNS TABLE(user_id int, permission_id int) AS
$func$

WITH affected_user AS (SELECT unnest($1) AS user_id) 
SELECT a.user_id, r.permission_id
FROM   user_role       u
JOIN   role_permission r USING (role_id)
JOIN   affected_user   a USING (user_id)

UNION
SELECT a.user_id, p.permission_id
FROM   user_permission p
JOIN   affected_user   a USING (user_id)

$func$  LANGUAGE sql;
  • Use proper join syntax. Much easier to read and maintain. I also simplified with USING, which is a matter of taste and style. It's simpler and shorter, but requires unambiguous column names. So that would not work in a plpgsql version for Postgres 8.4 either, but works in modern versions.

  • The final SELECT with the GROUP BY in your original query is just noise. UNION (as opposed to UNION ALL) already implicitly removes duplicates.

plpgsql function

If you avoid the naming conflicts, the plpgsql version should also work:

CREATE FUNCTION permission_cache_update(affected_user_list int[])
  RETURNS TABLE(user_id int, permission_id int) AS
$func$
BEGIN

RETURN QUERY
WITH affected_user AS (
   SELECT unnest(affected_user_list) AS u_id
   ) 
SELECT a.user_id, r.permission_id
FROM   user_role       u
JOIN   role_permission r USING (role_id)
JOIN   affected_user   a ON a.u_id = u.user_id

UNION
SELECT a.user_id, p.permission_id
FROM   user_permission p
JOIN   affected_user   a ON a.u_id = p.user_id;

END    
$func$  LANGUAGE plpgsql;

OTHER TIPS

Are you sure the problem is with the section you cited? I just created a reduced version of the function and it works. What version of Postgres are you trying this on? Maybe some functionality doesn't work in your version (I tested on 9.2).

What I tested:

DROP FUNCTION IF EXISTS permission_cache_update(int4[]);
CREATE FUNCTION permission_cache_update(
  IN affected_user_list INT4[]
)
  RETURNS TABLE(user_id INT4, user_id2 INT4)
AS
  $BODY$
  BEGIN
    RETURN QUERY
    WITH
    affected_user AS (
      SELECT unnest(affected_user_list) AS user_id
    ),
    foo as (select * from affected_user)
    select au.*, foo.*
    from affected_user au
    inner join foo using (user_id);
  END;
  $BODY$
LANGUAGE plpgsql VOLATILE;

select * from permission_cache_update(array[1,2,3]::int4[]);

 user_id | user_id2 
---------+----------
       1 |        1
       2 |        2
       3 |        3
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top