Another missing FROM-clause entry for table - postgresql

I'm new to postgresql and I have this problem with trigger, that should just take value from the sequence. If I understood correctly, (tried few examples) in case like this you don't need clause FROM (like in ORACLE you would add 'from dual').
Trigger looks something like that:
CREATE OR REPLACE FUNCTION public.trigger_fct_name_id_autoinc()
RETURNS trigger AS
$$
BEGIN
SELECT "NAME_ID_SEQ".NEXTVAL INTO NEW."ID" ;
RETURN NEW;
END
$$
LANGUAGE 'plpgsql';
Of course, I've done something wrong. Error I get is:
ERROR: missing FROM-clause entry for table "NAME_ID_SEQ" Where: PL/pgSQL function trigger_fct_name_id_autoinc() line 3 at SQL statement
Tried to find some answers in the web, but either there was none, or I don't understand postgresql well enough to understand what people write. In most topics the answer involves aliases, but I don't think that's the case here.
Should I somehow add FROM clause, or is it just a sign of a different issue?

You are using Oracle syntax to get the next value from your sequence. Use the Postgres' nextval:
SELECT nextval('NAME_ID_SEQ') INTO NEW."ID" ;

Related

How to get a postgresql string_to_array function working with Liquibase

The operative goal is to be able to compare an ID to a provided list passed in text form, e.g. "1,2,3,4,5." The thought was to simply use string_to_array to convert the input to an array and use the ANY operator on it. The issue arises because of Liquibase, when compiling the code an unexpected error occurs:
[ERROR] Unable to execute the query: {call testfunction(?)}
org.postgresql.util.PSQLException: ERROR: invalid input syntax for type integer: "str286"
Where: SQL statement "select string_to_array(id_list, ',')::int[]"
PL/pgSQL function testfunction(character varying) line 10 at SQL statement
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2440) ~[dao-generator.jar:?]
The code that generated the error is as follows:
CREATE OR REPLACE FUNCTION TestFunction
(
IN id_list VARCHAR(8000)
)
RETURNS TABLE
(
ID INT
)
AS
$$
DECLARE
my_array int[4];
BEGIN
select string_to_array(id_list, ',')::int[] into my_array;
RETURN QUERY
SELECT myTable.id
FROM myTable
WHERE myTable.parent_id = ANY(my_array);
END
$$
LANGUAGE plpgsql;
My question is how to resolve this error? Granted I am not much of a database programmer so if someone wants to recommend an alternative approach, that is also welcome.
Edits:
The call is executed as part of a liquibase compilation the changelog entries are
create_procedures entry:
<changeSet author="-" id="-" runAlways="true" runInTransaction="true">
<validCheckSum>any</validCheckSum>
<comment>Create TestFunction stored procedure.</comment>
<sqlFile dbms="postgresql" path="-/-/-/TestFunction.sql"
encoding="UTF-8"
relativeToChangelogFile="true"
endDelimiter="go"
splitStatements="true"/>
</changeSet>
GeneratorConfig entry:
<query name="TestFunction"/>
So after some brainstorming with the feedback from #a_horse_with_no_name, a coworker came up with a simple enough solution to get through the build process. We simply did a regex check on the input to make sure it was formatted as expected (probably not a bad idea anyway) and then altered the query according to #a_horse_with_no_name's comment.
This doesn't really answer why liquibase was testing with strings in the form of strXYZ but does at least provide a workaround in this situation

PL/PostgreSQL how to convert a variable into a table name

So I have a function in PostgreSQL that dynamically selects columns from a dynamic table. I got this solution from this post and it works great other than one thing.
This is inside of a file that is connected to a Node server, and so the $1 and $2 in the second SELECT * FROM represent values passed from there. The issue right now is that I am getting a syntax error that I don't understand (I am newer to SQL so that may be why).
$2 represents the name of the table to be selected from as a string, so for example it could be 'goals'. The error is syntax error at or near "'goals'". I realize that it cannot be a string with single quotes (I believe) and so I am wondering how to convert that variable to be a table name? using "goals" there as well as goals, for example works as expected, though I'm not sure how to do that outside of a function.
CREATE OR REPLACE FUNCTION get_data(user_id INT, table_name anyelement)
RETURNS SETOF ANYELEMENT AS $$
BEGIN
RETURN QUERY EXECUTE
format('SELECT * FROM %s WHERE user_id = $1', pg_typeof(table_name)) USING user_id;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM get_data($1, NULL::$2);
$1 is 5 and $2 is 'goals' for example
After many hours of trying to figure it out, thanks to Adrian's comment, I found MassiveJS (how I'm connecting to my PostgreSQL server) has inline functions to do queries. In my controller file in my server I was able to create a one line function as such:
const data = await db[tableName].where("user_id=$1", [userId])
Didn't know inline SQL existed in MassiveJS, so that was great to find out!

How to do variable substitution in plpgsql?

I've got a bit of complex sql code that I'm converting from MSSql to Postgres (using Entity Framework Core 2.1), to deal with potential race conditions when inserting to a table with a unique index. Here's the dumbed-down version:
const string QUERY = #"
DO
$$
BEGIN
insert into Foo (Field1,Field2,Field3)
values (#value1,#value2,#value3);
EXCEPTION WHEN others THEN
-- do nothing; it's a race condition
END;
$$ LANGUAGE plpgsql;
select *
from Foo
where Field1 = #value1
and Field2 = #value2;
";
return DbContext.Foos
.FromSql(QUERY,
new NpgsqlParameter("value1", value1),
new NpgsqlParameter("value2", value2),
new NpgsqlParameter("value3", value3))
.First();
In other words, try to insert the record, but don't throw an exception if the attempt to insert it results in a unique index violation (index is on Field1+Field2), and return the record, whether it was created by me or another thread.
This concept worked fine in MSSql, using a TRY..CATCH block. As far as I can tell, the way to handle Postgres exceptions is as I've done, in a plpgsql block.
BUT...
It appears that variable substitution in plpgsql blocks doesn't work. The code above fails on the .First() (no elements in sequence), and when I comment out the EXCEPTION line, I see the real problem, which is:
Npgsql.PostgresException : 42703: column "value1" does not exist
When I test using regular Sql, i.e. doing the insert without using a plpgsql block, this works fine.
So, what is the correct way to do variable substitution in a plpgsql block?
The reason this doesn't work is that the body of the DO statement is actually a string, a text. See reference
$$ is just another way to delimit text in postgresql. It can be just as well be replaced with ' or $somestuff$.
As it is a string, Npgsql and Postgresql have no reason to mess with #value1 in it.
Solutions? Only a very ugly one, so not using this construction, as you're not able to pass it any values. And messing with string concatenation is no different than doing concatenation in C# in the first place.
Alternatives? Yes!
You don't need to handle exceptions in plpgsql blocks. Simply insert, use the ON CONFLICT DO NOTHING, and be on your way.
INSERT INTO Foo (Field1,Field2,Field3)
VALUES (#value1,#value2,#value3)
ON CONFLICT DO NOTHING;
select *
from Foo
where Field1 = #value1
and Field2 = #value2;
Or if you really want to keep using plpgsql, you can simply create a temporary table, using the ON COMMIT DROP option, fill it up with these parameters as one row, then use it in the DO statement. For that to work all your code must execute as part of one transaction. You can use one explicitly just in case.
The only ways to pass parameters to plpgsql code is via these 2 methods:
Declaring a function, then calling it with arguments
When already inside a plpgsql block you can call:
EXECUTE $$ INSERT ... VALUES ($1, $2, $3); $$ USING 3, 'text value', 5.234;
End notes:
As a fellow T-SQL developer who loved its freedom, but transitioned to Postgresql, I have to say that the BIG difference is that on one side there's T-SQL which gives the power, and on the other side it's a very powerful Postgresql-flavored SQL. plpgsql is very rarely warranted. In fact, in a code base of megabytes of complex SQL stuff, I can rewrite pretty much every plpgsql code in SQL. That's how powerful it really is compared to MSSQL-flavored SQL. It just takes some getting used to, and befriending the very ample documentation. Good luck!

Creating a trigger gives "function does not exist" error in PostgreSQL

I'm working on a project right now where I'm trying to build a trigger that updates a column "lastedit" (in the table Person), which is a timestamp storing when the last change was made to the table in the schema specified in the trigger creation (in this case Certifications).
Now, my problem is that when I try to create the trigger, after creating the function "update_lastedit()", it gives me an error saying that the function does not exist. I think I might have a mismatch in my function somewhere, but I cannot seem to find it.
Could any of you help me out? I'm running PostgreSQL 9.5.5. Please let me know if I need to give a more extensive explanation, this is my first question, so I might have overlooked something important.
My code for the trigger and the function are as follows:
CREATE OR REPLACE FUNCTION update_lastedit() RETURNS TRIGGER AS
$update_edit$
BEGIN
UPDATE ovsoftware.person
SET lastedit = now();
END;
$update_edit$
LANGUAGE plpgsql;
and
CREATE TRIGGER cert_edit_trigger
BEFORE INSERT OR UPDATE ON ovsoftware.certifications
FOR EACH ROW
EXECUTE PROCEDURE update_lastedit();
The exact error:
SQL fout:
ERROR: function update_lastedit() does not exist
In statement:
CREATE TRIGGER cert_edit_trigger
BEFORE INSERT OR UPDATE ON ovsoftware.certifications
FOR EACH ROW
EXECUTE PROCEDURE update_lastedit();
The solution was to use a fully qualified name for the function as follows: ovsoftware.update_lastedit(). I am not sure why that is the case, as I did not need to do so in other cases.
Either way, the scope apparently did not include the ovsoftware schema, leading to the error.

How can I send column values as the payload in a postgresql NOTIFY message?

If an entry in a table satisfies certain conditions, a NOTIFY is sent out. I want the payload to include the ID number and several other columns of information. Is there a postgres method to convert variables (OLD.ColumnID, etc) to strings?
using postgres 9.3
#klin is correct that NOTIFY doesn't support anything other than string literals. However there is a function pg_notify() which takes normal arguments to deal with exactly this situation. It's been around since at least 9.0 and that link is to the official documentation - always worth reading it carefully, there is a wealth of information there.
My guess is that the notify has to be done within a trigger function. Use a dynamic query, e.g.
execute format('notify channel, ''id: %s''', old.id);
The solution was to upgrade Postgres to a version that supported JSON.
Even postgresql 9.3 supports json. You could have just used row_to_json(payload)::text
Sorry for the long answer, i just cant walk away without reacting to the other answers too.
The format version fails in many ways. Before EXECUTE, you shoud prepare the plan. The "pseudo command" does not fits the syntax of execute which is
EXECUTE somepreparedplanname (parameter1, ...)
The %s in format is again too bad, this way you can summon sql injection attacks. When constructing a query with format, you need to use %L for literals %I for column/table/function/etc ids, and use %s almost never.
The other solution with the pg_notify function is correct. Try
LISTEN channel;
SELECT pg_notify('channel','Id: '|| pg_backend_pid ());
in psql command line.
So back to the original question: sdemurjian,
Its not clarified in the question, if you wants to use this notification thing in some trigger function. So here is an example (maybe not) for you (because im a little late. sorry for that too):
CREATE TABLE columns("columnID" oid, "columnData" text);
CREATE FUNCTION column_trigger_func() RETURNS TRIGGER AS
$$ BEGIN PERFORM pg_notify('columnchannel', 'Id: '||OLD."columnID");
RETURN NEW; END; $$ LANGUAGE plpgsql;
CREATE TRIGGER column_notify BEFORE UPDATE ON columns FOR EACH ROW
EXECUTE PROCEDURE column_trigger_func();
LISTEN columnchannel;
INSERT INTO columns VALUES(1,'testdata');
BEGIN; UPDATE columns SET "columnData" = 'success'; END;
BEGIN; UPDATE columns SET "columnData" = 'fail'; ROLLBACK;
Please note that in early postgres versions (any before 9), the notify command does not accepts any payload and there is no pg_notify function.
In 8.1 the trigger function stil works if you define it like
CREATE FUNCTION column_trigger_func() RETURNS TRIGGER AS
$$ BEGIN NOTIFY columnchannel; RETURN NEW; END; $$ LANGUAGE plpgsql;