Re: updating before insert

"Eric S. Johansson" <[email protected]>
Newsgroups gmane.comp.python.db.pysqlite.user
Message-ID <[email protected]>
=?UTF-8?Q?Gerhard=20H=C3=A4ring=20 wrote:
> On Fri, 10 Aug 2007 00:53:51 -0400, "Eric S. Johansson" <[email protected]> wrote:
>> I've tried a couple different ways of doing this but I'm screwing
>> something up.  I know this is part SQL but it's also part Python and how
>> they interfere... I mean interacts with each other.
>>
>> What's the right way to update a few fields in a record if the record
>> exists otherwise, create the record?
> 
> Oracle has the nonstandard MERGE statement that comes in very handy for such cases. SQLite has "INSERT OR REPLACE", which AFAIK is less powerful (I think it does a UNIQUE check on all columns).
> 
> What I'd recommend is this:
> 
> Create a UNIQUE INDEX on the columns in question.
> 
> Then just INSERT from pysqlite. If this fails with an IntegrityError, do the equivalent UPDATE instead. Then COMMIT.
> 

I was afraid you were going to say that because that's what I am doing already.


address_table = '''
my_address      TEXT,
their_address   TEXT,
last_seen       TIMESTAMP,
PRIMARY KEY (my_address, their_address)
'''

Except I just noticed its primary key instead of unique index.   reading up on 
table creation apparently you can describe keys as unique.  Is there anyway to 
retrofit uniqueness to a key?  Can I define  UNIQUE PRIMARY KEY?

and this is the code for an insertion/update


         insert_command = "insert into %s (my_address, their_address, last_seen) 
values (?, ?, ?)" % self.table_name
         update_command = "update %s set my_address=?, their_address=?, 
last_seen=? where (my_address=? and their_address=?)" % self.table_name

         now = datetime.date.today()

         try:
             self.cursor.execute(insert_command, (user_address, their_address, now))
         except Exception, error:
             #print error
             self.cursor.execute(update_command, (user_address, their_address, 
now, user_address, their_address))

         self.connection.commit()

as for locking, I have no problem with that.  I can lock shared for everything 
except writing which gets a lock exclusive which is blocked until all of the 
shareds are done.

>> Related is:
>>
>> What's the right way to test for a record (including a predicate against
>> some of the fields (specifically timestamp calculations)) and if the
>> record doesn't exist, add it.
> 
> There's no way to do this reliably without locking the whole table. So do as I described: INSERT. If fails, then UPDATE. No race conditions here.
> 
okay.  I think I will lock in both cases because there is a race condition in 
each case.  In the first case (update versus insert) the race condition is the 
between the insert and the update.  Admittedly, it's a small window but one 
nevertheless.  I should just grab an exclusive lock before the insert and 
release after the commit.  The other problem where I  test first (select)I 
should also grab exclusive lock.  Then around all other queries I should place a 
shared lock.

After all of Dijkstra's work with semaphores, one would think that the bright 
people that create SQL could have been a little smarter about locking.  This 
whole transaction/retries stuff is a tad fuzzy.

---eric


-- 
Speech-recognition in use.  It makes mistakes, I correct some.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.