I thought immutable strict ment the database could not be modified.
The following inserts a new row in 'some_table':
CREATE FUNCTION insert_row() RETURNS void AS
$$
plv8.execute('INSERT INTO some_table (number) VALUES ($1)', [123]);
$$
LANGUAGE plv8 IMMUTABLE STRICT;
Is it not possible to prevent a function modifying the database?
This works as expected for me.
If I execute something like:
select insert_row() from some_OTHER_table;
I get EXACTLY one new row in some_table regardless of the number of rows in some_other_table.
If you don't want your function modifies your database, simply DON'T put insert or update statements in it.
Related
I'm trying to store a simple SELECT query with the new CREATE PROCEDURE method in PostgreSQL 11. My idea is to store the queries in the DB, because I can have a much simple code in my API server and maybe I don't need to develop a query builder if I can use if/else in an sql function with enforced type safety. I have this minimal example:
First I tried this plpgsql function:
CREATE OR REPLACE PROCEDURE test_proc() AS $$
BEGIN
SELECT * FROM my_db
LIMIT 1;
END;
$$ LANGUAGE plpgsql;
CALL test_proc();
However throws this error:
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function test_proc() line 3 at SQL statement SQL state: 42601
If I trying to use RETURN QUERY:
CREATE OR REPLACE PROCEDURE test_proc() AS $$
BEGIN
RETURN QUERY;
SELECT * FROM my_db
LIMIT 1;
END;
$$ LANGUAGE plpgsql;
I'm getting this error:
ERROR: cannot use RETURN QUERY in a non-SETOF function
LINE 17: RETURN QUERY; ^
SQL state: 42804
Character: 310
I'm also getting error when I try to use RETURNS void AS $$ or RETURNS table(...) AS $$. Seems like RETURNS not supported in CREATE PROCEDURE? So, is it possible to return a table with the new stored procedure method? Or if it's not, maybe JSON?
Procedures in PostgreSQL (Oracle, DB2) are not same like procedures in MS-SQL. It has different target, and you cannot use it. Usually, the best what you can do, forgot all what you know from MSSQL. The procedural part is really different.
Only functions can returns some data - so you need to use functions. Functions can returns scalar value, composite value or array value, or table. You want function that returns table.
CREATE OR REPLACE FUNCTION fx()
RETURNS SETOF mytab AS $$
BEGIN
RETURN QUERY SELECT * FROM mytab;
END
$$ LANGUAGE plpgsql;
SELECT * FROM fx();
For record:
You can use SQL function, that can have better (or worse) performance (depends on context). These functions are sometimes named as parametrized views.
CREATE OR REPLACE FUNCTION fx()
RETURNS SETOF mytab AS $$
SELECT * FROM mytab;
$$ LANGUAGE sql;
Attention: this technique is antipattern!!! Don't do it. It is really not good idea. The functions should not to wrap queries. If you want to hide some complexity of queries, then use a views. Don't use a functions. Functions are effective barier for query optimizer, and when you use this antipattern, then optimizer cannot to well optimize any non trivial queries that use in this form evaluated subqueries.
Use it - if you want very very slow applications - or if your data model or queries are primitive. In other cases, don't do it.
Don't afraid of SQL - it is great language designed for manual usage. It is good to place all data access to one module (model), to don't access database everywhere in your code, but it is bad too hide SQL in your code.
First of all Procedure was introduced in PostgreSQL 11, If you are using below 11th version, you cannot use Procedures. Instead to Procedure you can use functions.
Syntax to create function
CREATE or replace function function_name(_parameter varchar)
returns table(col1 varchar, col2 varchar, col3 varchar)
language 'plpgsql'
as $BODY$
BEGIN
return query select a.col1, a.col2, b.col3 from table a
join table2 as b on a.col1 = b.col1;
END;
$BODY$;
you can call a function same a like table
select * From function_name('sample data');
syntax to create Procedure.
CREATE OR REPLACE PROCEDURE procedure_name(_parameter varcar,INOUT result refcursor)
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
open result for SELECT , * from sampletable where a = _parameter;
END;
$BODY$;
you can execute a Procedure using call keyword, within a transaction
BEGIN;
CALL public.procedure_name( 'sample data', 'test');
fetch all in "test";
COMMIT;
The postgreSql 11. we have to create a stored procedure
there is the solution :
Create procedure to execute query in PostgreSQL
I need to show the Tree_Nodes table data
CREATE OR REPLACE FUNCTION sample()
RETURNS TABLE() AS
$BODY$
BEGIN
select * from "Tree_Nodes";
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
ALTER FUNCTION sample()
OWNER TO postgres;
It fails with:
ERROR: query has no destination for result data
Avoid the error and i will get the table column format in all data
To return the result of a SELECT, a pure SQL function is much more suitable:
CREATE OR REPLACE FUNCTION sample()
RETURNS TABLE ( .... ) AS
$BODY$
select * from "Tree_Nodes";
$BODY$
LANGUAGE sql;
Or if you really need PL/pgSQL, you need to use return query (which is clearly documented in the manual)
CREATE OR REPLACE FUNCTION sample()
RETURNS TABLE (....)
AS
$BODY$
BEGIN
return query select * from "Tree_Nodes";
END;
$BODY$
LANGUAGE plpgsql;
But you cannot just specify returns table() you have to also define the structure of the result:
CREATE OR REPLACE FUNCTION sample()
RETURNS TABLE(id integer, some_column text, other_column decimal(10,2), ...)
AS
The exact error you quote is caused by using SELECT without an INTO clause in PL/PgSQL. You must either use SELECT INTO somevariable, use RETURN QUERY, if you want to discard the data, use the PERFORM statement instead of SELECT, as covered by the PL/PgSQL manual.
Once you fix that by using RETURN QUERY SELECT .... you'll find that the function still doesn't work, because RETURNS TABLE() doesn't make sense. You're returning an empty result set. It'll fail, complaining that the statement is returning a result set that doesn't match the function.
It makes no sense to do this anyway, since you can just write it as a trivial SQL function like:
CREATE OR REPLACE FUNCTION sample()
RETURNS SETOF "Tree_Nodes"
AS $$
SELECT * FROM "Tree_Nodes";
$$ LANGUAGE sql;
This function appears to serve no purpose. What are you trying to achieve with it?
(By the way, you should generally avoid SELECT * in production code. List the columns. That way, if you add a column later, things that use the table won't suddenly stop working.)
In a plpgsql function, I have a variable of type record:
my_rec RECORD;
This record contains a row from an arbitrary table, so I do not know the columns before it is executed.
However, I do have the name of at least one of the columns available as a varchar.
The question is: How do I retrieve the value for a given column from my_rec?
Use hstore to work with records with dynamic columns in PL/PgSQL functions:
CREATE EXTENSION hstore;
CREATE OR REPLACE FUNCTION test_fn(col_name text) RETURNS text AS $$
DECLARE
input_row record;
col_value text;
BEGIN
SELECT INTO input_row
*
FROM ( VALUES ('a','b','c',1) ) AS dummyrow(col1,col2,col3,intcol);
SELECT INTO col_value
hstore(input_row) -> col_name;
RETURN col_value;
END;
$$ LANGUAGE 'plpgsql';
hstore is an extension, but it's an extension that's been bundled with PostgreSQL since 8.3 and has been installable using CREATE EXTENSION since 9.1. The record-to-hstore conversion has been supported since something like 8.4 or 9.0.
I don't know of a way to do this in plpgsql. I did a bit of testing for you and tried to make a "EXECUTE SELECT" solution work, such as:
EXECUTE 'select $1.' || quote_ident(the_param) USING my_rec INTO my_var;
This does not work for me and I get:
could not identify column "{{param_value here}}" in record data type
Here is a very similar question from a few years ago saying that it is not possible with plpgsql. Per it's suggestion, it appears that it should be possible with some other languages. Quoting Tom Lane's answer:
There is no way to do that in plpgsql. You could do it in the other PLs
(eg plperl, pltcl) since they are not as strongly typed as plpgsql.
I am trying to delete a row from one table and insert it with some additional data into another. I know this can be done in two separate commands, one to delete and another to insert into the new table. However I am trying to combine them and it is not working, this is my query so far:
insert into b (one,two,num) values delete from a where id = 1 returning one, two, 5;
When running that I get the following error:
ERROR: syntax error at or near "delete"
Can anyone point out how to accomplish this, or is there a better way? or is it not possible?
You cannot do this before PostgreSQL 9.1, which is not yet released. And then the syntax would be
WITH foo AS (DELETE FROM a WHERE id = 1 RETURNING one, two, 5)
INSERT INTO b (one, two, num) SELECT * FROM foo;
Before PostgreSQL 9.1 you can create a volatile function like this (untested):
create function move_from_a_to_b(_id integer, _num integer)
returns void language plpgsql volatile as
$$
declare
_one integer;
_two integer;
begin
delete from a where id = _id returning one, two into strict _one, _two;
insert into b (one,two,num) values (_one, _two, _num);
end;
$$
And then just use select move_from_a_to_b(1, 5). A function has the advantage over two statements that it will always run in single transaction — there's no need to explicitly start and commit transaction in client code.
For all version of PostgreSQL, you can create a trigger function for deleting rows from a table and inserting them to another table. But it seems slower than bulk insert that is released in PostgreSQL 9.1. You just need to move the old data into the another table before it gets deleted. This is done with the OLD data type:
CREATE FUNCTION moveDeleted() RETURNS trigger AS $$
BEGIN
INSERT INTO another_table VALUES(OLD.column1, OLD.column2,...);
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER moveDeleted
BEFORE DELETE ON table
FOR EACH ROW
EXECUTE PROCEDURE moveDeleted();
As above answer, after PostgreSQL 9.1 you can do this:
WITH tmp AS (DELETE FROM table RETURNING column1, column2, ...)
INSERT INTO another_table (column1, column2, ...) SELECT * FROM tmp;
That syntax you have there isn't valid. 2 statements is the best way to do this. The most intuitive way to do it would be to do the insert first and the delete second.
As "AI W", two statements are certainly the best option for you, but you could also consider writing a trigger for that. Each time something is deleted in your first table, another is filled.
I'm trying to use PostgreSQL's RETURNING clause on an UPDATE within in UPDATE statement, and running into trouble.
Postgres allows a query clause in an INSERT, for example:
INSERT INTO films
SELECT * FROM tmp_films WHERE date_prod < '2004-05-07';
I would like to use the RETURNING clause from an UPDATE as the query clause for an INSERT, for example:
INSERT INTO user_status_history(status)
UPDATE user_status SET status = 'ACTIVE' WHERE status = 'DISABLED' RETURNING status
All of the Postgres references I can find suggest that a RETURNING clause behaved exactly like a SELECT clause,
however when I run something like the above, I get the following:
ERROR: syntax error at or near "UPDATE"
LINE 2: UPDATE user_statuses
Despite being able to execute the UPDATE portion of the above query without error.
Is using the RETURNING clause from an UPDATE as the query clause for an INSERT's query clause possible?
The goal is to update one table and insert into another with a single query, if possible.
With PostgreSQL 9.1 (or higher) you may use the new functionality that allows data-modification commands (INSERT/UPDATE/DELETE) in WITH clauses, such as:
WITH updated_rows AS
(
UPDATE products
SET ...
WHERE ...
RETURNING *
)
INSERT INTO products_log
SELECT * FROM updated_rows;
With PostgreSQL 9.0 (or lower) you may embed the UPDATE command inside one function, and then use that function from another function which performs the INSERT command, such as:
FUNCTION update_rows()
RETURNS TABLE (id integer, descrip varchar)
AS $$
BEGIN
RETURN QUERY
UPDATE products
SET ...
WHERE ...
RETURNING *;
END;
$$ LANGUAGE plpgsql;
FUNCTION insert_rows()
RETURNS void
AS $$
BEGIN
INSERT INTO products_log
SELECT * FROM update_rows() AS x;
END;
$$ LANGUAGE plpgsql;
Right now, no.
There was a feature that almost made it into PostgreSQL 9.0 known as Writeable CTE's that does what you're thinking (although the syntax is different).
Currently, you could either do this via a trigger or as two separate statements.
I think this is not possible the way you are trying to do.
I'd suggest you to write an AFTER UPDATE trigger, which could perform the insert, then.