I am trying to create a Postgres SQL-function which runs some routine for my database.
The SQL-function calls a plpgsql-function which creates several temporary tables, but doesn't return anything (RETURNS void).
One of the tables created by the plpgsql-function is supposed to be used in my sql-function.
CREATE OR REPLACE FUNCTION public.my_sql_function()
RETURNS text AS
$BODY$
select public.my_plpsql_function(); -- this returns void, but has created a temp table "tmp_tbl"
DROP TABLE IF EXISTS mytable CASCADE;
CREATE TABLE mytable (
skov_id int8 PRIMARY KEY,
skov_stor int4,
skov_areal_ha numeric,
virkningfra timestamp(0) without time zone,
plannoejagtighed float8,
vertikalnoejagtighed float8,
geom geometry(MultiPolygon,25832),
orig_geom geometry(Polygon, 25832)
);
INSERT INTO mytable
select * from tmp_tbl ....
$BODY$ LANGUAGE sql;
When I try to run the lines, I get the following error:
ERROR: relation "tmp_tbl" does not exist
pgAdmin underlines the line select * from tmp_tbl ... as the part with an error.
So the SQL-function doesn't notice that the plpsql-function has created a temporary table.
Is there a workaround?
Creating and accessing a table in the same SQL function is generally impossible. Makes no difference whether you create the table in the SQL function directly or in a nested function call. All objects must be visible to begin with.
There is a big, fat note at the top of the chapter Query Language (SQL) Functions in the manual pointing that out:
Note
The entire body of a SQL function is parsed before any of it is
executed. While a SQL function can contain commands that alter the
system catalogs (e.g., CREATE TABLE), the effects of such commands
will not be visible during parse analysis of later commands in the
function. Thus, for example, CREATE TABLE foo (...); INSERT INTO foo VALUES(...); will not work as desired if packaged up into a single
SQL function, since foo won't exist yet when the INSERT command is
parsed. It's recommended to use PL/pgSQL instead of a SQL function in
this type of situation.
Related:
Why can PL/pgSQL functions have side effect, while SQL functions can't?
Difference between language sql and language plpgsql in PostgreSQL functions
I think so it is not possible - and minimally it should not by possible in future versions. SQL functions are similar to views, and then references to database object should be valid in function's creating time.
There is not any workaround - if you need temp table, use PLpgSQL, or try to write your code without temp table (it can be much better).
Related
I'm coming from a background in SQL Server where I would create temp tables using the:
select id
into #test
from table A
I've just moved into a PostGresql environment and I was hoping I could do the same, but I'm getting a syntax error. I did a search and it seems like you have to do a Create Table statement.
Is it not possible to easily create temp tables in Postgres?
Postgres supports SELECT INTO, so this should work fine:
SELECT id
INTO TEMP TABLE test
FROM a
You can also use CREATE TABLE AS:
CREATE TEMP TABLE test AS
SELECT id FROM a
This version is generally preferred, as the CREATE statement provides additional options, and can also be used in PL/pgSQL functions (where the SELECT INTO syntax has been hijacked for variable assignment).
So the thing is I am developing a contrib module and want to capture table name inside that contrib module.
question:
Is there any way to capture a table name during create table or insert table?
I have seen some of the triggers but not able to make it (I don't think there is any create table trigger). In case it is possible tell me a way to achieve it.
I though of extracting meta-data using pg_class but not helping it seems because I have to give explicitly a rel-name (table name) in where clause
do you think any other way to achieve it? Please elaborate if any and please let me know.
Here is some example which will make you understand a bit about the things I want to achieve.
creating a table:
create table new_table(name varchar , new integer);
insert into new_table values('abcdefghijkl' , 5004);
create table new_table1(name1 varchar , new1 integer) ;
insert into new_table1 values('mnopqrst' , 5005);
creating extension:
create extension table_name-extract;
select extract_tablename();
So my extension should extract a table name, means I should know table name with the built-in datatype I have declared.
Here what I expect as a output:
select extract_tablename();
table-name datatype-name
new_table name new
new_table1 name1 new1
You don't really need an extension to track the execution of DDL statements.
For that you can use an event trigger
The manual also has an example on how to write an event trigger using PL/pgSQL
CREATE OR REPLACE FUNCTION snitch()
RETURNS event_trigger
AS $$
BEGIN
RAISE NOTICE 'snitch: % %', tg_event, tg_tag;
END;
$$ LANGUAGE plpgsql;
CREATE EVENT TRIGGER snitch ON ddl_command_start
EXECUTE PROCEDURE snitch();
Inside the trigger function you would need to store the table name in some configuration table so that the information is not lost.
Of course you can package your trigger and "log table" (as a configuration table) into an extension if you want.
Another option is to enable DDL logging using
log_statement=ddl
in postgresql.conf - then you have all DDL statements in the Postgres logfile.
Here i need to select a column name by using function(stored procedure) which is present in other database table using PostgreSQL.
I have sql server query as shown below.
Example:
create procedure sp_testing
as
if not exists ( select ssn from testdb..testtable) /*ssn is the column-name of testtable which exists in testdb database */
...
Q: Can i do the same in PostgreSQL?
Your question is not very clear, but if you want to know if a column by a certain name exists in a table by a certain name in a remote PostgreSQL database, then you should first set up a foreign data wrapper, which is a multi-stage process. Then to test the existence of a certain column in a table you need to formulate a query that conforms to the standards of the particular DBMS that you are connecting to. Use the remote information_schema.tables table for optimal compatibility (which is here specified as remote_tables which you must have defined with a prior CREATE FOREIGN TABLE command):
CREATE FUNCTION sp_testing () AS $$
BEGIN
PERFORM *
FROM remote_tables
WHERE table_name = 'testtable'
AND column_name = 'ssn';
IF NOT FOUND THEN
...
END IF;
END;
$$ LANGUAGE plpgsql;
If you want to connect to another type of DBMS, you need to write some custom function in f.i. C or perl and then call that from within a PostgreSQL function on your local machine. The test on the column is then best done inside the function which should therefore take connection parameters, table name and column name as parameters, and return a boolean to inform the result.
Before you start testing this, make sure that you read all the documentation on connecting to remote servers and learning PL/pgSQL first would also be a nice gesture to demonstrate your own efforts before you ask for help.
In my PostgreSQL 9.1 database I have multiple tables and one trigger function.
Right now I am creating the trigger for each table by using that trigger function.
This methodology working fine. My boss has asked me to create the trigger commonly (only one time) by re-using that trigger function. That one trigger function should get used by all the tables in my database.
You can find an example of creating a trigger with dynamic SQL using PL/PgSQL in the Audit Trigger sample for PostgreSQL. The same approach will work with any other DDL.
See the function audit.audit_table and use of format and EXECUTE there.
That said, needing to create tables procedurally can be (but isn't always) a sign of questionable schema design.
Simple example of dynamic SQL creating a table:
CREATE OR REPLACE FUNCTION demo_dynamic_table(tablename text) RETURNS void AS $$
BEGIN
EXECUTE format('CREATE TABLE %I (id serial primary key);', tablename);
END;
$$ LANGUAGE plpgsql;
The same approach works for trigger creation, etc.
You can create PL/pgSQL procedure for table creation and move your trigger creation code inside it
If i don't qualify a "public." on account_category table, out account_category will have a conflict with account_category table name.
Does "public." also works on other rdbms?
CREATE OR REPLACE FUNCTION X_RAIN(x VARCHAR, OUT user_id VARCHAR, out account_category varchar, out depth int) returns setof record
AS
$$
BEGIN
return query
select uar.account_id, c.account_category, c.depth
from account_with_types_chart_of_account uar
inner join public.account_category c using(account_category_id);
END;
$$ LANGUAGE 'plpgsql';
Regarding public in PostgreSQL, public is defined as the default schema name when no schema name is specified. However, this can changed in the postgresql.conf file on the search_path = xxx line. To see what your current default schemas are set to issue the following SQL command:
SHOW_ search_path;
If you want to change your default schema path in your open query session, issue the following SQL command:
SET search_path = new_path;
However, in the example you posted I believe that the naming conflict you are having problems with is not with the schema name but with the function parameter name account_category and the table name account_category. You could rename your parameter name to avoid this conflict. In databases with many schemas, for clarities sake I often explicitly specify public at the start of database object names.
Regarding your second question, I don't think PostgreSQL is unique in its usage of public, but I do know that many other databases do their schemas in a different way.
Public is the default schema name.
For example, in MySQL it doesn't have schema (if I recall). Also, if you use another schema instead of public, your code will break.
http://www.postgresql.org/docs/8.3/interactive/ddl-schemas.html
I'd recommend using another variable name. There's probably another way too.
The conflict happens because you used the same name for your variable and table name.
This is a very bad choice of naming, and can lead to many problems.
For example:
create function x (somename TEXT) returns bool as $$
declare
tempbool int4;
begin
select true INTO tempbool from some_table where somename = somename;
return tempbool;
end;
$$ language plpgsql;
This code basically doesn't make any sense, because parser is not able to tell what "somename = somename" means. Same goes for table names (to some degree).
Generally you want your identifiers (table names, column names, variable names) to be unique.
I prefer to use "in_" prefix for arguments, but your choice of naming schema might be different.