/**
 * Copyright 2016 Palantir Technologies
 *
 * Licensed under the BSD-3 License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/BSD-3-Clause
 *
 * 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.palantir.atlasdb.keyvalue.dbkvs;

import com.google.common.base.Preconditions;
import com.palantir.atlasdb.AtlasDbConstants;
import com.palantir.atlasdb.keyvalue.api.Namespace;
import com.palantir.atlasdb.keyvalue.api.TableReference;
import com.palantir.atlasdb.keyvalue.dbkvs.impl.ConnectionSupplier;
import com.palantir.atlasdb.keyvalue.dbkvs.impl.DbKvs;
import com.palantir.nexus.db.sql.AgnosticResultSet;

public class OracleTableNameMapper {
    public static final int ORACLE_MAX_TABLE_NAME_LENGTH = 30;
    public static final int SUFFIX_NUMBER_LENGTH = 6;
    public static final int MAX_NAMESPACE_LENGTH = 2;
    private static final int PREFIXED_TABLE_NAME_LENGTH = ORACLE_MAX_TABLE_NAME_LENGTH - SUFFIX_NUMBER_LENGTH;

    private final ConnectionSupplier conns;

    public OracleTableNameMapper(ConnectionSupplier conns) {
        this.conns = conns;
    }

    public String getShortPrefixedTableName(String tablePrefix, TableReference tableRef) {
        Preconditions.checkState(tablePrefix.length() <= AtlasDbConstants.MAX_TABLE_PREFIX_LENGTH,
                "The tablePrefix can be at most %s characters long", AtlasDbConstants.MAX_TABLE_PREFIX_LENGTH);

        String fullTableName = tablePrefix + DbKvs.internalTableName(tableRef);
        if (fullTableName.length() <= PREFIXED_TABLE_NAME_LENGTH) {
            return fullTableName;
        }

        TableReference shortenedNamespaceTableRef = truncateNamespace(tableRef);

        String prefixedTableName = tablePrefix + DbKvs.internalTableName(shortenedNamespaceTableRef);
        String truncatedTableName = truncate(prefixedTableName, PREFIXED_TABLE_NAME_LENGTH);
        return truncatedTableName + getTableNumberSuffix(fullTableName, truncatedTableName);
    }

    private TableReference truncateNamespace(TableReference tableRef) {
        if (tableRef.getNamespace().isEmptyNamespace()) {
            return tableRef;
        }
        String namespace = tableRef.getNamespace().getName();
        namespace = truncate(namespace, MAX_NAMESPACE_LENGTH);
        return TableReference.create(Namespace.create(namespace), tableRef.getTablename());
    }

    private String truncate(String name, int length) {
        return name.substring(0, Math.min(name.length(), length));
    }

    private String getTableNumberSuffix(String fullTableName, String truncatedTableName) {
        int tableSuffixNumber = getNextTableNumber(truncatedTableName);
        if (tableSuffixNumber >= 100_000) {
            throw new IllegalArgumentException(
                    "Cannot create any more tables with name starting with " + truncatedTableName
                    + ". 100,000 tables might have already been created. Please rename the table." + fullTableName);
        }
        return "_" + String.format("%05d", tableSuffixNumber);
    }

    private int getNextTableNumber(String truncatedTableName) {
        AgnosticResultSet results = conns.get().selectResultSetUnregisteredQuery(
                "SELECT short_table_name "
                + "FROM " + AtlasDbConstants.ORACLE_NAME_MAPPING_TABLE
                + "WHERE LOWER(short_table_name) LIKE LOWER(? || '\\______%') ESCAPE '\\'"
                + " ORDER BY short_table_name DESC", truncatedTableName);
        return getTableNumberFromTableNames(truncatedTableName, results);
    }

    private int getTableNumberFromTableNames(String truncatedTableName, AgnosticResultSet results) {
        for (int i = 0; i < results.size(); i++) {
            String shortName = results.get(i).getString("short_table_name");
            try {
                return Integer.parseInt(shortName.substring(truncatedTableName.length() + 1)) + 1;
            } catch (NumberFormatException e) {
                //Table in different format - Do nothing;
            }
        }
        return 0;
    }
}

@@ -3689,16 +3689,17 @@
      + %22
+ 
 WHERE LO
@@ -3736,12 +3736,10 @@
 ER(?
- 
 %7C%7C
- 
 '%5C%5C_

Fix query