Finally, correct semantics for unique-valued indexes. BLD200403071900
authorjsichi@netbeans.org
Sun, 07 Mar 2004 03:27:01 +0000
changeset 14711abee244c60d
parent 1470 9ccb68adc09c
child 1472 1bb7ea5f050d
Finally, correct semantics for unique-valued indexes.
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcMultivaluedIndex.java
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcStorageException.java
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/package.html
     1.1 --- a/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcMultivaluedIndex.java	Fri Mar 05 00:33:57 2004 +0000
     1.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcMultivaluedIndex.java	Sun Mar 07 03:27:01 2004 +0000
     1.3 @@ -17,6 +17,8 @@
     1.4  
     1.5  import java.util.*;
     1.6  import java.io.*;
     1.7 +import java.text.*;
     1.8 +import java.sql.*;
     1.9  
    1.10  /**
    1.11   * JdbcMultivaluedIndex implements the MDR MultivaluedIndex interface using
    1.12 @@ -29,8 +31,9 @@
    1.13      extends JdbcIndex implements MultivaluedIndex
    1.14  {
    1.15      protected LazyPreparedStatement sqlDeleteWithValue;
    1.16 +    protected LazyPreparedStatement sqlDeleteWithSurrogate;
    1.17      protected LazyPreparedStatement sqlFindCount;
    1.18 -    protected LazyPreparedStatement sqlFindCountWithValue;
    1.19 +    protected LazyPreparedStatement sqlFindSurrogate;
    1.20      
    1.21      protected void defineSql()
    1.22      {
    1.23 @@ -41,12 +44,18 @@
    1.24              + " where " + keyColName + " = ?"
    1.25              + " and " + valColName + " = ?");
    1.26          
    1.27 +        sqlDeleteWithSurrogate = new LazyPreparedStatement(
    1.28 +            "delete from " + tableName
    1.29 +            + " where " + keyColName + " = ?"
    1.30 +            + " and " + JdbcStorage.SURROGATE_COL_NAME + " = ?");
    1.31 +        
    1.32          sqlFindCount = new LazyPreparedStatement(
    1.33              "select count(*) from " + tableName
    1.34              + " where " + keyColName + " = ?");
    1.35 -        
    1.36 -        sqlFindCountWithValue = new LazyPreparedStatement(
    1.37 -            "select count(*) from " + tableName
    1.38 +
    1.39 +        sqlFindSurrogate = new LazyPreparedStatement(
    1.40 +            "select " + JdbcStorage.SURROGATE_COL_NAME
    1.41 +            + " from " + tableName
    1.42              + " where " + keyColName + " = ?"
    1.43              + " and " + valColName + " = ?");
    1.44      }
    1.45 @@ -74,41 +83,28 @@
    1.46          return !needSurrogate;
    1.47      }
    1.48  
    1.49 -    // override JdbcIndex
    1.50 -    public void add(Object key, Object value) throws StorageException
    1.51 -    {
    1.52 -        if (!needSurrogate) {
    1.53 -            // REVIEW: Values are supposed to be unique.  Apparently this means
    1.54 -            // duplicates are discarded rather than causing errors, since
    1.55 -            // during MOF boot duplicate keys are generated.  It would be most
    1.56 -            // efficient to get the INSERT statement to detect the problem and
    1.57 -            // suppress the resulting exception; however, that requires
    1.58 -            // detailed backend knowledge.  Instead, we prevent the error by
    1.59 -            // checking first.  What I don't understand is why this isn't an
    1.60 -            // issue with BtreeStorage, which also enforces constraints.
    1.61 -            int n = storage.getSingletonInt(
    1.62 -                sqlFindCountWithValue,
    1.63 -                new Object[]{key,value});
    1.64 -            if (n == 1) {
    1.65 -                /*
    1.66 -                System.out.println(
    1.67 -                    "duplicate detected for index " + getName()
    1.68 -                    + ":  key = " + key + ", value = " + value);
    1.69 -                */
    1.70 -                return;
    1.71 -            }
    1.72 -            // TODO:  assert n == 0
    1.73 -        }
    1.74 -        addImpl(key,value);
    1.75 -    }
    1.76 -
    1.77      // implement MultivaluedIndex
    1.78      public boolean remove(Object key, Object value) throws StorageException
    1.79      {
    1.80 -        // REVIEW:  spec says we're only supposed to delete the "first"
    1.81 -        // one; does this matter?
    1.82 -        return storage.executeUpdate(
    1.83 -            sqlDeleteWithValue,new Object[]{key,value}) > 0;
    1.84 +        Object [] pair = new Object[]{key,value};
    1.85 +
    1.86 +        int rowCount;
    1.87 +        if (isUnique()) {
    1.88 +            // since this index is unique, don't need to worry about
    1.89 +            // duplicate handling
    1.90 +            rowCount = storage.executeUpdate(sqlDeleteWithValue,pair);
    1.91 +        } else {
    1.92 +            // there may be duplicates, and we're only supposed to delete
    1.93 +            // one of them
    1.94 +            int surrogateKey = storage.getSingletonInt(sqlFindSurrogate,pair);
    1.95 +            if (surrogateKey == -1) {
    1.96 +                return false;
    1.97 +            }
    1.98 +            rowCount = storage.executeUpdate(
    1.99 +                sqlDeleteWithSurrogate,
   1.100 +                new Object[]{key,new Integer(surrogateKey)});
   1.101 +        }
   1.102 +        return rowCount > 0;
   1.103      }
   1.104      
   1.105      // implement MultivaluedIndex
   1.106 @@ -204,10 +200,12 @@
   1.107          // implement Iterator
   1.108          public void remove()
   1.109          {
   1.110 -            // REVIEW:  if values aren't unique, this will remove
   1.111 -            // all matching values; does this matter?  if so, need
   1.112 -            // to remember the surrogate and use it to narrow delete
   1.113              try {
   1.114 +                // NOTE:  in the presence of duplicates, this won't necessarily
   1.115 +                // delete the one we're on, but it doesn't really matter
   1.116 +                // since the duplicates are externally indistinguishable
   1.117 +                // (they only differ in their surrogate keys, which are
   1.118 +                // totally internal)
   1.119                  JdbcMultivaluedIndex.this.remove(key,value);
   1.120              } catch (StorageException ex) {
   1.121                  throw new RuntimeStorageException(ex);
     2.1 --- a/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcStorageException.java	Fri Mar 05 00:33:57 2004 +0000
     2.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcStorageException.java	Sun Mar 07 03:27:01 2004 +0000
     2.3 @@ -24,7 +24,7 @@
     2.4   *
     2.5   * @see StorageIOException
     2.6   */
     2.7 -public class JdbcStorageException extends StorageException
     2.8 +public class JdbcStorageException extends StorageBadRequestException
     2.9  {
    2.10      /**
    2.11       * The original SQLException that we are converting to a StorageException.
     3.1 --- a/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/package.html	Fri Mar 05 00:33:57 2004 +0000
     3.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/package.html	Sun Mar 07 03:27:01 2004 +0000
     3.3 @@ -277,17 +277,6 @@
     3.4  <ul>
     3.5  
     3.6  <li>
     3.7 -I wasn't clear on the semantics of unique-valued MultivaluedIndexes,
     3.8 -and memoryimpl and btreeimpl didn't provide a consistent answer.  The
     3.9 -MOF boot sequence resulted in some attempts to insert duplicates.  To
    3.10 -prevent this from causing a primary key violation, I put in an extra
    3.11 -existence check on each insert, and skipped the insert if the value
    3.12 -already existed.  Also, I didn't strictly follow the spec for
    3.13 -non-unique values:  remove(key,value) removes all, not just the
    3.14 -"first" one (since "first" has no meaning for an unordered index), and
    3.15 -the same goes for the iter.remove() equivalent. 
    3.16 -
    3.17 -<li>
    3.18  I did not implement keySet() and values() for JdbcPrimaryIndex because
    3.19  in my testing I never saw them get called.  Nor did I implement
    3.20  queryByKeyPrefix() for any of the indexes, or fail-fast iterators anywhere.