2017-11-30 14:46:13 +01:00
|
|
|
CALL nonexistent(); -- error
|
|
|
|
ERROR: function nonexistent() does not exist
|
|
|
|
LINE 1: CALL nonexistent();
|
|
|
|
^
|
|
|
|
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
|
|
|
|
CALL random(); -- error
|
|
|
|
ERROR: random() is not a procedure
|
|
|
|
LINE 1: CALL random();
|
|
|
|
^
|
|
|
|
HINT: To call a function, use SELECT.
|
|
|
|
CREATE FUNCTION testfunc1(a int) RETURNS int LANGUAGE SQL AS $$ SELECT a $$;
|
|
|
|
CREATE TABLE cp_test (a int, b text);
|
|
|
|
CREATE PROCEDURE ptest1(x text)
|
|
|
|
LANGUAGE SQL
|
|
|
|
AS $$
|
|
|
|
INSERT INTO cp_test VALUES (1, x);
|
|
|
|
$$;
|
|
|
|
SELECT ptest1('x'); -- error
|
|
|
|
ERROR: ptest1(unknown) is a procedure
|
|
|
|
LINE 1: SELECT ptest1('x');
|
|
|
|
^
|
|
|
|
HINT: To call a procedure, use CALL.
|
|
|
|
CALL ptest1('a'); -- ok
|
Avoid premature free of pass-by-reference CALL arguments.
Prematurely freeing the EState used to evaluate CALL arguments led, in some
cases, to passing dangling pointers to the procedure. This was masked in
trivial cases because the argument pointers would point to Const nodes in
the original expression tree, and in some other cases because the result
value would end up in the standalone ExprContext rather than in memory
belonging to the EState --- but that wasn't exactly high quality
programming either, because the standalone ExprContext was never
explicitly freed, breaking assorted API contracts.
In addition, using a separate EState for each argument was just silly.
So let's use just one EState, and one ExprContext, and make the latter
belong to the former rather than be standalone, and clean up the EState
(and hence the ExprContext) post-call.
While at it, improve the function's commentary a bit.
Discussion: https://postgr.es/m/29173.1518282748@sss.pgh.pa.us
2018-02-10 19:37:12 +01:00
|
|
|
CALL ptest1('xy' || 'zzy'); -- ok, constant-folded arg
|
|
|
|
CALL ptest1(substring(random()::text, 1, 1)); -- ok, volatile arg
|
2017-11-30 14:46:13 +01:00
|
|
|
\df ptest1
|
|
|
|
List of functions
|
|
|
|
Schema | Name | Result data type | Argument data types | Type
|
|
|
|
--------+--------+------------------+---------------------+------
|
|
|
|
public | ptest1 | | x text | proc
|
|
|
|
(1 row)
|
|
|
|
|
2018-02-13 16:34:04 +01:00
|
|
|
SELECT pg_get_functiondef('ptest1'::regproc);
|
|
|
|
pg_get_functiondef
|
|
|
|
---------------------------------------------------
|
|
|
|
CREATE OR REPLACE PROCEDURE public.ptest1(x text)+
|
|
|
|
LANGUAGE sql +
|
|
|
|
AS $procedure$ +
|
|
|
|
INSERT INTO cp_test VALUES (1, x); +
|
|
|
|
$procedure$ +
|
|
|
|
|
|
|
|
(1 row)
|
|
|
|
|
Avoid premature free of pass-by-reference CALL arguments.
Prematurely freeing the EState used to evaluate CALL arguments led, in some
cases, to passing dangling pointers to the procedure. This was masked in
trivial cases because the argument pointers would point to Const nodes in
the original expression tree, and in some other cases because the result
value would end up in the standalone ExprContext rather than in memory
belonging to the EState --- but that wasn't exactly high quality
programming either, because the standalone ExprContext was never
explicitly freed, breaking assorted API contracts.
In addition, using a separate EState for each argument was just silly.
So let's use just one EState, and one ExprContext, and make the latter
belong to the former rather than be standalone, and clean up the EState
(and hence the ExprContext) post-call.
While at it, improve the function's commentary a bit.
Discussion: https://postgr.es/m/29173.1518282748@sss.pgh.pa.us
2018-02-10 19:37:12 +01:00
|
|
|
SELECT * FROM cp_test ORDER BY b COLLATE "C";
|
|
|
|
a | b
|
|
|
|
---+-------
|
|
|
|
1 | 0
|
2017-11-30 14:46:13 +01:00
|
|
|
1 | a
|
Avoid premature free of pass-by-reference CALL arguments.
Prematurely freeing the EState used to evaluate CALL arguments led, in some
cases, to passing dangling pointers to the procedure. This was masked in
trivial cases because the argument pointers would point to Const nodes in
the original expression tree, and in some other cases because the result
value would end up in the standalone ExprContext rather than in memory
belonging to the EState --- but that wasn't exactly high quality
programming either, because the standalone ExprContext was never
explicitly freed, breaking assorted API contracts.
In addition, using a separate EState for each argument was just silly.
So let's use just one EState, and one ExprContext, and make the latter
belong to the former rather than be standalone, and clean up the EState
(and hence the ExprContext) post-call.
While at it, improve the function's commentary a bit.
Discussion: https://postgr.es/m/29173.1518282748@sss.pgh.pa.us
2018-02-10 19:37:12 +01:00
|
|
|
1 | xyzzy
|
|
|
|
(3 rows)
|
2017-11-30 14:46:13 +01:00
|
|
|
|
|
|
|
CREATE PROCEDURE ptest2()
|
|
|
|
LANGUAGE SQL
|
|
|
|
AS $$
|
|
|
|
SELECT 5;
|
|
|
|
$$;
|
|
|
|
CALL ptest2();
|
2018-02-21 00:03:31 +01:00
|
|
|
-- nested CALL
|
|
|
|
TRUNCATE cp_test;
|
|
|
|
CREATE PROCEDURE ptest3(y text)
|
|
|
|
LANGUAGE SQL
|
|
|
|
AS $$
|
|
|
|
CALL ptest1(y);
|
|
|
|
CALL ptest1($1);
|
|
|
|
$$;
|
|
|
|
CALL ptest3('b');
|
|
|
|
SELECT * FROM cp_test;
|
|
|
|
a | b
|
|
|
|
---+---
|
|
|
|
1 | b
|
|
|
|
1 | b
|
|
|
|
(2 rows)
|
|
|
|
|
2017-11-30 14:46:13 +01:00
|
|
|
-- various error cases
|
2017-12-13 16:37:48 +01:00
|
|
|
CALL version(); -- error: not a procedure
|
|
|
|
ERROR: version() is not a procedure
|
|
|
|
LINE 1: CALL version();
|
|
|
|
^
|
|
|
|
HINT: To call a function, use SELECT.
|
|
|
|
CALL sum(1); -- error: not a procedure
|
|
|
|
ERROR: sum(integer) is not a procedure
|
|
|
|
LINE 1: CALL sum(1);
|
|
|
|
^
|
2017-11-30 14:46:13 +01:00
|
|
|
CREATE PROCEDURE ptestx() LANGUAGE SQL WINDOW AS $$ INSERT INTO cp_test VALUES (1, 'a') $$;
|
|
|
|
ERROR: invalid attribute in procedure definition
|
|
|
|
LINE 1: CREATE PROCEDURE ptestx() LANGUAGE SQL WINDOW AS $$ INSERT I...
|
|
|
|
^
|
|
|
|
CREATE PROCEDURE ptestx() LANGUAGE SQL STRICT AS $$ INSERT INTO cp_test VALUES (1, 'a') $$;
|
|
|
|
ERROR: invalid attribute in procedure definition
|
|
|
|
LINE 1: CREATE PROCEDURE ptestx() LANGUAGE SQL STRICT AS $$ INSERT I...
|
|
|
|
^
|
|
|
|
CREATE PROCEDURE ptestx(OUT a int) LANGUAGE SQL AS $$ INSERT INTO cp_test VALUES (1, 'a') $$;
|
|
|
|
ERROR: procedures cannot have OUT parameters
|
|
|
|
ALTER PROCEDURE ptest1(text) STRICT;
|
|
|
|
ERROR: invalid attribute in procedure definition
|
|
|
|
LINE 1: ALTER PROCEDURE ptest1(text) STRICT;
|
|
|
|
^
|
|
|
|
ALTER FUNCTION ptest1(text) VOLATILE; -- error: not a function
|
|
|
|
ERROR: ptest1(text) is not a function
|
|
|
|
ALTER PROCEDURE testfunc1(int) VOLATILE; -- error: not a procedure
|
|
|
|
ERROR: testfunc1(integer) is not a procedure
|
|
|
|
ALTER PROCEDURE nonexistent() VOLATILE;
|
|
|
|
ERROR: procedure nonexistent() does not exist
|
|
|
|
DROP FUNCTION ptest1(text); -- error: not a function
|
|
|
|
ERROR: ptest1(text) is not a function
|
|
|
|
DROP PROCEDURE testfunc1(int); -- error: not a procedure
|
|
|
|
ERROR: testfunc1(integer) is not a procedure
|
|
|
|
DROP PROCEDURE nonexistent();
|
|
|
|
ERROR: procedure nonexistent() does not exist
|
|
|
|
-- privileges
|
|
|
|
CREATE USER regress_user1;
|
|
|
|
GRANT INSERT ON cp_test TO regress_user1;
|
|
|
|
REVOKE EXECUTE ON PROCEDURE ptest1(text) FROM PUBLIC;
|
|
|
|
SET ROLE regress_user1;
|
|
|
|
CALL ptest1('a'); -- error
|
2017-12-02 15:26:34 +01:00
|
|
|
ERROR: permission denied for procedure ptest1
|
2017-11-30 14:46:13 +01:00
|
|
|
RESET ROLE;
|
|
|
|
GRANT EXECUTE ON PROCEDURE ptest1(text) TO regress_user1;
|
|
|
|
SET ROLE regress_user1;
|
|
|
|
CALL ptest1('a'); -- ok
|
|
|
|
RESET ROLE;
|
|
|
|
-- ROUTINE syntax
|
|
|
|
ALTER ROUTINE testfunc1(int) RENAME TO testfunc1a;
|
|
|
|
ALTER ROUTINE testfunc1a RENAME TO testfunc1;
|
|
|
|
ALTER ROUTINE ptest1(text) RENAME TO ptest1a;
|
|
|
|
ALTER ROUTINE ptest1a RENAME TO ptest1;
|
|
|
|
DROP ROUTINE testfunc1(int);
|
|
|
|
-- cleanup
|
|
|
|
DROP PROCEDURE ptest1;
|
|
|
|
DROP PROCEDURE ptest2;
|
|
|
|
DROP TABLE cp_test;
|
|
|
|
DROP USER regress_user1;
|