I created new database named test1, new user test1_user and i granted all on database test1 to test1_user. Moreover, I created schema test1_user. Now, I also created new schema functions_package and its owner is test1_user.
my code :
CREATE OR REPLACE FUNCTION functions_package.newFunction(file_id
utl_file.file_type) RETURNS VOID AS $body$
BEGIN
functions_package.old_function1(file_id);
functions_package.old_function2(file_id);
functions_package.old_function3(file_id);
End;
--
$body$
LANGUAGE PLPGSQL
SECURITY DEFINER
STABLE;
I created few functions under schema functions_package and now I`m trying to create new function that uses the old functions but im getting eror :
ERROR: syntax error at or near "functions_package"
LINE 3: functions_package.old_function('aa');
IM trying to create the new function from user test1_user;
Im getting this error for every function in schema functions_package that i`m trying to summon in my new function. In all the old function I didnt summon any function at all so i didnt have this kind of error.
Some help ?
you lack select before function name:
try:
CREATE OR REPLACE FUNCTION functions_package.newFunction(file_id
utl_file.file_type) RETURNS VOID AS $body$
BEGIN
perform functions_package.old_function1(file_id);
perform functions_package.old_function2(file_id);
perform functions_package.old_function3(file_id);
End;
--
$body$
LANGUAGE PLPGSQL
SECURITY DEFINER
STABLE;
Related
I have defined several functions on composite types to act like calculated fields, something like this :
CREATE OR REPLACE FUNCTION status_affaireannulee(affaires) RETURNS BOOLEAN
AS $BODY$
SELECT COALESCE($1.affannulee,FALSE) OR $1.constatdecarence;
$BODY$ LANGUAGE sql IMMUTABLE;
Here, affaires is the name of a table. So I can call :
SELECT a.status_affaireannulee FROM affaires a WHERE idaffaire=1234;
Now I wish to use this function from within an update trigger, like so :
CREATE OR REPLACE FUNCTION affaires_update_CaseProgress() RETURNS trigger AS
$BODY$
BEGIN
IF NEW.status_affaireannulee THEN
RAISE NOTICE 'affaire annulée';
END IF;
RETURN NEW;
END;
$BODY$ LANGUAGE plpgsql;
CREATE TRIGGER affaires_caseprogress
BEFORE INSERT OR UPDATE ON affaires
FOR EACH ROW
EXECUTE PROCEDURE affaires_update_CaseProgress();
But PostgreSQL tells me "Record « new » has no « status_affaireannulee » field" (free translation from french).
Am I doing something wrong or is this impossible? I am using PostgreSQL 11.
You can pass the NEW parameter to the function instead of using the dot notation:
status_affaireannulee(NEW)
We started to use Postgres much more recently, having moved from SQL Server. I've noticed that Postgres parser/compiler allows creation of functions that (it seems to me) can be rejected at creation time.
One example of what I'm talking about is select statements in plpgsql blocks:
create or replace function test() returns void as $$
begin
select * from pg_database;
end;
$$ language plpgsql;
This function fails at runtime with "query has no destination for result data". Why would this error not be caught at function creation time? Is there any case when using select without 'return' in plpgsql block is allowed?
The other type of errors that are not always caught at compile time is type mismatch errors between a declared return type and actual type of the value. These are caught in simple cases, but start to make it to runtime in more complicated functions. I suspect there's some limitations in Postgres type inference/analysis, is there any additional information on this available?
tldr: Is there any way to make Postgres parser/compiler fail more on function creation?
Is there any way to make Postgres parser/compiler fail more on function creation?
That's the purpose of the plpgsql_check extension. It won't prevent the function to be created, though.
test=# create extension plpgsql_check;
CREATE EXTENSION
test=# create or replace function test() returns void as $$
begin
select * from pg_database;
end;
$$ language plpgsql;
test=# select * from plpgsql_check_function('test');
plpgsql_check_function
----------------------------------------------------------------------
error:42601:3:SQL statement:query has no destination for result data
Explain me please, why I'm getting error here:
djabase=# CREATE or REPLACE FUNCTION upset(domain_name varchar)
RETURNS TABLE (id int, domain varchar(50)) AS $$
BEGIN
SELECT domain from separser_domains where domain=$1;
EXCEPTION
when sqlstate 'no_data' then
INSERT into separser_domains(domain) VALUES (domain_name);
END; $$
LANGUAGE 'sql' STABLE;
ERROR: syntax error at or near "SELECT"
LINE 4: SELECT domain from separser_domains where domain=$1;
You are using PLPGSQL syntax but declaring the function as SQL. This the reason, why the function fails with a SYNTAX ERROR. The language of the function is declared with the statement LANGUAGE and you declared it in the last line:
LANGUAGE 'sql' STABLE;
SQL functions do not support the BEGIN - END statement and exceptions trapping.
To fix it, simple change LANGUAGE 'sql' in LANGUAGE 'plpgsql'.
Some other considerations:
Catching a exception in this place is probably not necessary. Use the FOUND variable instead.
With the STABLE keyword, the function can not perform INSERT. Change STABLEto VOLATILE.
You declared RETURNS TABLE but the function does not return anything. Use RETURNS VOID instead.
Using SELECT and discarding the result is not allowed. Use PERFORM instead.
A valid version of your version could be this:
DROP FUNCTION IF EXISTS upset(varchar);
CREATE or REPLACE FUNCTION upset(domain_name varchar)
RETURNS VOID AS $$
BEGIN
PERFORM domain from separser_domains where domain=domain_name;
IF NOT FOUND THEN
INSERT into separser_domains(domain) VALUES (domain_name);
END IF;
RETURN;
END;
$$
LANGUAGE 'plpgsql' VOLATILE;
I have two similar schemas in a single database with the same function names.
Each schema is owned by a role that matches the schema name.
I have issues about function name resolution with nested functions.
I was expecting that the outer function would call inner functions within the same schema, but it does not!
The name is resolved dynamically based on the search_path at run time which make some sens, but not as I would.
Here is a test case. Let say for example that the schemas and roles are named test and prod as follow.
Test schema:
CREATE ROLE test NOLOGIN;
CREATE SCHEMA test AUTHORIZATION test;
CREATE OR REPLACE FUNCTION test.inner_func() RETURNS TEXT
AS $BODY$
BEGIN
RETURN 'test function';
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION test.inner_func() OWNER TO test;
CREATE OR REPLACE FUNCTION test.outer_func() RETURNS SETOF TEXT
AS $BODY$
BEGIN
RETURN QUERY SELECT inner_func();
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION test.outer_func() OWNER TO test;
Prod schema:
CREATE ROLE prod NOLOGIN;
CREATE SCHEMA prod AUTHORIZATION prod;
CREATE OR REPLACE FUNCTION prod.inner_func() RETURNS TEXT
AS $BODY$
BEGIN
RETURN 'prod function';
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION prod.inner_func() OWNER TO prod;
CREATE OR REPLACE FUNCTION prod.outer_func() RETURNS SETOF TEXT
AS $BODY$
BEGIN
RETURN QUERY SELECT inner_func();
END
$BODY$ LANGUAGE 'plpgsql';
ALTER FUNCTION prod.outer_func() OWNER TO prod;
Test cases:
SET search_path=test,public;
SELECT outer_func();
> test function
SELECT prod.outer_func();
> test function <<<---- was expecting prod function
SET search_path=prod,public;
SELECT prod.outer_func();
> prod function
The test shows that function names are resolved dynamically based on the search_path at run time. Is there a way to bind inner function within the scope of a schema?
I can get such a behavior by using SECURITY DEFINER functions with dynamic SQL and CURRENT_USER, but I am looking for something more straightforward.
The clean solution is to either schema-qualify the function:
CREATE OR REPLACE FUNCTION test.outer_func()
RETURNS SETOF text AS
$func$
BEGIN
RETURN QUERY SELECT test.inner_func();
END
$func$ LANGUAGE plpgsql; -- no quotes!
Or you explicitly set the search_path per function. You can set configuration parameters this way:
CREATE OR REPLACE FUNCTION test.outer_func()
RETURNS SETOF text AS
$func$
BEGIN
RETURN QUERY SELECT inner_func();
END
$func$ LANGUAGE plpgsql SET search_path = test, pg_temp;
Customize the search_path to your needs, possibly add public to the list. I put pg_temp at the end, so objects in the temporary schema cannot hide persisted objects. (But that's not applicable for functions.) Similar to what's explained in the manual for SECURITY DEFINER functions.
I would not advise to rely on the user setting the proper search_path. That would only make sense for "public" functions, it wouldn't be consistent with your design. Why create separate functions and then still have to rely on user settings after all? You could have a single function in the public schema to begin with, but I would not got that route in any case. Very confusing and error prone.
Also, PL/pgSQL executes statements like prepared statements internally. every time you change the search_path, all "prepared" statements from plpgsql functions have to be de-allocated, which is not helping to optimize performance.
Actually, your test case in the question only works if you set the search_path first:
SET search_path=test,public;
Else you get an error when trying to create
CREATE OR REPLACE FUNCTION test.outer_func() RETURNS SETOF TEXT
AS $BODY$
BEGIN
RETURN QUERY SELECT inner_func();
...
ERROR: function inner_func() does not exist
Syntax checks are run against the current search_path at creation time - unless you provide the search_path as suggested. That was fixed 2010 after I reported a bug.
Details for search_path:
How does the search_path influence identifier resolution and the "current schema"
How can I fake inet_client_addr() for unit tests in PostgreSQL?
And don't quote the language name. It's an identifier.
I'm using PostgreSQL with pgAdmin and I can't get a trigger function to work. However, as far as I am aware, you can return type trigger in PostgreSQL?
CREATE OR REPLACE FUNCTION validate_Cat()
RETURNS TRIGGER AS
$BODY$
BEGIN
-- CODE here
END;
$BODY$
LANGUAGE SQL;
CREATE TRIGGER validate_Cat
AFTER INSERT OR UPDATE ON Category
FOR EACH ROW execute procedure validate_Cat();
SOLVED, had to change language to PLPGSQL