On 2001-09-28, Hardy Merrill <[EMAIL PROTECTED]> wrote:

> I can think of 2 methods to handle this:
>   1. before doing the insert or update, for *EACH AND EVERY* unique
>      key defined for a table, do a select with the where clause
>      looking for equality on all the fields in the unique key - if any
>      of those selects find rows, then your insert or update would
>      cause a duplicate.

This is not a good solution -- in addition to being a performance drain,  
there's a race condition if two or more processes try this at the same  
time.

>   2. allow the database to detect the "duplicate key" error [...]
>
> I prefer number 2, but it has a big problem - what do you do about the
> fact that different databases return different error numbers and
> different error messages for the "duplicate" key situation? Do you
> just build a regular expression that attempts to catch all the
> different duplicate key error messages that could come back from
> different db's?

Better yet, use DBIx::AnyDBD to create a DBI wrapper class, in which you  
define your new insert_and_catch_duplicates() method; then create  
subclasses for each supported database that provide the server-specific  
information.

My recollection of the syntax isn't perfect, but you'd end up with  
something like this:

  package My::DBI::Default;

  sub insert_and_catch_duplicates {
    my $dbh = shift;
    eval {
      $sth = $dbh->prepare( shift );
      $rows_affected = $sth->execute( @_ );
    };
    if ($@) {
      if ( $dbh->_error_is_dup_key( $@ ) {
            # Your duplicate key handling code goes here
      } else {
        # Other error-handling, or just re-throw the error
        die( $@ );
      }
    }
  }

  package My::DBI::MySQL;

  sub _error_is_dup_key {
    my $dbh = shift;
    local $_ = shift;
    /duplicate key/;
  }

  package My::DBI::Oracle;

  sub _error_is_dup_key {
    my $dbh = shift;
    local $_ = shift;
    /key violation/;
  }

  package main;

  use DBIx::AnyDBD;
  my $dbh = DBIx::AnyDBD->connect("dbi:whatever", "user", "pass", {},
                 "My::DBI");
  $dbh->insert_and_catch_duplicates('insert into whatever whatever');

If you've connected to a MySQL server, your $dbh will automatically be  
reblessed into that package, and likewise for other server types.

It takes a little getting used to, but I'd heartily recommend  
DBIx::AnyDBD for almost any case in which you're trying to extend DBI  
with "high-level" functionality in a way that needs to work with multiple  
drivers and server types.

-Simon

Reply via email to