summaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java228
1 files 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<IndexInfo> infoList = new ArrayList<IndexInfo>();
+ List<String> pkProps = new ArrayList<String>();
try {
- String indexName = null;
- boolean unique = false;
- boolean clustered = false;
- List<String> indexProperties = new ArrayList<String>();
- List<Direction> directions = new ArrayList<Direction>();
-
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<S> 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<S> altKey = mainInfo.getAlternateKey(i);
- Set<? extends OrderedProperty<S>> 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<S> orderedProp : altKeyProps) {
- StorableProperty<S> 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<S>
(mainInfo, catalog, schema, tableName, qualifiedTableName, indexInfo, jProperties);
}
+ private static boolean matchesKey(Collection<String> 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<String> 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);
}