All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.palantir.atlasdb.keyvalue.dbkvs.OracleTableNameUnmapper Maven / Gradle / Ivy

There is a newer version: 0.1152.0
Show newest version
/*
 * (c) Copyright 2018 Palantir Technologies Inc. All rights reserved.
 *
 * 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.palantir.atlasdb.keyvalue.dbkvs;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.palantir.atlasdb.AtlasDbConstants;
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.common.exception.TableMappingNotFoundException;
import com.palantir.exception.PalantirSqlException;
import com.palantir.logsafe.Preconditions;
import com.palantir.logsafe.SafeArg;
import com.palantir.logsafe.UnsafeArg;
import com.palantir.logsafe.exceptions.SafeIllegalStateException;
import com.palantir.logsafe.logger.SafeLogger;
import com.palantir.logsafe.logger.SafeLoggerFactory;
import com.palantir.nexus.db.sql.AgnosticResultSet;
import com.palantir.nexus.db.sql.SqlConnection;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;

class OracleTableNameUnmapper {
    private static final SafeLogger log = SafeLoggerFactory.get(OracleTableNameUnmapper.class);

    private final Cache unmappingCache;

    OracleTableNameUnmapper() {
        unmappingCache = Caffeine.newBuilder().build();
    }

    @SuppressWarnings("checkstyle:NestedTryDepth")
    public String getShortTableNameFromMappingTable(
            ConnectionSupplier connectionSupplier, String tablePrefix, TableReference tableRef)
            throws TableMappingNotFoundException {
        String fullTableName = tablePrefix + DbKvs.internalTableName(tableRef);
        try {
            return unmappingCache.get(fullTableName, tableNameKey -> {
                SqlConnection conn = connectionSupplier.get();
                AgnosticResultSet results = conn.selectResultSetUnregisteredQuery(
                        "SELECT short_table_name "
                                + "FROM " + AtlasDbConstants.ORACLE_NAME_MAPPING_TABLE
                                + " WHERE table_name = ?",
                        tableNameKey);
                if (results.size() == 0) {
                    throw new SafeIllegalStateException(
                            "The table does not have a mapping. This might be because the table does not exist.",
                            UnsafeArg.of("tableName", tableNameKey));
                }

                return Iterables.getOnlyElement(results.rows()).getString("short_table_name");
            });
        } catch (SafeIllegalStateException e) {
            throw new TableMappingNotFoundException(
                    "The table " + fullTableName
                            + " does not have a mapping. This might be because the table does not exist.",
                    e);
        }
    }

    public Map getShortToLongTableNamesFromMappingTable(
            ConnectionSupplier connectionSupplier, Set shortTableNames) {
        if (shortTableNames.isEmpty()) {
            return Map.of();
        }
        ImmutableMap.Builder shortToLongTableNames = ImmutableMap.builder();
        for (List batch :
                Iterables.partition(shortTableNames, AtlasDbConstants.MINIMUM_IN_CLAUSE_EXPRESSION_LIMIT)) {
            shortToLongTableNames.putAll(getBatchOfLongTableNamesFromMappingTable(connectionSupplier, batch));
        }
        return shortToLongTableNames.buildOrThrow();
    }

    private Map getBatchOfLongTableNamesFromMappingTable(
            ConnectionSupplier connectionSupplier, Collection shortTableNames) {
        String placeHolders = String.join(",", Collections.nCopies(shortTableNames.size(), "LOWER(?)"));

        SqlConnection conn = connectionSupplier.get();

        // Our Oracle name mapping table contains entries in the user-provided case. However, we don't necessarily
        // have the user-supplied casing when calling this method (e.g, the short table names were retrieved from
        // all_tables, which are stored in caps unless explicitly opted out of on a per-entry basis).
        // Thus, we need to do a case-insensitive search, implemented by LOWER(x) = LOWER(y). For completeness sake, one
        // likely also needs to do OR UPPER(x) = UPPER(y), but there's little benefit in doing it here.

        // We use the Oracle LOWER function rather than mapping to lower case on the client to ensure that we're
        // using the same locale and conversion.
        Map shortTableToLongTableMapping = runIgnoringTableNotFound(
                () -> {
                    AgnosticResultSet results = conn.selectResultSetUnregisteredQuery(
                            "SELECT short_table_name, table_name FROM " + AtlasDbConstants.ORACLE_NAME_MAPPING_TABLE
                                    + " WHERE LOWER(short_table_name) IN (" + placeHolders + ")",
                            shortTableNames.toArray());

                    return results.rows().stream()
                            .collect(Collectors.toMap(
                                    row -> row.getString("short_table_name"), row -> row.getString("table_name")));
                },
                Map::of);

        verifyMappingSetSizeNotLargerThanExpected(shortTableToLongTableMapping, shortTableNames);

        return shortTableToLongTableMapping;
    }

    private void verifyMappingSetSizeNotLargerThanExpected(
            Map shortTableToLongTableMapping, Collection expectedShortTableNames) {
        Preconditions.checkState(
                shortTableToLongTableMapping.size() <= expectedShortTableNames.size(),
                "There are more returned long table names than provided short table names. This likely indicates"
                        + " a bug in AtlasDB",
                SafeArg.of("numLongTables", shortTableToLongTableMapping.size()),
                SafeArg.of("numShortTables", expectedShortTableNames.size()),
                UnsafeArg.of("returnedMapping", shortTableToLongTableMapping),
                UnsafeArg.of("expectedShortTableNames", expectedShortTableNames));
    }

    public void clearCacheForTable(String fullTableName) {
        unmappingCache.invalidate(fullTableName);
    }

    private static  T runIgnoringTableNotFound(Supplier task, Supplier defaultValueSupplier) {
        try {
            return task.get();
        } catch (PalantirSqlException e) {
            if (!e.getMessage().contains(OracleErrorConstants.ORACLE_NOT_EXISTS_ERROR)) {
                log.error("Error occurred trying to execute the task", e);
                throw e;
            }
            return defaultValueSupplier.get();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy