How to know the parent schema name from inside a function - postgresql

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)

Related

psql , creating stored procedure - converting string to schema type

I have a stored producedure that does this, my_schema is the name of the current schema I'm using in my db.
CREATE OR REPLACE myprocedure(data TEXT) LANGUAGE plpgsql AS $$
DECLARE
my_variable my_schema.my_table%ROWTYPE;
BEGIN
SELECT * FROM my_schema.my_table WHERE oid=3;
END;
$$;
But, I need this to be a bit more generic, and I'd like to use this for any schema I create in my db, so, I'd like to pass in the schema name instead like this, so that I won't have to create a procedure for every schema created in the future:
note I've used angle brackets for syntax I'm not sure of:
CREATE OR REPLACE myprocedure(data TEXT, my_schema_name <SOME_TYPE>) LANGUAGE plpgsql AS $$
DECLARE
my_variable <my_schema_name>.my_table%ROWTYPE;
BEGIN
SELECT * FROM <my_schema_name>.my_table WHERE oid=3;
END;
$$;
and then call it like this:
CALL myprocedure('SOMETHIGN', <MY_SCHEMA_NAME>);
what is the correct syntax to do this?
You can use the record type for a generic record, and you use dynamic SQL for the query. Here is an example:
CREATE PROCEDURE x(schema_name text) LANGUAGE plpgsql AS
$$DECLARE
q record;
BEGIN
EXECUTE format(
'SELECT * FROM %I.data WHERE id = 1',
schema_name
) INTO q;
RAISE NOTICE '%', q.id;
END;$$;

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 do I reference tables in the same schema as my function?

I am creating a function within a schema that I am going to make a series of queries from. I want all of these queries to be made as if the schema of the function was first in the search path. I know I can change the search path within the context of the function, but I can't find how to learn the schema that my function is contained in from within my function.
You can try to parse the pg_context return value of get diagnostics:
create schema test_schema;
create or replace function test_schema.test_function()
returns text language plpgsql as $$
declare
stack text;
begin
get diagnostics stack = pg_context;
return stack;
end $$;
select test_schema.test_function();
test_function
-------------------------------------------------------------------------
PL/pgSQL function test_schema.test_function() line 5 at GET DIAGNOSTICS
(1 row)
If the function name is unique you can get the schema name querying the system catalog pg_namespace:
create or replace function test_schema.test_function_2()
returns text language sql as $$
select nspname::text
from pg_namespace n
join pg_proc p on n.oid = pronamespace
where proname = 'test_function_2'
$$;
select test_schema.test_function_2() as schema_name;
schema_name
-------------
test_schema
(1 row)

PostgreSQL plpgsql get current procedures oid

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;

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).