sentry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jar...@apache.org
Subject git commit: SENTRY-193: Add schematool for creating Sentry store schema from the SQL scripts (Prasad Mujumdar via Jarek Jarcec Cecho)
Date Sun, 11 May 2014 14:56:22 GMT
Repository: incubator-sentry
Updated Branches:
  refs/heads/master 4adf59a69 -> 7fb9ac4fe


SENTRY-193: Add schematool for creating Sentry store schema from the SQL scripts (Prasad Mujumdar via Jarek Jarcec Cecho)


Project: http://git-wip-us.apache.org/repos/asf/incubator-sentry/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-sentry/commit/7fb9ac4f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-sentry/tree/7fb9ac4f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-sentry/diff/7fb9ac4f

Branch: refs/heads/master
Commit: 7fb9ac4fecc231473004a325fcce5de2916570c8
Parents: 4adf59a
Author: Jarek Jarcec Cecho <jarcec@apache.org>
Authored: Sun May 11 07:55:51 2014 -0700
Committer: Jarek Jarcec Cecho <jarcec@apache.org>
Committed: Sun May 11 07:55:51 2014 -0700

----------------------------------------------------------------------
 pom.xml                                         |  30 +
 .../main/java/org/apache/sentry/SentryMain.java |   2 +
 sentry-dist/src/main/assembly/bin.xml           |   7 +
 sentry-dist/src/main/assembly/src.xml           |   7 +
 sentry-policy/sentry-policy-db/pom.xml          |   5 +
 sentry-provider/sentry-provider-db/pom.xml      |   4 +
 .../db/service/model/MSentryVersion.java        |  66 +++
 .../provider/db/service/model/package.jdo       |  11 +
 .../db/service/persistent/SentryStore.java      | 153 ++++-
 .../persistent/SentryStoreSchemaInfo.java       | 120 ++++
 .../thrift/SentryPolicyStoreProcessor.java      |  21 +-
 .../provider/db/tools/SentrySchemaHelper.java   | 296 ++++++++++
 .../provider/db/tools/SentrySchemaTool.java     | 574 +++++++++++++++++++
 .../sentry/service/thrift/SentryService.java    |  60 +-
 .../sentry/service/thrift/ServiceConstants.java |  19 +-
 .../src/main/resources/sentry-derby-1.4.0.sql   | 112 ++++
 .../src/main/resources/sentry-oracle-1.4.0.sql  |   2 +-
 .../src/main/resources/upgrade.order.derby      |   0
 .../src/main/resources/upgrade.order.mysql      |   0
 .../src/main/resources/upgrade.order.oracle     |   0
 .../src/main/resources/upgrade.order.postgres   |   0
 .../db/service/persistent/TestSentryStore.java  |  23 +-
 .../service/persistent/TestSentryVersion.java   |  84 +++
 .../provider/db/tools/TestSentrySchemaTool.java |  80 +++
 24 files changed, 1605 insertions(+), 71 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 373c25b..490bb01 100644
--- a/pom.xml
+++ b/pom.xml
@@ -156,6 +156,33 @@ limitations under the License.
         <version>${hive.version}</version>
       </dependency>
       <dependency>
+        <groupId>org.apache.hive</groupId>
+        <artifactId>hive-beeline</artifactId>
+        <version>${hive.version}</version>
+        <exclusions>
+          <exclusion>
+            <groupId>org.apache.hive</groupId>
+            <artifactId>hive-metastore</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.apache.hive</groupId>
+            <artifactId>hive-serde</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.apache.hive</groupId>
+            <artifactId>hive-common</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.apache.hive</groupId>
+            <artifactId>hive-serde</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.apache.hive</groupId>
+            <artifactId>hive-shims</artifactId>
+          </exclusion>
+        </exclusions>
+      </dependency>
+      <dependency>
         <groupId>org.apache.solr</groupId>
         <artifactId>solr-test-framework</artifactId>
         <version>${solr.version}</version>
@@ -468,6 +495,9 @@ limitations under the License.
                   <!-- README and test data with exact format -->
                   <exclude>README*</exclude>
                   <exclude>**/kv1.dat</exclude>
+                  <exclude>**/*.sql</exclude>
+                  <exclude>**/upgrade.*</exclude>
+                  <exclude>**/datanucleus.log</exclude>
                 </excludes>
               </configuration>
             </execution>

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/SentryMain.java
----------------------------------------------------------------------
diff --git a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/SentryMain.java b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/SentryMain.java
index bf028a2..c1ca6c5 100644
--- a/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/SentryMain.java
+++ b/sentry-core/sentry-core-common/src/main/java/org/apache/sentry/SentryMain.java
@@ -33,6 +33,8 @@ public class SentryMain {
       .<String, String>builder()
       .put("service", "org.apache.sentry.service.thrift.SentryService$CommandImpl")
       .put("config-tool", "org.apache.sentry.binding.hive.authz.SentryConfigTool$CommandImpl")
+      .put("schema-tool",
+          "org.apache.sentry.provider.db.tools.SentrySchemaTool$CommandImpl")
       .build();
   public static void main(String[] args)
       throws Exception {

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-dist/src/main/assembly/bin.xml
----------------------------------------------------------------------
diff --git a/sentry-dist/src/main/assembly/bin.xml b/sentry-dist/src/main/assembly/bin.xml
index 7c7c4a9..8d9d753 100644
--- a/sentry-dist/src/main/assembly/bin.xml
+++ b/sentry-dist/src/main/assembly/bin.xml
@@ -76,6 +76,13 @@
       </includes>
       <outputDirectory>/</outputDirectory>
     </fileSet>
+    <fileSet>
+      <directory>${project.parent.basedir}/sentry-provider/sentry-provider-db/src/main/resources</directory>
+      <includes>
+        <include>**/*</include>
+      </includes>
+      <outputDirectory>scripts/sentrystore/upgrade</outputDirectory>
+    </fileSet>
   </fileSets>
 
 </assembly>

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-dist/src/main/assembly/src.xml
----------------------------------------------------------------------
diff --git a/sentry-dist/src/main/assembly/src.xml b/sentry-dist/src/main/assembly/src.xml
index a06e521..208d455 100644
--- a/sentry-dist/src/main/assembly/src.xml
+++ b/sentry-dist/src/main/assembly/src.xml
@@ -62,6 +62,13 @@
       </includes>
       <outputDirectory>/</outputDirectory>
     </fileSet>
+    <fileSet>
+      <directory>${project.parent.basedir}/sentry-provider/sentry-provider-db/src/main/resources</directory>
+      <includes>
+        <include>**/*</include>
+      </includes>
+      <outputDirectory>scripts/sentrystore/upgrade</outputDirectory>
+    </fileSet>
   </fileSets>
 
 </assembly>

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-policy/sentry-policy-db/pom.xml
----------------------------------------------------------------------
diff --git a/sentry-policy/sentry-policy-db/pom.xml b/sentry-policy/sentry-policy-db/pom.xml
index 159cff2..59498f3 100644
--- a/sentry-policy/sentry-policy-db/pom.xml
+++ b/sentry-policy/sentry-policy-db/pom.xml
@@ -65,6 +65,11 @@ limitations under the License.
       <artifactId>slf4j-log4j12</artifactId>
     </dependency>
     <dependency>
+      <groupId>org.apache.hive</groupId>
+      <artifactId>hive-beeline</artifactId>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
       <groupId>org.apache.sentry</groupId>
       <artifactId>sentry-core-common</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/pom.xml
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/pom.xml b/sentry-provider/sentry-provider-db/pom.xml
index 3fd5fc9..0cda1e2 100644
--- a/sentry-provider/sentry-provider-db/pom.xml
+++ b/sentry-provider/sentry-provider-db/pom.xml
@@ -92,6 +92,10 @@ limitations under the License.
       <scope>provided</scope>
     </dependency>
     <dependency>
+      <groupId>org.apache.hive</groupId>
+      <artifactId>hive-beeline</artifactId>
+    </dependency>
+    <dependency>
       <groupId>org.apache.thrift</groupId>
       <artifactId>libfb303</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryVersion.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryVersion.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryVersion.java
new file mode 100644
index 0000000..ff8830f
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryVersion.java
@@ -0,0 +1,66 @@
+/**
+ * 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.sentry.provider.db.service.model;
+
+import javax.jdo.annotations.PersistenceCapable;
+
+@PersistenceCapable
+public class MSentryVersion {
+  private String schemaVersion;
+  private String versionComment;
+
+  public MSentryVersion() {
+  }
+
+  public MSentryVersion(String schemaVersion, String versionComment) {
+    this.schemaVersion = schemaVersion;
+    this.versionComment = versionComment;
+  }
+
+  /**
+   * @return the versionComment
+   */
+  public String getVersionComment() {
+    return versionComment;
+  }
+
+  /**
+   * @param versionComment
+   *          the versionComment to set
+   */
+  public void setVersionComment(String versionComment) {
+    this.versionComment = versionComment;
+  }
+
+  /**
+   * @return the schemaVersion
+   */
+  public String getSchemaVersion() {
+    return schemaVersion;
+  }
+
+  /**
+   * @param schemaVersion
+   *          the schemaVersion to set
+   */
+  public void setSchemaVersion(String schemaVersion) {
+    this.schemaVersion = schemaVersion;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo
index 9d68dbf..52e5026 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo
@@ -119,6 +119,17 @@
          <collection element-type="org.apache.sentry.provider.db.service.model.MSentryRole"/>
       </field>  
     </class>
+    <class name="MSentryVersion" table="SENTRY_VERSION" identity-type="datastore" detachable="true">
+      <datastore-identity>
+        <column name="VER_ID"/>
+      </datastore-identity>
+      <field name ="schemaVersion">
+        <column name="SCHEMA_VERSION" length="127" jdbc-type="VARCHAR" allows-null="false"/>
+      </field>
+      <field name ="versionComment">
+        <column name="VERSION_COMMENT" length="255" jdbc-type="VARCHAR" allows-null="false"/>
+      </field>
+     </class>
 
   </package>
 </jdo>

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
index 7301a1a..09af86d 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
@@ -18,46 +18,52 @@
 
 package org.apache.sentry.provider.db.service.persistent;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
+import static org.apache.sentry.provider.common.ProviderConstants.AUTHORIZABLE_JOINER;
+import static org.apache.sentry.provider.common.ProviderConstants.KV_JOINER;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.UUID;
+
+import javax.jdo.JDODataStoreException;
+import javax.jdo.JDOHelper;
+import javax.jdo.PersistenceManager;
+import javax.jdo.PersistenceManagerFactory;
+import javax.jdo.Query;
+import javax.jdo.Transaction;
+
 import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.sentry.core.model.db.AccessConstants;
 import org.apache.sentry.core.model.db.DBModelAuthorizable.AuthorizableType;
 import org.apache.sentry.provider.common.ProviderConstants;
+import org.apache.sentry.provider.db.SentryAccessDeniedException;
 import org.apache.sentry.provider.db.SentryAlreadyExistsException;
 import org.apache.sentry.provider.db.SentryInvalidInputException;
 import org.apache.sentry.provider.db.SentryNoSuchObjectException;
 import org.apache.sentry.provider.db.service.model.MSentryGroup;
 import org.apache.sentry.provider.db.service.model.MSentryPrivilege;
 import org.apache.sentry.provider.db.service.model.MSentryRole;
+import org.apache.sentry.provider.db.service.model.MSentryVersion;
 import org.apache.sentry.provider.db.service.thrift.TSentryActiveRoleSet;
 import org.apache.sentry.provider.db.service.thrift.TSentryGroup;
 import org.apache.sentry.provider.db.service.thrift.TSentryPrivilege;
 import org.apache.sentry.provider.db.service.thrift.TSentryRole;
 import org.apache.sentry.service.thrift.ServiceConstants.PrivilegeScope;
 import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig;
+import org.datanucleus.store.rdbms.exceptions.MissingTableException;
 
-import javax.jdo.JDOHelper;
-import javax.jdo.PersistenceManager;
-import javax.jdo.PersistenceManagerFactory;
-import javax.jdo.Query;
-import javax.jdo.Transaction;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-import java.util.UUID;
-
-import static org.apache.sentry.provider.common.ProviderConstants.AUTHORIZABLE_JOINER;
-import static org.apache.sentry.provider.common.ProviderConstants.KV_JOINER;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
 
 /**
  * SentryStore is the data access object for Sentry data. Strings
@@ -77,7 +83,8 @@ public class SentryStore {
   private long commitSequenceId;
   private final PersistenceManagerFactory pmf;
 
-  public SentryStore(Configuration conf) {
+  public SentryStore(Configuration conf) throws SentryNoSuchObjectException,
+      SentryAccessDeniedException {
     commitSequenceId = 0;
     Properties prop = new Properties();
     prop.putAll(ServerConfig.SENTRY_STORE_DEFAULTS);
@@ -99,7 +106,35 @@ public class SentryStore {
         prop.setProperty(key, entry.getValue());
       }
     }
+
+    boolean checkSchemaVersion = conf.get(
+        ServerConfig.SENTRY_VERIFY_SCHEM_VERSION,
+        ServerConfig.SENTRY_VERIFY_SCHEM_VERSION_DEFAULT).equalsIgnoreCase(
+        "true");
+    if (!checkSchemaVersion) {
+      prop.setProperty("datanucleus.autoCreateSchema", "true");
+      prop.setProperty("datanucleus.fixedDatastore", "false");
+    }
     pmf = JDOHelper.getPersistenceManagerFactory(prop);
+    verifySentryStoreSchema(conf, checkSchemaVersion);
+  }
+
+  // ensure that the backend DB schema is set
+  private void verifySentryStoreSchema(Configuration serverConf,
+      boolean checkVersion)
+      throws SentryNoSuchObjectException, SentryAccessDeniedException {
+    if (!checkVersion) {
+      setSentryVersion(SentryStoreSchemaInfo.getSentryVersion(),
+          "Schema version set implicitly");
+    } else {
+      String currentVersion = getSentryVersion();
+      if (!SentryStoreSchemaInfo.getSentryVersion().equals(currentVersion)) {
+        throw new SentryAccessDeniedException(
+            "The Sentry store schema version " + currentVersion
+                + " is different from distribution version "
+                + SentryStoreSchemaInfo.getSentryVersion());
+      }
+    }
   }
 
   public synchronized void stop() {
@@ -778,4 +813,76 @@ public class SentryStore {
     }
     return s.trim();
   }
+
+  public String getSentryVersion() throws SentryNoSuchObjectException,
+      SentryAccessDeniedException {
+    MSentryVersion mVersion = getMSentryVersion();
+    return mVersion.getSchemaVersion();
+  }
+
+  public void setSentryVersion(String newVersion, String verComment)
+      throws SentryNoSuchObjectException, SentryAccessDeniedException {
+    MSentryVersion mVersion;
+    boolean rollbackTransaction = true;
+    PersistenceManager pm = null;
+
+    try {
+      mVersion = getMSentryVersion();
+      if (newVersion.equals(mVersion.getSchemaVersion())) {
+        // specified version already in there
+        return;
+      }
+    } catch (SentryNoSuchObjectException e) {
+      // if the version doesn't exist, then create it
+      mVersion = new MSentryVersion();
+    }
+    mVersion.setSchemaVersion(newVersion);
+    mVersion.setVersionComment(verComment);
+    try {
+      pm = openTransaction();
+      pm.makePersistent(mVersion);
+      rollbackTransaction = false;
+      commitTransaction(pm);
+    } finally {
+      if (rollbackTransaction) {
+        rollbackTransaction(pm);
+      }
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private MSentryVersion getMSentryVersion()
+      throws SentryNoSuchObjectException, SentryAccessDeniedException {
+    boolean rollbackTransaction = true;
+    PersistenceManager pm = null;
+    try {
+      pm = openTransaction();
+      Query query = pm.newQuery(MSentryVersion.class);
+      List<MSentryVersion> mSentryVersions = (List<MSentryVersion>) query
+          .execute();
+      pm.retrieveAll(mSentryVersions);
+      rollbackTransaction = false;
+      commitTransaction(pm);
+      if (mSentryVersions.isEmpty()) {
+        throw new SentryNoSuchObjectException("No matching version found");
+      }
+      if (mSentryVersions.size() > 1) {
+        throw new SentryAccessDeniedException(
+            "Metastore contains multiple versions");
+      }
+      return mSentryVersions.get(0);
+    } catch (JDODataStoreException e) {
+      if (e.getCause() instanceof MissingTableException) {
+        throw new SentryAccessDeniedException("Version table not found. "
+            + "The sentry store is not set or corrupt ");
+      } else {
+        throw e;
+      }
+    } finally {
+      if (rollbackTransaction) {
+        rollbackTransaction(pm);
+      }
+    }
+
+  }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreSchemaInfo.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreSchemaInfo.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreSchemaInfo.java
new file mode 100644
index 0000000..22f1b08
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStoreSchemaInfo.java
@@ -0,0 +1,120 @@
+/**
+ * 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.sentry.provider.db.service.persistent;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.hadoop.hive.metastore.HiveMetaException;
+import org.apache.sentry.SentryUserException;
+
+public class SentryStoreSchemaInfo {
+  private static String SQL_FILE_EXTENSION = ".sql";
+  private static String UPGRADE_FILE_PREFIX = "upgrade-";
+  private static String INIT_FILE_PREFIX = "sentry-";
+  private static String VERSION_UPGRADE_LIST = "upgrade.order";
+  private final String dbType;
+  private final String sentrySchemaVersions[];
+  private final String sentryScriptDir;
+
+  private static final String SENTRY_VERSION = "1.4.0";
+
+  public SentryStoreSchemaInfo(String sentryScriptDir, String dbType)
+      throws SentryUserException {
+    this.sentryScriptDir = sentryScriptDir;
+    this.dbType = dbType;
+    // load upgrade order for the given dbType
+    List<String> upgradeOrderList = new ArrayList<String>();
+    String upgradeListFile = getSentryStoreScriptDir() + File.separator
+        + VERSION_UPGRADE_LIST + "." + dbType;
+    try {
+      BufferedReader bfReader = new BufferedReader(new FileReader(
+          upgradeListFile));
+      String currSchemaVersion;
+      while ((currSchemaVersion = bfReader.readLine()) != null) {
+        upgradeOrderList.add(currSchemaVersion.trim());
+      }
+    } catch (FileNotFoundException e) {
+      throw new SentryUserException("File " + upgradeListFile + "not found ", e);
+    } catch (IOException e) {
+      throw new SentryUserException("Error reading " + upgradeListFile, e);
+    }
+    sentrySchemaVersions = upgradeOrderList.toArray(new String[0]);
+  }
+
+  public String getSentrySchemaVersion() {
+    return SENTRY_VERSION;
+  }
+
+  public List<String> getUpgradeScripts(String fromSchemaVer) {
+    // NO upgrade scripts for first version
+    return new ArrayList<String>();
+  }
+
+  /***
+   * Get the name of the script to initialize the schema for given version
+   *
+   * @param toVersion
+   *          Target version. If it's null, then the current server version is
+   *          used
+   * @return
+   * @throws HiveMetaException
+   */
+  public String generateInitFileName(String toVersion)
+      throws SentryUserException {
+    if (toVersion == null) {
+      toVersion = getSentryVersion();
+    }
+    String initScriptName = INIT_FILE_PREFIX + dbType + "-" + toVersion
+        + SQL_FILE_EXTENSION;
+    // check if the file exists
+    if (!(new File(getSentryStoreScriptDir() + File.separatorChar
+        + initScriptName).exists())) {
+      throw new SentryUserException(
+          "Unknown version specified for initialization: " + toVersion);
+    }
+    return initScriptName;
+  }
+
+  /**
+   * Find the directory of metastore scripts
+   *
+   * @return
+   */
+  public String getSentryStoreScriptDir() {
+    return sentryScriptDir;
+  }
+
+  // format the upgrade script name eg upgrade-x-y-dbType.sql
+  private String generateUpgradeFileName(String fileVersion) {
+    return UPGRADE_FILE_PREFIX + fileVersion + "." + dbType
+        + SQL_FILE_EXTENSION;
+  }
+
+  // Current hive version, in majorVersion.minorVersion.changeVersion format
+  // TODO: store the version using the build script
+  public static String getSentryVersion() {
+    return SENTRY_VERSION;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
index 3b3bdc3..8e58202 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
@@ -18,12 +18,11 @@
 
 package org.apache.sentry.provider.db.service.thrift;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
+import java.lang.reflect.Constructor;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 import org.apache.hadoop.conf.Configuration;
 import org.apache.sentry.provider.db.SentryAccessDeniedException;
 import org.apache.sentry.provider.db.SentryAlreadyExistsException;
@@ -39,10 +38,12 @@ import org.apache.thrift.TException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.lang.reflect.Constructor;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 
 @SuppressWarnings("unused")
 public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaHelper.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaHelper.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaHelper.java
new file mode 100644
index 0000000..0afa60b
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaHelper.java
@@ -0,0 +1,296 @@
+/**
+ * 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.sentry.provider.db.tools;
+
+import java.util.IllegalFormatException;
+
+public class SentrySchemaHelper {
+  public static final String DB_DERBY = "derby";
+  public static final String DB_MYSQL = "mysql";
+  public static final String DB_POSTGRACE = "postgres";
+  public static final String DB_ORACLE = "oracle";
+
+  public interface NestedScriptParser {
+
+    public enum CommandType {
+      PARTIAL_STATEMENT,
+      TERMINATED_STATEMENT,
+      COMMENT
+    }
+
+    static final String DEFAUTL_DELIMITER = ";";
+    /***
+     * Find the type of given command
+     * @param dbCommand
+     * @return
+     */
+    public boolean isPartialCommand(String dbCommand) throws IllegalArgumentException;
+
+    /** Parse the DB specific nesting format and extract the inner script name if any
+     * @param dbCommand command from parent script
+     * @return
+     * @throws IllegalFormatException
+     */
+    public String getScriptName(String dbCommand) throws IllegalArgumentException;
+
+    /***
+     * Find if the given command is a nested script execution
+     * @param dbCommand
+     * @return
+     */
+    public boolean isNestedScript(String dbCommand);
+
+    /***
+     * Find if the given command is should be passed to DB
+     * @param dbCommand
+     * @return
+     */
+    public boolean isNonExecCommand(String dbCommand);
+
+    /***
+     * Get the SQL statement delimiter
+     * @return
+     */
+    public String getDelimiter();
+
+    /***
+     * Clear any client specific tags
+     * @return
+     */
+    public String cleanseCommand(String dbCommand);
+
+    /***
+     * Does the DB required table/column names quoted
+     * @return
+     */
+    public boolean needsQuotedIdentifier();
+
+    /***
+     * Set DB specific options if any
+     * @param dbOps
+     */
+    public void setDbOpts(String dbOps);
+  }
+
+
+  /***
+   * Base implemenation of NestedScriptParser
+   * abstractCommandParser.
+   *
+   */
+  private static abstract class AbstractCommandParser implements NestedScriptParser {
+    private String dbOpts = null;
+
+    @Override
+    public boolean isPartialCommand(String dbCommand) throws IllegalArgumentException{
+      if (dbCommand == null || dbCommand.isEmpty()) {
+        throw new IllegalArgumentException("invalid command line " + dbCommand);
+      }
+      dbCommand = dbCommand.trim();
+      if (dbCommand.endsWith(getDelimiter()) || isNonExecCommand(dbCommand)) {
+        return false;
+      } else {
+        return true;
+      }
+    }
+
+    @Override
+    public boolean isNonExecCommand(String dbCommand) {
+      return (dbCommand.startsWith("--") || dbCommand.startsWith("#"));
+    }
+
+    @Override
+    public String getDelimiter() {
+      return DEFAUTL_DELIMITER;
+    }
+
+    @Override
+    public String cleanseCommand(String dbCommand) {
+      // strip off the delimiter
+      if (dbCommand.endsWith(getDelimiter())) {
+        dbCommand = dbCommand.substring(0,
+            dbCommand.length() - getDelimiter().length());
+      }
+      return dbCommand;
+    }
+
+    @Override
+    public boolean needsQuotedIdentifier() {
+      return false;
+    }
+
+    @Override
+    public void setDbOpts(String dbOpts) {
+      this.dbOpts = dbOpts;
+    }
+
+    protected String getDbOpts() {
+      return dbOpts;
+    }
+  }
+
+
+  // Derby commandline parser
+  public static class DerbyCommandParser extends AbstractCommandParser {
+    private static String DERBY_NESTING_TOKEN = "RUN";
+
+    @Override
+    public String getScriptName(String dbCommand) throws IllegalArgumentException {
+
+      if (!isNestedScript(dbCommand)) {
+        throw new IllegalArgumentException("Not a script format " + dbCommand);
+      }
+      String[] tokens = dbCommand.split(" ");
+      if (tokens.length != 2) {
+        throw new IllegalArgumentException("Couldn't parse line " + dbCommand);
+      }
+      return tokens[1].replace(";", "").replaceAll("'", "");
+    }
+
+    @Override
+    public boolean isNestedScript(String dbCommand) {
+      // Derby script format is RUN '<file>'
+     return dbCommand.startsWith(DERBY_NESTING_TOKEN);
+    }
+  }
+
+
+  // MySQL parser
+  public static class MySqlCommandParser extends AbstractCommandParser {
+    private static final String MYSQL_NESTING_TOKEN = "SOURCE";
+    private static final String DELIMITER_TOKEN = "DELIMITER";
+    private String delimiter = DEFAUTL_DELIMITER;
+
+    @Override
+    public boolean isPartialCommand(String dbCommand) throws IllegalArgumentException{
+      boolean isPartial = super.isPartialCommand(dbCommand);
+      // if this is a delimiter directive, reset our delimiter
+      if (dbCommand.startsWith(DELIMITER_TOKEN)) {
+        String[] tokens = dbCommand.split(" ");
+        if (tokens.length != 2) {
+          throw new IllegalArgumentException("Couldn't parse line " + dbCommand);
+        }
+        delimiter = tokens[1];
+      }
+      return isPartial;
+    }
+
+    @Override
+    public String getScriptName(String dbCommand) throws IllegalArgumentException {
+      String[] tokens = dbCommand.split(" ");
+      if (tokens.length != 2) {
+        throw new IllegalArgumentException("Couldn't parse line " + dbCommand);
+      }
+      // remove ending ';'
+      return tokens[1].replace(";", "");
+    }
+
+    @Override
+    public boolean isNestedScript(String dbCommand) {
+      return dbCommand.startsWith(MYSQL_NESTING_TOKEN);
+    }
+
+    @Override
+    public String getDelimiter() {
+      return delimiter;
+    }
+
+    @Override
+    public boolean isNonExecCommand(String dbCommand) {
+      return super.isNonExecCommand(dbCommand) ||
+          (dbCommand.startsWith("/*") && dbCommand.endsWith("*/")) ||
+          dbCommand.startsWith(DELIMITER_TOKEN);
+    }
+
+    @Override
+    public String cleanseCommand(String dbCommand) {
+      return super.cleanseCommand(dbCommand).replaceAll("/\\*.*?\\*/[^;]", "");
+    }
+
+  }
+
+  // Postgres specific parser
+  public static class PostgresCommandParser extends AbstractCommandParser {
+    public static String POSTGRES_STRING_COMMAND_FILTER = "SET standard_conforming_strings";
+    public static String POSTGRES_SKIP_STANDARD_STRING = "postgres.filter.81";
+    private static String POSTGRES_NESTING_TOKEN = "\\i";
+
+    @Override
+    public String getScriptName(String dbCommand) throws IllegalArgumentException {
+      String[] tokens = dbCommand.split(" ");
+      if (tokens.length != 2) {
+        throw new IllegalArgumentException("Couldn't parse line " + dbCommand);
+      }
+      // remove ending ';'
+      return tokens[1].replace(";", "");
+    }
+
+    @Override
+    public boolean isNestedScript(String dbCommand) {
+      return dbCommand.startsWith(POSTGRES_NESTING_TOKEN);
+    }
+
+    @Override
+    public boolean needsQuotedIdentifier() {
+      return true;
+    }
+
+    @Override
+    public boolean isNonExecCommand(String dbCommand) {
+      // Skip "standard_conforming_strings" command which is not supported in older postgres
+      if (POSTGRES_SKIP_STANDARD_STRING.equalsIgnoreCase(getDbOpts())) {
+        if (dbCommand.startsWith(POSTGRES_STRING_COMMAND_FILTER)) {
+          return true;
+        }
+      }
+      return super.isNonExecCommand(dbCommand);
+    }
+  }
+
+  //Oracle specific parser
+  public static class OracleCommandParser extends AbstractCommandParser {
+    private static String ORACLE_NESTING_TOKEN = "@";
+    @Override
+    public String getScriptName(String dbCommand) throws IllegalArgumentException {
+      if (!isNestedScript(dbCommand)) {
+        throw new IllegalArgumentException("Not a nested script format " + dbCommand);
+      }
+      // remove ending ';' and starting '@'
+      return dbCommand.replace(";", "").replace(ORACLE_NESTING_TOKEN, "");
+    }
+
+    @Override
+    public boolean isNestedScript(String dbCommand) {
+      return dbCommand.startsWith(ORACLE_NESTING_TOKEN);
+    }
+  }
+
+  public static NestedScriptParser getDbCommandParser(String dbName) {
+    if (dbName.equalsIgnoreCase(DB_DERBY)) {
+      return new DerbyCommandParser();
+    } else if (dbName.equalsIgnoreCase(DB_MYSQL)) {
+      return new MySqlCommandParser();
+    } else if (dbName.equalsIgnoreCase(DB_POSTGRACE)) {
+      return new PostgresCommandParser();
+    } else if (dbName.equalsIgnoreCase(DB_ORACLE)) {
+      return new OracleCommandParser();
+    } else {
+      throw new IllegalArgumentException("Unknown dbType " + dbName);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaTool.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaTool.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaTool.java
new file mode 100644
index 0000000..124c17e
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/tools/SentrySchemaTool.java
@@ -0,0 +1,574 @@
+/**
+ * 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.sentry.provider.db.tools;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.MalformedURLException;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.IllegalFormatException;
+import java.util.List;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.OptionGroup;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.io.output.NullOutputStream;
+import org.apache.commons.lang.StringUtils;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hive.metastore.api.MetaException;
+import org.apache.hive.beeline.BeeLine;
+import org.apache.sentry.Command;
+import org.apache.sentry.SentryUserException;
+import org.apache.sentry.provider.db.service.persistent.SentryStoreSchemaInfo;
+import org.apache.sentry.provider.db.tools.SentrySchemaHelper.NestedScriptParser;
+import org.apache.sentry.service.thrift.SentryService;
+import org.apache.sentry.service.thrift.ServiceConstants;
+import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig;
+
+public class SentrySchemaTool {
+  private static final String SENTRY_SCRIP_DIR = File.separatorChar + "scripts"
+      + File.separatorChar + "sentrystore" + File.separatorChar + "upgrade";
+  private String userName = null;
+  private String passWord = null;
+  private boolean dryRun = false;
+  private boolean verbose = false;
+  private String dbOpts = null;
+  private final Configuration sentryConf;
+  private final String dbType;
+  private final SentryStoreSchemaInfo SentryStoreSchemaInfo;
+
+  public SentrySchemaTool(Configuration sentryConf, String dbType)
+      throws SentryUserException {
+    this(System.getenv("SENTRY_HOME") + SENTRY_SCRIP_DIR, sentryConf, dbType);
+  }
+
+  public SentrySchemaTool(String sentryScripPath, Configuration sentryConf,
+      String dbType) throws SentryUserException {
+    if (sentryScripPath == null || sentryScripPath.isEmpty()) {
+      throw new SentryUserException("No Sentry script dir provided");
+    }
+    this.sentryConf = sentryConf;
+    this.dbType = dbType;
+    this.SentryStoreSchemaInfo = new SentryStoreSchemaInfo(sentryScripPath,
+        dbType);
+    userName = sentryConf
+        .get(ServiceConstants.ServerConfig.SENTRY_STORE_JDBC_USER);
+    passWord = sentryConf
+        .get(ServiceConstants.ServerConfig.SENTRY_STORE_JDBC_PASS);
+  }
+
+  public Configuration getConfiguration() {
+    return sentryConf;
+  }
+
+  public void setUserName(String userName) {
+    this.userName = userName;
+  }
+
+  public void setPassWord(String passWord) {
+    this.passWord = passWord;
+  }
+
+  public void setDryRun(boolean dryRun) {
+    this.dryRun = dryRun;
+  }
+
+  public void setVerbose(boolean verbose) {
+    this.verbose = verbose;
+  }
+
+  public String getDbOpts() {
+    return dbOpts;
+  }
+
+  public void setDbOpts(String dbOpts) {
+    this.dbOpts = dbOpts;
+  }
+
+  private static void printAndExit(Options cmdLineOptions) {
+    HelpFormatter formatter = new HelpFormatter();
+    formatter.printHelp("schemaTool", cmdLineOptions);
+    System.exit(1);
+  }
+
+  /***
+   * Print Hive version and schema version
+   * @throws MetaException
+   */
+  public void showInfo() throws SentryUserException {
+    Connection metastoreConn = getConnectionToMetastore(true);
+    System.out.println("Sentry distribution version:\t "
+        + SentryStoreSchemaInfo.getSentryVersion());
+    System.out.println("SentryStore schema version:\t "
+        + getMetaStoreSchemaVersion(metastoreConn));
+  }
+
+  // read schema version from metastore
+  private String getMetaStoreSchemaVersion(Connection metastoreConn)
+      throws SentryUserException {
+    String versionQuery;
+    if (SentrySchemaHelper.getDbCommandParser(dbType).needsQuotedIdentifier()) {
+      versionQuery = "select t.\"SCHEMA_VERSION\" from \"SENTRY_VERSION\" t";
+    } else {
+      versionQuery = "select t.SCHEMA_VERSION from SENTRY_VERSION t";
+    }
+    try {
+      Statement stmt = metastoreConn.createStatement();
+      ResultSet res = stmt.executeQuery(versionQuery);
+      if (!res.next()) {
+        throw new SentryUserException("Didn't find version data in metastore");
+      }
+      String currentSchemaVersion = res.getString(1);
+      metastoreConn.close();
+      return currentSchemaVersion;
+    } catch (SQLException e) {
+      throw new SentryUserException("Failed to get schema version.", e);
+    }
+  }
+
+  // test the connection metastore using the config property
+  private void testConnectionToMetastore() throws SentryUserException {
+    Connection conn = getConnectionToMetastore(true);
+    try {
+      conn.close();
+    } catch (SQLException e) {
+      throw new SentryUserException("Failed to close metastore connection", e);
+    }
+  }
+
+  /***
+   * get JDBC connection to metastore db
+   *
+   * @param printInfo print connection parameters
+   * @return
+   * @throws MetaException
+   */
+  private Connection getConnectionToMetastore(boolean printInfo)
+      throws SentryUserException {
+    try {
+      String connectionURL = getValidConfVar(ServiceConstants.ServerConfig.SENTRY_STORE_JDBC_URL);
+      String driver = getValidConfVar("javax.jdo.option.ConnectionDriverName");
+      if (printInfo) {
+        System.out.println("Metastore connection URL:\t " + connectionURL);
+        System.out.println("Metastore Connection Driver :\t " + driver);
+        System.out.println("Metastore connection User:\t " + userName);
+      }
+      if ((userName == null) || userName.isEmpty()) {
+        throw new SentryUserException("UserName empty ");
+      }
+
+      // load required JDBC driver
+      Class.forName(driver);
+
+      // Connect using the JDBC URL and user/pass from conf
+      return DriverManager.getConnection(connectionURL, userName, passWord);
+    } catch (IOException e) {
+      throw new SentryUserException("Failed to get schema version.", e);
+    } catch (SQLException e) {
+      throw new SentryUserException("Failed to get schema version.", e);
+    } catch (ClassNotFoundException e) {
+      throw new SentryUserException("Failed to load driver", e);
+    }
+  }
+
+  /**
+   * check if the current schema version in metastore matches the Hive version
+   * @throws MetaException
+   */
+  public void verifySchemaVersion() throws SentryUserException {
+    // don't check version if its a dry run
+    if (dryRun) {
+      return;
+    }
+    String newSchemaVersion =
+        getMetaStoreSchemaVersion(getConnectionToMetastore(false));
+    // verify that the new version is added to schema
+    if (!SentryStoreSchemaInfo.getSentrySchemaVersion().equalsIgnoreCase(
+        newSchemaVersion)) {
+      throw new SentryUserException("Found unexpected schema version "
+          + newSchemaVersion);
+    }
+  }
+
+  /**
+   * Perform metastore schema upgrade. extract the current schema version from metastore
+   * @throws MetaException
+   */
+  public void doUpgrade() throws SentryUserException {
+    String fromVersion = getMetaStoreSchemaVersion(getConnectionToMetastore(false));
+    if (fromVersion == null || fromVersion.isEmpty()) {
+      throw new SentryUserException(
+          "Schema version not stored in the metastore. "
+              +
+          "Metastore schema is too old or corrupt. Try specifying the version manually");
+    }
+    doUpgrade(fromVersion);
+  }
+
+  /**
+   * Perform metastore schema upgrade
+   *
+   * @param fromSchemaVer
+   *          Existing version of the metastore. If null, then read from the metastore
+   * @throws MetaException
+   */
+  public void doUpgrade(String fromSchemaVer) throws SentryUserException {
+    if (SentryStoreSchemaInfo.getSentrySchemaVersion().equals(fromSchemaVer)) {
+      System.out.println("No schema upgrade required from version " + fromSchemaVer);
+      return;
+    }
+    // Find the list of scripts to execute for this upgrade
+    List<String> upgradeScripts =
+        SentryStoreSchemaInfo.getUpgradeScripts(fromSchemaVer);
+    testConnectionToMetastore();
+    System.out.println("Starting upgrade metastore schema from version " +
+ fromSchemaVer + " to "
+        + SentryStoreSchemaInfo.getSentrySchemaVersion());
+    String scriptDir = SentryStoreSchemaInfo.getSentryStoreScriptDir();
+    try {
+      for (String scriptFile : upgradeScripts) {
+        System.out.println("Upgrade script " + scriptFile);
+        if (!dryRun) {
+          runBeeLine(scriptDir, scriptFile);
+          System.out.println("Completed " + scriptFile);
+        }
+      }
+    } catch (IOException eIO) {
+      throw new SentryUserException(
+          "Upgrade FAILED! Metastore state would be inconsistent !!", eIO);
+    }
+
+    // Revalidated the new version after upgrade
+    verifySchemaVersion();
+  }
+
+  /**
+   * Initialize the metastore schema to current version
+   *
+   * @throws MetaException
+   */
+  public void doInit() throws SentryUserException {
+    doInit(SentryStoreSchemaInfo.getSentrySchemaVersion());
+
+    // Revalidated the new version after upgrade
+    verifySchemaVersion();
+  }
+
+  /**
+   * Initialize the metastore schema
+   *
+   * @param toVersion
+   *          If null then current hive version is used
+   * @throws MetaException
+   */
+  public void doInit(String toVersion) throws SentryUserException {
+    testConnectionToMetastore();
+    System.out.println("Starting metastore schema initialization to " + toVersion);
+
+    String initScriptDir = SentryStoreSchemaInfo.getSentryStoreScriptDir();
+    String initScriptFile = SentryStoreSchemaInfo.generateInitFileName(toVersion);
+
+    try {
+      System.out.println("Initialization script " + initScriptFile);
+      if (!dryRun) {
+        runBeeLine(initScriptDir, initScriptFile);
+        System.out.println("Initialization script completed");
+      }
+    } catch (IOException e) {
+      throw new SentryUserException("Schema initialization FAILED!"
+          + " Metastore state would be inconsistent !!", e);
+    }
+  }
+
+  // Flatten the nested upgrade script into a buffer
+  public static String buildCommand(NestedScriptParser dbCommandParser,
+        String scriptDir, String scriptFile) throws IllegalFormatException, IOException {
+
+    BufferedReader bfReader =
+        new BufferedReader(new FileReader(scriptDir + File.separatorChar + scriptFile));
+    String currLine;
+    StringBuilder sb = new StringBuilder();
+    String currentCommand = null;
+    while ((currLine = bfReader.readLine()) != null) {
+      currLine = currLine.trim();
+      if (currLine.isEmpty()) {
+        continue; // skip empty lines
+      }
+
+      if (currentCommand == null) {
+        currentCommand = currLine;
+      } else {
+        currentCommand = currentCommand + " " + currLine;
+      }
+      if (dbCommandParser.isPartialCommand(currLine)) {
+        // if its a partial line, continue collecting the pieces
+        continue;
+      }
+
+      // if this is a valid executable command then add it to the buffer
+      if (!dbCommandParser.isNonExecCommand(currentCommand)) {
+        currentCommand = dbCommandParser.cleanseCommand(currentCommand);
+
+        if (dbCommandParser.isNestedScript(currentCommand)) {
+          // if this is a nested sql script then flatten it
+          String currScript = dbCommandParser.getScriptName(currentCommand);
+          sb.append(buildCommand(dbCommandParser, scriptDir, currScript));
+        } else {
+          // Now we have a complete statement, process it
+          // write the line to buffer
+          sb.append(currentCommand);
+          sb.append(System.getProperty("line.separator"));
+        }
+      }
+      currentCommand = null;
+    }
+    bfReader.close();
+    return sb.toString();
+  }
+
+  // run beeline on the given metastore scrip, flatten the nested scripts into single file
+  private void runBeeLine(String scriptDir, String scriptFile) throws IOException {
+    NestedScriptParser dbCommandParser =
+        SentrySchemaHelper.getDbCommandParser(dbType);
+    dbCommandParser.setDbOpts(getDbOpts());
+    // expand the nested script
+    String sqlCommands = buildCommand(dbCommandParser, scriptDir, scriptFile);
+    File tmpFile = File.createTempFile("schematool", ".sql");
+    tmpFile.deleteOnExit();
+
+    // write out the buffer into a file. Add beeline commands for autocommit and close
+    FileWriter fstream = new FileWriter(tmpFile.getPath());
+    BufferedWriter out = new BufferedWriter(fstream);
+
+    out.write("!set Silent " + verbose + System.getProperty("line.separator"));
+    out.write("!autocommit on" + System.getProperty("line.separator"));
+    out.write("!set Isolation TRANSACTION_READ_COMMITTED"
+        + System.getProperty("line.separator"));
+    out.write("!set AllowMultiLineCommand false"
+        + System.getProperty("line.separator"));
+    out.write(sqlCommands);
+    out.write("!closeall" + System.getProperty("line.separator"));
+    out.close();
+    runBeeLine(tmpFile.getPath());
+  }
+
+  // Generate the beeline args per hive conf and execute the given script
+  public void runBeeLine(String sqlScriptFile) throws IOException {
+    List<String> argList = new ArrayList<String>();
+    argList.add("-u");
+    argList.add(getValidConfVar(ServiceConstants.ServerConfig.SENTRY_STORE_JDBC_URL));
+    argList.add("-d");
+    argList.add(getValidConfVar("javax.jdo.option.ConnectionDriverName"));
+    argList.add("-n");
+    argList.add(userName);
+    argList.add("-p");
+    argList.add(passWord);
+    argList.add("-f");
+    argList.add(sqlScriptFile);
+
+    BeeLine beeLine = new BeeLine();
+    if (!verbose) {
+      beeLine.setOutputStream(new PrintStream(new NullOutputStream()));
+      // beeLine.getOpts().setSilent(true);
+    }
+    // beeLine.getOpts().setAllowMultiLineCommand(false);
+    // beeLine.getOpts().setIsolation("TRANSACTION_READ_COMMITTED");
+    int status = beeLine.begin(argList.toArray(new String[0]), null);
+    if (status != 0) {
+      throw new IOException("Schema script failed, errorcode " + status);
+    }
+  }
+
+  private String getValidConfVar(String confVar) throws IOException {
+    String confVarKey = confVar;
+    if (confVar.startsWith(ServerConfig.SENTRY_DATANUCLEUS_PROPERTY_PREFIX)) {
+      confVarKey = StringUtils.removeStart(confVar,
+          ServerConfig.SENTRY_DB_PROPERTY_PREFIX);
+    }
+    String confVarValue = sentryConf.get(confVarKey);
+    if (confVarValue == null || confVarValue.isEmpty()) {
+      throw new IOException("Empty " + confVar);
+    }
+    return confVarValue;
+  }
+
+  // Create the required command line options
+  @SuppressWarnings("static-access")
+  private static void initOptions(Options cmdLineOptions) {
+    Option help = new Option("help", "print this message");
+    Option upgradeOpt = new Option("upgradeSchema", "Schema upgrade");
+    Option upgradeFromOpt = OptionBuilder.withArgName("upgradeFrom").hasArg().
+                withDescription("Schema upgrade from a version").
+                create("upgradeSchemaFrom");
+    Option initOpt = new Option("initSchema", "Schema initialization");
+    Option initToOpt = OptionBuilder.withArgName("initTo").hasArg().
+                withDescription("Schema initialization to a version").
+                create("initSchemaTo");
+    Option infoOpt = new Option("info", "Show config and schema details");
+
+    OptionGroup optGroup = new OptionGroup();
+    optGroup.addOption(upgradeOpt).addOption(initOpt).
+                addOption(help).addOption(upgradeFromOpt).
+                addOption(initToOpt).addOption(infoOpt);
+    optGroup.setRequired(true);
+
+    Option userNameOpt = OptionBuilder.withArgName("user")
+                .hasArgs()
+                .withDescription("Override config file user name")
+                .create("userName");
+    Option passwdOpt = OptionBuilder.withArgName("password")
+                .hasArgs()
+                 .withDescription("Override config file password")
+                 .create("passWord");
+    Option dbTypeOpt = OptionBuilder.withArgName("databaseType")
+                .hasArgs().withDescription("Metastore database type")
+                .create("dbType");
+    Option dbOpts = OptionBuilder.withArgName("databaseOpts")
+                .hasArgs().withDescription("Backend DB specific options")
+                .create("dbOpts");
+
+    Option dryRunOpt = new Option("dryRun", "list SQL scripts (no execute)");
+    Option verboseOpt = new Option("verbose", "only print SQL statements");
+
+    Option configOpt = OptionBuilder.withArgName("confName").hasArgs()
+        .withDescription("Sentry Service configuration file").isRequired(true)
+        .create(ServiceConstants.ServiceArgs.CONFIG_FILE_LONG);
+
+    cmdLineOptions.addOption(help);
+    cmdLineOptions.addOption(dryRunOpt);
+    cmdLineOptions.addOption(userNameOpt);
+    cmdLineOptions.addOption(passwdOpt);
+    cmdLineOptions.addOption(dbTypeOpt);
+    cmdLineOptions.addOption(verboseOpt);
+    cmdLineOptions.addOption(dbOpts);
+    cmdLineOptions.addOption(configOpt);
+    cmdLineOptions.addOptionGroup(optGroup);
+  }
+
+  public static class CommandImpl implements Command {
+    @Override
+    public void run(String[] args) throws Exception {
+      CommandLineParser parser = new GnuParser();
+      CommandLine line = null;
+      String dbType = null;
+      String schemaVer = null;
+      Options cmdLineOptions = new Options();
+      String configFileName = null;
+
+      // Argument handling
+      initOptions(cmdLineOptions);
+      try {
+        line = parser.parse(cmdLineOptions, args);
+      } catch (ParseException e) {
+        System.err.println("SentrySchemaTool:Parsing failed.  Reason: "
+            + e.getLocalizedMessage());
+        printAndExit(cmdLineOptions);
+      }
+
+      if (line.hasOption("help")) {
+        HelpFormatter formatter = new HelpFormatter();
+        formatter.printHelp("schemaTool", cmdLineOptions);
+        return;
+      }
+
+      if (line.hasOption("dbType")) {
+        dbType = line.getOptionValue("dbType");
+        if ((!dbType.equalsIgnoreCase(SentrySchemaHelper.DB_DERBY)
+            && !dbType.equalsIgnoreCase(SentrySchemaHelper.DB_MYSQL)
+            && !dbType.equalsIgnoreCase(SentrySchemaHelper.DB_POSTGRACE) && !dbType
+              .equalsIgnoreCase(SentrySchemaHelper.DB_ORACLE))) {
+          System.err.println("Unsupported dbType " + dbType);
+          printAndExit(cmdLineOptions);
+        }
+      } else {
+        System.err.println("no dbType supplied");
+        printAndExit(cmdLineOptions);
+      }
+      if (line.hasOption(ServiceConstants.ServiceArgs.CONFIG_FILE_LONG)) {
+        configFileName = line
+            .getOptionValue(ServiceConstants.ServiceArgs.CONFIG_FILE_LONG);
+      } else {
+        System.err.println("no config file specified");
+        printAndExit(cmdLineOptions);
+      }
+
+      try {
+        SentrySchemaTool schemaTool = new SentrySchemaTool(
+            SentryService.loadConfig(configFileName), dbType);
+
+        if (line.hasOption("userName")) {
+          schemaTool.setUserName(line.getOptionValue("userName"));
+        }
+        if (line.hasOption("passWord")) {
+          schemaTool.setPassWord(line.getOptionValue("passWord"));
+        }
+        if (line.hasOption("dryRun")) {
+          schemaTool.setDryRun(true);
+        }
+        if (line.hasOption("verbose")) {
+          schemaTool.setVerbose(true);
+        }
+        if (line.hasOption("dbOpts")) {
+          schemaTool.setDbOpts(line.getOptionValue("dbOpts"));
+        }
+
+        if (line.hasOption("info")) {
+          schemaTool.showInfo();
+        } else if (line.hasOption("upgradeSchema")) {
+          schemaTool.doUpgrade();
+        } else if (line.hasOption("upgradeSchemaFrom")) {
+          schemaVer = line.getOptionValue("upgradeSchemaFrom");
+          schemaTool.doUpgrade(schemaVer);
+        } else if (line.hasOption("initSchema")) {
+          schemaTool.doInit();
+        } else if (line.hasOption("initSchemaTo")) {
+          schemaVer = line.getOptionValue("initSchemaTo");
+          schemaTool.doInit(schemaVer);
+        } else {
+          System.err.println("no valid option supplied");
+          printAndExit(cmdLineOptions);
+        }
+      } catch (SentryUserException | MalformedURLException e) {
+        System.err.println(e);
+        if (line.hasOption("verbose")) {
+          e.printStackTrace();
+        }
+        System.err.println("*** Sentry schemaTool failed ***");
+        System.exit(1);
+      }
+      System.out.println("Sentry schemaTool completed");
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java
index 107636a..a0ac224 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/SentryService.java
@@ -18,8 +18,23 @@
 
 package org.apache.sentry.service.thrift;
 
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Sets;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.ServerSocket;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashSet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.CommandLineParser;
 import org.apache.commons.cli.GnuParser;
@@ -43,20 +58,8 @@ import org.apache.thrift.transport.TTransportFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.security.auth.Subject;
-import javax.security.auth.kerberos.KerberosPrincipal;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.Constructor;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.security.PrivilegedExceptionAction;
-import java.util.HashSet;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Sets;
 
 public class SentryService implements Runnable {
 
@@ -257,9 +260,27 @@ public class SentryService implements Runnable {
     }
     throw new IllegalStateException("Unable to find a port after 1000 attempts");
   }
+
+  @SuppressWarnings("deprecation")
+  public static Configuration loadConfig(String configFileName)
+      throws MalformedURLException {
+    File configFile = null;
+    if (configFileName == null) {
+      throw new IllegalArgumentException("Usage: "
+          + ServiceConstants.ServiceArgs.CONFIG_FILE_LONG
+          + " path/to/sentry-service.xml");
+    } else if (!((configFile = new File(configFileName)).isFile() && configFile
+        .canRead())) {
+      throw new IllegalArgumentException("Cannot read configuration file "
+          + configFile);
+    }
+    Configuration conf = new Configuration(false);
+    conf.addResource(configFile.toURL());
+    return conf;
+  }
+
   public static class CommandImpl implements Command {
     @Override
-    @SuppressWarnings("deprecation")
     public void run(String[] args) throws Exception {
       CommandLineParser parser = new GnuParser();
       Options options = new Options();
@@ -278,9 +299,8 @@ public class SentryService implements Runnable {
       } else if(!((configFile = new File(configFileName)).isFile() && configFile.canRead())) {
         throw new IllegalArgumentException("Cannot read configuration file " + configFile);
       }
-      Configuration conf = new Configuration(false);
-      conf.addResource(configFile.toURL());
-      final SentryService server = new SentryService(conf);
+      Configuration serverConf = loadConfig(configFileName);
+      final SentryService server = new SentryService(serverConf);
       server.start();
       Runtime.getRuntime().addShutdownHook(new Thread() {
         @Override

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java
index 76b3ebe..2a5553c 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/ServiceConstants.java
@@ -17,13 +17,14 @@
  */
 package org.apache.sentry.service.thrift;
 
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableMap;
-
-import javax.security.sasl.Sasl;
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.security.sasl.Sasl;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableMap;
+
 public class ServiceConstants {
 
   private static final ImmutableMap<String, String> SASL_PROPERTIES;
@@ -43,6 +44,7 @@ public class ServiceConstants {
     public static final String CONFIG_FILE_SHORT = "c";
     public static final String CONFIG_FILE_LONG = "conffile";
   }
+
   public static class ServerConfig {
     public static final ImmutableMap<String, String> SASL_PROPERTIES = ServiceConstants.SASL_PROPERTIES;
     /**
@@ -80,6 +82,9 @@ public class ServiceConstants {
     public static final String SENTRY_JAVAX_JDO_PROPERTY_PREFIX = SENTRY_DB_PROPERTY_PREFIX + "javax.jdo";
     public static final String SENTRY_DATANUCLEUS_PROPERTY_PREFIX = SENTRY_DB_PROPERTY_PREFIX + "datanucleus";
 
+    public static final String SENTRY_VERIFY_SCHEM_VERSION = "sentry.verify.schema.version";
+    public static final String SENTRY_VERIFY_SCHEM_VERSION_DEFAULT = "true";
+
     public static final ImmutableMap<String, String> SENTRY_STORE_DEFAULTS =
         ImmutableMap.<String, String>builder()
     .put("datanucleus.connectionPoolingType", "BoneCP")
@@ -87,8 +92,8 @@ public class ServiceConstants {
     .put("datanucleus.validateColumns", "false")
     .put("datanucleus.validateConstraints", "false")
     .put("datanucleus.storeManagerType", "rdbms")
-    .put("datanucleus.autoCreateSchema", "true")
-    .put("datanucleus.fixedDatastore", "false")
+        .put("datanucleus.autoCreateSchema", "false")
+        .put("datanucleus.fixedDatastore", "true")
     .put("datanucleus.autoStartMechanismMode", "checked")
     .put("datanucleus.transactionIsolation", "read-committed")
     .put("datanucleus.cache.level2", "false")
@@ -131,4 +136,4 @@ public class ServiceConstants {
     TABLE,
     COLUMN
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/resources/sentry-derby-1.4.0.sql
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry-derby-1.4.0.sql b/sentry-provider/sentry-provider-db/src/main/resources/sentry-derby-1.4.0.sql
new file mode 100644
index 0000000..b48d028
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry-derby-1.4.0.sql
@@ -0,0 +1,112 @@
+--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.
+
+-- Table SENTRY_DB_PRIVILEGE for classes [org.apache.sentry.provider.db.service.model.MSentryPrivilege]
+CREATE TABLE SENTRY_DB_PRIVILEGE
+(
+    DB_PRIVILEGE_ID BIGINT NOT NULL generated always as identity (start with 1),
+    URI VARCHAR(4000),
+    "ACTION" VARCHAR(40),
+    CREATE_TIME BIGINT NOT NULL,
+    DB_NAME VARCHAR(4000),
+    GRANTOR_PRINCIPAL VARCHAR(4000),
+    PRIVILEGE_NAME VARCHAR(128),
+    PRIVILEGE_SCOPE VARCHAR(40),
+    "SERVER_NAME" VARCHAR(4000),
+    "TABLE_NAME" VARCHAR(4000)
+);
+
+ALTER TABLE SENTRY_DB_PRIVILEGE ADD CONSTRAINT SENTRY_DB_PRIVILEGE_PK PRIMARY KEY (DB_PRIVILEGE_ID);
+
+-- Table SENTRY_ROLE for classes [org.apache.sentry.provider.db.service.model.MSentryRole]
+CREATE TABLE SENTRY_ROLE
+(
+    ROLE_ID BIGINT NOT NULL generated always as identity (start with 1),
+    CREATE_TIME BIGINT NOT NULL,
+    GRANTOR_PRINCIPAL VARCHAR(4000),
+    ROLE_NAME VARCHAR(128)
+);
+
+ALTER TABLE SENTRY_ROLE ADD CONSTRAINT SENTRY_ROLE_PK PRIMARY KEY (ROLE_ID);
+
+-- Table SENTRY_GROUP for classes [org.apache.sentry.provider.db.service.model.MSentryGroup]
+CREATE TABLE SENTRY_GROUP
+(
+    GROUP_ID BIGINT NOT NULL generated always as identity (start with 1),
+    CREATE_TIME BIGINT NOT NULL,
+    GRANTOR_PRINCIPAL VARCHAR(4000),
+    GROUP_NAME VARCHAR(128)
+);
+
+ALTER TABLE SENTRY_GROUP ADD CONSTRAINT SENTRY_GROUP_PK PRIMARY KEY (GROUP_ID);
+
+-- Table SENTRY_ROLE_GROUP_MAP for join relationship
+CREATE TABLE SENTRY_ROLE_GROUP_MAP
+(
+    GROUP_ID BIGINT NOT NULL,
+    ROLE_ID BIGINT NOT NULL
+);
+
+ALTER TABLE SENTRY_ROLE_GROUP_MAP ADD CONSTRAINT SENTRY_ROLE_GROUP_MAP_PK PRIMARY KEY (GROUP_ID,ROLE_ID);
+
+-- Table SENTRY_ROLE_DB_PRIVILEGE_MAP for join relationship
+CREATE TABLE SENTRY_ROLE_DB_PRIVILEGE_MAP
+(
+    ROLE_ID BIGINT NOT NULL,
+    DB_PRIVILEGE_ID BIGINT NOT NULL
+);
+
+ALTER TABLE SENTRY_ROLE_DB_PRIVILEGE_MAP ADD CONSTRAINT SENTRY_ROLE_DB_PRIVILEGE_MAP_PK PRIMARY KEY (ROLE_ID,DB_PRIVILEGE_ID);
+
+CREATE TABLE "SENTRY_VERSION" (
+  VER_ID BIGINT NOT NULL,
+  SCHEMA_VERSION VARCHAR(127),
+  VERSION_COMMENT VARCHAR(255)
+);
+
+ALTER TABLE SENTRY_VERSION ADD CONSTRAINT SENTRY_VERSION_PK PRIMARY KEY (VER_ID);
+
+-- Constraints for table SENTRY_DB_PRIVILEGE for class(es) [org.apache.sentry.provider.db.service.model.MSentryPrivilege]
+CREATE UNIQUE INDEX SENTRYPRIVILEGENAME ON SENTRY_DB_PRIVILEGE (PRIVILEGE_NAME);
+
+
+-- Constraints for table SENTRY_ROLE for class(es) [org.apache.sentry.provider.db.service.model.MSentryRole]
+CREATE UNIQUE INDEX SENTRYROLENAME ON SENTRY_ROLE (ROLE_NAME);
+
+
+-- Constraints for table SENTRY_GROUP for class(es) [org.apache.sentry.provider.db.service.model.MSentryGroup]
+CREATE UNIQUE INDEX SENTRYGROUPNAME ON SENTRY_GROUP (GROUP_NAME);
+
+
+-- Constraints for table SENTRY_ROLE_GROUP_MAP
+CREATE INDEX SENTRY_ROLE_GROUP_MAP_N49 ON SENTRY_ROLE_GROUP_MAP (GROUP_ID);
+
+CREATE INDEX SENTRY_ROLE_GROUP_MAP_N50 ON SENTRY_ROLE_GROUP_MAP (ROLE_ID);
+
+ALTER TABLE SENTRY_ROLE_GROUP_MAP ADD CONSTRAINT SENTRY_ROLE_GROUP_MAP_FK2 FOREIGN KEY (ROLE_ID) REFERENCES SENTRY_ROLE (ROLE_ID) ;
+
+ALTER TABLE SENTRY_ROLE_GROUP_MAP ADD CONSTRAINT SENTRY_ROLE_GROUP_MAP_FK1 FOREIGN KEY (GROUP_ID) REFERENCES SENTRY_GROUP (GROUP_ID) ;
+
+
+-- Constraints for table SENTRY_ROLE_DB_PRIVILEGE_MAP
+CREATE INDEX SENTRY_ROLE_DB_PRIVILEGE_MAP_N50 ON SENTRY_ROLE_DB_PRIVILEGE_MAP (ROLE_ID);
+
+CREATE INDEX SENTRY_ROLE_DB_PRIVILEGE_MAP_N49 ON SENTRY_ROLE_DB_PRIVILEGE_MAP (DB_PRIVILEGE_ID);
+
+ALTER TABLE SENTRY_ROLE_DB_PRIVILEGE_MAP ADD CONSTRAINT SENTRY_ROLE_DB_PRIVILEGE_MAP_FK2 FOREIGN KEY (DB_PRIVILEGE_ID) REFERENCES SENTRY_DB_PRIVILEGE (DB_PRIVILEGE_ID) ;
+
+ALTER TABLE SENTRY_ROLE_DB_PRIVILEGE_MAP ADD CONSTRAINT SENTRY_ROLE_DB_PRIVILEGE_MAP_FK1 FOREIGN KEY (ROLE_ID) REFERENCES SENTRY_ROLE (ROLE_ID) ;
+
+INSERT INTO SENTRY_VERSION (VER_ID, SCHEMA_VERSION, VERSION_COMMENT) VALUES (1, '1.4.0', 'Sentry release version 1.4.0');

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql b/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql
index 83d972b..d71fda7 100644
--- a/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql
+++ b/sentry-provider/sentry-provider-db/src/main/resources/sentry-oracle-1.4.0.sql
@@ -100,5 +100,5 @@ ALTER TABLE "SENTRY_ROLE_GROUP_MAP"
   ADD CONSTRAINT "SEN_ROLE_GROUP_MAP_SEN_GRP_FK"
   FOREIGN KEY ("GROUP_ID") REFERENCES "SENTRY_GROUP"("GROUP_ID") INITIALLY DEFERRED;
 
-INSERT INTO VERSION (VER_ID, SCHEMA_VERSION, VERSION_COMMENT) VALUES (1, '1.4.0', 'Sentry release version 1.4.0');
+INSERT INTO SENTRY_VERSION (VER_ID, SCHEMA_VERSION, VERSION_COMMENT) VALUES (1, '1.4.0', 'Sentry release version 1.4.0');
 

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/resources/upgrade.order.derby
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/resources/upgrade.order.derby b/sentry-provider/sentry-provider-db/src/main/resources/upgrade.order.derby
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/resources/upgrade.order.mysql
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/resources/upgrade.order.mysql b/sentry-provider/sentry-provider-db/src/main/resources/upgrade.order.mysql
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/resources/upgrade.order.oracle
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/resources/upgrade.order.oracle b/sentry-provider/sentry-provider-db/src/main/resources/upgrade.order.oracle
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/main/resources/upgrade.order.postgres
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/resources/upgrade.order.postgres b/sentry-provider/sentry-provider-db/src/main/resources/upgrade.order.postgres
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java
index 7f3415e..26bbf97 100644
--- a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryStore.java
@@ -18,9 +18,14 @@
 
 package org.apache.sentry.provider.db.service.persistent;
 
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
-import com.google.common.io.Files;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
 import org.apache.commons.io.FileUtils;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.sentry.core.model.db.AccessConstants;
@@ -36,13 +41,9 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-import java.io.File;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.fail;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
 
 public class TestSentryStore {
 
@@ -53,6 +54,7 @@ public class TestSentryStore {
   public void setup() throws Exception {
     dataDir = new File(Files.createTempDir(), "sentry_policy_db");
     Configuration conf = new Configuration(false);
+    conf.set(ServerConfig.SENTRY_VERIFY_SCHEM_VERSION, "false");
     conf.set(ServerConfig.SENTRY_STORE_JDBC_URL,
         "jdbc:derby:;databaseName=" + dataDir.getPath() + ";create=true");
     sentryStore = new SentryStore(conf);
@@ -285,4 +287,5 @@ public class TestSentryStore {
             newHashSet(groupName1, groupName2),
             new TSentryActiveRoleSet(false, new HashSet<String>()))));
   }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryVersion.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryVersion.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryVersion.java
new file mode 100644
index 0000000..0add58b
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/service/persistent/TestSentryVersion.java
@@ -0,0 +1,84 @@
+/**
+ * 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.sentry.provider.db.service.persistent;
+
+import static junit.framework.Assert.assertEquals;
+
+import java.io.File;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.provider.db.SentryNoSuchObjectException;
+import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.io.Files;
+
+public class TestSentryVersion {
+
+  private File dataDir;
+  private Configuration conf;
+
+  @Before
+  public void setup() throws Exception {
+    dataDir = new File(Files.createTempDir(), "sentry_policy_db");
+    conf = new Configuration(false);
+    conf.set(ServerConfig.SENTRY_STORE_JDBC_URL, "jdbc:derby:;databaseName="
+        + dataDir.getPath() + ";create=true");
+  }
+
+  /**
+   * Create the schema using auto creation Create new sentry store without
+   * implicit schema creation on the same backend db and make sure it starts
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testVerifySentryVersionCheck() throws Exception {
+    conf.set(ServerConfig.SENTRY_VERIFY_SCHEM_VERSION, "false");
+    SentryStore sentryStore = new SentryStore(conf);
+    sentryStore.stop();
+    conf.set(ServerConfig.SENTRY_VERIFY_SCHEM_VERSION, "true");
+    sentryStore = new SentryStore(conf);
+  }
+
+  /**
+   * Verify that store is not initialized by default without schema pre-created
+   * 
+   * @throws Exception
+   */
+  @Test(expected = SentryNoSuchObjectException.class)
+  public void testNegSentrySchemaDefault() throws Exception {
+    SentryStore sentryStore = new SentryStore(conf);
+  }
+
+  /**
+   * With schema verification turned off, Sentry Store should autoCreate the
+   * schema
+   * @throws Exception
+   */
+  @Test
+  public void testSentryImplicitVersion() throws Exception {
+    conf.set(ServerConfig.SENTRY_VERIFY_SCHEM_VERSION, "false");
+    SentryStore sentryStore = new SentryStore(conf);
+    assertEquals(SentryStoreSchemaInfo.getSentryVersion(),
+        sentryStore.getSentryVersion());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/7fb9ac4f/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/tools/TestSentrySchemaTool.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/tools/TestSentrySchemaTool.java b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/tools/TestSentrySchemaTool.java
new file mode 100644
index 0000000..17755e3
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/test/java/org/apache/sentry/provider/db/tools/TestSentrySchemaTool.java
@@ -0,0 +1,80 @@
+/**
+ * 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.sentry.provider.db.tools;
+
+import java.io.File;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.sentry.SentryUserException;
+import org.apache.sentry.provider.db.service.persistent.SentryStoreSchemaInfo;
+import org.apache.sentry.service.thrift.ServiceConstants.ServerConfig;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.io.Files;
+
+public class TestSentrySchemaTool {
+  private Configuration sentryConf;
+  private SentrySchemaTool schemaTool;
+
+  @Before
+  public void setup() throws Exception {
+    sentryConf = new Configuration();
+    File dbDir = new File(Files.createTempDir(), "sentry_policy_db");
+    sentryConf.set(ServerConfig.SENTRY_STORE_JDBC_URL,
+        "jdbc:derby:;databaseName=" + dbDir.getPath() + ";create=true");
+    sentryConf.set("javax.jdo.option.ConnectionDriverName",
+        "org.apache.derby.jdbc.EmbeddedDriver");
+    sentryConf.set(ServerConfig.SENTRY_STORE_JDBC_USER,
+        ServerConfig.SENTRY_STORE_JDBC_USER_DEFAULT);
+    sentryConf.set(ServerConfig.SENTRY_STORE_JDBC_PASS,
+        ServerConfig.SENTRY_STORE_JDBC_PASS_DEFAULT);
+    schemaTool = new SentrySchemaTool("./src/main/resources", sentryConf,
+        "derby");
+  }
+
+  @Test
+  public void testInit() throws Exception {
+    schemaTool.doInit();
+    schemaTool.verifySchemaVersion();
+  }
+
+  @Test
+  public void testInitTo() throws Exception {
+    schemaTool.doInit(SentryStoreSchemaInfo.getSentryVersion());
+    schemaTool.verifySchemaVersion();
+  }
+
+  @Test(expected = SentryUserException.class)
+  public void testDryRun() throws Exception {
+    schemaTool.setDryRun(true);
+    schemaTool.doInit();
+    schemaTool.setDryRun(false);
+    // verification should fail since dryRun didn't create the actual schema
+    schemaTool.verifySchemaVersion();
+  }
+
+  @Test
+  public void testUpgrade() throws Exception {
+    schemaTool.doInit(SentryStoreSchemaInfo.getSentryVersion());
+    schemaTool.doUpgrade();
+    schemaTool.verifySchemaVersion();
+  }
+
+}


Mime
View raw message