ERROR: function expression in form cannot refer to other relations of same query level : How to work around LATERAL

∥☆過路亽.° 提交于 2019-12-13 08:02:34

问题


DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;

CREATE TABLE primes
        ( pos SERIAL NOT NULL PRIMARY KEY
        , val INTEGER NOT NULL
        , CONSTRAINT primes_alt UNIQUE (val)
        );

CREATE FUNCTION is_prime(_val INTEGER)
RETURNS BOOLEAN
AS $func$
DECLARE ret BOOLEAN ;
BEGIN

SELECT False INTO ret
WHERE EXISTS (SELECT *
        FROM primes ex
        WHERE ex.val = $1
        OR ( (ex.val * ex.val) <= $1 AND ($1 % ex.val) = 0 )
        );
RETURN COALESCE(ret, True);
END;
$func$ LANGUAGE plpgsql STABLE;

CREATE VIEW vw_prime_step AS (
        -- Note when the table is empty we return {2,3,1} as a bootstrap
        SELECT
           COALESCE(MAX(val) +2,2) AS start
         , COALESCE((MAX(val) * MAX(val))-1, 3) AS stop
         , COALESCE(min(val), 1) AS step
        FROM primes
        );
SELECT * FROM vw_prime_step;

        -- The same as a function.
        -- Works, but is not usable in a query that alters the primes table.
        -- ; even not with the TEMP TABLE construct
CREATE FUNCTION fnc_prime_step ( OUT start INTEGER, OUT stop INTEGER, OUT step INTEGER)
RETURNS RECORD
AS $func$
BEGIN
/***
CREATE TEMP TABLE tmp_limits
        ON COMMIT DROP
        AS SELECT ps.start,ps.stop,ps.step FROM vw_prime_step ps
        ;
-- RETURN QUERY 
SELECT tl.start,tl.stop,tl.step INTO $1,$2,$3
FROM tmp_limits tl
LIMIT 1
        ; 
***/
SELECT tl.start,tl.stop,tl.step INTO $1,$2,$3
FROM vw_prime_step tl
LIMIT 1;
END;
$func$
-- Try lying ...
-- IMMUTABLE LANGUAGE plpgsql;
-- Try lying ...
Stable LANGUAGE plpgsql;

        -- This works
SELECT * FROM fnc_prime_step();
INSERT INTO primes (val)
SELECT gs FROM fnc_prime_step() sss
 , generate_series( 2, 3, 1 ) gs
WHERE is_prime(gs) = True
        ;
        -- This works
SELECT * FROM fnc_prime_step();
INSERT INTO primes (val)
SELECT gs FROM fnc_prime_step() sss
 , generate_series( 5, 24, 2 ) gs
WHERE is_prime(gs) = True
        ;

        -- This does not work
        -- ERROR:  function expression in FROM cannot refer to other relations of same query level:1
SELECT * FROM fnc_prime_step();
INSERT INTO primes (val)
SELECT gs FROM fnc_prime_step() sss
 , generate_series( sss.start, sss.stop, sss.step ) gs
WHERE is_prime(gs) = True
        ;

SELECT * FROM primes;
SELECT * FROM fnc_prime_step();

Of course, this question is purely hypothetic, I am not stupid enough to attempt to calculate a table of prime numbers in an DBMS. But the question remains: is there a clean way to hack around the absence of LATERAL?

As you can see, I tried with a view (does not work), function around this view (does not work either), a temp table in this function (njet), and twiddling the function's attributes.

Next step will probably be some trigger-hack (but I really,really hate triggers, basically because they are invisible to the strictness of the DBMS schema)


回答1:


you can use SRF function in target list, but there should be some strange corner cases. LATERAL is best.

postgres=# select i, generate_series(1,i) X from generate_series(1,3) g(i);
 i | x 
---+---
 1 | 1
 2 | 1
 2 | 2
 3 | 1
 3 | 2
 3 | 3
(6 rows)


来源:https://stackoverflow.com/questions/14678734/error-function-expression-in-form-cannot-refer-to-other-relations-of-same-query

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!