/* * 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.facebook.presto.raptor.util; import com.facebook.presto.spi.PrestoException; import com.google.common.base.Throwables; import org.skife.jdbi.v2.Handle; import org.skife.jdbi.v2.IDBI; import org.skife.jdbi.v2.TransactionCallback; import org.skife.jdbi.v2.exceptions.DBIException; import java.lang.reflect.InvocationTargetException; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.OptionalInt; import java.util.OptionalLong; import java.util.function.Consumer; import static com.facebook.presto.raptor.RaptorErrorCode.RAPTOR_METADATA_ERROR; import static com.google.common.base.Throwables.propagateIfInstanceOf; import static com.google.common.reflect.Reflection.newProxy; import static java.sql.Types.INTEGER; import static java.util.Objects.requireNonNull; public final class DatabaseUtil { private DatabaseUtil() {} public static <T> T onDemandDao(IDBI dbi, Class<T> daoType) { requireNonNull(dbi, "dbi is null"); return newProxy(daoType, (proxy, method, args) -> { try (Handle handle = dbi.open()) { T dao = handle.attach(daoType); return method.invoke(dao, args); } catch (DBIException e) { throw metadataError(e); } catch (InvocationTargetException e) { throw metadataError(e.getCause()); } }); } public static <T> T runTransaction(IDBI dbi, TransactionCallback<T> callback) { try { return dbi.inTransaction(callback); } catch (DBIException e) { propagateIfInstanceOf(e.getCause(), PrestoException.class); throw metadataError(e); } } public static <T> void daoTransaction(IDBI dbi, Class<T> daoType, Consumer<T> callback) { runTransaction(dbi, (handle, status) -> { callback.accept(handle.attach(daoType)); return null; }); } public static PrestoException metadataError(Throwable cause) { return new PrestoException(RAPTOR_METADATA_ERROR, "Failed to perform metadata operation", cause); } /** * Run a SQL query as ignoring any constraint violations. * This allows idempotent inserts (equivalent to INSERT IGNORE). */ public static void runIgnoringConstraintViolation(Runnable task) { try { task.run(); } catch (RuntimeException e) { if (!sqlCodeStartsWith(e, "23")) { throw e; } } } public static void enableStreamingResults(Statement statement) throws SQLException { if (statement.isWrapperFor(com.mysql.jdbc.Statement.class)) { statement.unwrap(com.mysql.jdbc.Statement.class).enableStreamingResults(); } } public static OptionalInt getOptionalInt(ResultSet rs, String name) throws SQLException { int value = rs.getInt(name); return rs.wasNull() ? OptionalInt.empty() : OptionalInt.of(value); } public static OptionalLong getOptionalLong(ResultSet rs, String name) throws SQLException { long value = rs.getLong(name); return rs.wasNull() ? OptionalLong.empty() : OptionalLong.of(value); } public static void bindOptionalInt(PreparedStatement statement, int index, OptionalInt value) throws SQLException { if (value.isPresent()) { statement.setInt(index, value.getAsInt()); } else { statement.setNull(index, INTEGER); } } public static boolean isSyntaxOrAccessError(Exception e) { return sqlCodeStartsWith(e, "42"); } private static boolean sqlCodeStartsWith(Exception e, String code) { for (Throwable throwable : Throwables.getCausalChain(e)) { if (throwable instanceof SQLException) { String state = ((SQLException) throwable).getSQLState(); if (state != null && state.startsWith(code)) { return true; } } } return false; } }