Finally, correct semantics for unique-valued indexes.
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.