up vote 0 down vote favorite

Hi guys, I'm new with postgresql, and already have my first problem..

Well, I wrote some code to understand how transactions work, following step by step the manual.

To make it short, I've created 2 tables, user and movements: in the first one there are the name, email and credit columns, in the second the columns from, to, import.

So, i was triyng that way:

BEGIN;
INSERT INTO movements (from, to, import) VALUES ('mary', 'steve', 600);
UPDATE users SET credit = credit - 600 WHERE name = 'mary';
UPDATE users SET credit = credit + 600 WHERE name = 'steve';
--here comes the problem!
IF (SELECT credit FROM users WHERE name = 'mary') < 0 THEN
 ROLLBACK;
END IF
COMMIT;

I always get the error

ERROR: syntax error at or near "IF"

Where am i mistaken?

p.s: Don't focus on the example functionality, its just a trial for me to understand the transactions.. and now, the IF clause...

flag

I have added sample code, by your request. This ought to help you out. :-) – Cloud Apr 29 '09 at 21:58
Use a check constraint, then you don't need this construction. – Frank Heikens Apr 29 at 8:56
@frank: i was trying to learn hot transactions works, that's just an example ;) – DaNieL Apr 29 at 9:23

5 Answers

up vote 3 down vote accepted

As Johannes already says: you are mixing regular SQL with PL/pgSQL, the stored procedure language. The link that Johannes provides should explain the concept of stored procedures to you.

I take it you're doing this as a script? Executing one statement after another? I'm afraid you can only do what you want to do inside a Stored Procedure, or Function, as you might call it. This is because when you are executing statements in this way, every statement stands on its own with no relation or information regarding the other statements.

Furthermore you can look at the following link for more information on how to use IF ... THEN ... ELSE ... END IF; conditionals inside plpgsql: link.


EDIT:

I don't know if ROLLBACK is allowed at that point (because each stored procedure is already in its own transaction), but you must be able to figure that out for yourself using the extensive documentation @ http://www.postgresql.org. Here's a sample function with your code in it, also demonstrating some other syntax:

CREATE OR REPLACE FUNCTION public.test()
RETURNS integer AS
$$
DECLARE
tempvar integer;

BEGIN    
     tempvar := 1;

     INSERT INTO movements (from, to, import) VALUES ('mary', 'steve', 600);
     UPDATE users SET credit = credit - 600 WHERE name = 'mary';
     UPDATE users SET credit = credit + 600 WHERE name = 'steve';

     --here comes the problem!
     IF (SELECT credit FROM users WHERE name = 'mary') < 0 THEN
        ROLLBACK;
     END IF;

     RETURN tempvar;
END
$$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER;

However, if you are really going this road, I recommend using a GUI DB manager. It's easier for learning all this.

link|flag
Ok, i have understood and im reading the johannes link (that i didn't see first time i read the answer). Can you post an example of how may look the sql i wrote in PL/pgSQL? Thanks.. – DaNieL Apr 29 '09 at 15:21
Thanks mate, i've found the postgres documentation very good and well-written.. just little 'big', but that's ok, i've purchased the 3 volume paper version and i promise to read them all. I just cant understand why the if statement cant be used in plain sql when, in mysql, it can ben used as well.. – DaNieL Apr 30 '09 at 6:19
Ho, last thing: i've googled around and even in the operator section of the manual, but i cant find the meaning of ':='.. what's that used for? – DaNieL Apr 30 '09 at 6:25
The operator ':=' is used for assigning variables their values without a corresponding query. – Cloud Apr 30 '09 at 18:30
up vote 1 down vote

You seem to use plain SQL but the IF statement is part of the PL/pgSQL procedural language which is part of PostgreSQL.

link|flag
can explain better that? – DaNieL Apr 29 '09 at 15:05
up vote 1 down vote

I think others have answered what the problem with the syntax is, but I have a comment. It looks like the invariant that your code is trying to maintain could be maintained with a check constraint on the credit column.

link|flag
Yep, a check constraint is the only real solution. The current idea is wrong. – Frank Heikens Apr 29 at 8:56
up vote 0 down vote

If you want to avoid the if you could rewrite your code as:

BEGIN;

INSERT INTO movements (from, to, import)

SELECT 'mary', 'steve', CASE credit < 600 WHEN TRUE THEN 0 ELSE 600 END;

UPDATE users SET credit = credit - CASE credit < 600 WHEN TRUE THEN 0 ELSE 600 END

WHERE name = 'mary';

UPDATE users u SET u.credit = u.credit + CASE v.credit < 600 WHEN TRUE THEN 0 ELSE 600 END

FROM users v

WHERE u.name = 'steve' and v.name = 'mary'

COMMIT;

Yes, this is stupid :) .

link|flag
lol im new of postgresql.. i think to have understood your example ;) – DaNieL Apr 29 '09 at 15:26
It's a terrible example, really, it was just a thought experiment :) . – Adam Luter Apr 29 '09 at 16:10
up vote 0 down vote

You could try to modify the IF part, from:

IF (SELECT credit FROM users WHERE name = 'mary') < 0 THEN
 ROLLBACK;
END IF

to

SELECT SUM(credit) INTO v_credit FROM users WHERE name = 'mary';
IF (v_credit) < 0 THEN
 ROLLBACK;
END IF

Assuming v_credit is a variable you defined previously. IMHO, Postgre assumes SELECT query returns more than one result, even though you're very certain that it's unique. So I think you could try to assign the value to a variable first beforehand.

link|flag
for testing it, i've even tryed the IF 2 = 2 THEN [...] but still throw the error.. – DaNieL Apr 30 '09 at 6:17

Your Answer

get an OpenID
or
never shown

Not the answer you're looking for? Browse other questions tagged or ask your own question.