ON CONFLlCT DO UPDATE command cannot affect row a second time - Mailing list pgsql-novice

From cryptodactyl
Subject ON CONFLlCT DO UPDATE command cannot affect row a second time
Date
Msg-id 1588267624876-0.post@n3.nabble.com
Whole thread Raw
Responses Re: ON CONFLlCT DO UPDATE command cannot affect row a second time  ("David G. Johnston" <david.g.johnston@gmail.com>)
List pgsql-novice
This is a Python script that runs every night, but it stopped working. It
pulls data from a tool and moves them to Postgres databases.

In the Python shell I get the following error/hint:

psycopg2.ProgrammingError: ON CONFLICT DO UPDATE command cannot affect row a
second time

HINT: Ensure that no rows proposed for insertion within the same command
have duplicate constrained values.

Here is the code part:

def update_tbl(job):
""" Try to insert new record into the table. If that
    record already exists, then update that row. This is based
    off primary keys marked in the settings file."""

to_tbl = job.get('to_table')
from_tbl = get_temp_name(job['to_table'])
col_map = job.get('col_map')
cols = ', '.join([x[1] for x in col_map])
sel_cols = ', '.join(map(
    lambda x: x[1] if len(x) <= 4 else x[4].replace('__alias__', x[1]), 
    col_map))
uids = ', '.join([x[1] for x in col_map if x[3]])
exclusions = list(filterfalse(lambda x: x[3], col_map))
live_excs = ', '.join([x[1] for x in exclusions])
temp_excs = ', '.join(["Excluded.%s" %(x[1]) for x in exclusions])
clause_up = build_exc_clause(col_map, 'live', 'Excluded')
conn = get_conn(job.get('to_db'))

with conn.cursor() as cur:
    upsert = """
        INSERT INTO {t} as live ({cols}) (
            SELECT {sel_cols} FROM {f}
        )
    """.format(t=to_tbl, f=from_tbl, cols=cols, sel_cols=sel_cols)

    # If there are no fields other than primary keys
    #  (such as document_type_map),
    # just do the insert
    if job.get("noupdateondupe", False):
           upsert += """
            ON CONFLICT ({uids})
            DO NOTHING
        """.format(uids=uids)
    elif len(uids) > 0 and len(live_excs) > 0:
        upsert += """
            ON CONFLICT ({uids})
            DO UPDATE SET ({lexc}) = ({exc})
            WHERE ({c})
        """.format(uids=uids, lexc=live_excs, exc = temp_excs, c=clause_up)

    logging.info("about to update %s"%(to_tbl))
    logging.info(upsert)

    try:
        # logging.info("upsert query")
        # logging.info(upsert)
        cur.execute(upsert)
        conn.commit()
    except IntegrityError:
        conn.rollback()
        if job.get('add_sequence', False):
            add_sequence(to_tbl, job)
            cur.execute(upsert)
            conn.commit()
            logging.info(upsert)
        else:
            raise
conn.close()
I found this in a "utils" file which could be helpful since it has "upsert"
in it:

# If there are no fields other than primary keys
    #  (such as document_type_map),
    # just do the insert
    if job.get("noupdateondupe", False):
           upsert += """
            ON CONFLICT ({uids})
            DO NOTHING
        """.format(uids=uids)
    elif len(uids) > 0 and len(live_excs) > 0:
        upsert += """
            ON CONFLICT ({uids})
            DO UPDATE SET ({lexc}) = ({exc})
            WHERE ({c})
        """.format(uids=uids, lexc=live_excs, exc = temp_excs, c=clause_up)

    logging.info("about to update %s"%(to_tbl))
    logging.info(upsert)

    try:
        # logging.info("upsert query")
        # logging.info(upsert)
        cur.execute(upsert)
        conn.commit()
    except IntegrityError:
        conn.rollback()
        if job.get('add_sequence', False):
            add_sequence(to_tbl, job)
            cur.execute(upsert)
            conn.commit()
            logging.info(upsert)
        else:
            raise
conn.close()
So in the log file, the script seems to work until here and the last bit of
text shows:

2020-04-30 10:33:18,164 about to update my_data
2020-04-30 10:33:18,164 
            INSERT INTO my_data as live (partid, id, refnum, originalnum,
catalognum, catalogpnwp, originalMFG, manufacturerid, originaldescription,
originalrevision, contentid, status_id, createddate, comment, na_id,
na_date, site_id) (
                SELECT partid, id, refnum, originalnum, catalognum,
catalogpnwp, originalMFG, manufacturerid, originaldescription,
originalrevision, contentid, status_id, createddate, comment, na_id,
na_date, site_id FROM temp_my_data
            )

                ON CONFLICT (id)
                DO UPDATE SET (partid, refnum, originalnum, catalognum,
catalogpnwp, originalMFG, manufacturerid, originaldescription,
originalrevision, contentid, status_id, createddate, comment, na_id,
na_date, site_id) = (Excluded.partid, Excluded.refnum, Excluded.originalnum,
Excluded.catalognum, Excluded.catalogpnwp, Excluded.originalMFG,
Excluded.manufacturerid, Excluded.originaldescription,
Excluded.originalrevision, Excluded.contentid, Excluded.status_id,
Excluded.createddate, Excluded.comment, Excluded.na_id, Excluded.na_date,
Excluded.site_id)
                WHERE (live.id = Excluded.id)
and the script stops working.

I'm not a database guy and certainly not a Python expert. How do I fix this
?



--
Sent from: https://www.postgresql-archive.org/PostgreSQL-novice-f2132464.html



pgsql-novice by date:

Previous
From: David Roper
Date:
Subject: Re: Advice on a table structure
Next
From: "David G. Johnston"
Date:
Subject: Re: ON CONFLlCT DO UPDATE command cannot affect row a second time