Repository: ambari
Updated Branches:
  refs/heads/trunk a24262182 -> 346dfe7eb


http://git-wip-us.apache.org/repos/asf/ambari/blob/346dfe7e/ambari-server/src/test/java/org/apache/ambari/server/orm/db/DDLTests.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/orm/db/DDLTests.java 
b/ambari-server/src/test/java/org/apache/ambari/server/orm/db/DDLTests.java
new file mode 100644
index 0000000..779c937
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/db/DDLTests.java
@@ -0,0 +1,277 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.orm.db;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+
+/**
+ * Test to check the sanity and conisistence of DDL scripts for different SQL 
dialects.
+ * (e.g. no unnamed constraints, the same tables with the same columns and 
constraints must exist)
+ */
+public class DDLTests {
+  private static final Logger LOG = 
LoggerFactory.getLogger(DDLTestUtils.class);
+
+  private static final int EXPECTED_ALTER_TABLE_COUNT = 1;
+
+  @Test
+  public void testVerifyDerby() throws Exception {
+    verifyDDL("Derby");
+  }
+
+  @Test
+  public void testVerifyPostgres() throws Exception {
+    verifyDDL("Postgres");
+  }
+
+  @Test
+  public void testVerifyPostgresEmbedded() throws Exception {
+    verifyDDL("Postgres-EMBEDDED");
+  }
+
+  @Test
+  public void testVerifyMySQL() throws Exception {
+    verifyDDL("MySQL");
+  }
+
+  @Test
+  public void testVerifyOracle() throws Exception {
+    verifyDDL("Oracle");
+  }
+
+  @Test
+  public void testVerifySqlAnywhere() throws Exception {
+    verifyDDL("SQLAnywhere");
+  }
+
+  @Test
+  public void testVerifyMsSqlServer() throws Exception {
+    verifyDDL("SQLServer");
+  }
+
+  /**
+   * To verify if DDL have certain characteristics:
+   * <ul>
+   *   <li>There are no unnamed constraints</li>
+   *   <li>Alter tables are only used in exceptional cases</li>
+   *   <li>Table and constraint name lenghts doesn't exceed 30 (a restriction 
imposed by Oracle)</li>
+   * </ul>
+   *
+   */
+  private void verifyDDL(String dbType) throws Exception {
+    LOG.info("Checking DDL for {}", dbType);
+    DDL ddl = DDLTestUtils.getDdl(dbType);
+    printDDLMetrics(ddl);
+
+    // check for unwanted alter tables
+    Assert.assertEquals("Expected count of alter tables mismatch. Please 
include all constraint definitions in " +
+            "the create table statement, only use alter table in exceptional 
cases, such as to work around a circular " +
+            "FK dependency. Would another such case occur, please document it 
in the DDL's and adjust the " +
+            "EXPECTED_ALTER_TABLE_COUNT in this test.",
+        EXPECTED_ALTER_TABLE_COUNT,
+        ddl.alterTables.size());
+
+    // check for too long table/constraint names
+    for (String tableName: ddl.tableNames()) {
+      Assert.assertTrue("Table name exceeds the 30 character limit: " + 
tableName, tableName.length() <= 30);
+    }
+    for (Table table: ddl.tables.values()) {
+      Assert.assertTrue("PK name exceeds the 30 character limit: " + 
table.primaryKey,
+          !table.primaryKey.isPresent() || 
table.primaryKey.get().name().length() <= 30);
+      for (Constraint constr: Sets.union(table.foreignKeys, 
table.uniqueConstraints)) {
+        Assert.assertTrue("Constraint name exceeds the 30 character limit: " + 
constr, constr.name().length() <= 30);
+      }
+    }
+    // check for unnamed PK's (skip quartz tables)
+    for (Table table: ddl.tables.values()) {
+      Assert.assertFalse("Unnamed PK exists for table: " + table.name,
+          !table.name.startsWith("qrtz") && table.primaryKey.isPresent() && 
table.primaryKey.get().name().equals("<default>"));
+      for (Constraint constr: Sets.union(table.foreignKeys, 
table.uniqueConstraints)) {
+        Assert.assertTrue("Constraint name exceeds the 30 character limit: " + 
constr, constr.name().length() <= 30);
+      }
+    }
+
+  }
+
+  @Test
+  public void testComparePostgresEmbedded() throws Exception {
+    compareAgainstPostgres("Postgres-EMBEDDED");
+  }
+
+  @Test
+  public void testCompareDerby() throws Exception {
+    compareAgainstPostgres("Derby");
+  }
+
+  @Test
+  public void testCompareOracle() throws Exception {
+    compareAgainstPostgres("Oracle");
+  }
+
+  @Test
+  public void testCompareMySQL() throws Exception {
+    compareAgainstPostgres("MySQL");
+  }
+
+  @Test
+  public void testCompareSQLAnywhere() throws Exception {
+    compareAgainstPostgres("SQLAnywhere");
+  }
+
+  @Test
+  public void testCompareSQLServer() throws Exception {
+    compareAgainstPostgres("SQLServer");
+  }
+
+  static void compareAgainstPostgres(String dbType) throws Exception {
+    LOG.info("Comparing {} against Postgres", dbType);
+    DDL postgres = DDLTestUtils.getDdl("Postgres");
+    DDL other = DDLTestUtils.getDdl(dbType);
+    List<String> diffs = compareDdls(postgres, other);
+    if (diffs.isEmpty()) {
+      LOG.info("Compare OK.");
+    }
+    else {
+      LOG.info("{} differences found:", diffs.size());
+      for (String diff: diffs) { LOG.info(diff); }
+      Assert.fail("Found " + diffs.size() + " differences when comparing " + 
other + " against Postgres.");
+    }
+  }
+
+  static void printDDLMetrics(DDL ddl) {
+    LOG.info("DDL metrics for {}", ddl.dbType);
+    int colCount = 0;
+    int pkCount = 0;
+    int fkCount = 0;
+    int uqCount = 0;
+    for (Table t: ddl.tables.values()) {
+      colCount += t.columns.size();
+      if (t.primaryKey.isPresent()) pkCount ++;
+      fkCount += t.foreignKeys.size();
+      uqCount += t.uniqueConstraints.size();
+    }
+    LOG.info("Found {} tables", ddl.tables.size());
+    List<String> tableNames = new ArrayList<>();
+    tableNames.addAll(ddl.tableNames());
+    Collections.sort(tableNames);
+    LOG.info("Table names: {}", Joiner.on(',').join(tableNames));
+    LOG.info("Total number of Columns: {}", colCount);
+    LOG.info("Total number of PK's: {}", pkCount);
+    LOG.info("Total number of FK's: {}", fkCount);
+    LOG.info("Total number of UQ's: {}", uqCount);
+    LOG.info("Number of Alter table statements: {}", ddl.alterTables.size());
+
+  }
+
+  static List<String> compareDdls(DDL base, DDL other) {
+    List<String> diffs = new ArrayList<>();
+
+    if (!base.tableNames().equals(other.tableNames())) {
+      Set<String> missingTables = Sets.difference(base.tableNames(), 
other.tableNames());
+      if (!missingTables.isEmpty()) {
+        diffs.add("Missing tables: " + Joiner.on(", ").join(missingTables));
+      }
+
+      Set<String> extraTables = Sets.difference(other.tableNames(), 
base.tableNames());
+      if (!extraTables.isEmpty()) {
+        diffs.add("Extra tables: " + Joiner.on(", ").join(extraTables));
+      }
+    }
+
+    Set<String> commonTables = Sets.intersection(base.tableNames(), 
other.tableNames());
+    for (String tableName: commonTables) {
+      Table baseTable = base.tables.get(tableName);
+      Table otherTable = other.tables.get(tableName);
+      diffs.addAll(
+          compareSets(String.format("Comparing columns of table %s.", 
tableName), baseTable.columns, otherTable.columns));
+      diffs.addAll(
+          DDLTests.<FKConstraintContent>compareConstraints(tableName, "FK", 
baseTable.foreignKeys, otherTable.foreignKeys, false));
+      diffs.addAll(
+          DDLTests.<Set<String>>compareConstraints(tableName, "UQ", 
baseTable.uniqueConstraints, otherTable.uniqueConstraints, false));
+      boolean comparePKName = !tableName.contains("qrtz"); // we are more 
lenient with quartz tables
+      diffs.addAll(
+          DDLTests.<Set<String>>compareConstraints(tableName, "PK", 
toSet(baseTable.primaryKey), toSet(otherTable.primaryKey), comparePKName));
+    }
+
+    return diffs;
+  }
+
+  static <T> Set<T> toSet(Optional<T> arg) {
+    return arg.isPresent() ? ImmutableSet.of(arg.get()) : ImmutableSet.<T>of();
+  }
+
+  static <ContentType> List<String> compareSets(String message, 
Set<ContentType> base, Set<ContentType> other) {
+    List<String> diffs = new ArrayList<>(2);
+    Set<ContentType> missingItems = Sets.difference(base, other);
+    if (!missingItems.isEmpty()) {
+      diffs.add(message + " Missing items: " + Joiner.on(", 
").join(missingItems));
+    }
+    Set<ContentType> extraItems = Sets.difference(other, base);
+    if (!extraItems.isEmpty()) {
+      diffs.add(message + " Extra items: " + Joiner.on(", ").join(extraItems));
+    }
+    return diffs;
+  }
+
+
+  static <ContentType> List<String> compareConstraints(String tableName,
+                                             String constraintType,
+                                             Set<? extends 
Constraint<ContentType>> base,
+                                             Set<? extends 
Constraint<ContentType>> other,
+                                             boolean compareConstraintNames) {
+    List<String> diffs = new ArrayList<>();
+    Map<ContentType, Constraint<ContentType>> baseByContent = 
Maps.newHashMap();
+    Map<ContentType, Constraint<ContentType>> otherByContent = 
Maps.newHashMap();
+    for (Constraint<ContentType> c: base) {
+      baseByContent.put(c.content(), c);
+    }
+    for (Constraint<ContentType> c: other) {
+      otherByContent.put(c.content(), c);
+    }
+    diffs.addAll(compareSets(String.format("Comparing %ss of table %s.", 
constraintType, tableName),
+        baseByContent.keySet(),
+        otherByContent.keySet()));
+
+    Set<ContentType> common = Sets.intersection(baseByContent.keySet(), 
otherByContent.keySet());
+    for (ContentType constrContent : common) {
+      Constraint b = baseByContent.get(constrContent);
+      Constraint o = otherByContent.get(constrContent);
+      if (!b.name().equals(o.name())) {
+        if (compareConstraintNames) {
+          diffs.add(String.format("Constraint name mismatch for table %s: %s 
vs. %s", tableName, b, o));
+        }
+        else {
+          LOG.info("Ignoring constraint name mismatch for table {}: {} vs. 
{}", tableName, b, o);
+        }
+      }
+    }
+
+    return diffs;
+  }
+
+}

Reply via email to