this is my exam
DO $$DECLARE
oldName varchar :='old';
newName varchar := 'new';
BEGIN
....
END$$;
If I run this script get this:
ERROR: syntax error at or near "DO"
LINE 1: DO $$ DECLARE
^
What I did wrong?
I'd say you are using PostgreSQL 8.4 or older, which does not support DO blocks. You will need to either upgrade, or use CREATE OR REPLACE FUNCTION to create a PL/PgSQL function, then SELECT function_name() to call it.
Always specify your PostgreSQL version in questions.
Related
I'm trying to debug this adn find out why I'm getting syntax error:
CREATE OR REPLACE FUNCTION public.myfunc(_report_id integer, _cutoff_date date)
RETURNS record
LANGUAGE plpgsql
AS $function$
declare
_deliverable_id RECORD ;
BEGIN
FOR _deliverable_id IN
SELECT deliverable_id FROM public.deliverables where report_id=_report_id
LOOP
execute format('DROP TABLE IF EXISTS report.products_%I',_deliverable_id);
END LOOP;
END
$function$
;
When I execute this, I get:
syntax error at or near ""(1111)""
1111 is one deliverable for sure, so this leads me to think it has something to do with the execute statement format, or the way I'm using %I?
%I is replaced as a whole identifier. If you want to concatenate things, you need to do it before replacement.
You can test/debug this for yourself by inspecting the result of the format() function:
select format('DROP TABLE IF EXISTS report.products_%I',42);
returns DROP TABLE IF EXISTS report.products_"42"
you need to use:
select format('DROP TABLE IF EXISTS report.%I',concat('products_', 42));
which correctly returns DROP TABLE IF EXISTS report.products_42
(obviously you need to replace 42 with your variable.
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" ;
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 can't run this block in PostgreSQL 8.2.
DECLARE
curtime char;
BEGIN
curtime := 'now';
INSERT INTO logtable VALUES (logtxt, curtime);
RETURN curtime;
END;
When I try it shows the error:
ERROR: syntax error at or near "char"
SQL state: 42601
It sounds like you're trying to run a PL/PgSQL code block stand-alone, without wrapping it up in a function using CREATE OR REPLACE FUNCTION. That won't work, you need to include it in a function or (from PostgreSQL 9.0) a DO block. PL/PgSQL and plain SQL are different languages so you can't just run PL/PgSQL code directly.
It'd help if you explained why you're trying to write the code you pasted. I suspect you're trying to solve a problem that's better handled with a trigger function like an audit trigger.
Some important notes:
You need to update PostgreSQL: PostgreSQL 8.2 is dangerously out of date and unsupported. security and bug fixes are no longer being released. Upgrade urgently to a supported version, but make sure to read the release notes for each major ".0" version like "8.3.0", "8.4.0", etc for migration and compatibility advice.
Avoid 'now': Also, instead of using 'now' you should usually use the current date/time functions, particularly current_timestamp.
current_timestamp is stable: The hoop-jumping you are doing is probably unnecessary because the value of current_timestamp (and 'now'::timestamp) doesn't change for the duration of a transaction. Eg:
regress=# BEGIN;
regress=# SELECT current_timestamp;
2012-08-14 14:52:43.382596+08
regress=# SELECT pg_sleep(5);
regress=# SELECT current_timestamp;
2012-08-14 14:52:43.382596+08
Details
Your intention appears to be something like the following (incorrect, do not use) code:
CREATE OR REPLACE FUNCTION some_function(logtxt text) RETURNS timestamptz AS $$
DECLARE
curtime char;
BEGIN
curtime := 'now';
INSERT INTO logtable VALUES (logtxt, curtime);
RETURN curtime;
END;
$$ LANGUAGE 'plpgsql';
but you've misused the char datatype, which requires a length parameter. It defaults to 1 if not supplied so you'll get:
regress=# SELECT some_function();
ERROR: value too long for type character(1)
CONTEXT: PL/pgSQL function "some_function" line 5 at assignment
NEVER use the char datatype in SQL; use varchar or text. For cross-database portability varchar(n) where n is a maximum length is required; if portability isn't needed use text.
If you change char to text in the above, your code might run, but it still doesn't make any sense. I strongly suspect that you really want to write:
CREATE OR REPLACE FUNCTION some_function(logtxt text) RETURNS timestamptz AS $$
BEGIN
INSERT INTO logtable VALUES (logtxt, current_timestamp);
RETURN current_timestamp;
END;
$$ LANGUAGE 'plpgsql';
... but you didn't know about the current date/time functions.
Even that's too much, really. I think you're trying to solve a problem that's a better fit for a trigger.
Without functions, I can do:
DELETE FROM table1
WHERE something='hello'
And my rows with the value of something='hello' get deleted, but as soon I implement functions, I begin to have problems with quotation marks.
CREATE OR REPLACE FUNCTION somefunc(varchar)
RETURNS varchar AS $$
BEGIN
DELETE FROM table1
WHERE something='$1';
DELETE FROM table2
WHERE something='$1';
RETURN $1;
END;
$$ LANGUAGE plpgsql;`
Nothing seems to work. I have tried (all variations that I saw on SO or elsewhere):
something=$1 <-- says column "hello" doesn't exist (because no quotes are given)
something=''$1''
something='''$1'''
something=''''$1''''
something='''||$1||'''
something=$Q$$1$Q1$ <--- gives syntax error
something=$Q1$ $1 $Q1$
something=$$ $1 $$
something=quote_literal($1)
And many other variations. How do I get around this??
Btw, I am using a python script to run the function. Here's the line that runs it. I've also tried adding quotes into this line as well to no avail:
cur.execute("SELECT somefunc(%s);" % (sys.argv[2]))
Thank you!
This behavior is based on the implicit use of prepare statements. When prepared statements are used, query and parameters are passed to the database server separately. Do not quote values in that scenario.
PL/pgSQL uses prepared statements, psycopg2 uses prepared statements, too:
...
DECLARE myvar int;
BEGIN
DELETE FROM mytab WHERE column = myvar; -- quietly using prepared statement
versus
DECLARE myvar int;
BEGIN
-- using dynamic SQL is similar to classic languages, quoting is necessary
-- but use the quote_literal() function to protect against SQL injection
EXECUTE 'DELETE FROM mytab WHERE column = ' || quote_literal(myvar);
-- or dynamic SQL with "USING" clause
EXECUTE 'DELETE FROM mytab WHERE column = $1' USING myvar;