public StoreObject merge() {
      boolean alreadySetDatabaseObject = false;

      // first pass
      for (StoreObject store : this.storeObjects) {
        copySchemaInfo(store);
      }

      // second pass
      for (StoreObject store : this.storeObjects) {
        if (store.getSchema().getVersion() == targetStore.getSchema().getVersion()
            && !alreadySetDatabaseObject) {
          BaseSchema targetSchema = targetStore.getSchema();
          targetSchema.clearObjects();
          targetSchema.addObjects(mergeDatabaseObjects(store.getSchema().getObjects()));

          alreadySetDatabaseObject = true;
        }

        mergePatches(store.getPatches());
        mergeExistQueries(store.getExistQueries());
        mergeDropStatements(store.getDropStatements());
      }

      return this.targetStore;
    }
    protected void mergeExistQueries(List<SQLObject> queries) {
      for (SQLObject query : queries) {
        validateSQLObject(queries, query);

        targetStore.addExistQuery(query);
      }
    }
    protected void mergeDropStatements(List<SQLObject> queries) {
      for (SQLObject query : queries) {
        validateSQLObject(queries, query);

        targetStore.addDropStatement(query);
      }
    }
  protected String getDropSQL(DatabaseObjectType type, String name) {
    SQLObject foundDropQuery = null;
    String sqlStatement = "DROP " + type.toString() + " " + name;

    for (SQLObject dropQuery : catalogStore.getDropStatements()) {
      if (type == dropQuery.getType()) {
        foundDropQuery = dropQuery;
        break;
      }
    }

    if (foundDropQuery != null
        && foundDropQuery.getSql() != null
        && !foundDropQuery.getSql().isEmpty()) {
      String dropStatement = foundDropQuery.getSql();
      StringBuffer sqlBuffer = new StringBuffer(dropStatement.length() + name.length());
      int identifier = dropStatement.indexOf('?');

      sqlBuffer
          .append(dropStatement.substring(0, identifier))
          .append(name)
          .append(dropStatement.substring(identifier + 1));
      sqlStatement = sqlBuffer.toString();
    }

    return sqlStatement;
  }
  protected PreparedStatement getExistQuery(Connection conn, DatabaseObjectType type)
      throws SQLException {
    PreparedStatement pstmt = null;

    for (SQLObject existQuery : catalogStore.getExistQueries()) {
      if (type.equals(existQuery.getType())) {
        pstmt = conn.prepareStatement(existQuery.getSql());
        break;
      }
    }

    return pstmt;
  }
    protected void mergePatches(List<SchemaPatch> patches) {
      final List<DatabaseObject> objects = new ArrayList<>();

      Collections.sort(patches);

      for (SchemaPatch patch : patches) {
        validatePatch(patches, patch);

        objects.clear();
        List<DatabaseObject> tempObjects = new ArrayList<>();
        tempObjects.addAll(patch.getObjects());
        patch.clearObjects();
        patch.addObjects(mergeDatabaseObjects(tempObjects));

        targetStore.addPatch(patch);
      }
    }
  public void dropBaseSchema(Connection conn) {
    if (!isLoaded()) {
      throw new TajoInternalError("Schema files are not loaded yet.");
    }

    List<DatabaseObject> failedObjects = new ArrayList<>();
    Statement stmt = null;

    try {
      stmt = conn.createStatement();
    } catch (SQLException e) {
      throw new TajoInternalError(e);
    }

    for (DatabaseObject object : catalogStore.getSchema().getObjects()) {
      try {
        if (DatabaseObjectType.TABLE == object.getType()
            || DatabaseObjectType.SEQUENCE == object.getType()
            || DatabaseObjectType.VIEW == object.getType()) {
          stmt.executeUpdate(getDropSQL(object.getType(), object.getName()));
        }
      } catch (SQLException e) {
        failedObjects.add(object);
      }
    }
    CatalogUtil.closeQuietly(stmt);

    if (failedObjects.size() > 0) {
      StringBuffer errorMessage = new StringBuffer(64);
      errorMessage.append("Failed to drop database objects ");

      for (int idx = 0; idx < failedObjects.size(); idx++) {
        DatabaseObject object = failedObjects.get(idx);
        errorMessage.append(object.getType().toString()).append(" ").append(object.getName());
        if ((idx + 1) < failedObjects.size()) {
          errorMessage.append(',');
        }
      }

      LOG.warn(errorMessage.toString());
    }
  }
  public void createBaseSchema(Connection conn) {
    Statement stmt;

    if (!isLoaded()) {
      throw new TajoInternalError("Database schema files are not loaded.");
    }

    try {
      stmt = conn.createStatement();
    } catch (SQLException e) {
      throw new TajoInternalError(e);
    }

    for (DatabaseObject object : catalogStore.getSchema().getObjects()) {
      try {
        String[] params;
        if (DatabaseObjectType.INDEX == object.getType()) {
          params = new String[2];
          params[0] = object.getDependsOn();
          params[1] = object.getName();
        } else {
          params = new String[1];
          params[0] = object.getName();
        }

        if (checkExistence(conn, object.getType(), params)) {
          LOG.info("Skip to create " + object.getName() + " databse object. Already exists.");
        } else {
          stmt.executeUpdate(object.getSql());
          LOG.info(object.getName() + " " + object.getType() + " is created.");
        }
      } catch (SQLException e) {
        throw new TajoInternalError(e);
      }
    }

    CatalogUtil.closeQuietly(stmt);
  }
  public boolean isInitialized(Connection conn) {
    if (!isLoaded()) {
      throw new TajoInternalError("Database schema files are not loaded.");
    }

    boolean result = true;

    for (DatabaseObject object : catalogStore.getSchema().getObjects()) {
      try {
        if (DatabaseObjectType.INDEX == object.getType()) {
          result &= checkExistence(conn, object.getType(), object.getDependsOn(), object.getName());
        } else {
          result &= checkExistence(conn, object.getType(), object.getName());
        }
      } catch (SQLException e) {
        throw new TajoInternalError(e);
      }

      if (!result) {
        break;
      }
    }
    return result;
  }
    protected void copySchemaInfo(StoreObject sourceStore) {
      if (sourceStore.getSchema().getSchemaName() != null
          && !sourceStore.getSchema().getSchemaName().isEmpty()) {
        if (targetStore.getSchema().getSchemaName() != null
            && !targetStore.getSchema().getSchemaName().isEmpty()
            && !targetStore
                .getSchema()
                .getSchemaName()
                .equalsIgnoreCase(sourceStore.getSchema().getSchemaName())) {
          throw new TajoInternalError(
              "different schema names are specified. One is "
                  + sourceStore.getSchema().getSchemaName()
                  + " and other is "
                  + targetStore.getSchema().getSchemaName());
        }

        if (targetStore.getSchema().getSchemaName() == null
            || targetStore.getSchema().getSchemaName().isEmpty()) {
          targetStore.getSchema().setSchemaName(sourceStore.getSchema().getSchemaName());
        }
      }

      if (sourceStore.getSchema().getVersion() > -1
          && targetStore.getSchema().getVersion() < sourceStore.getSchema().getVersion()) {
        targetStore.getSchema().setVersion(sourceStore.getSchema().getVersion());
      }
    }
  protected boolean checkExistence(Connection conn, DatabaseObjectType type, String... params)
      throws SQLException {
    boolean result = false;
    DatabaseMetaData metadata = null;
    PreparedStatement pstmt = null;
    BaseSchema baseSchema = catalogStore.getSchema();

    if (params == null || params.length < 1) {
      throw new IllegalArgumentException("checkExistence function needs at least one argument.");
    }

    switch (type) {
      case DATA:
        metadata = conn.getMetaData();
        ResultSet data =
            metadata.getUDTs(
                null,
                baseSchema.getSchemaName() != null && !baseSchema.getSchemaName().isEmpty()
                    ? baseSchema.getSchemaName().toUpperCase()
                    : null,
                params[0].toUpperCase(),
                null);
        result = data.next();
        CatalogUtil.closeQuietly(data);
        break;
      case FUNCTION:
        metadata = conn.getMetaData();
        ResultSet functions =
            metadata.getFunctions(
                null,
                baseSchema.getSchemaName() != null && !baseSchema.getSchemaName().isEmpty()
                    ? baseSchema.getSchemaName().toUpperCase()
                    : null,
                params[0].toUpperCase());
        result = functions.next();
        CatalogUtil.closeQuietly(functions);
        break;
      case INDEX:
        if (params.length != 2) {
          throw new IllegalArgumentException(
              "Finding index object is needed two strings, table name and index name");
        }

        pstmt = getExistQuery(conn, type);
        if (pstmt != null) {
          result = checkExistenceByQuery(pstmt, baseSchema, params);
        } else {
          metadata = conn.getMetaData();
          ResultSet indexes =
              metadata.getIndexInfo(
                  null,
                  baseSchema.getSchemaName() != null && !baseSchema.getSchemaName().isEmpty()
                      ? baseSchema.getSchemaName().toUpperCase()
                      : null,
                  params[0].toUpperCase(),
                  false,
                  true);
          while (indexes.next()) {
            if (indexes.getString("INDEX_NAME").equals(params[1].toUpperCase())) {
              result = true;
              break;
            }
          }
          CatalogUtil.closeQuietly(indexes);
        }
        break;
      case TABLE:
        pstmt = getExistQuery(conn, type);
        if (pstmt != null) {
          result = checkExistenceByQuery(pstmt, baseSchema, params);
        } else {
          metadata = conn.getMetaData();
          ResultSet tables =
              metadata.getTables(
                  null,
                  baseSchema.getSchemaName() != null && !baseSchema.getSchemaName().isEmpty()
                      ? baseSchema.getSchemaName().toUpperCase()
                      : null,
                  params[0].toUpperCase(),
                  new String[] {"TABLE"});
          result = tables.next();
          CatalogUtil.closeQuietly(tables);
        }
        break;
      case DOMAIN:
      case OPERATOR:
      case RULE:
      case SEQUENCE:
      case TRIGGER:
      case VIEW:
        pstmt = getExistQuery(conn, type);

        if (pstmt == null) {
          throw new TajoInternalError(
              "Finding "
                  + type
                  + " type of database object is not supported on this database system.");
        }

        result = checkExistenceByQuery(pstmt, baseSchema, params);
        break;
    }

    return result;
  }