mysqlpython-3.xpeeweeaws-aurora-serverless

Python peewee, using EXCLUDED to resolve conflict resolution


I am trying to upsert a record, using the syntax shown here.

from peewee import EXCLUDED
query = (MyTable.insert(
    id=sched.id,
    idenc=sched.idenc,
    createdon=sched.createdon,
    modifiedon=sched.modifiedon,
    deletedon=sched.deletedon,
    canceledon=sched.canceledon,
    is_deleted=sched.is_deleted,
    start_date=sched.start_date,
    end_date=sched.end_date,
    label=sched.label
)
    .on_conflict(conflict_target=[MyTable.id,
                                  MyTable.start_date,
                                  MyTable.end_date],
                 update={
                     MyTable.modifiedon: EXCLUDED.modifiedon,
                     MyTable.label: EXCLUDED.label
    },
    where=(EXCLUDED.modifiedon > MyTable.modifiedon)))
query.execute()

The idea is to update the entry if it is the latest, where modifiedon is a varchar field holding a timestamp, for example: 2022-11-22T17:00:34.965Z

This creates sql statement as shown below,

(
    'INSERT INTO "mytable" ("id", "idenc", "createdon", "modifiedon", "deletedon", "canceledon", "isdeleted", "startdate", "enddate", "label") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT ("id", "startdate", "enddate") DO UPDATE SET "modifiedon" = EXCLUDED."modifiedon", "label" = EXCLUDED."label" WHERE
     (EXCLUDED."modifiedon" > "mytable"."modifiedon")',
    [ 'ymmqzHviWsMgabzPTEKU',
    'ebbe37ec-cb75-5bc6-9466-b170a458c469',
    '2022-11-22T17:00:05.175Z',
    '2022-12-05T11:23:31.563569Z',
    '',
    'None',
    False,
    '2022-11-21T18:01:00.000Z',
    '2022-12-31T17:59:00.000Z',
    '1' ]
)

But this does nothing and no exception is thrown either. Running the query straight on MySQL raises syntax error.

Any idea?

[EDIT]

To add more to the context, how do I upsert a row, with a where condition. Here, the primary key is composite(id,start_date, end_date), So when an entry with more recent modifiedon is already in the db, how do I make sure that I am not overwriting the latest one?

Theoretically speaking, an INSERT/REPLACE with a WHERE clause on a non primary key. Is that possible with peewee?

Currently what I'm doing is,

item_existing, created = MyTable.get_or_create()
if not created:
 if item_existing.modifiedon < item.modifiedon:
   Mytable.replace()

But this is prone to race condition. Any other good way to solve this?

PS: DB Engine: 5.7.mysql_aurora.2.08.3


Solution

  • MySQL does not not support the specification of conflict targets, nor does it support EXCLUDED, nor does it support a WHERE clause. It also depends, somewhat, on whether you're using MySQL or MariaDB as they each are slightly different, but the jist is that you have limited options and can only choose to update certain values.

    Attempting to use those APIs should be raising an exception, so perhaps you are on an older version of Peewee.

    Basically with MySQL you can choose to preserve data that would be inserted (e.g., overwrite the existing data with the data for the columns listed in preserve), or you can update certain columns with entirely different values.

    Example of inserting a row and, if it already exists, just applying a change to it's "modified":

    (Table
     .insert({'created': now(), 'modified': now(), 'name': 'Something'})
     .on_conflict(preserve=[Table.modified])
     .execute())
    

    Example of inserting a row and, if it already exists, setting its "modified" to something entirely different:

    (Table
     .insert({'created': now(), 'modified': now(), 'name': 'Something'})
     .on_conflict(update={Table.modified: something_totally_different()})
     .execute())