unterminated dollar-quoted string postgress for creating function - sql

this question might ask many here but I have tried their advice, but it still doesn't work on me, so I am going to ask this
I am trying to create a function where to trigger update_at automatically when i update something on tables
here is my syntax on .sql file:
CREATE OR REPLACE FUNCTION trigger_set_timestamp()
RETURNS TRIGGER AS $BODY$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$BODY$ LANGUAGE plpgsql;
i always got error:
unterminated dollar-quoted string at or near "$BODY$
BEGIN
NEW.updated_at = NOW()"
I have tried to change it to $$ but still doesn't work,
I am using Go for this case with the migration tool called sql-migrate
can anyone has a solution for this? I have tried from yesterday and I get really stuck now for this

It might come from your client, that does not recognize dollar quoting.
A typical workaround is to use regular single quotes. This requires double quoting all embedded single quotes, but fortunately you code has none, so:
CREATE OR REPLACE FUNCTION trigger_set_timestamp()
RETURNS TRIGGER AS '
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
' LANGUAGE plpgsql;

Related

Different ways to make "Create or replace" function in PLpgSQL

I am very new to database systems, SQL,PLpgsql,postgresql etc. I am learning about create and replace, and i can only think of one way to make the create and replace function for example:
RETURNS trigger AS $$
--Begins function
BEGIN
perform phone, email from contact ;
if (new.phone) is null OR (new.email) is null then RAISE EXCEPTION 'Bitte geben Sie mindestens eine der folgenden Informationen an: 1. Handy-Nummer 2. E-Mail Adresse' ;
END IF;
Return new;
END;
$$
How can I write this same function differently something that is much shorter, is that possible, if possible can someone show me how it could be don, the possible ways to rewrite this function?
The PERFORM, which actually executes a sequential scan of the complete table and then discards the result, is totally unnecessary, and you should remove it.
NEW is automatically populated in FOR EACH ROW triggers on INSERT OR UPDATE, so the second line alone is good enough. Use a BEFORE trigger for performance.

Can't use LOOP PostgreSQL

I'm facing an issue from yesterday and I can't understand why my SQL is not working..
This may be a simple error since i'm a beginner in SQL but I can't find where it is.
Here is what I try to do:
CREATE FUNCTION test() RETURN integer AS $$
BEGIN
FOR i IN 1..5 LOOP
SELECT * from result WHERE id=i;
end loop;
RETURN 1;
END;
$$ LANGUAGE plpgsql;
This is just a simple loop as I can find in the documentation but I have this error:
Error report -
ERROR: syntax error at or near "RETURN" (this is the first RETURN statement in the function)
The database is in PostgreSQL and the version is 9.4.5
Why it's not working ?
There are several problems, apart from the fact that the function isn't doing anything useful:
It must be RETURNS integer, not RETURN integer.
That't what causes the error.
The SELECT has no destination. Either add an INTO clause or discard the result with
PERFORM * from result WHERE id=i;
You should indent the code correctly, so that you can read and understand it.

Does Oracle 12 have problems with local collection types in SQL?

To make a long story short I propose to discuss the code you see below.
When running it:
Oracle 11 compiler raises
"PLS-00306: wrong number or types of arguments tips in call to 'PIPE_TABLE'"
"PLS-00642: Local Collection Types Not Allowed in SQL Statement"
Oracle 12 compiles the following package with no such warnings, but we have a surprise in runtime
when executing the anonymous block as is - everything is fine
(we may pipe some rows in the pipe_table function - it doesn't affect)
now let's uncomment the line with hello; or put there a call to any procedure, and run the changed anonumous block again
we get "ORA-22163: left hand and right hand side collections are not of same type"
And the question is:
Does Oracle 12 allow local collection types in SQL?
If yes then what's wrong with the code of PACKAGE buggy_report?
CREATE OR REPLACE PACKAGE buggy_report IS
SUBTYPE t_id IS NUMBER(10);
TYPE t_id_table IS TABLE OF t_id;
TYPE t_info_rec IS RECORD ( first NUMBER );
TYPE t_info_table IS TABLE OF t_info_rec;
TYPE t_info_cur IS REF CURSOR RETURN t_info_rec;
FUNCTION pipe_table(p t_id_table) RETURN t_info_table PIPELINED;
FUNCTION get_cursor RETURN t_info_cur;
END buggy_report;
/
CREATE OR REPLACE PACKAGE BODY buggy_report IS
FUNCTION pipe_table(p t_id_table) RETURN t_info_table PIPELINED IS
l_table t_id_table;
BEGIN
l_table := p;
END;
FUNCTION get_cursor RETURN t_info_cur IS
l_table t_id_table;
l_result t_info_cur;
BEGIN
OPEN l_result FOR SELECT * FROM TABLE (buggy_report.pipe_table(l_table));
RETURN l_result;
END;
END;
/
DECLARE
l_cur buggy_report.t_info_cur;
l_rec l_cur%ROWTYPE;
PROCEDURE hello IS BEGIN NULL; END;
BEGIN
l_cur := buggy_report.get_cursor();
-- hello;
LOOP
FETCH l_cur INTO l_rec;
EXIT WHEN l_cur%NOTFOUND;
END LOOP;
CLOSE l_cur;
dbms_output.put_line('success');
END;
/
In further experiments we found out that problems are even deeper than it's been assumed.
For example, varying elements used in the package buggy_report we can get an ORA-03113: end-of-file on communication channel
when running the script (in the question). It can be done with changing the type of t_id_table to VARRAY or TABLE .. INDEX BY ... There are a lot of ways and variations leading us to different exceptions, which are off topic to this post.
The one more interesting thing is that compilation time of buggy_report package specification can take up to 25 seconds,
when normally it takes about 0.05 seconds. I can definitely say that it depends on presence of TYPE t_id_table parameter in the pipe_table function declaration, and "long time compilation" happen in 40% of installation cases. So it seems that the problem with local collection types in SQL latently appear during the compilation.
So we see that Oracle 12.1.0.2 obviously have a bug in realization of using local collection types in SQL.
The minimal examples to get ORA-22163 and ORA-03113 are following. There we assume the same buggy_report package as in the question.
-- produces 'ORA-03113: end-of-file on communication channel'
DECLARE
l_cur buggy_report.t_info_cur;
FUNCTION get_it RETURN buggy_report.t_info_cur IS BEGIN RETURN buggy_report.get_cursor(); END;
BEGIN
l_cur := get_it();
dbms_output.put_line('');
END;
/
-- produces 'ORA-22163: left hand and right hand side collections are not of same type'
DECLARE
l_cur buggy_report.t_info_cur;
PROCEDURE hello IS BEGIN NULL; END;
BEGIN
l_cur := buggy_report.get_cursor;
-- comment `hello` and exception disappears
hello;
CLOSE l_cur;
END;
/
Yes, in Oracle 12c you are allowed to use local collection types in SQL.
Documentation Database New Features Guide says:
PL/SQL-Specific Data Types Allowed Across the PL/SQL-to-SQL Interface
The table operator can now be used in a PL/SQL program on a collection whose data type is declared in PL/SQL. This also allows the data type to be a PL/SQL associative array. (In prior releases, the collection's data type had to be declared at the schema level.)
However, I don't know why your code is not working, maybe this new feature has still a bug.
I fiddled around your example. The trick how Oracle 12c can use PL/SQL collections in SQL statements is that Oracle creates surrogate schema object types with compatible SQL type attributes and uses these surrogate types in a query. Your case looks like a bug. I traced the execution and the surrogate types are created only once if not exist. So the effective type doesn't change nor recompile (don't know if implicit recompilation are done using ALTER statement) during execution of pipelined function. And the issue only occurs if you use the p parameter in pipe_table function. If you don't call l_table := p; the code executes successfully even with enabled method call.

How convert '<unnamed portal 1>' to dataset of a plpgsql function in VB.NET

I was learning some basics of PostgreSQL basics. while i am trying to return a resutset from PostgreSQL stored function to a VB.NET application i stuck with the following scenarios
##--- my PostgreSQL function is
CREATE OR REPLACE FUNCTION checkLogin(
IN p_uname TEXT,IN p_pwd TEXT) RETURNS refCursor AS
$BODY$
DECLARE ref refCursor;
BEGIN
OPEN ref FOR SELECT UserMaster.* FROM UserMaster WHERE username=p_uname AND userpwd=p_pwd AND coalesce(userdel,FALSE)=FALSE;
RETUR ref;
END;
$BODY$
LANGUAGE PLPGSQL
-- My VB function To return dataset is following
Function CheckLogin(ByVal username As String,ByVal pwd As String) As DataSet
Try
Dim ds As New DataSet
Dim conString As String="Server=localhost;port=5432;Database=myDB;UserId=postgres;password=postgres"
Dim con As NpgsqlConnection=New NpgsqlConnection(ConString)
con.open()
Dim com As NpgsqlCommand=New NpgsqlCommand("select * from checkLogin('"+ username +"','"+ pwd +"')",con)
Dim da As NpgsqlDataAdapter=New NpgsqlDataAdapter(com)
da.Fill(ds)
Return ds
Catch ex As Exception
Return Nothing
End Try
End Function
From this function it just return 'unnamed portal 1' within a tag
my question is how can i convert it into DataSet. its really helpful if anybody answer it and mention what i do wrong. I googled and read most of the article related with this. But i didn't find a proper solution for this. if there is a link please mention it to me and excuse me for this question.
thanks in advance
If what you want is a function that returns a set of rows (much like a table), then your function should probably return a table (see the docs, or this question for an example) instead of a refcursor.
A refcursor is a server-side object which allows you to retrieve the query results in special ways (e.g. fetching a certain number of rows at a time, resetting it). If your function returns a refcursor you will need to interact with it by sending further queries as described in the docs. It doesn't seem like your use-case warrants this complexity.
I changed my stored function like following
CREATE OR REPLACE FUNCTION checkLogin(
IN p_uname TEXT,IN p_pwd TEXT) RETURNS TABLE(uid INTEGER,uname CHARACTOR varying,utype integer) AS
$BODY$
BEGIN
RETURN QUERY
SELECT userId,userName,userType FROM UserMaster WHERE username=p_uname AND userpwd=p_pwd AND coalesce(userdel,FALSE)=FALSE;
END;
$BODY$
LANGUAGE PLPGSQL
this pgSQL function works fine and returns resultset and can to convert it to dataset easily.

Selecting large sequence value into global variable not working in PL/SQL code

I have a script where I would like to have a global variable to store a transaction number for later use. The code is working fine on one schema where the sequence value that I'm fetching is relatively low. It is not working on another schema with a higher sequence value where I get a "Numeric Overflow". If I change that sequence value to a lower number it is working as well but that is not an option.
VAR TRANSACTIONNR NUMBER;
BEGIN
--Works with NEXTVAL being around 946713241
--Doesn't work with NEXTVAL being around 2961725541
SELECT MY_SEQUENCE.NEXTVAL INTO :TRANSACTIONNR FROM DUAL;
MY_PACKAGE.STARTTRANSACTION(:TRANSACTIONNR);
END;
/
-- SQL Statements
BEGIN
MY_PACKAGE.ENDTRANSACTION;
MY_PACKAGE.DO_SOMETHING(:TRANSACTIONNR);
END;
/
What is also working is selecting the sequence into a variable declared in the DECLARE block:
DECLARE
TRANSACTIONNR NUMBER;
BEGIN
SELECT MY_SEQUENCE.NEXTVAL INTO TRANSACTIONNR FROM DUAL;
MY_PACKAGE.STARTTRANSACTION(TRANSACTIONNR);
END;
/
But that means I won't be able to reuse it in the block at the end. Setting the size of the number is not possible.
VAR TRANSACTIONNR NUMBER(15)
is not valid.
Any ideas what I could try or other ways to store global state?
On further investigation this looks like it might be a SQL Developer bug (making assumptions about what you're doing again, of course...). I can get the same error with:
VAR TRANSACTIONNR NUMBER;
BEGIN
SELECT 2961725541 INTO :TRANSACTIONNR FROM DUAL;
END;
/
It appears that SQL Developer's NUMBER is limited to 2^31, which isn't the case normally for Oracle.
A possibly workaround is to use BINARY_FLOAT to store the value, but you'll run into precision problems eventually (not sure where, but looks OK up to 2^53-ish), and you'll need to cast() it back to NUMBER when using it.
VAR TRANSACTIONNR BINARY_DOUBLE;
BEGIN
SELECT 2961725541 INTO :TRANSACTIONNR FROM DUAL;
-- dbms_output.put_line(cast(:TRANSACTIONNR as NUMBER)); -- null for some reason
END;
/
...
BEGIN
dbms_output.put_line(cast(:TRANSACTIONNR as NUMBER));
END;
/
For some reason I don't seem to be able to refer to the bind variable again in the anonymous block I set it in - it's null in the commented-out code - which seems to be another SQL Developer quirk, regardless of the var type; but as you were doing so in your code, I may again have assumed too much...
Original answer for posterity, as it may still be relevant in other circumstances...
Presumably you're doing something to end the current transaction, e.g. a commit in endtransaction; otherwise you could just refer to my_sequence.currval in the do_something call. A number variable is fine for this size of number though, it won't have a problem with a sequence that size, and it won't make any difference that it is from a sequence rather than manually assigned. I don't think the problem is with the storage or the sequence.
It seems rather more likely that the error is coming from one of the package procedures you're calling, though I can't quite imagine what you might be doing with it; something like this will cause the same error though:
create sequence my_sequence start with 2961725541;
create package my_package as
procedure starttransaction(v_num number);
procedure endtransaction;
procedure do_something(v_num number);
end my_package;
/
create package body my_package as
procedure starttransaction(v_num number) is
begin
dbms_output.put_line('starttransaction(): ' || v_num);
for i in 1..v_num loop
null;
end loop;
end starttransaction;
procedure endtransaction is
begin
dbms_output.put_line('endtransaction()');
end endtransaction;
procedure do_something(v_num number) is
begin
dbms_output.put_line('do_something(): ' || v_num);
end do_something;
end my_package;
/
When your code is run against that it throws your error:
BEGIN
*
ERROR at line 1:
ORA-01426: numeric overflow
ORA-06512: at "STACKOVERFLOW.MY_PACKAGE", line 6
ORA-06512: at line 5
endtransaction()
do_something():
Note the error is reported against line 6 in the package, which is the for ... loop line, not from the assignment in your anonymous block.
Looping like that would be an odd thing to do of course, but there are probably other ways to generate that error. The breakpoint for it working is if the nextval is above 2^31. If I start the sequence with 2147483647 it works, with 2147483648 it errors.
I'm assuming you are actually getting an ORA-01426 from the original question; if it's actually a ORA-1438 or ORA-06502 then it's easier to reproduce, by trying to assign the value to a number(9) column or variable. 'Numeric overflow' is pretty specific though.