Thread

  1. How do I upsert depending on a second table?

    Samuel Marks <samuelmarks@gmail.com> — 2025-09-23T20:36:53Z

    Attempt:
    ```sql
    CREATE TABLE org
    (
        "name"      VARCHAR(50) PRIMARY KEY,
        owner       VARCHAR(50) NOT NULL
    );
    
    CREATE TABLE repo
    (
        "id"           INTEGER PRIMARY KEY,
        full_name      VARCHAR(255) UNIQUE NOT NULL,
        org            VARCHAR(50)         NOT NULL REFERENCES org ("name")
    );
    
    INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    
    INSERT INTO repo (id, full_name, org)
    VALUES (0, 'org0/name0 by wrong user', 'org0')
    ON CONFLICT (full_name) DO UPDATE
        SET full_name = EXCLUDED.full_name,
            org       = EXCLUDED.org
    WHERE EXISTS (SELECT 1
                  FROM org org_tbl
                  WHERE org_tbl.name = EXCLUDED.org
                    AND org_tbl.owner = 'wrong user')
    RETURNING *;
    
    SELECT * FROM repo WHERE id = 0;
    ```
    
    This all succeeds. It should fail because the 'wrong user' is trying
    to create a new—or update an existing—repo.
    
    Thanks for all suggestions
    
    
    
    
  2. Re: How do I upsert depending on a second table?

    Adrian Klaver <adrian.klaver@aklaver.com> — 2025-09-23T20:52:03Z

    On 9/23/25 13:36, Samuel Marks wrote:
    > Attempt:
    > ```sql
    > CREATE TABLE org
    > (
    >      "name"      VARCHAR(50) PRIMARY KEY,
    >      owner       VARCHAR(50) NOT NULL
    > );
    > 
    > CREATE TABLE repo
    > (
    >      "id"           INTEGER PRIMARY KEY,
    >      full_name      VARCHAR(255) UNIQUE NOT NULL,
    >      org            VARCHAR(50)         NOT NULL REFERENCES org ("name")
    > );
    > 
    > INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    > 
    > INSERT INTO repo (id, full_name, org)
    > VALUES (0, 'org0/name0 by wrong user', 'org0')
    > ON CONFLICT (full_name) DO UPDATE
    >      SET full_name = EXCLUDED.full_name,
    >          org       = EXCLUDED.org
    > WHERE EXISTS (SELECT 1
    >                FROM org org_tbl
    >                WHERE org_tbl.name = EXCLUDED.org
    >                  AND org_tbl.owner = 'wrong user')
    
    Where is org_tbl?
    
    Or is this a copy and paste error?
    
    > RETURNING *;
    > 
    > SELECT * FROM repo WHERE id = 0;
    > ```
    > 
    > This all succeeds. It should fail because the 'wrong user' is trying
    > to create a new—or update an existing—repo.
    > 
    > Thanks for all suggestions
    > 
    > 
    
    
    -- 
    Adrian Klaver
    adrian.klaver@aklaver.com
    
    
    
    
  3. Re: How do I upsert depending on a second table?

    Samuel Marks <samuelmarks@gmail.com> — 2025-09-23T20:56:05Z

    the AS syntax can alternatively be used for aliases
    https://www.postgresql.org/docs/current/sql-select.html
    
    `SELECT actual_tablename table0 WHERE table0.column00 = 1`
    
    (I used a space)
    
    On Tue, Sep 23, 2025 at 3:52 PM Adrian Klaver <adrian.klaver@aklaver.com> wrote:
    >
    > On 9/23/25 13:36, Samuel Marks wrote:
    > > Attempt:
    > > ```sql
    > > CREATE TABLE org
    > > (
    > >      "name"      VARCHAR(50) PRIMARY KEY,
    > >      owner       VARCHAR(50) NOT NULL
    > > );
    > >
    > > CREATE TABLE repo
    > > (
    > >      "id"           INTEGER PRIMARY KEY,
    > >      full_name      VARCHAR(255) UNIQUE NOT NULL,
    > >      org            VARCHAR(50)         NOT NULL REFERENCES org ("name")
    > > );
    > >
    > > INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    > >
    > > INSERT INTO repo (id, full_name, org)
    > > VALUES (0, 'org0/name0 by wrong user', 'org0')
    > > ON CONFLICT (full_name) DO UPDATE
    > >      SET full_name = EXCLUDED.full_name,
    > >          org       = EXCLUDED.org
    > > WHERE EXISTS (SELECT 1
    > >                FROM org org_tbl
    > >                WHERE org_tbl.name = EXCLUDED.org
    > >                  AND org_tbl.owner = 'wrong user')
    >
    > Where is org_tbl?
    >
    > Or is this a copy and paste error?
    >
    > > RETURNING *;
    > >
    > > SELECT * FROM repo WHERE id = 0;
    > > ```
    > >
    > > This all succeeds. It should fail because the 'wrong user' is trying
    > > to create a new—or update an existing—repo.
    > >
    > > Thanks for all suggestions
    > >
    > >
    >
    >
    > --
    > Adrian Klaver
    > adrian.klaver@aklaver.com
    
    
    
    
  4. Re: How do I upsert depending on a second table?

    Adrian Klaver <adrian.klaver@aklaver.com> — 2025-09-23T20:57:53Z

    On 9/23/25 13:36, Samuel Marks wrote:
    > Attempt:
    > ```sql
    > CREATE TABLE org
    > (
    >      "name"      VARCHAR(50) PRIMARY KEY,
    >      owner       VARCHAR(50) NOT NULL
    > );
    > 
    > CREATE TABLE repo
    > (
    >      "id"           INTEGER PRIMARY KEY,
    >      full_name      VARCHAR(255) UNIQUE NOT NULL,
    >      org            VARCHAR(50)         NOT NULL REFERENCES org ("name")
    > );
    > 
    > INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    > 
    > INSERT INTO repo (id, full_name, org)
    > VALUES (0, 'org0/name0 by wrong user', 'org0')
    > ON CONFLICT (full_name) DO UPDATE
    >      SET full_name = EXCLUDED.full_name,
    >          org       = EXCLUDED.org
    > WHERE EXISTS (SELECT 1
    >                FROM org org_tbl
    >                WHERE org_tbl.name = EXCLUDED.org
    >                  AND org_tbl.owner = 'wrong user')
    > RETURNING *;
    > 
    > SELECT * FROM repo WHERE id = 0;
    > ```
    
    Also, as shown, there is no conflict so I don't see the condition being 
    run per:
    
    https://www.postgresql.org/docs/current/sql-insert.html#SQL-ON-CONFLICT
    
    "
    condition
    
         An expression that returns a value of type boolean. Only rows for 
    which this expression returns true will be updated, although all rows 
    will be locked when the ON CONFLICT DO UPDATE action is taken. Note that 
    condition is evaluated last, after a conflict has been identified as a 
    candidate to update.
    
    "
    > 
    > This all succeeds. It should fail because the 'wrong user' is trying
    > to create a new—or update an existing—repo.
    > 
    > Thanks for all suggestions
    > 
    > 
    
    
    -- 
    Adrian Klaver
    adrian.klaver@aklaver.com
    
    
    
    
  5. Re: How do I upsert depending on a second table?

    Adrian Klaver <adrian.klaver@aklaver.com> — 2025-09-23T21:00:50Z

    On 9/23/25 13:56, Samuel Marks wrote:
    > the AS syntax can alternatively be used for aliases
    > https://www.postgresql.org/docs/current/sql-select.html
    > 
    > `SELECT actual_tablename table0 WHERE table0.column00 = 1`
    
    Ok, I missed the:
    
    ... FROM org org_tbl ...
    > 
    > (I used a space)
    
    Yeah, I use tbl_name AS alias_name to help these old eyes catch this 
    sort of thing in my queries.
    
    
    
    -- 
    Adrian Klaver
    adrian.klaver@aklaver.com
    
    
    
    
  6. Re: How do I upsert depending on a second table?

    Samuel Marks <samuelmarks@gmail.com> — 2025-09-23T21:02:38Z

    Yeah I know my approach doesn't work, my question is, what is the
    correct way to do an upsert for this schema?
    
    Specifically:
    
    - Create a new repo if one by that name doesn't exist + requestor is
    `owner` of associated `org`
    - Update an existing repo if one by that name does exist + requestor
    is `owner` of associated `org`
    
    On Tue, Sep 23, 2025 at 3:57 PM Adrian Klaver <adrian.klaver@aklaver.com> wrote:
    >
    > On 9/23/25 13:36, Samuel Marks wrote:
    > > Attempt:
    > > ```sql
    > > CREATE TABLE org
    > > (
    > >      "name"      VARCHAR(50) PRIMARY KEY,
    > >      owner       VARCHAR(50) NOT NULL
    > > );
    > >
    > > CREATE TABLE repo
    > > (
    > >      "id"           INTEGER PRIMARY KEY,
    > >      full_name      VARCHAR(255) UNIQUE NOT NULL,
    > >      org            VARCHAR(50)         NOT NULL REFERENCES org ("name")
    > > );
    > >
    > > INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    > >
    > > INSERT INTO repo (id, full_name, org)
    > > VALUES (0, 'org0/name0 by wrong user', 'org0')
    > > ON CONFLICT (full_name) DO UPDATE
    > >      SET full_name = EXCLUDED.full_name,
    > >          org       = EXCLUDED.org
    > > WHERE EXISTS (SELECT 1
    > >                FROM org org_tbl
    > >                WHERE org_tbl.name = EXCLUDED.org
    > >                  AND org_tbl.owner = 'wrong user')
    > > RETURNING *;
    > >
    > > SELECT * FROM repo WHERE id = 0;
    > > ```
    >
    > Also, as shown, there is no conflict so I don't see the condition being
    > run per:
    >
    > https://www.postgresql.org/docs/current/sql-insert.html#SQL-ON-CONFLICT
    >
    > "
    > condition
    >
    >      An expression that returns a value of type boolean. Only rows for
    > which this expression returns true will be updated, although all rows
    > will be locked when the ON CONFLICT DO UPDATE action is taken. Note that
    > condition is evaluated last, after a conflict has been identified as a
    > candidate to update.
    >
    > "
    > >
    > > This all succeeds. It should fail because the 'wrong user' is trying
    > > to create a new—or update an existing—repo.
    > >
    > > Thanks for all suggestions
    > >
    > >
    >
    >
    > --
    > Adrian Klaver
    > adrian.klaver@aklaver.com
    
    
    
    
  7. Re: How do I upsert depending on a second table?

    David G. Johnston <david.g.johnston@gmail.com> — 2025-09-23T21:31:46Z

    On Tuesday, September 23, 2025, Samuel Marks <samuelmarks@gmail.com> wrote:
    
    >
    >
    $subject
    
    You can only upsert/provoke a meaningful conflict on the singular table
    being inserted into.
    
    There are other features like functions and triggers that may get you
    something usable.
    
    David J.
    
  8. Re: How do I upsert depending on a second table?

    Juan Rodrigo Alejandro Burgos Mella <rodrigoburgosmella@gmail.com> — 2025-09-24T00:25:10Z

    Hi Samuel
    
    Using ON CONFLICT is a headache.
    It's better to use the versatility of a Trigger: you have the full record
    at your fingertips, and if you're going to UPDATE, you have the previous
    record too.
    There's much more control.
    
    Also, you can always count on the beloved foreign keys, which are also
    quite useful.
    
    Atte.
    JRBM
    
    
    El mar, 23 sept 2025 a las 15:37, Samuel Marks (<samuelmarks@gmail.com>)
    escribió:
    
    > Attempt:
    > ```sql
    > CREATE TABLE org
    > (
    >     "name"      VARCHAR(50) PRIMARY KEY,
    >     owner       VARCHAR(50) NOT NULL
    > );
    >
    > CREATE TABLE repo
    > (
    >     "id"           INTEGER PRIMARY KEY,
    >     full_name      VARCHAR(255) UNIQUE NOT NULL,
    >     org            VARCHAR(50)         NOT NULL REFERENCES org ("name")
    > );
    >
    > INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    >
    > INSERT INTO repo (id, full_name, org)
    > VALUES (0, 'org0/name0 by wrong user', 'org0')
    > ON CONFLICT (full_name) DO UPDATE
    >     SET full_name = EXCLUDED.full_name,
    >         org       = EXCLUDED.org
    > WHERE EXISTS (SELECT 1
    >               FROM org org_tbl
    >               WHERE org_tbl.name = EXCLUDED.org
    >                 AND org_tbl.owner = 'wrong user')
    > RETURNING *;
    >
    > SELECT * FROM repo WHERE id = 0;
    > ```
    >
    > This all succeeds. It should fail because the 'wrong user' is trying
    > to create a new—or update an existing—repo.
    >
    > Thanks for all suggestions
    >
    >
    >
    
  9. Re: How do I upsert depending on a second table?

    Samuel Marks <samuelmarks@gmail.com> — 2025-09-24T04:19:33Z

    Ok so you're thinking I give up on putting it all in one query and
    instead use a transaction? - Is that the recommended way?
    
    ```sql
    TRUNCATE repo, org;
    INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    ```
    
    ```sql
    START TRANSACTION READ WRITE;
    
    SELECT 1/COUNT(*)
    FROM org
    WHERE name = 'org0'
      AND owner = 'wrong user';
    
    INSERT INTO repo (id, full_name, org)
    VALUES (0, 'org0/name0 by wrong user', 'org0')
    ON CONFLICT (full_name) DO UPDATE
        SET full_name = EXCLUDED.full_name,
            org       = EXCLUDED.org
    RETURNING id;
    
    COMMIT;
    ```
    
    
    
    On Tue, Sep 23, 2025 at 7:25 PM Juan Rodrigo Alejandro Burgos Mella
    <rodrigoburgosmella@gmail.com> wrote:
    >
    > Hi Samuel
    >
    > Using ON CONFLICT is a headache.
    > It's better to use the versatility of a Trigger: you have the full record at your fingertips, and if you're going to UPDATE, you have the previous record too.
    > There's much more control.
    >
    > Also, you can always count on the beloved foreign keys, which are also quite useful.
    >
    > Atte.
    > JRBM
    >
    >
    > El mar, 23 sept 2025 a las 15:37, Samuel Marks (<samuelmarks@gmail.com>) escribió:
    >>
    >> Attempt:
    >> ```sql
    >> CREATE TABLE org
    >> (
    >>     "name"      VARCHAR(50) PRIMARY KEY,
    >>     owner       VARCHAR(50) NOT NULL
    >> );
    >>
    >> CREATE TABLE repo
    >> (
    >>     "id"           INTEGER PRIMARY KEY,
    >>     full_name      VARCHAR(255) UNIQUE NOT NULL,
    >>     org            VARCHAR(50)         NOT NULL REFERENCES org ("name")
    >> );
    >>
    >> INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    >>
    >> INSERT INTO repo (id, full_name, org)
    >> VALUES (0, 'org0/name0 by wrong user', 'org0')
    >> ON CONFLICT (full_name) DO UPDATE
    >>     SET full_name = EXCLUDED.full_name,
    >>         org       = EXCLUDED.org
    >> WHERE EXISTS (SELECT 1
    >>               FROM org org_tbl
    >>               WHERE org_tbl.name = EXCLUDED.org
    >>                 AND org_tbl.owner = 'wrong user')
    >> RETURNING *;
    >>
    >> SELECT * FROM repo WHERE id = 0;
    >> ```
    >>
    >> This all succeeds. It should fail because the 'wrong user' is trying
    >> to create a new—or update an existing—repo.
    >>
    >> Thanks for all suggestions
    >>
    >>
    
    
    
    
  10. Re: How do I upsert depending on a second table?

    Adrian Klaver <adrian.klaver@aklaver.com> — 2025-09-24T04:31:44Z

    On 9/23/25 17:25, Juan Rodrigo Alejandro Burgos Mella wrote:
    > Hi Samuel
    > 
    > Using ON CONFLICT is a headache.
    
    Like any tool ON CONFLICT has usage it is best for, if you try to force 
    it do something it was not designed for then it will not perform as 
    expected. Stick to what it good at and it will not be a headache. It is 
    good at moving data into a table where the incoming data is a mix of 
    entirely new rows and changes to existing rows for which there is some 
    sort of arbiter to decide on whether there is a conflict or not.
    
    > It's better to use the versatility of a Trigger: you have the full 
    > record at your fingertips, and if you're going to UPDATE, you have the 
    > previous record too.
    
    https://www.postgresql.org/docs/current/sql-insert.html#SQL-ON-CONFLICT
    
    "
    The SET and WHERE clauses in ON CONFLICT DO UPDATE have access to the 
    existing row using the table's name (or an alias), and to the row 
    proposed for insertion using the special excluded table.
    "
    
    > There's much more control.
    > 
    > Also, you can always count on the beloved foreign keys, which are also 
    > quite useful.
    > 
    > Atte.
    > JRBM
    -- 
    Adrian Klaver
    adrian.klaver@aklaver.com
    
    
    
    
  11. Re: How do I upsert depending on a second table?

    Juan Rodrigo Alejandro Burgos Mella <rodrigoburgosmella@gmail.com> — 2025-09-24T06:44:07Z

    The insert works because there is no data in the repo table that conflicts
    with the entered full name.
    
    JRBm
    
    El mar, 23 sept 2025, 23:19, Samuel Marks <samuelmarks@gmail.com> escribió:
    
    > Ok so you're thinking I give up on putting it all in one query and
    > instead use a transaction? - Is that the recommended way?
    >
    > ```sql
    > TRUNCATE repo, org;
    > INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    > ```
    >
    > ```sql
    > START TRANSACTION READ WRITE;
    >
    > SELECT 1/COUNT(*)
    > FROM org
    > WHERE name = 'org0'
    >   AND owner = 'wrong user';
    >
    > INSERT INTO repo (id, full_name, org)
    > VALUES (0, 'org0/name0 by wrong user', 'org0')
    > ON CONFLICT (full_name) DO UPDATE
    >     SET full_name = EXCLUDED.full_name,
    >         org       = EXCLUDED.org
    > RETURNING id;
    >
    > COMMIT;
    > ```
    >
    >
    >
    > On Tue, Sep 23, 2025 at 7:25 PM Juan Rodrigo Alejandro Burgos Mella
    > <rodrigoburgosmella@gmail.com> wrote:
    > >
    > > Hi Samuel
    > >
    > > Using ON CONFLICT is a headache.
    > > It's better to use the versatility of a Trigger: you have the full
    > record at your fingertips, and if you're going to UPDATE, you have the
    > previous record too.
    > > There's much more control.
    > >
    > > Also, you can always count on the beloved foreign keys, which are also
    > quite useful.
    > >
    > > Atte.
    > > JRBM
    > >
    > >
    > > El mar, 23 sept 2025 a las 15:37, Samuel Marks (<samuelmarks@gmail.com>)
    > escribió:
    > >>
    > >> Attempt:
    > >> ```sql
    > >> CREATE TABLE org
    > >> (
    > >>     "name"      VARCHAR(50) PRIMARY KEY,
    > >>     owner       VARCHAR(50) NOT NULL
    > >> );
    > >>
    > >> CREATE TABLE repo
    > >> (
    > >>     "id"           INTEGER PRIMARY KEY,
    > >>     full_name      VARCHAR(255) UNIQUE NOT NULL,
    > >>     org            VARCHAR(50)         NOT NULL REFERENCES org ("name")
    > >> );
    > >>
    > >> INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    > >>
    > >> INSERT INTO repo (id, full_name, org)
    > >> VALUES (0, 'org0/name0 by wrong user', 'org0')
    > >> ON CONFLICT (full_name) DO UPDATE
    > >>     SET full_name = EXCLUDED.full_name,
    > >>         org       = EXCLUDED.org
    > >> WHERE EXISTS (SELECT 1
    > >>               FROM org org_tbl
    > >>               WHERE org_tbl.name = EXCLUDED.org
    > >>                 AND org_tbl.owner = 'wrong user')
    > >> RETURNING *;
    > >>
    > >> SELECT * FROM repo WHERE id = 0;
    > >> ```
    > >>
    > >> This all succeeds. It should fail because the 'wrong user' is trying
    > >> to create a new—or update an existing—repo.
    > >>
    > >> Thanks for all suggestions
    > >>
    > >>
    >
    
  12. Re: How do I upsert depending on a second table?

    Adrian Klaver <adrian.klaver@aklaver.com> — 2025-09-24T15:13:29Z

    On 9/23/25 23:44, Juan Rodrigo Alejandro Burgos Mella wrote:
    > The insert works because there is no data in the repo table that 
    > conflicts with the entered full name.
    
    Except this part:
    
    SELECT 1/COUNT(*)
    FROM org
    WHERE name = 'org0'
       AND owner = 'wrong user';
    
    will cause a divide by 0 error and abort the transaction preventing the 
    INSERT from happening.
    
    Example:
    
    test=# begin ;
    BEGIN
    test=*# select 1/0;
    ERROR:  division by zero
    test=!# select 1;
    ERROR:  current transaction is aborted, commands ignored until end of 
    transaction block
    test=!# rollback ;
    ROLLBACK
    
    
    
    > 
    > JRBm
    > 
    > El mar, 23 sept 2025, 23:19, Samuel Marks <samuelmarks@gmail.com 
    > <mailto:samuelmarks@gmail.com>> escribió:
    > 
    >     Ok so you're thinking I give up on putting it all in one query and
    >     instead use a transaction? - Is that the recommended way?
    > 
    >     ```sql
    >     TRUNCATE repo, org;
    >     INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    >     ```
    > 
    >     ```sql
    >     START TRANSACTION READ WRITE;
    > 
    >     SELECT 1/COUNT(*)
    >     FROM org
    >     WHERE name = 'org0'
    >        AND owner = 'wrong user';
    > 
    >     INSERT INTO repo (id, full_name, org)
    >     VALUES (0, 'org0/name0 by wrong user', 'org0')
    >     ON CONFLICT (full_name) DO UPDATE
    >          SET full_name = EXCLUDED.full_name,
    >              org       = EXCLUDED.org
    >     RETURNING id;
    > 
    >     COMMIT;
    >     ```
    > 
    > 
    > 
    >     On Tue, Sep 23, 2025 at 7:25 PM Juan Rodrigo Alejandro Burgos Mella
    >     <rodrigoburgosmella@gmail.com <mailto:rodrigoburgosmella@gmail.com>>
    >     wrote:
    >      >
    >      > Hi Samuel
    >      >
    >      > Using ON CONFLICT is a headache.
    >      > It's better to use the versatility of a Trigger: you have the
    >     full record at your fingertips, and if you're going to UPDATE, you
    >     have the previous record too.
    >      > There's much more control.
    >      >
    >      > Also, you can always count on the beloved foreign keys, which are
    >     also quite useful.
    >      >
    >      > Atte.
    >      > JRBM
    >      >
    >      >
    >      > El mar, 23 sept 2025 a las 15:37, Samuel Marks
    >     (<samuelmarks@gmail.com <mailto:samuelmarks@gmail.com>>) escribió:
    >      >>
    >      >> Attempt:
    >      >> ```sql
    >      >> CREATE TABLE org
    >      >> (
    >      >>     "name"      VARCHAR(50) PRIMARY KEY,
    >      >>     owner       VARCHAR(50) NOT NULL
    >      >> );
    >      >>
    >      >> CREATE TABLE repo
    >      >> (
    >      >>     "id"           INTEGER PRIMARY KEY,
    >      >>     full_name      VARCHAR(255) UNIQUE NOT NULL,
    >      >>     org            VARCHAR(50)         NOT NULL REFERENCES org
    >     ("name")
    >      >> );
    >      >>
    >      >> INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    >      >>
    >      >> INSERT INTO repo (id, full_name, org)
    >      >> VALUES (0, 'org0/name0 by wrong user', 'org0')
    >      >> ON CONFLICT (full_name) DO UPDATE
    >      >>     SET full_name = EXCLUDED.full_name,
    >      >>         org       = EXCLUDED.org
    >      >> WHERE EXISTS (SELECT 1
    >      >>               FROM org org_tbl
    >      >>               WHERE org_tbl.name <http://org_tbl.name> =
    >     EXCLUDED.org
    >      >>                 AND org_tbl.owner = 'wrong user')
    >      >> RETURNING *;
    >      >>
    >      >> SELECT * FROM repo WHERE id = 0;
    >      >> ```
    >      >>
    >      >> This all succeeds. It should fail because the 'wrong user' is trying
    >      >> to create a new—or update an existing—repo.
    >      >>
    >      >> Thanks for all suggestions
    >      >>
    >      >>
    > 
    
    
    -- 
    Adrian Klaver
    adrian.klaver@aklaver.com
    
    
    
    
  13. Re: How do I upsert depending on a second table?

    David G. Johnston <david.g.johnston@gmail.com> — 2025-09-24T16:51:34Z

    This thread is annoyingly full of replies that do not follow the
    established conventions for making threads like this readable online and in
    the archive.
    
    Please:
    1. Avoid top-posting and instead include your replies inline (or, at worse,
    at the end)
    2. Remove content not relevant to your immediate reply.
    3. After you’ve made your last inline comment REMOVE all subsequent
    content.  This is just a special case of point 2 but this last message
    makes it extremely obvious just how obnoxious leaving trailing off-topic
    content is.
    
    David J.
    
  14. Re: How do I upsert depending on a second table?

    Samuel Marks <samuelmarks@gmail.com> — 2025-09-24T17:02:42Z

    On Wed, Sep 24, 2025 at 10:13 AM Adrian Klaver <adrian.klaver@aklaver.com>
    wrote:
    >
    > On 9/23/25 23:44, Juan Rodrigo Alejandro Burgos Mella wrote:
    > > The insert works because there is no data in the repo table that
    > > conflicts with the entered full name.
    >
    > Except this part:
    >
    > SELECT 1/COUNT(*)
    > FROM org
    > WHERE name = 'org0'
    >    AND owner = 'wrong user';
    >
    > will cause a divide by 0 error and abort the transaction preventing the
    > INSERT from happening.
    >
    > Example:
    >
    > test=# begin ;
    > BEGIN
    > test=*# select 1/0;
    > ERROR:  division by zero
    > test=!# select 1;
    > ERROR:  current transaction is aborted, commands ignored until end of
    > transaction block
    > test=!# rollback ;
    > ROLLBACK
    >
    >
    >
    
    Yes but it's meant to divide by zero. That cancels the whole transaction
    stopping it from going through. It being a transaction lets me guarantee
    that at point of update or insert [upsert] the org owner matches the
    requestor.
    
    I would preference a single statement (one semicolon) solution; but for now
    at least this works 🤷
    
    > >
    > > El mar, 23 sept 2025, 23:19, Samuel Marks <samuelmarks@gmail.com
    > > <mailto:samuelmarks@gmail.com>> escribió:
    > >
    > >     Ok so you're thinking I give up on putting it all in one query and
    > >     instead use a transaction? - Is that the recommended way?
    > >
    > >     ```sql
    > >     TRUNCATE repo, org;
    > >     INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    > >     ```
    > >
    > >     ```sql
    > >     START TRANSACTION READ WRITE;
    > >
    > >     SELECT 1/COUNT(*)
    > >     FROM org
    > >     WHERE name = 'org0'
    > >        AND owner = 'wrong user';
    > >
    > >     INSERT INTO repo (id, full_name, org)
    > >     VALUES (0, 'org0/name0 by wrong user', 'org0')
    > >     ON CONFLICT (full_name) DO UPDATE
    > >          SET full_name = EXCLUDED.full_name,
    > >              org       = EXCLUDED.org
    > >     RETURNING id;
    > >
    > >     COMMIT;
    > >     ```
    
  15. Re: How do I upsert depending on a second table?

    Adrian Klaver <adrian.klaver@aklaver.com> — 2025-09-24T18:28:23Z

    On 9/24/25 10:02, Samuel Marks wrote:
    > On Wed, Sep 24, 2025 at 10:13 AM Adrian Klaver 
    > <adrian.klaver@aklaver.com <mailto:adrian.klaver@aklaver.com>> wrote:
    
    > 
    > Yes but it's meant to divide by zero. That cancels the whole transaction 
    > stopping it from going through. It being a transaction lets me guarantee 
    > that at point of update or insert [upsert] the org owner matches the 
    > requestor.
    
    My reply was to Juan Rodrigo Alejandro Burgos Mella referencing the comment:
    
    "The insert works because there is no data in the repo table that 
    conflicts with the entered full name. "
    
    I was pointing out that in your second example the INSERT would not 
    happen as the org table does not have a row:
    
    name 	owner
    org0    wrong_user
    
    So the SELECT 1/COUNT(*) [...] would result in a divide by 0 error and 
    the transaction would abort. Therefore ON CONFLICT (full_name) DO UPDATE 
    does not apply as the INSERT never happens.
    
    I should have added previously this only applies for the 'wrong user' 
    case. For cases where the correct name/owner exists in the org table 
    then the INSERT and it's ON CONFLICT come into play and what happens 
    then is dependent on whether there is an existing row in the repo with 
    the same full_name or not. The issue I see is that the full_name is 
    UNIQUE across all orgs and I not sure that is good idea. It would seem 
    to me UNIQUE(org, full_name) would be better.
    
    > 
    > I would preference a single statement (one semicolon) solution; but for 
    > now at least this works 🤷
    > 
    
    
    
    
    -- 
    Adrian Klaver
    adrian.klaver@aklaver.com
    
    
    
    
  16. Re: How do I upsert depending on a second table?

    Adrian Klaver <adrian.klaver@aklaver.com> — 2025-09-24T23:03:10Z

    On 9/24/25 10:02, Samuel Marks wrote:
    > On Wed, Sep 24, 2025 at 10:13 AM Adrian Klaver 
    
    > Yes but it's meant to divide by zero. That cancels the whole transaction 
    > stopping it from going through. It being a transaction lets me guarantee 
    > that at point of update or insert [upsert] the org owner matches the 
    > requestor.
    > 
    > I would preference a single statement (one semicolon) solution; but for 
    > now at least this works 🤷
    
    I don't have enough experience with below to come up with an off the top 
    of my head examples, but they look like they may offer alternatives.
    
    MERGE:
    
    https://www.postgresql.org/docs/current/sql-merge.html
    
    and/or Row level Security:
    
    https://www.postgresql.org/docs/current/ddl-rowsecurity.html
    
    In above link see example that starts below the phrase:
    
    "... If it is necessary to consult other rows or other tables to make a 
    policy decision, that can be accomplished using sub-SELECTs, or 
    functions that contain SELECTs, in the policy expressions. ... "
    
    
    -- 
    Adrian Klaver
    adrian.klaver@aklaver.com
    
    
    
    
  17. Re: How do I upsert depending on a second table?

    Adrian Klaver <adrian.klaver@aklaver.com> — 2025-09-26T22:17:02Z

    On 9/24/25 16:03, Adrian Klaver wrote:
    > On 9/24/25 10:02, Samuel Marks wrote:
    >> On Wed, Sep 24, 2025 at 10:13 AM Adrian Klaver 
    
    > I don't have enough experience with below to come up with an off the top 
    > of my head examples, but they look like they may offer alternatives.
    > 
    > MERGE:
    > 
    > https://www.postgresql.org/docs/current/sql-merge.html
    > 
    
    First time working with MERGE, so approach the below with caution:
    
    CREATE TABLE org
    (
         "name"      VARCHAR(50) PRIMARY KEY,
         owner       VARCHAR(50) NOT NULL
    );
    
    
    CREATE TABLE repo
    (
         "id"           INTEGER PRIMARY KEY,
         full_name      VARCHAR(255) UNIQUE NOT NULL,
         org            VARCHAR(50)         NOT NULL REFERENCES org ("name")
    );
    
    
    INSERT INTO org(name, owner) VALUES ('org0', 'user0');
    
    WITH t AS (SELECT
         *
    FROM
         org
    RIGHT JOIN
         (values(0 , 'org0/name0 by wrong user', 'org0', 'wrong_user'))
             AS v(id, full_name, org, user_name)
          ON
             org.name = v.org
          AND
             org.owner = v.user_name
    )
    MERGE INTO repo as r
    USING t
    ON
         r.org = t.name
    
    WHEN MATCHED AND t.id = r.id THEN
         UPDATE  SET (id, full_name, org) = (t.id, t.full_name, t.org)
    WHEN NOT MATCHED AND t.user_name = COALESCE(t.owner, '') THEN
         INSERT VALUES(t.id, t.full_name, t.org)
    
    RETURNING r.*;
    
      id | full_name | org
    ----+-----------+-----
    (0 rows)
    
    MERGE 0
    
    
    select * from repo ;
      id | full_name | org
    ----+-----------+-----
    
    WITH t AS (SELECT
         *
    FROM
         org
    RIGHT JOIN
         (values(0 , 'org0/name0 by right user', 'org0', 'user0'))
             AS v(id, full_name, org, user_name)
          ON
             org.name = v.org
          AND
             org.owner = v.user_name
    )
    MERGE INTO repo as r
    USING t
    ON
         r.org = t.name
    
    WHEN MATCHED AND t.id = r.id THEN
         UPDATE  SET (id, full_name, org) = (t.id, t.full_name, t.org)
    WHEN NOT MATCHED AND t.user_name = COALESCE(t.owner, '') THEN
         INSERT VALUES(t.id, t.full_name, t.org)
    
    RETURNING r.*;
      id |        full_name         | org
    ----+--------------------------+------
       0 | org0/name0 by right user | org0
    (1 row)
    
    MERGE 1
    
    select * from repo ;
      id |        full_name         | org
    ----+--------------------------+------
       0 | org0/name0 by right user | org0
    
    WITH t AS (SELECT
         *
    FROM
         org
    RIGHT JOIN
         (values(0 , 'org0/name0 by right user update', 'org0', 'user0'))
             AS v(id, full_name, org, user_name)
          ON
             org.name = v.org
          AND
             org.owner = v.user_name
    )
    MERGE INTO repo as r
    USING t
    ON
         r.org = t.name
    
    WHEN MATCHED AND t.id = r.id THEN
         UPDATE  SET (id, full_name, org) = (t.id, t.full_name, t.org)
    WHEN NOT MATCHED AND t.user_name = COALESCE(t.owner, '') THEN
         INSERT VALUES(t.id, t.full_name, t.org)
    
    RETURNING r.*;
      id |            full_name            | org
    ----+---------------------------------+------
       0 | org0/name0 by right user update | org0
    (1 row)
    
    select * from repo ;
      id |            full_name            | org
    ----+---------------------------------+------
       0 | org0/name0 by right user update | org0
    (1 row)
    
    
    -- 
    Adrian Klaver
    adrian.klaver@aklaver.com