/* * 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.metadata; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.skife.jdbi.v2.DBI; import org.skife.jdbi.v2.util.BooleanMapper; import java.sql.Connection; import java.sql.JDBCType; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.List; import java.util.Map; import java.util.OptionalInt; import java.util.Set; import java.util.StringJoiner; import java.util.UUID; import static com.facebook.presto.raptor.RaptorColumnHandle.isHiddenColumn; import static com.facebook.presto.raptor.metadata.DatabaseShardManager.maxColumn; import static com.facebook.presto.raptor.metadata.DatabaseShardManager.minColumn; import static com.facebook.presto.raptor.metadata.DatabaseShardManager.shardIndexTable; import static com.facebook.presto.raptor.metadata.ShardPredicate.bindValue; import static com.facebook.presto.raptor.storage.ColumnIndexStatsUtils.jdbcType; import static com.facebook.presto.raptor.util.ArrayUtil.intArrayToBytes; import static com.facebook.presto.raptor.util.UuidUtil.uuidToBytes; import static com.google.common.base.Preconditions.checkArgument; import static io.airlift.slice.Slices.utf8Slice; class IndexInserter implements AutoCloseable { private final boolean bucketed; private final List<ColumnInfo> columns; private final Map<Long, Integer> indexes; private final Map<Long, JDBCType> types; private final PreparedStatement statement; public IndexInserter(Connection connection, long tableId, List<ColumnInfo> columns) throws SQLException { this.bucketed = DBI.open(connection) .createQuery("SELECT distribution_id IS NOT NULL FROM tables WHERE table_id = ?") .bind(0, tableId) .map(BooleanMapper.FIRST) .first(); ImmutableList.Builder<ColumnInfo> columnBuilder = ImmutableList.builder(); ImmutableMap.Builder<Long, Integer> indexBuilder = ImmutableMap.builder(); ImmutableMap.Builder<Long, JDBCType> typeBuilder = ImmutableMap.builder(); StringJoiner nameJoiner = new StringJoiner(", "); StringJoiner valueJoiner = new StringJoiner(", "); int index = 1; nameJoiner.add("shard_id").add("shard_uuid"); valueJoiner.add("?").add("?").add("?"); index += 3; if (bucketed) { nameJoiner.add("bucket_number"); } else { nameJoiner.add("node_ids"); } for (ColumnInfo column : columns) { JDBCType jdbcType = jdbcType(column.getType()); if (jdbcType == null) { continue; } long columnId = column.getColumnId(); if (isHiddenColumn(columnId)) { continue; } columnBuilder.add(column); nameJoiner.add(minColumn(columnId)); nameJoiner.add(maxColumn(columnId)); valueJoiner.add("?").add("?"); indexBuilder.put(columnId, index); index += 2; typeBuilder.put(columnId, jdbcType); } this.columns = columnBuilder.build(); this.indexes = indexBuilder.build(); this.types = typeBuilder.build(); String sql = "" + "INSERT INTO " + shardIndexTable(tableId) + "\n" + "(" + nameJoiner + ")\n" + "VALUES (" + valueJoiner + ")"; this.statement = connection.prepareStatement(sql); } @Override public void close() throws SQLException { statement.close(); } public void insert(long shardId, UUID shardUuid, OptionalInt bucketNumber, Set<Integer> nodeIds, List<ColumnStats> stats) throws SQLException { statement.setLong(1, shardId); statement.setBytes(2, uuidToBytes(shardUuid)); if (bucketed) { checkArgument(bucketNumber.isPresent(), "shard bucket missing for bucketed table"); statement.setInt(3, bucketNumber.getAsInt()); } else { checkArgument(!bucketNumber.isPresent(), "shard bucket present for non-bucketed table"); statement.setBytes(3, intArrayToBytes(nodeIds)); } for (ColumnInfo column : columns) { int index = indexes.get(column.getColumnId()); int type = types.get(column.getColumnId()).getVendorTypeNumber(); statement.setNull(index, type); statement.setNull(index + 1, type); } for (ColumnStats column : stats) { int index = indexes.get(column.getColumnId()); JDBCType type = types.get(column.getColumnId()); bindValue(statement, type, convert(column.getMin()), index); bindValue(statement, type, convert(column.getMax()), index + 1); } statement.addBatch(); } public void execute() throws SQLException { statement.executeBatch(); } private static Object convert(Object value) { if (value instanceof String) { return utf8Slice((String) value); } return value; } }