PostgreSQL plpgsql get current procedures oid - postgresql

Is it possible to get the current OID within a function? Like:
CREATE FUNCTION foo()
RETURNS numeric
LANGUAGE plpgsql
AS '
BEGIN
return THIS_FUNCTIONS_OID;
END
';
I need this, because I created function foo within different schemas so the functions name is not helpful here.

I guess you are looking smth like
return select oid from pg_proc where proname='$0';
I doubt you can get it as variable. You can get the name from current_query(), but it will be very not reliable... Unless you define function name as first argument each time you call it :), then you can use $1, but it is not much reliable either...

I don't know what your are doing, but I am sure you don't do it well :). Usually, these strange requirements are related to a strange design and result in code that's hard to maintain.
But you can get the oid of the current function easily with PostgreSQL 9.4 and higher. (This info is easily accessible in C PL functions, but it is hidden in PLpgSQL.) Much easier if your functions are from other schemas than public:
CREATE OR REPLACE FUNCTION omega.inner_func()
RETURNS oid AS $$
DECLARE
stack text; fcesig text;
BEGIN
GET DIAGNOSTICS stack = PG_CONTEXT;
fcesig := substring(stack from 'function (.*?) line');
RETURN fcesig::regprocedure::oid;
END;
$$ LANGUAGE plpgsql;
For functions from the public schema it is a little bit more difficult - there is an inconsistency and without explicitly appending the prefix "public" the cast to regprocedure should not work when public is not in search_path. A generic solution needs a few more lines:
CREATE OR REPLACE FUNCTION omega.inner_func()
RETURNS oid AS $$
DECLARE
stack text; fcesig text; retoid oid;
BEGIN
GET DIAGNOSTICS stack = PG_CONTEXT;
fcesig := substring(stack from 'function (.*?) line');
retoid := to_regprocedure(fcesig::cstring);
IF retoid IS NOT NULL THEN RETURN retoid; END IF;
RETURN to_regprocedure(('public.' || fcesig)::cstring);
END;
$$ LANGUAGE plpgsql;

Related

How to access outer scope variables from a function in PostgreSQL?

I have this code:
DO $$
DECLARE
NODE_ID bigint := 46;
BEGIN
CREATE OR REPLACE FUNCTION funk(VAL bigint)
RETURNS bigint AS $f$
BEGIN
RETURN VAL;
END; $f$ LANGUAGE plpgsql;
RAISE NOTICE '%', funk(NODE_ID);
END $$;
I works as expected and prints 46 to the console.
I want to get rid of the parameters, because the variable is global. But I am getting errors:
DO $$
DECLARE
NODE_ID bigint := 46;
BEGIN
CREATE OR REPLACE FUNCTION funk()
RETURNS bigint AS $f$
BEGIN
RETURN NODE_ID;
END; $f$ LANGUAGE plpgsql;
RAISE NOTICE '%', funk();
END $$;
I'm getting "NODE_ID not exist". Is there a way to access the outer variable in the function?
No, that won't work, because the function has no connection to your DO block whatsoever. It is a persistent database object that will continue to exist in the database after the DO block has finished.
In essence, a function is just a string with the function body (and some metadata, see pg_proc); in this case, the function body consists of the text between the opening and the closing $f$. It is interpreted by the language handler when the function is run.
The only database data you can reference in a function are other persistent database objects, and a variable in a DO block isn't one of those.
There are no global variables in PostgreSQL except for – in a way – the configuration parameters. You can access these with the SET and SHOW SQL commands and, more conveniently in code, with the set_config and current_setting functions.
Or use dynamic SQL:
DO $$
DECLARE
NODE_ID bigint := 46;
src text := format('
CREATE OR REPLACE FUNCTION funk()
RETURNS bigint AS $f$
BEGIN
RETURN %s;
END;
$f$ LANGUAGE plpgsql;
', NODE_ID::text);
BEGIN
execute src;
RAISE NOTICE '%', funk();
END $$;
(works for me, landing on your question searching for solution of same problem)

PostgreSQL - not a known variable in procedure

I'm having trouble with writing a procedure in PostgreSQL. I can create the procedure, yet when i try to execute i get an error.The error i get
ERROR: "v_all_atts" is not a known variable
the query is:
CREATE OR REPLACE FUNCTION rebuild_views_with_extra_atts()
RETURNS VOID AS $$
DECLARE
v_all_atts varchar(4000);
BEGIN
CREATE OR REPLACE FUNCTION add_column(p_table text, p_column text,p_category text) RETURNS VOID AS $nothing$
declare
v_column_exists bigint := false ;
BEGIN
SELECT
string_agg( CASE WHEN owner='alarm' THEN 'ai' WHEN owner='fault' THEN 'fi'
END ||'.'||lower(alias) , ', ' ORDER BY owner, alias) AS string
INTO STRICT
v_all_atts
FROM
extra_attribute_cfg
WHERE
owner NOT LIKE 'virtual' and enable = true and v_column_exists = true;
IF LENGTH(v_all_atts) is not null THEN
v_all_atts := ', '||v_all_atts;
END IF;
v_view:= q'#
CREATE OR REPLACE VIEW alarm_view AS
SELECT
fi.fault_id, ai.alarm_id,
#'||v_all_atts||q'#
FROM
alarm ai
INNER JOIN fault fi
ON fi.fault_id = ai.fault_id
#';
EXECUTE v_view;
END;
$nothing$ language plpgsql;
end;
$$ LANGUAGE plpgsql;
I have taken a long look at Postgres documentation and cannot find what is wrong and didn't find any answer to this specific situation
Your rebuild_views_with_extra_atts() function is creating the add_column() function.
add_column() uses the v_all_atts variable, but it doesn't exist in that function, it exists only in the rebuild_views_with_extra_atts() function.
To resolve this, it really depends on what you're trying to do. If that variable should exist in the add_column() function, then declare it in there. If you're trying to use the value of v_all_atts when creating add_column() (e.g. so that the content of the function's body is dependent on the value of that variable), then you really need to use dynamic sql to generate a TEXT version of the CREATE OR REPLACE ... code, then EXECUTE it.

How to know the parent schema name from inside a function

From inside a function, I need to know in which schema the function belongs to. Is there any way to know that in Postgresql?
FYI, current_schema() function provides me the set schema of the current session, which is not what I want. For example, I am in schema test1 now and I can call function test2.test_function(). Now, from inside that function current_schema() will give test1. But I need to get test2, which is the function schema.
After doing some research and connecting several SO answers, I come up with this solution.
CREATE OR REPLACE FUNCTION parent_schema()
RETURNS text AS $$
DECLARE
stack text; fcesig text;
function_oid oid;
schema_name text;
BEGIN
GET DIAGNOSTICS stack = PG_CONTEXT;
fcesig := substring(stack from 'function (.*?) line');
function_oid := fcesig::regprocedure::oid;
SELECT routine_schema INTO schema_name FROM information_schema.routines WHERE regexp_replace(specific_name, '^.+?([^_]+)$', '\1')::int = function_oid;
RETURN schema_name;
END;
$$ LANGUAGE plpgsql;
The answers which helped me to form this :
https://stackoverflow.com/a/32016935/5645769
https://stackoverflow.com/a/1347639/5645769
https://stackoverflow.com/a/24034604/5645769
As far as I can tell this is not possible in a reliable way.
The function you provide in your answer does not work:
CREATE FUNCTION public.myfunc() RETURNS text
LANGUAGE sql
AS 'SELECT laurenz.parent_schema()';
CREATE FUNCTION laurenz.myfunc() RETURNS text
LANGUAGE sql
AS 'SELECT laurenz.parent_schema()';
Your function gives the wrong answer:
test=> SELECT public.myfunc();
myfunc
---------
laurenz
(1 row)

I want to have my pl/pgsql script output to the screen

I have the following script that I want output to the screen from.
CREATE OR REPLACE FUNCTION randomnametest() RETURNS integer AS $$
DECLARE
rec RECORD;
BEGIN
FOR rec IN SELECT * FROM my_table LOOP
SELECT levenshtein('mystring',lower('rec.Name')) ORDER BY levenshtein;
END LOOP;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
I want to get the output of the levenshein() function in a table along with the rec.Name. How would I do that? Also, it is giving me an error about the line where I call levenshtein(), saying that I should use perform instead.
Assuming that you want to insert the function's return value and the rec.name into a different table. Here is what you can do (create the table new_tab first)-
SELECT levenshtein('mystring',lower(rec.Name)) AS L_val;
INSERT INTO new_tab (L_val, rec.name);
The usage above is demonstrated below.
I guess, you can use RAISE INFO 'This is %', rec.name; to view the values.
CREATE OR REPLACE FUNCTION randomnametest() RETURNS integer AS $$
DECLARE
rec RECORD;
BEGIN
FOR rec IN SELECT * FROM my_table LOOP
SELECT levenshtein('mystring',lower(rec.Name))
AS L_val;
RAISE INFO '% - %', L_val, rec.name;
END LOOP;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
Note- the FROM clause is optional in case you select from a function in a select like netxval(sequence_name) and don't have any actual table to select from i.e. like SELECT nextval(sequence_name) AS next_value;, in Oracle terms it would be SELECT sequence_name.nextval FROM dual; or SELECT function() FROM dual;. There is no dual in postgreSQL.
I also think that the ORDER BY is not necessary since my assumption would be that your function levenshtein() will most likely return only one value at any point of time, and hence wouldn't have enough data to ORDER.
If you want the output from a plpgsql function like the title says:
CREATE OR REPLACE FUNCTION randomnametest(_mystring text)
RETURNS TABLE (l_dist int, name text) AS
$BODY$
BEGIN
RETURN QUERY
SELECT levenshtein(_mystring, lower(t.name)), t.name
FROM my_table t
ORDER BY 1;
END;
$$ LANGUAGE plpgsql;
Declare the table with RETURNS TABLE.
Use RETURN QUERY to return records from the function.
Avoid naming conflicts between column names and OUT parameters (from the RETURNS TABLE clause) by table-qualifying column names in queries. OUT parameters are visible everywhere in the function body.
I made the string to compare to a parameter to the function to make this more useful.
There are other ways, but this is the most effective for the task. You need PostgreSQL 8.4 or later.
For a one-time use I would consider to just use a plain query (= function body without the RETURN QUERY above).

Declare variable of composite type in PostgreSQL using %TYPE

Question: How can I declare a variable of the same type a parameter in a stored function?
The simple answer is use %TYPE, this works:
CREATE OR REPLACE FUNCTION test_function_1(param1 text)
RETURNS integer AS
$BODY$
DECLARE
myVariable param1%TYPE;
BEGIN
return 1;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
But the problem is when param1 is a composite type:
CREATE TYPE comp_type as
(
field1 text
)
CREATE OR REPLACE FUNCTION test_function_2(param1 comp_type)
RETURNS integer AS
$BODY$
DECLARE
myVariable param1%TYPE;
BEGIN
return 1;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
This doesn't work:
ERROR: type comp_type does not exist [SQL State=42704]
So how can I do when param1 is a composite type?
(Note: Just myVariable comp_type is not a good option because my function is slightly more complex.)
Edited:
I had a mistake on copy&paste, the real error is:
ERROR: invalid type name "param1%TYPE"
Position: 130 [SQL State=42601]
And using param1%ROWTYPE the error is:
ERROR: relation "param1" does not exist
Where: compilation of PL/pgSQL function "test_function_2" near line 3 [SQL State=42P01]
Use %ROWTYPE in that case.
Simple case
Tests by A.H. and DavidEG have shown this won't work. Interesting problem!
You could try a workaround. As long as your definition is like the example you can simply resort to
CREATE FUNCTION test(param1 comp_type)
RETURNS integer
LANGUAGE plpgsql VOLATILE AS
$func$
DECLARE
myvar comp_type;
BEGIN
RETURN 1;
END
$func$;
But your real problem is probably not as simple?
The real problem
As expected, the real problem turns out to be more complex: a polymorphic input type.
Workaround for that scenario was harder, but should work flawlessly:
CREATE FUNCTION test(param1 anyelement, OUT a integer, OUT myvar anyelement)
RETURNS record
LANGUAGE plpgsql VOLATILE AS
$func$
BEGIN
myvar := $1; -- myvar has the required type now.
-- do stuff with myvar.
myvar := NULL; -- reset if you don't want to output ..
a := 1;
END;
$func$;
Call:
SELECT a FROM test('("foo")'::comp_type); -- just retrieve a, ignore myvar
See full output:
SELECT * FROM test('("foo")'::comp_type);
Note for Postgres 9.0+
There has been a crucial update in Postgres 9.0. The release notes:
Allow input parameters to be assigned values within PL/pgSQL functions
(Steve Prentice)
Formerly, input parameters were treated as being declared CONST, so
the function's code could not change their values. This restriction
has been removed to simplify porting of functions from other DBMSes
that do not impose the equivalent restriction. An input parameter now
acts like a local variable initialized to the passed-in value.
In addition to my workaround, you can (ab)use input variables directly now.
Dynamic field names
See:
How to clone a RECORD in PostgreSQL
How to set value of composite variable field using dynamic SQL