From a4aa08342e5afe143df42a641a23efd24a0cafb0 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Mon, 13 Aug 2007 19:37:50 +0000 Subject: Allow actual primary key definition to be declared as an alternate key. --- .../repo/jdbc/JDBCStorableIntrospector.java | 228 +++++++-------------- 1 file changed, 74 insertions(+), 154 deletions(-) diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java index 84abe42..6188bfa 100644 --- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java +++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java @@ -22,10 +22,12 @@ import java.io.IOException; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -387,192 +389,84 @@ public class JDBCStorableIntrospector extends StorableIntrospector { throw new MismatchException(mainInfo.getStorableType(), errorMessages); } - // Gather index info... - IndexInfo[] indexInfo = new IndexInfo[0]; - boolean hasIndexInfo = false; - - /* Oracle driver has a bug that always causes an analyze to run when - requesting index info. Checking indexes is not that important so - don't bother checking. Revisit if Oracle bug ever gets fixed. - gatherIndexInfo: { - if (resolvedTableName == null) { - indexInfo = new IndexInfo[0]; - break gatherIndexInfo; - } + // Now verify that primary or alternate keys match. + if (resolvedTableName != null) checkPrimaryKey: { ResultSet rs; try { - rs = meta.getIndexInfo(catalog, schema, resolvedTableName, false, true); + rs = meta.getPrimaryKeys(catalog, schema, resolvedTableName); } catch (SQLException e) { getLog().info - ("Unable to get index info for table \"" + resolvedTableName + + ("Unable to get primary keys for table \"" + resolvedTableName + "\" with catalog " + catalog + " and schema " + schema + ": " + e); - indexInfo = new IndexInfo[0]; - break gatherIndexInfo; + break checkPrimaryKey; } - List infoList = new ArrayList(); + List pkProps = new ArrayList(); try { - String indexName = null; - boolean unique = false; - boolean clustered = false; - List indexProperties = new ArrayList(); - List directions = new ArrayList(); - while (rs.next()) { - if (rs.getInt("TYPE") == DatabaseMetaData.tableIndexStatistic) { - // Ignore this type. - continue; - } + String columnName = rs.getString("COLUMN_NAME"); + String propertyName = columnToProperty.get(columnName); - String propertyName = columnToProperty.get(rs.getString("COLUMN_NAME")); if (propertyName == null) { - // Ignore indexes on unknown columns. + errorMessages.add + ("Column \"" + columnName + + "\" must be part of primary or alternate key"); continue; } - String nextName = rs.getString("INDEX_NAME"); - - if (indexName != null && !indexName.equals(nextName)) { - infoList.add(new IndexInfoImpl(indexName, unique, clustered, - indexProperties.toArray(new String[0]), - directions.toArray(new Direction[0]))); - indexProperties.clear(); - directions.clear(); - } - - indexName = nextName; - unique = !rs.getBoolean("NON_UNIQUE"); - clustered = rs.getInt("TYPE") == DatabaseMetaData.tableIndexClustered; - - String ascOrDesc = rs.getString("ASC_OR_DESC"); - Direction direction = Direction.UNSPECIFIED; - if ("A".equals(ascOrDesc)) { - direction = Direction.ASCENDING; - } else if ("D".equals(ascOrDesc)) { - direction = Direction.DESCENDING; - } - - indexProperties.add(propertyName); - directions.add(direction); - } - - if (indexProperties.size() > 0) { - infoList.add(new IndexInfoImpl(indexName, unique, clustered, - indexProperties.toArray(new String[0]), - directions.toArray(new Direction[0]))); + pkProps.add(propertyName); } } finally { rs.close(); } - indexInfo = infoList.toArray(new IndexInfo[0]); - hasIndexInfo = true; - } - */ - - // Now verify that primary keys match. - - // As primary keys are found, remove from columnToProperty map. + if (errorMessages.size() > 0) { + // Skip any extra checks. + break checkPrimaryKey; + } - if (resolvedTableName != null) checkPrimaryKey: { - ResultSet rs; - try { - rs = meta.getPrimaryKeys(catalog, schema, resolvedTableName); - } catch (SQLException e) { - getLog().info - ("Unable to get primary keys for table \"" + resolvedTableName + - "\" with catalog " + catalog + " and schema " + schema + ": " + e); + if (pkProps.size() == 0) { + // If no primary keys are reported, don't even bother checking. + // There's no consistent way to get primary keys, and entities + // like views and synonyms don't usually report primary keys. + // A primary key might even be logically defined as a unique + // constraint. break checkPrimaryKey; } - try { - if (!rs.next()) { - // If no primary keys are reported, don't even bother checking. - // There's no consistent way to get primary keys, and entities - // like views and synonyms don't usually report primary keys. - // A primary key might even be logically defined as a unique - // constraint. - break checkPrimaryKey; - } - do { - String columnName = rs.getString("COLUMN_NAME"); - String propertyName = columnToProperty.remove(columnName); + if (matchesKey(pkProps, mainInfo.getPrimaryKey())) { + // Good. Primary key in database is same as in Storable. + break checkPrimaryKey; + } - if (propertyName == null) { - errorMessages.add - ("Column \"" + columnName + "\" must be part of primary key"); - continue; - } + // Check if Storable has an alternate key which matches the + // database's primary key. + boolean foundAnyAltKey = false; + for (StorableKey altKey : mainInfo.getAlternateKeys()) { + if (matchesKey(pkProps, altKey)) { + // Okay. Primary key in database matches a Storable + // alternate key. + foundAnyAltKey = true; - StorableProperty mainProperty = mainProperties.get(propertyName); + // Also check that declared primary key is a strict subset + // of the alternate key. If not, keep checking alt keys. - if (!mainProperty.isPrimaryKeyMember()) { - errorMessages.add - ("Property \"" + propertyName + "\" must be part of primary key"); + if (matchesSubKey(pkProps, mainInfo.getPrimaryKey())) { + break checkPrimaryKey; } - } while (rs.next()); - } finally { - rs.close(); - } - - // All remaining properties must not have a primary key annotation. - for (String propertyName : columnToProperty.values()) { - StorableProperty mainProperty = mainProperties.get(propertyName); - - if (mainProperty.isPrimaryKeyMember()) { - errorMessages.add - ("Property \"" + propertyName + "\" cannot be part of primary key"); } } - } - // Verify AlternateKey annotations are backed by unique indexes. Unlike - // for primary keys, there is no requirement that unique indexes must - // have an AlternateKey annotation. - if (hasIndexInfo) { - // Note the deep nesting of loops. I hope the index sets are small. - - int altKeyCount = mainInfo.getAlternateKeyCount(); - altKeyScan: - for (int i=altKeyCount; --i>=0; ) { - StorableKey altKey = mainInfo.getAlternateKey(i); - Set> altKeyProps = altKey.getProperties(); - indexMatch: - for (int j=indexInfo.length; --j>=0; ) { - IndexInfo ii = indexInfo[j]; - if (ii.isUnique()) { - String[] indexPropNames = ii.getPropertyNames(); - if (indexPropNames.length == altKeyProps.size()) { - propertyMatch: - for (OrderedProperty orderedProp : altKeyProps) { - StorableProperty altKeyProp = - orderedProp.getChainedProperty().getPrimeProperty(); - String keyPropName = altKeyProp.getName(); - for (int k=indexPropNames.length; --k>=0; ) { - if (indexPropNames[k].equals(keyPropName)) { - // This property matched... - continue propertyMatch; - } - } - // Didn't match a property, so move on to next index. - continue indexMatch; - } - // Fully matched an index, move on to next alt key. - continue altKeyScan; - } - } - } - // No indexes match, so error. - StringBuilder buf = new StringBuilder(); - buf.append("No matching unique index for alternate key: "); - try { - altKey.appendTo(buf); - } catch (IOException e) { - // Not gonna happen. - } - errorMessages.add(buf.toString()); + if (foundAnyAltKey) { + errorMessages.add("Actual primary key matches a declared alternate key, " + + "but declared primary key must be a strict subset. " + + mainInfo.getPrimaryKey().getProperties() + + " is not a subset of " + pkProps); + } else { + errorMessages.add("Actual primary key does not match any " + + "declared primary or alternate key: " + pkProps); } } @@ -580,10 +474,36 @@ public class JDBCStorableIntrospector extends StorableIntrospector { throw new MismatchException(mainInfo.getStorableType(), errorMessages); } + // IndexInfo is empty, as querying for it tends to cause a table analyze to run. + IndexInfo[] indexInfo = new IndexInfo[0]; + return new JInfo (mainInfo, catalog, schema, tableName, qualifiedTableName, indexInfo, jProperties); } + private static boolean matchesKey(Collection keyProps, StorableKey declaredKey) { + if (keyProps.size() != declaredKey.getProperties().size()) { + return false; + } + return matchesSubKey(keyProps, declaredKey); + } + + /** + * @return true if declared key properties are all found in the given keyProps set + */ + private static boolean matchesSubKey(Collection keyProps, StorableKey declaredKey) { + for (OrderedProperty declaredKeyProp : declaredKey.getProperties()) { + ChainedProperty chained = declaredKeyProp.getChainedProperty(); + if (chained.getChainCount() > 0) { + return false; + } + if (!keyProps.contains(chained.getLastProperty().getName())) { + return false; + } + } + return true; + } + private static Log getLog() { return LogFactory.getLog(JDBCStorableIntrospector.class); } -- cgit v1.2.3