/** * Copyright 2016 Hortonworks. * * Licensed 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 com.hortonworks.registries.storage.impl.jdbc.provider.phoenix.query; import com.hortonworks.registries.storage.impl.jdbc.config.ExecutionConfig; import com.hortonworks.registries.storage.impl.jdbc.connection.ConnectionBuilder; import com.hortonworks.registries.storage.impl.jdbc.provider.sql.query.AbstractSqlQuery; import com.hortonworks.registries.storage.impl.jdbc.provider.sql.statement.PreparedStatementBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.util.UUID; /** * Query to get next sequence id in phoenix for a given name space. */ public class PhoenixSequenceIdQuery { private static final Logger log = LoggerFactory.getLogger(PhoenixSequenceIdQuery.class); private static final String ID = "id"; private static final String VALUE = "value"; private static final String SEQUENCE_TABLE = "sequence_table"; private final String namespace; private final ConnectionBuilder connectionBuilder; private final int queryTimeoutSecs; public PhoenixSequenceIdQuery(String namespace, ConnectionBuilder connectionBuilder, int queryTimeoutSecs) { this.namespace = namespace; this.connectionBuilder = connectionBuilder; this.queryTimeoutSecs = queryTimeoutSecs; } public Long getNextID() { // this is kind of work around as there is no direct support in phoenix to get next sequence-id without using any tables, // it involves 3 roundtrips to phoenix/hbase (inefficient but there is a limitation from phoenix!). // SEQUENCE can be used for such columns in UPSERT queries directly but to get a simple sequence-id involves all this. // create sequence for each namespace and insert it into with a value uuid. // get the id for inserted uuid. // delete that entry from the table. long nextId = 0; UUID uuid = UUID.randomUUID(); PhoenixSqlQuery updateQuery = new PhoenixSqlQuery("UPSERT INTO " + SEQUENCE_TABLE + "(\""+ID+"\", \"" + namespace + "\") VALUES('" + uuid + "', NEXT VALUE FOR " + namespace + "_sequence)"); PhoenixSqlQuery selectQuery = new PhoenixSqlQuery("SELECT \"" + namespace + "\" FROM " + SEQUENCE_TABLE + " WHERE \"" + ID + "\"='" + uuid + "'"); PhoenixSqlQuery deleteQuery = new PhoenixSqlQuery("DELETE FROM " + SEQUENCE_TABLE + " WHERE \"id\"='" + uuid + "'"); try (Connection connection = connectionBuilder.getConnection()) { int upsertResult = PreparedStatementBuilder.of(connection, new ExecutionConfig(queryTimeoutSecs), updateQuery).getPreparedStatement(updateQuery).executeUpdate(); log.debug("Query [{}] is executed and returns result with [{}]", updateQuery, upsertResult); ResultSet selectResultSet = PreparedStatementBuilder.of(connection, new ExecutionConfig(queryTimeoutSecs), selectQuery).getPreparedStatement(selectQuery).executeQuery(); if (selectResultSet.next()) { nextId = selectResultSet.getLong(namespace); } else { throw new RuntimeException("No sequence-id created for the current sequence of [" + namespace + "]"); } log.debug("Generated sequence id [{}] for [{}]", nextId, namespace); int deleteResult = PreparedStatementBuilder.of(connection, new ExecutionConfig(queryTimeoutSecs), deleteQuery).getPreparedStatement(deleteQuery).executeUpdate(); if (deleteResult == 0) { log.error("Could not delete entry in " + SEQUENCE_TABLE + " for value [{}]", namespace, uuid); } else { log.debug("Deleted entry with id [{}] and value [{}] successfully",uuid, nextId); } } catch (SQLException e) { log.error(e.getMessage(), e); throw new RuntimeException(e); } return nextId; } static class PhoenixSqlQuery extends AbstractSqlQuery { public PhoenixSqlQuery(String sql) { this.sql = sql; } @Override protected void initParameterizedSql() { } } }