IMPALA-10140: Fix CatalogExeception for creating database with sync_ddl as true

IMPALA-7961 handle the cases for query "create table if not exists"
with sync_ddl as true. Customers reported similar issue which happened
for query "create database if not exists" with sync_ddl as true.
This patch adds the similar fixing as the fixing for IMPALA-7961 to
function CatalogOpExecutor.createDatabase() to fix the issue.

Testing:
 - Manual tests
   Since this is a racy bug, I could only reproduce it by forcing
   frequent topicUpdateLog GCs along with a specific sequence of
   actions, like: run some DDLs and REFRESHs to trigger a GC in
   topicUpdateLog, then run query "create database if not exists" with
   sync_ddl as true. Verified that the issue couldn't be reproduced
   after applying this patch.
 - Passed exhaustive test.

Change-Id: Id623118f8938f416414c45d93404fb70d036a9df
Reviewed-on: http://gerrit.cloudera.org:8080/16421
Reviewed-by: Impala Public Jenkins <impala-public-jenkins@cloudera.com>
Tested-by: Impala Public Jenkins <impala-public-jenkins@cloudera.com>
diff --git a/fe/src/main/java/org/apache/impala/service/CatalogOpExecutor.java b/fe/src/main/java/org/apache/impala/service/CatalogOpExecutor.java
index cbd0ea3..e8270bb 100644
--- a/fe/src/main/java/org/apache/impala/service/CatalogOpExecutor.java
+++ b/fe/src/main/java/org/apache/impala/service/CatalogOpExecutor.java
@@ -367,7 +367,7 @@
           TCreateDbParams create_db_params = ddlRequest.getCreate_db_params();
           tTableName = Optional.of(new TTableName(create_db_params.db, ""));
           catalogOpMetric_.increment(ddl_type, tTableName);
-          createDatabase(create_db_params, response);
+          createDatabase(create_db_params, response, syncDdl);
           break;
         case CREATE_TABLE_AS_SELECT:
           TCreateTableParams create_table_as_select_params =
@@ -1275,9 +1275,10 @@
    * metadata cache, marking its metadata to be lazily loaded on the next access.
    * Re-throws any Hive Meta Store exceptions encountered during the create, these
    * may vary depending on the Meta Store connection type (thrift vs direct db).
+   * @param  syncDdl tells if SYNC_DDL option is enabled on this DDL request.
    */
-  private void createDatabase(TCreateDbParams params, TDdlExecResponse resp)
-      throws ImpalaException {
+  private void createDatabase(TCreateDbParams params, TDdlExecResponse resp,
+      boolean syncDdl) throws ImpalaException {
     Preconditions.checkNotNull(params);
     String dbName = params.getDb();
     Preconditions.checkState(dbName != null && !dbName.isEmpty(),
@@ -1292,6 +1293,29 @@
             + "and IF NOT EXISTS was specified.");
       }
       Preconditions.checkNotNull(existingDb);
+      if (syncDdl) {
+        tryLock(existingDb, "create database");
+        try {
+          // When SYNC_DDL is enabled and the database already exists, we force a version
+          // bump on it so that it is added to the next statestore update. Without this
+          // we could potentially be referring to a database object that has already been
+          // GC'ed from the TopicUpdateLog and waitForSyncDdlVersion() cannot find a
+          // covering topic version (IMPALA-7961).
+          //
+          // This is a conservative hack to not break the SYNC_DDL semantics and could
+          // possibly result in false-positive invalidates on this database. However,
+          // that is better than breaking the SYNC_DDL semantics and the subsequent
+          // queries referring to this database failing with "database not found" errors.
+          long newVersion = catalog_.incrementAndGetCatalogVersion();
+          existingDb.setCatalogVersion(newVersion);
+          LOG.trace("Database {} version bumped to {} because SYNC_DDL is enabled.",
+              dbName, newVersion);
+        } finally {
+          // Release the locks held in tryLock().
+          catalog_.getLock().writeLock().unlock();
+          existingDb.getLock().unlock();
+        }
+      }
       // TODO(IMPALA-9936): if client is a 'v2' impalad, only send back invalidation
       resp.getResult().addToUpdated_catalog_objects(existingDb.toTCatalogObject());
       resp.getResult().setVersion(existingDb.getCatalogVersion());