0
  1. How do I reuse the value returned by pair called in the function below?

    CREATE FUNCTION messages_add(bigint, bigint, text) RETURNS void AS $$
        INSERT INTO chats SELECT pair($1, $2), $1, $2
            WHERE NOT EXISTS (SELECT 1 FROM chats WHERE id = pair($1, $2));
        INSERT INTO messages VALUES (pair($1, $2), $1, $3);
    $$ LANGUAGE SQL;
    

    I know that the SQL query language doesn't support storing simple values in variables as does a typical programming language. So, I looked at WITH Queries (Common Table Expressions), but I'm not sure if I should use WITH, and anyway, I couldn't figure out the correct syntax for what I'm doing.

  2. Here's my SQLFiddle and my original question about storing chats & messages in PostgreSQL. This function inserts-if-not-exists then inserts. I'm not using a transaction because I want to keep things fast, and storing a chat without messages is not so bad but worse the other way around. So, query order matters. If there's a better way to do things, please advise.

  3. I want to reuse the value mainly to speed up the code. But, does the SQL interpreter automatically optimize the function above anyway? Still, I want to write good, DRY code.

Community
  • 1
  • 1
ma11hew28
  • 121,420
  • 116
  • 450
  • 651

3 Answers3

2

Since the function body is procedural, use the plpgsql language as opposed to SQL:

CREATE FUNCTION messages_add(bigint, bigint, text) RETURNS void AS $$
BEGIN
    INSERT INTO chats
        SELECT pair($1, $2), $1, $2
        WHERE NOT EXISTS (SELECT 1 FROM chats WHERE id = pair($1, $2));
    INSERT INTO messages VALUES (pair($1, $2), $1, $3);
END
$$ LANGUAGE plpgsql;

Also, if the result to reuse is pair($1,$2) you may store it into a variable:

CREATE FUNCTION messages_add(bigint, bigint, text) RETURNS void AS $$
DECLARE
    pair bigint := pair($1, $2);
BEGIN
    INSERT INTO chats
        SELECT pair, $1, $2
        WHERE NOT EXISTS (SELECT 1 FROM chats WHERE id = pair);
    INSERT INTO messages VALUES (pair, $1, $3);
END
$$ LANGUAGE plpgsql;
ma11hew28
  • 121,420
  • 116
  • 450
  • 651
Daniel Vérité
  • 58,074
  • 15
  • 129
  • 156
  • Why does the function body being procedural merit using PL/pgSQL? – ma11hew28 Mar 15 '14 at 19:04
  • @MattDiPasquale: PL stands for procedural language, it provides what SQL lacks in terms of variables and execution control. The advantage of SQL as a func language is the potential inlining of single SELECTs, but it doesn't apply in this case of two INSERTs. – Daniel Vérité Mar 17 '14 at 12:43
1
create function messages_add(bigint, bigint, text) returns void as $$
    with p as (
        select pair($1, $2) as p
    ), i as (
        insert into chats
        select (select p from p), $1, $2
        where not exists (
            select 1
            from chats
            where id = (select p from p)
        )
    )
    insert into messages
    select (select p from p), $1, $3
    where exists (
        select 1
        from chats
        where id = (select p from p)
    )
    ;
$$ language sql;

It will only insert into messages if it exists in chats.

Clodoaldo Neto
  • 118,695
  • 26
  • 233
  • 260
  • Thanks, Clodoaldo! But, I want it to insert into `messages` even if the row already exists in `chats` so that you can add multiple messages to one chat. By chat, I mean conversation, or thread. – ma11hew28 Mar 14 '14 at 20:24
0
  1. I actually don't have an answer for this part of your question, but I'll address it anyway because Markdown SUCKS and Stack Overflow doesn't support the start attribute on the ol element.

  2. OK. Now, that we're at 2, :-) PostgreSQL functions are transactional. So, order actually doesn't matter since both inserts will be committed together in one transaction.

  3. No, the optimizer only pre-evaluates immutable functions when they're called with constant (not variable) arguments, e.g., pair(4, 5). See Function Volatility Categories.

Community
  • 1
  • 1
ma11hew28
  • 121,420
  • 116
  • 450
  • 651