Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.hadoop.hive.ql.metadata;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import static org.apache.hadoop.hive.common.AcidConstants.SOFT_DELETE_TABLE;
import static org.apache.hadoop.hive.conf.Constants.MATERIALIZED_VIEW_REWRITING_TIME_WINDOW;
import static org.apache.hadoop.hive.conf.HiveConf.ConfVars.HIVE_LOAD_DYNAMIC_PARTITIONS_SCAN_SPECIFIC_PARTITIONS;
import static org.apache.hadoop.hive.conf.HiveConf.ConfVars.HIVE_WRITE_NOTIFICATION_MAX_BATCH_SIZE;
import static org.apache.hadoop.hive.metastore.api.hive_metastoreConstants.CTAS_LEGACY_CONFIG;
import static org.apache.hadoop.hive.metastore.api.hive_metastoreConstants.META_TABLE_STORAGE;
import static org.apache.hadoop.hive.metastore.utils.MetaStoreUtils.convertToGetPartitionsByNamesRequest;
import static org.apache.hadoop.hive.metastore.utils.MetaStoreUtils.getDefaultCatalog;
import static org.apache.hadoop.hive.ql.ddl.DDLUtils.isIcebergStatsSource;
import static org.apache.hadoop.hive.ql.ddl.DDLUtils.isIcebergTable;
import static org.apache.hadoop.hive.ql.io.AcidUtils.getFullTableName;
import static org.apache.hadoop.hive.ql.metadata.HiveRelOptMaterialization.RewriteAlgorithm.CALCITE;
import static org.apache.hadoop.hive.ql.metadata.HiveRelOptMaterialization.RewriteAlgorithm.ALL;
import static org.apache.hadoop.hive.ql.optimizer.calcite.rules.views.HiveMaterializedViewUtils.extractTable;
import static org.apache.hadoop.hive.serde.serdeConstants.SERIALIZATION_FORMAT;
import static org.apache.hadoop.hive.serde.serdeConstants.STRING_TYPE_NAME;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.jdo.JDODataStoreException;
import com.google.common.collect.ImmutableList;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileChecksum;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.PathFilter;
import org.apache.hadoop.fs.permission.FsAction;
import org.apache.hadoop.hdfs.DFSUtilClient;
import org.apache.hadoop.hdfs.DistributedFileSystem;
import org.apache.hadoop.hive.common.FileUtils;
import org.apache.hadoop.hive.common.HiveStatsUtils;
import org.apache.hadoop.hive.common.MaterializationSnapshot;
import org.apache.hadoop.hive.common.StatsSetupConst;
import org.apache.hadoop.hive.common.TableName;
import org.apache.hadoop.hive.common.ValidReaderWriteIdList;
import org.apache.hadoop.hive.common.ValidTxnList;
import org.apache.hadoop.hive.common.ValidWriteIdList;
import org.apache.hadoop.hive.common.DataCopyStatistics;
import org.apache.hadoop.hive.common.classification.InterfaceAudience.LimitedPrivate;
import org.apache.hadoop.hive.common.classification.InterfaceStability.Unstable;
import org.apache.hadoop.hive.common.log.InPlaceUpdate;
import org.apache.hadoop.hive.conf.Constants;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
import org.apache.hadoop.hive.metastore.api.AbortTxnsRequest;
import org.apache.hadoop.hive.metastore.api.CompactionRequest;
import org.apache.hadoop.hive.metastore.api.CreateTableRequest;
import org.apache.hadoop.hive.metastore.api.GetPartitionsByNamesRequest;
import org.apache.hadoop.hive.metastore.api.GetTableRequest;
import org.apache.hadoop.hive.metastore.api.SourceTable;
import org.apache.hadoop.hive.metastore.api.UpdateTransactionalStatsRequest;
import org.apache.hadoop.hive.metastore.api.NoSuchObjectException;
import org.apache.hadoop.hive.metastore.conf.MetastoreConf;
import org.apache.hadoop.hive.metastore.utils.RetryUtilities;
import org.apache.hadoop.hive.ql.ddl.table.AlterTableType;
import org.apache.hadoop.hive.ql.io.HdfsUtils;
import org.apache.hadoop.hive.metastore.HiveMetaException;
import org.apache.hadoop.hive.metastore.HiveMetaHook;
import org.apache.hadoop.hive.metastore.HiveMetaHookLoader;
import org.apache.hadoop.hive.metastore.HiveMetaStoreClient;
import org.apache.hadoop.hive.metastore.HiveMetaStoreUtils;
import org.apache.hadoop.hive.metastore.IMetaStoreClient;
import org.apache.hadoop.hive.metastore.PartitionDropOptions;
import org.apache.hadoop.hive.metastore.RetryingMetaStoreClient;
import org.apache.hadoop.hive.metastore.SynchronizedMetaStoreClient;
import org.apache.hadoop.hive.metastore.TableType;
import org.apache.hadoop.hive.metastore.Warehouse;
import org.apache.hadoop.hive.metastore.api.AggrStats;
import org.apache.hadoop.hive.metastore.api.AllTableConstraintsRequest;
import org.apache.hadoop.hive.metastore.api.AlreadyExistsException;
import org.apache.hadoop.hive.metastore.api.CheckConstraintsRequest;
import org.apache.hadoop.hive.metastore.api.CmRecycleRequest;
import org.apache.hadoop.hive.metastore.api.ColumnStatistics;
import org.apache.hadoop.hive.metastore.api.ColumnStatisticsDesc;
import org.apache.hadoop.hive.metastore.api.ColumnStatisticsObj;
import org.apache.hadoop.hive.metastore.api.CompactionResponse;
import org.apache.hadoop.hive.metastore.api.CompactionType;
import org.apache.hadoop.hive.metastore.api.Database;
import org.apache.hadoop.hive.metastore.api.DataConnector;
import org.apache.hadoop.hive.metastore.api.DefaultConstraintsRequest;
import org.apache.hadoop.hive.metastore.api.DropDatabaseRequest;
import org.apache.hadoop.hive.metastore.api.EnvironmentContext;
import org.apache.hadoop.hive.metastore.api.FieldSchema;
import org.apache.hadoop.hive.metastore.api.FireEventRequest;
import org.apache.hadoop.hive.metastore.api.FireEventRequestData;
import org.apache.hadoop.hive.metastore.api.ForeignKeysRequest;
import org.apache.hadoop.hive.metastore.api.Function;
import org.apache.hadoop.hive.metastore.api.GetOpenTxnsInfoResponse;
import org.apache.hadoop.hive.metastore.api.GetPartitionNamesPsRequest;
import org.apache.hadoop.hive.metastore.api.GetPartitionNamesPsResponse;
import org.apache.hadoop.hive.metastore.api.GetPartitionRequest;
import org.apache.hadoop.hive.metastore.api.GetPartitionResponse;
import org.apache.hadoop.hive.metastore.api.GetPartitionsPsWithAuthRequest;
import org.apache.hadoop.hive.metastore.api.GetPartitionsPsWithAuthResponse;
import org.apache.hadoop.hive.metastore.api.GetRoleGrantsForPrincipalRequest;
import org.apache.hadoop.hive.metastore.api.GetRoleGrantsForPrincipalResponse;
import org.apache.hadoop.hive.metastore.api.hive_metastoreConstants;
import org.apache.hadoop.hive.metastore.api.HiveObjectPrivilege;
import org.apache.hadoop.hive.metastore.api.HiveObjectRef;
import org.apache.hadoop.hive.metastore.api.HiveObjectType;
import org.apache.hadoop.hive.metastore.api.InsertEventRequestData;
import org.apache.hadoop.hive.metastore.api.InvalidOperationException;
import org.apache.hadoop.hive.metastore.api.Materialization;
import org.apache.hadoop.hive.metastore.api.MetaException;
import org.apache.hadoop.hive.metastore.api.MetadataPpdResult;
import org.apache.hadoop.hive.metastore.api.NotNullConstraintsRequest;
import org.apache.hadoop.hive.metastore.api.PartitionSpec;
import org.apache.hadoop.hive.metastore.api.PartitionWithoutSD;
import org.apache.hadoop.hive.metastore.api.PartitionsByExprRequest;
import org.apache.hadoop.hive.metastore.api.PrimaryKeysRequest;
import org.apache.hadoop.hive.metastore.api.PrincipalPrivilegeSet;
import org.apache.hadoop.hive.metastore.api.PrincipalType;
import org.apache.hadoop.hive.metastore.api.PrivilegeBag;
import org.apache.hadoop.hive.metastore.api.Role;
import org.apache.hadoop.hive.metastore.api.RolePrincipalGrant;
import org.apache.hadoop.hive.metastore.api.SQLAllTableConstraints;
import org.apache.hadoop.hive.metastore.api.SQLCheckConstraint;
import org.apache.hadoop.hive.metastore.api.SQLDefaultConstraint;
import org.apache.hadoop.hive.metastore.api.SQLForeignKey;
import org.apache.hadoop.hive.metastore.api.SQLNotNullConstraint;
import org.apache.hadoop.hive.metastore.api.SQLPrimaryKey;
import org.apache.hadoop.hive.metastore.api.SQLUniqueConstraint;
import org.apache.hadoop.hive.metastore.api.SetPartitionsStatsRequest;
import org.apache.hadoop.hive.metastore.api.ShowCompactRequest;
import org.apache.hadoop.hive.metastore.api.ShowCompactResponse;
import org.apache.hadoop.hive.metastore.api.SkewedInfo;
import org.apache.hadoop.hive.metastore.api.UniqueConstraintsRequest;
import org.apache.hadoop.hive.metastore.api.WMFullResourcePlan;
import org.apache.hadoop.hive.metastore.api.WMMapping;
import org.apache.hadoop.hive.metastore.api.WMNullablePool;
import org.apache.hadoop.hive.metastore.api.WMNullableResourcePlan;
import org.apache.hadoop.hive.metastore.api.WMPool;
import org.apache.hadoop.hive.metastore.api.WMResourcePlan;
import org.apache.hadoop.hive.metastore.api.WMTrigger;
import org.apache.hadoop.hive.metastore.api.WMValidateResourcePlanResponse;
import org.apache.hadoop.hive.metastore.api.WriteNotificationLogRequest;
import org.apache.hadoop.hive.metastore.api.WriteNotificationLogBatchRequest;
import org.apache.hadoop.hive.metastore.api.AbortCompactionRequest;
import org.apache.hadoop.hive.metastore.api.AbortCompactResponse;
import org.apache.hadoop.hive.metastore.ReplChangeManager;
import org.apache.hadoop.hive.metastore.utils.MetaStoreServerUtils;
import org.apache.hadoop.hive.metastore.utils.MetaStoreUtils;
import org.apache.hadoop.hive.ql.ErrorMsg;
import org.apache.hadoop.hive.ql.ddl.database.drop.DropDatabaseDesc;
import org.apache.hadoop.hive.ql.exec.AbstractFileMergeOperator;
import org.apache.hadoop.hive.ql.exec.FunctionRegistry;
import org.apache.hadoop.hive.ql.exec.FunctionUtils;
import org.apache.hadoop.hive.ql.exec.SerializationUtilities;
import org.apache.hadoop.hive.ql.exec.Utilities;
import org.apache.hadoop.hive.ql.exec.Utilities.PartitionDetails;
import org.apache.hadoop.hive.ql.io.AcidUtils;
import org.apache.hadoop.hive.ql.io.AcidUtils.TableSnapshot;
import org.apache.hadoop.hive.ql.lockmgr.DbTxnManager;
import org.apache.hadoop.hive.ql.lockmgr.HiveTxnManager;
import org.apache.hadoop.hive.ql.lockmgr.LockException;
import org.apache.hadoop.hive.ql.log.PerfLogger;
import org.apache.hadoop.hive.ql.optimizer.calcite.rules.views.HiveMaterializedViewUtils;
import org.apache.hadoop.hive.ql.optimizer.listbucketingpruner.ListBucketingPrunerUtils;
import org.apache.hadoop.hive.ql.parse.ASTNode;
import org.apache.hadoop.hive.ql.parse.AlterTableSnapshotRefSpec;
import org.apache.hadoop.hive.ql.parse.AlterTableExecuteSpec;
import org.apache.hadoop.hive.ql.parse.SemanticException;
import org.apache.hadoop.hive.ql.plan.ExprNodeDesc;
import org.apache.hadoop.hive.ql.plan.ExprNodeGenericFuncDesc;
import org.apache.hadoop.hive.ql.plan.LoadTableDesc;
import org.apache.hadoop.hive.ql.plan.LoadTableDesc.LoadFileType;
import org.apache.hadoop.hive.ql.session.CreateTableAutomaticGrant;
import org.apache.hadoop.hive.ql.session.SessionState;
import org.apache.hadoop.hive.serde2.Deserializer;
import org.apache.hadoop.hive.serde2.SerDeException;
import org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe;
import org.apache.hadoop.hive.shims.HadoopShims;
import org.apache.hadoop.hive.shims.ShimLoader;
import org.apache.hadoop.mapred.InputFormat;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.util.StringUtils;
import org.apache.hive.common.util.HiveVersionInfo;
import org.apache.thrift.TException;
import org.apache.thrift.TApplicationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class has functions that implement meta data/DDL operations using calls
* to the metastore.
* It has a metastore client instance it uses to communicate with the metastore.
*
* It is a thread local variable, and the instances is accessed using static
* get methods in this class.
*/
@SuppressWarnings({"deprecation", "rawtypes"})
public class Hive {
static final private Logger LOG = LoggerFactory.getLogger("hive.ql.metadata.Hive");
private final String CLASS_NAME = Hive.class.getName();
private HiveConf conf = null;
private IMetaStoreClient metaStoreClient;
private SynchronizedMetaStoreClient syncMetaStoreClient;
private UserGroupInformation owner;
private boolean isAllowClose = true;
private final static int DEFAULT_BATCH_DECAYING_FACTOR = 2;
// metastore calls timing information
private final ConcurrentHashMap metaCallTimeMap = new ConcurrentHashMap<>();
// Static class to store thread local Hive object.
private static class ThreadLocalHive extends ThreadLocal {
@Override
protected Hive initialValue() {
return null;
}
@Override
public synchronized void set(Hive hiveObj) {
Hive currentHive = this.get();
if (currentHive != hiveObj) {
// Remove/close current thread-local Hive object before overwriting with new Hive object.
remove();
super.set(hiveObj);
}
}
@Override
public synchronized void remove() {
Hive currentHive = this.get();
if (currentHive != null) {
// Close the metastore connections before removing it from thread local hiveDB.
currentHive.close(false);
super.remove();
}
}
}
private static ThreadLocalHive hiveDB = new ThreadLocalHive();
// Note that while this is an improvement over static initialization, it is still not,
// technically, valid, cause nothing prevents us from connecting to several metastores in
// the same process. This will still only get the functions from the first metastore.
private final static AtomicInteger didRegisterAllFuncs = new AtomicInteger(0);
private final static int REG_FUNCS_NO = 0, REG_FUNCS_DONE = 2, REG_FUNCS_PENDING = 1;
// register all permanent functions. need improvement
private void registerAllFunctionsOnce() throws HiveException {
boolean breakLoop = false;
while (!breakLoop) {
int val = didRegisterAllFuncs.get();
switch (val) {
case REG_FUNCS_NO: {
if (didRegisterAllFuncs.compareAndSet(val, REG_FUNCS_PENDING)) {
breakLoop = true;
break;
}
continue;
}
case REG_FUNCS_PENDING: {
synchronized (didRegisterAllFuncs) {
try {
didRegisterAllFuncs.wait(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
continue;
}
case REG_FUNCS_DONE: return;
default: throw new AssertionError(val);
}
}
try {
reloadFunctions();
didRegisterAllFuncs.compareAndSet(REG_FUNCS_PENDING, REG_FUNCS_DONE);
} catch (Exception | Error e) {
LOG.warn("Failed to register all functions.", e);
didRegisterAllFuncs.compareAndSet(REG_FUNCS_PENDING, REG_FUNCS_NO);
if (e instanceof Exception) {
throw new HiveException(e);
} else {
throw e;
}
} finally {
synchronized (didRegisterAllFuncs) {
didRegisterAllFuncs.notifyAll();
}
}
}
public void reloadFunctions() throws HiveException {
HashSet registryFunctions = new HashSet(
FunctionRegistry.getFunctionNames(".+\\..+"));
for (Function function : getAllFunctions()) {
String functionName = function.getFunctionName();
try {
LOG.info("Registering function " + functionName + " " + function.getClassName());
String qualFunc = FunctionUtils.qualifyFunctionName(functionName, function.getDbName());
FunctionRegistry.registerPermanentFunction(qualFunc, function.getClassName(), false,
FunctionUtils.toFunctionResource(function.getResourceUris()));
registryFunctions.remove(qualFunc);
} catch (Exception e) {
LOG.warn("Failed to register persistent function " +
functionName + ":" + function.getClassName() + ". Ignore and continue.");
}
}
// unregister functions from local system registry that are not in getAllFunctions()
for (String functionName : registryFunctions) {
try {
FunctionRegistry.unregisterPermanentFunction(functionName);
} catch (Exception e) {
LOG.warn("Failed to unregister persistent function " +
functionName + "on reload. Ignore and continue.");
}
}
}
public static Hive get(Configuration c, Class> clazz) throws HiveException {
return get(c instanceof HiveConf ? (HiveConf)c : new HiveConf(c, clazz));
}
/**
* Gets hive object for the current thread. If one is not initialized then a
* new one is created If the new configuration is different in metadata conf
* vars, or the owner will be different then a new one is created.
*
* @param c
* new Hive Configuration
* @return Hive object for current thread
* @throws HiveException
*
*/
public static Hive get(HiveConf c) throws HiveException {
return getInternal(c, false, false, true);
}
public static Hive createHiveForSession(HiveConf c) throws HiveException {
return create(c, true);
}
public void setConf(HiveConf c) {
this.conf = c;
}
/**
* Same as {@link #get(HiveConf)}, except that it checks only the object identity of existing
* MS client, assuming the relevant settings would be unchanged within the same conf object.
*/
public static Hive getWithFastCheck(HiveConf c) throws HiveException {
return getWithFastCheck(c, true);
}
/**
* Same as {@link #get(HiveConf)}, except that it checks only the object identity of existing
* MS client, assuming the relevant settings would be unchanged within the same conf object.
*/
public static Hive getWithFastCheck(HiveConf c, boolean doRegisterAllFns) throws HiveException {
return getInternal(c, false, true, doRegisterAllFns);
}
/**
* Same as {@link #get(HiveConf)}, except that it does not register all functions.
*/
public static Hive getWithoutRegisterFns(HiveConf c) throws HiveException {
return getInternal(c, false, false, false);
}
private static Hive getInternal(HiveConf c, boolean needsRefresh, boolean isFastCheck,
boolean doRegisterAllFns) throws HiveException {
Hive db = hiveDB.get();
if (db == null || !db.isCurrentUserOwner() || needsRefresh
|| (c != null && !isCompatible(db, c, isFastCheck))) {
if (db != null) {
LOG.debug("Creating new db. db = " + db + ", needsRefresh = " + needsRefresh +
", db.isCurrentUserOwner = " + db.isCurrentUserOwner());
closeCurrent();
}
db = create(c, doRegisterAllFns);
}
if (c != null) {
db.conf = c;
}
return db;
}
private static Hive create(HiveConf c, boolean doRegisterAllFns) throws HiveException {
if (c == null) {
c = createHiveConf();
}
c.set("fs.scheme.class", "dfs");
Hive newdb = new Hive(c, doRegisterAllFns);
if (newdb.getHMSClientCapabilities() == null || newdb.getHMSClientCapabilities().length == 0) {
if (c.get(HiveConf.ConfVars.METASTORE_CLIENT_CAPABILITIES.varname) != null) {
String[] capabilities = c.get(HiveConf.ConfVars.METASTORE_CLIENT_CAPABILITIES.varname).split(",");
newdb.setHMSClientCapabilities(capabilities);
String hostName = "unknown";
try {
hostName = InetAddress.getLocalHost().getCanonicalHostName();
} catch (UnknownHostException ue) {
}
newdb.setHMSClientIdentifier("Hiveserver2#" + HiveVersionInfo.getVersion() + "@" + hostName);
}
}
hiveDB.set(newdb);
return newdb;
}
private static HiveConf createHiveConf() {
SessionState session = SessionState.get();
return (session == null) ? new HiveConf(Hive.class) : session.getConf();
}
public void setHMSClientCapabilities(String[] capabilities) {
HiveMetaStoreClient.setProcessorCapabilities(capabilities);
}
public void setHMSClientIdentifier(final String id) {
HiveMetaStoreClient.setProcessorIdentifier(id);
}
public String[] getHMSClientCapabilities() {
return HiveMetaStoreClient.getProcessorCapabilities();
}
public String getHMSClientIdentifier() {
return HiveMetaStoreClient.getProcessorIdentifier();
}
private static boolean isCompatible(Hive db, HiveConf c, boolean isFastCheck) {
if (isFastCheck) {
return (db.metaStoreClient == null || db.metaStoreClient.isSameConfObj(c))
&& (db.syncMetaStoreClient == null || db.syncMetaStoreClient.isSameConfObj(c));
} else {
return (db.metaStoreClient == null || db.metaStoreClient.isCompatibleWith(c))
&& (db.syncMetaStoreClient == null || db.syncMetaStoreClient.isCompatibleWith(c));
}
}
private boolean isCurrentUserOwner() throws HiveException {
try {
return owner == null || owner.equals(UserGroupInformation.getCurrentUser());
} catch(IOException e) {
throw new HiveException("Error getting current user: " + e.getMessage(), e);
}
}
public static Hive getThreadLocal() {
return hiveDB.get();
}
public static Hive get() throws HiveException {
return get(true);
}
@VisibleForTesting
public static Hive get(IMetaStoreClient msc) throws HiveException, MetaException {
Hive hive = get(true);
hive.setMSC(msc);
return hive;
}
public static Hive get(boolean doRegisterAllFns) throws HiveException {
return getInternal(null, false, false, doRegisterAllFns);
}
/**
* get a connection to metastore. see get(HiveConf) function for comments
*
* @param c
* new conf
* @param needsRefresh
* if true then creates a new one
* @return The connection to the metastore
* @throws HiveException
*/
public static Hive get(HiveConf c, boolean needsRefresh) throws HiveException {
return getInternal(c, needsRefresh, false, true);
}
public static void set(Hive hive) {
hiveDB.set(hive);
}
public static void closeCurrent() {
hiveDB.remove();
}
/**
* Hive
*
* @param c
*
*/
private Hive(HiveConf c, boolean doRegisterAllFns) throws HiveException {
conf = c;
// turn off calcite rexnode normalization
System.setProperty("calcite.enable.rexnode.digest.normalize", "false");
if (doRegisterAllFns) {
registerAllFunctionsOnce();
}
}
/**
* GC is attempting to destroy the object.
* No one references this Hive anymore, so HMS connection from this Hive object can be closed.
* @throws Throwable
*/
@Override
protected void finalize() throws Throwable {
close(true);
super.finalize();
}
/**
* Marks if the given Hive object is allowed to close metastore connections.
* @param allowClose
*/
public void setAllowClose(boolean allowClose) {
isAllowClose = allowClose;
}
/**
* Gets the allowClose flag which determines if it is allowed to close metastore connections.
* @return allowClose flag
*/
public boolean allowClose() {
return isAllowClose;
}
/**
* Closes the connection to metastore for the calling thread if allow to close.
* @param forceClose - Override the isAllowClose flag to forcefully close the MS connections.
*/
public void close(boolean forceClose) {
if (allowClose() || forceClose) {
LOG.debug("Closing current thread's connection to Hive Metastore.");
if (metaStoreClient != null) {
metaStoreClient.close();
metaStoreClient = null;
}
// syncMetaStoreClient is wrapped on metaStoreClient. So, it is enough to close it once.
syncMetaStoreClient = null;
if (owner != null) {
owner = null;
}
}
}
/**
* Create a database
* @param db
* @param ifNotExist if true, will ignore AlreadyExistsException exception
* @throws AlreadyExistsException
* @throws HiveException
*/
public void createDatabase(Database db, boolean ifNotExist)
throws AlreadyExistsException, HiveException {
try {
getMSC().createDatabase(db);
} catch (AlreadyExistsException e) {
if (!ifNotExist) {
throw e;
}
} catch (Exception e) {
throw new HiveException(e);
}
}
/**
* Create a Database. Raise an error if a database with the same name already exists.
* @param db
* @throws AlreadyExistsException
* @throws HiveException
*/
public void createDatabase(Database db) throws AlreadyExistsException, HiveException {
createDatabase(db, false);
}
/**
* Drop a database.
* @param name
* @throws NoSuchObjectException
* @throws HiveException
* @see org.apache.hadoop.hive.metastore.HiveMetaStoreClient#dropDatabase(java.lang.String)
*/
public void dropDatabase(String name) throws HiveException, NoSuchObjectException {
dropDatabase(name, true, false, false);
}
/**
* Drop a database
* @param name
* @param deleteData
* @param ignoreUnknownDb if true, will ignore NoSuchObjectException
* @throws HiveException
* @throws NoSuchObjectException
*/
public void dropDatabase(String name, boolean deleteData, boolean ignoreUnknownDb)
throws HiveException, NoSuchObjectException {
dropDatabase(name, deleteData, ignoreUnknownDb, false);
}
/**
* Drop a database
* @param name
* @param deleteData
* @param ignoreUnknownDb if true, will ignore NoSuchObjectException
* @param cascade if true, delete all tables on the DB if exists. Otherwise, the query
* will fail if table still exists.
* @throws HiveException
* @throws NoSuchObjectException
*/
public void dropDatabase(String name, boolean deleteData, boolean ignoreUnknownDb, boolean cascade)
throws HiveException, NoSuchObjectException {
dropDatabase(new DropDatabaseDesc(name, ignoreUnknownDb, cascade, deleteData));
}
public void dropDatabase(DropDatabaseDesc desc)
throws HiveException, NoSuchObjectException {
boolean isSoftDelete = HiveConf.getBoolVar(conf, ConfVars.HIVE_ACID_LOCKLESS_READS_ENABLED);
long txnId = Optional.ofNullable(SessionState.get())
.map(SessionState::getTxnMgr)
.map(HiveTxnManager::getCurrentTxnId).orElse(0L);
DropDatabaseRequest req = new DropDatabaseRequest();
req.setCatalogName(getDefaultCatalog(conf));
req.setName(desc.getDatabaseName());
req.setIgnoreUnknownDb(desc.getIfExists());
req.setDeleteData(desc.isDeleteData());
req.setCascade(desc.isCasdade());
req.setSoftDelete(isSoftDelete);
req.setTxnId(txnId);
try {
getMSC().dropDatabase(req);
} catch (NoSuchObjectException e) {
throw e;
} catch (Exception e) {
throw new HiveException(e);
}
}
/**
* Dry run that translates table
*
* @param tbl
* a table object
* @throws HiveException
*/
public Table getTranslateTableDryrun(org.apache.hadoop.hive.metastore.api.Table tbl) throws HiveException {
org.apache.hadoop.hive.metastore.api.Table tTable = null;
try {
tTable = getMSC().getTranslateTableDryrun(tbl);
} catch (AlreadyExistsException e) {
throw new HiveException(e);
} catch (Exception e) {
throw new HiveException(e);
}
return new Table(tTable);
}
/**
* Creates a table metadata and the directory for the table data
*
* @param tableName
* name of the table
* @param columns
* list of fields of the table
* @param partCols
* partition keys of the table
* @param fileInputFormat
* Class of the input format of the table data file
* @param fileOutputFormat
* Class of the output format of the table data file
* @throws HiveException
* thrown if the args are invalid or if the metadata or the data
* directory couldn't be created
*/
public void createTable(String tableName, List columns,
List partCols, Class extends InputFormat> fileInputFormat,
Class> fileOutputFormat) throws HiveException {
this.createTable(tableName, columns, partCols, fileInputFormat,
fileOutputFormat, -1, null);
}
/**
* Creates a table metadata and the directory for the table data
*
* @param tableName
* name of the table
* @param columns
* list of fields of the table
* @param partCols
* partition keys of the table
* @param fileInputFormat
* Class of the input format of the table data file
* @param fileOutputFormat
* Class of the output format of the table data file
* @param bucketCount
* number of buckets that each partition (or the table itself) should
* be divided into
* @throws HiveException
* thrown if the args are invalid or if the metadata or the data
* directory couldn't be created
*/
public void createTable(String tableName, List columns,
List partCols, Class extends InputFormat> fileInputFormat,
Class> fileOutputFormat, int bucketCount, List bucketCols)
throws HiveException {
createTable(tableName, columns, partCols, fileInputFormat, fileOutputFormat, bucketCount,
bucketCols, null);
}
/**
* Create a table metadata and the directory for the table data
* @param tableName table name
* @param columns list of fields of the table
* @param partCols partition keys of the table
* @param fileInputFormat Class of the input format of the table data file
* @param fileOutputFormat Class of the output format of the table data file
* @param bucketCount number of buckets that each partition (or the table itself) should be
* divided into
* @param bucketCols Bucket columns
* @param parameters Parameters for the table
* @throws HiveException
*/
public void createTable(String tableName, List columns, List partCols,
Class extends InputFormat> fileInputFormat,
Class> fileOutputFormat, int bucketCount, List bucketCols,
Map parameters) throws HiveException {
if (columns == null) {
throw new HiveException("columns not specified for table " + tableName);
}
Table tbl = newTable(tableName);
tbl.setInputFormatClass(fileInputFormat.getName());
tbl.setOutputFormatClass(fileOutputFormat.getName());
for (String col : columns) {
FieldSchema field = new FieldSchema(col, STRING_TYPE_NAME, "default");
tbl.getCols().add(field);
}
if (partCols != null) {
for (String partCol : partCols) {
FieldSchema part = new FieldSchema();
part.setName(partCol);
part.setType(STRING_TYPE_NAME); // default partition key
tbl.getPartCols().add(part);
}
}
tbl.setSerializationLib(LazySimpleSerDe.class.getName());
tbl.setNumBuckets(bucketCount);
tbl.setBucketCols(bucketCols);
if (parameters != null) {
tbl.setParameters(parameters);
}
createTable(tbl);
}
public void alterTable(Table newTbl, boolean cascade, EnvironmentContext environmentContext,
boolean transactional) throws HiveException {
alterTable(newTbl.getCatName(), newTbl.getDbName(),
newTbl.getTableName(), newTbl, cascade, environmentContext, transactional);
}
/**
* Updates the existing table metadata with the new metadata.
*
* @param fullyQlfdTblName
* name of the existing table
* @param newTbl
* new name of the table. could be the old name
* @param transactional
* Need to generate and save a table snapshot into the metastore?
* @throws HiveException
*/
public void alterTable(String fullyQlfdTblName, Table newTbl, EnvironmentContext environmentContext,
boolean transactional)
throws HiveException {
String[] names = Utilities.getDbTableName(fullyQlfdTblName);
alterTable(null, names[0], names[1], newTbl, false, environmentContext, transactional);
}
public void alterTable(String fullyQlfdTblName, Table newTbl, boolean cascade,
EnvironmentContext environmentContext, boolean transactional)
throws HiveException {
String[] names = Utilities.getDbTableName(fullyQlfdTblName);
alterTable(null, names[0], names[1], newTbl, cascade, environmentContext, transactional);
}
public void alterTable(String fullyQlfdTblName, Table newTbl, boolean cascade,
EnvironmentContext environmentContext, boolean transactional, long writeId)
throws HiveException {
String[] names = Utilities.getDbTableName(fullyQlfdTblName);
alterTable(null, names[0], names[1], newTbl, cascade, environmentContext, transactional,
writeId);
}
public void alterTable(String catName, String dbName, String tblName, Table newTbl, boolean cascade,
EnvironmentContext environmentContext, boolean transactional) throws HiveException {
alterTable(catName, dbName, tblName, newTbl, cascade, environmentContext, transactional, 0);
}
public void alterTable(String catName, String dbName, String tblName, Table newTbl, boolean cascade,
EnvironmentContext environmentContext, boolean transactional, long replWriteId)
throws HiveException {
if (catName == null) {
catName = getDefaultCatalog(conf);
}
try {
// Remove the DDL_TIME so it gets refreshed
if (newTbl.getParameters() != null) {
newTbl.getParameters().remove(hive_metastoreConstants.DDL_TIME);
}
if (environmentContext == null) {
environmentContext = new EnvironmentContext();
}
if (isRename(environmentContext)) {
newTbl.validateName(conf);
environmentContext.putToProperties(HiveMetaHook.OLD_TABLE_NAME, tblName);
environmentContext.putToProperties(HiveMetaHook.OLD_DB_NAME, dbName);
} else {
newTbl.checkValidity(conf);
}
if (cascade) {
environmentContext.putToProperties(StatsSetupConst.CASCADE, StatsSetupConst.TRUE);
}
// Take a table snapshot and set it to newTbl.
AcidUtils.TableSnapshot tableSnapshot = null;
if (transactional) {
if (replWriteId > 0) {
// We need a valid writeId list for a transactional table modification. During
// replication we do not have a valid writeId list which was used to modify the table
// on the source. But we know for sure that the writeId associated with it was valid
// then (otherwise modification would have failed on the source). So use a valid
// transaction list with only that writeId.
ValidWriteIdList writeIds = new ValidReaderWriteIdList(TableName.getDbTable(dbName, tblName),
new long[0], new BitSet(),
replWriteId);
tableSnapshot = new TableSnapshot(replWriteId, writeIds.writeToString());
} else {
// Make sure we pass in the names, so we can get the correct snapshot for rename table.
tableSnapshot = AcidUtils.getTableSnapshot(conf, newTbl, dbName, tblName, true);
}
if (tableSnapshot != null) {
newTbl.getTTable().setWriteId(tableSnapshot.getWriteId());
} else {
LOG.warn("Cannot get a table snapshot for " + tblName);
}
}
// Why is alter_partitions synchronized while this isn't?
getMSC().alter_table(
catName, dbName, tblName, newTbl.getTTable(), environmentContext,
tableSnapshot == null ? null : tableSnapshot.getValidWriteIdList());
} catch (MetaException e) {
throw new HiveException("Unable to alter table. " + e.getMessage(), e);
} catch (TException e) {
throw new HiveException("Unable to alter table. " + e.getMessage(), e);
}
}
private static boolean isRename(EnvironmentContext environmentContext) {
if (environmentContext.isSetProperties()) {
String operation = environmentContext.getProperties().get(HiveMetaHook.ALTER_TABLE_OPERATION_TYPE);
return operation != null && AlterTableType.RENAME == AlterTableType.valueOf(operation);
}
return false;
}
/**
* Create a dataconnector
* @param connector
* @param ifNotExist if true, will ignore AlreadyExistsException exception
* @throws AlreadyExistsException
* @throws HiveException
*/
public void createDataConnector(DataConnector connector, boolean ifNotExist)
throws AlreadyExistsException, HiveException {
try {
getMSC().createDataConnector(connector);
} catch (AlreadyExistsException e) {
if (!ifNotExist) {
throw e;
}
} catch (Exception e) {
throw new HiveException(e);
}
}
/**
* Create a DataConnector. Raise an error if a dataconnector with the same name already exists.
* @param connector
* @throws AlreadyExistsException
* @throws HiveException
*/
public void createDataConnector(DataConnector connector) throws AlreadyExistsException, HiveException {
createDataConnector(connector, false);
}
/**
* Drop a dataconnector.
* @param name
* @throws NoSuchObjectException
* @throws HiveException
* @see org.apache.hadoop.hive.metastore.HiveMetaStoreClient#dropDataConnector(java.lang.String, boolean, boolean)
*/
public void dropDataConnector(String name, boolean ifNotExists) throws HiveException, NoSuchObjectException {
dropDataConnector(name, ifNotExists, true);
}
/**
* Drop a dataconnector
* @param name
* @param checkReferences drop only if there are no dbs referencing this connector
* @throws HiveException
* @throws NoSuchObjectException
*/
public void dropDataConnector(String name, boolean ifNotExists, boolean checkReferences)
throws HiveException, NoSuchObjectException {
try {
getMSC().dropDataConnector(name, ifNotExists, checkReferences);
} catch (Exception e) {
throw new HiveException(e);
}
}
/**
* Get the dataconnector by name.
* @param dcName the name of the dataconnector.
* @return a DataConnector object if this dataconnector exists, null otherwise.
* @throws HiveException
*/
public DataConnector getDataConnector(String dcName) throws HiveException {
try {
return getMSC().getDataConnector(dcName);
} catch (NoSuchObjectException e) {
return null;
} catch (Exception e) {
throw new HiveException(e);
}
}
/**
* Get all dataconnector names.
* @return List of all dataconnector names.
* @throws HiveException
*/
public List getAllDataConnectorNames() throws HiveException {
try {
return getMSC().getAllDataConnectorNames();
} catch (NoSuchObjectException e) {
return null;
} catch (Exception e) {
throw new HiveException(e);
}
}
public void alterDataConnector(String dcName, DataConnector connector)
throws HiveException {
try {
getMSC().alterDataConnector(dcName, connector);
} catch (MetaException e) {
throw new HiveException("Unable to alter dataconnector " + dcName + ". " + e.getMessage(), e);
} catch (NoSuchObjectException e) {
throw new HiveException("DataConnector " + dcName + " does not exists.", e);
} catch (TException e) {
throw new HiveException("Unable to alter dataconnector " + dcName + ". " + e.getMessage(), e);
}
}
public void updateCreationMetadata(String dbName, String tableName, MaterializedViewMetadata metadata)
throws HiveException {
try {
getMSC().updateCreationMetadata(dbName, tableName, metadata.creationMetadata);
} catch (TException e) {
throw new HiveException("Unable to update creation metadata " + e.getMessage(), e);
}
}
/**
* Updates the existing partition metadata with the new metadata.
*
* @param tblName
* name of the existing table
* @param newPart
* new partition
* @throws InvalidOperationException
* if the changes in metadata is not acceptable
* @throws HiveException
*/
@Deprecated
public void alterPartition(String tblName, Partition newPart,
EnvironmentContext environmentContext, boolean transactional)
throws InvalidOperationException, HiveException {
String[] names = Utilities.getDbTableName(tblName);
alterPartition(null, names[0], names[1], newPart, environmentContext, transactional);
}
/**
* Updates the existing partition metadata with the new metadata.
*
* @param dbName
* name of the exiting table's database
* @param tblName
* name of the existing table
* @param newPart
* new partition
* @param environmentContext
* environment context for the method
* @param transactional
* indicates this call is for transaction stats
* @throws InvalidOperationException
* if the changes in metadata is not acceptable
* @throws HiveException
*/
public void alterPartition(String catName, String dbName, String tblName, Partition newPart,
EnvironmentContext environmentContext, boolean transactional)
throws InvalidOperationException, HiveException {
try {
if (catName == null) {
catName = getDefaultCatalog(conf);
}
validatePartition(newPart);
String location = newPart.getLocation();
if (location != null) {
location = Utilities.getQualifiedPath(conf, new Path(location));
newPart.setLocation(location);
}
if (environmentContext == null) {
environmentContext = new EnvironmentContext();
}
AcidUtils.TableSnapshot tableSnapshot = null;
if (transactional) {
tableSnapshot = AcidUtils.getTableSnapshot(conf, newPart.getTable(), true);
if (tableSnapshot != null) {
newPart.getTPartition().setWriteId(tableSnapshot.getWriteId());
} else {
LOG.warn("Cannot get a table snapshot for " + tblName);
}
}
getSynchronizedMSC().alter_partition(catName,
dbName, tblName, newPart.getTPartition(), environmentContext,
tableSnapshot == null ? null : tableSnapshot.getValidWriteIdList());
} catch (MetaException e) {
throw new HiveException("Unable to alter partition. " + e.getMessage(), e);
} catch (TException e) {
throw new HiveException("Unable to alter partition. " + e.getMessage(), e);
}
}
private void validatePartition(Partition newPart) throws HiveException {
// Remove the DDL time so that it gets refreshed
if (newPart.getParameters() != null) {
newPart.getParameters().remove(hive_metastoreConstants.DDL_TIME);
}
newPart.checkValidity();
}
/**
* Updates the existing table metadata with the new metadata.
*
* @param tblName
* name of the existing table
* @param newParts
* new partitions
* @param transactional
* Need to generate and save a table snapshot into the metastore?
* @throws InvalidOperationException
* if the changes in metadata is not acceptable
* @throws HiveException
*/
public void alterPartitions(String tblName, List newParts,
EnvironmentContext environmentContext, boolean transactional)
throws InvalidOperationException, HiveException {
String[] names = Utilities.getDbTableName(tblName);
List newTParts =
new ArrayList();
try {
AcidUtils.TableSnapshot tableSnapshot = null;
if (transactional) {
tableSnapshot = AcidUtils.getTableSnapshot(conf, newParts.get(0).getTable(), true);
}
// Remove the DDL time so that it gets refreshed
for (Partition tmpPart: newParts) {
if (tmpPart.getParameters() != null) {
tmpPart.getParameters().remove(hive_metastoreConstants.DDL_TIME);
}
String location = tmpPart.getLocation();
if (location != null) {
location = Utilities.getQualifiedPath(conf, new Path(location));
tmpPart.setLocation(location);
}
newTParts.add(tmpPart.getTPartition());
}
getMSC().alter_partitions(names[0], names[1], newTParts, environmentContext,
tableSnapshot != null ? tableSnapshot.getValidWriteIdList() : null,
tableSnapshot != null ? tableSnapshot.getWriteId() : -1);
} catch (MetaException e) {
throw new HiveException("Unable to alter partition. " + e.getMessage(), e);
} catch (TException e) {
throw new HiveException("Unable to alter partition. " + e.getMessage(), e);
}
}
/**
* Rename a old partition to new partition
*
* @param tbl
* existing table
* @param oldPartSpec
* spec of old partition
* @param newPart
* new partition
* @throws HiveException
*/
public void renamePartition(Table tbl, Map oldPartSpec, Partition newPart,
long replWriteId)
throws HiveException {
try {
Map newPartSpec = newPart.getSpec();
if (oldPartSpec.keySet().size() != tbl.getPartCols().size()
|| newPartSpec.keySet().size() != tbl.getPartCols().size()) {
throw new HiveException("Unable to rename partition to the same name: number of partition cols don't match. ");
}
if (!oldPartSpec.keySet().equals(newPartSpec.keySet())){
throw new HiveException("Unable to rename partition to the same name: old and new partition cols don't match. ");
}
List pvals = new ArrayList();
for (FieldSchema field : tbl.getPartCols()) {
String val = oldPartSpec.get(field.getName());
if (val == null || val.length() == 0) {
throw new HiveException("get partition: Value for key "
+ field.getName() + " is null or empty");
} else if (val != null){
pvals.add(val);
}
}
String validWriteIds = null;
boolean clonePart = false;
long txnId = 0;
if (AcidUtils.isTransactionalTable(tbl)) {
TableSnapshot tableSnapshot;
if (replWriteId > 0) {
// We need a valid writeId list for a transactional table modification. During
// replication we do not have a valid writeId list which was used to modify the table
// on the source. But we know for sure that the writeId associated with it was valid
// then (otherwise modification would have failed on the source). So use a valid
// transaction list with only that writeId.
ValidWriteIdList writeIds = new ValidReaderWriteIdList(TableName.getDbTable(tbl.getDbName(),
tbl.getTableName()), new long[0], new BitSet(), replWriteId);
tableSnapshot = new TableSnapshot(replWriteId, writeIds.writeToString());
} else {
// Set table snapshot to api.Table to make it persistent.
tableSnapshot = AcidUtils.getTableSnapshot(conf, tbl, true);
}
if (tableSnapshot != null) {
newPart.getTPartition().setWriteId(tableSnapshot.getWriteId());
validWriteIds = tableSnapshot.getValidWriteIdList();
}
clonePart = HiveConf.getBoolVar(conf, ConfVars.HIVE_ACID_RENAME_PARTITION_MAKE_COPY)
|| HiveConf.getBoolVar(conf, ConfVars.HIVE_ACID_LOCKLESS_READS_ENABLED);
txnId = Optional.ofNullable(SessionState.get())
.map(ss -> ss.getTxnMgr().getCurrentTxnId()).orElse(0L);
}
String catName = (tbl.getCatalogName() != null) ? tbl.getCatalogName() : getDefaultCatalog(conf);
getMSC().renamePartition(catName, tbl.getDbName(), tbl.getTableName(), pvals,
newPart.getTPartition(), validWriteIds, txnId, clonePart);
} catch (InvalidOperationException e){
throw new HiveException("Unable to rename partition. " + e.getMessage(), e);
} catch (MetaException e) {
throw new HiveException("Unable to rename partition. " + e.getMessage(), e);
} catch (TException e) {
throw new HiveException("Unable to rename partition. " + e.getMessage(), e);
}
}
// TODO: this whole path won't work with catalogs
public void alterDatabase(String dbName, Database db)
throws HiveException {
try {
getMSC().alterDatabase(dbName, db);
} catch (MetaException e) {
throw new HiveException("Unable to alter database " + dbName + ". " + e.getMessage(), e);
} catch (NoSuchObjectException e) {
throw new HiveException("Database " + dbName + " does not exists.", e);
} catch (TException e) {
throw new HiveException("Unable to alter database " + dbName + ". " + e.getMessage(), e);
}
}
/**
* Creates the table with the give objects
*
* @param tbl
* a table object
* @throws HiveException
*/
public void createTable(Table tbl) throws HiveException {
createTable(tbl, false);
}
// TODO: from here down dozens of methods do not support catalog. I got tired marking them.
/**
* Creates the table with the given objects. It takes additional arguments for
* primary keys and foreign keys associated with the table.
*
* @param tbl
* a table object
* @param ifNotExists
* if true, ignore AlreadyExistsException
* @param primaryKeys
* primary key columns associated with the table
* @param foreignKeys
* foreign key columns associated with the table
* @param uniqueConstraints
* UNIQUE constraints associated with the table
* @param notNullConstraints
* NOT NULL constraints associated with the table
* @param defaultConstraints
* DEFAULT constraints associated with the table
* @param checkConstraints
* CHECK constraints associated with the table
* @throws HiveException
*/
public void createTable(Table tbl, boolean ifNotExists,
List primaryKeys,
List foreignKeys,
List uniqueConstraints,
List notNullConstraints,
List defaultConstraints,
List checkConstraints)
throws HiveException {
try {
if (org.apache.commons.lang3.StringUtils.isBlank(tbl.getDbName())) {
tbl.setDbName(SessionState.get().getCurrentDatabase());
}
if (tbl.getCols().size() == 0 || tbl.getSd().getColsSize() == 0) {
tbl.setFields(HiveMetaStoreUtils.getFieldsFromDeserializer(tbl.getTableName(), tbl.getDeserializer(), conf));
}
tbl.checkValidity(conf);
if (tbl.getParameters() != null) {
tbl.getParameters().remove(hive_metastoreConstants.DDL_TIME);
}
org.apache.hadoop.hive.metastore.api.Table tTbl = tbl.getTTable();
PrincipalPrivilegeSet principalPrivs = new PrincipalPrivilegeSet();
SessionState ss = SessionState.get();
if (ss != null) {
CreateTableAutomaticGrant grants = ss.getCreateTableGrants();
if (grants != null) {
principalPrivs.setUserPrivileges(grants.getUserGrants());
principalPrivs.setGroupPrivileges(grants.getGroupGrants());
principalPrivs.setRolePrivileges(grants.getRoleGrants());
tTbl.setPrivileges(principalPrivs);
}
if (AcidUtils.isTransactionalTable(tbl)) {
boolean createTableUseSuffix = HiveConf.getBoolVar(conf, ConfVars.HIVE_ACID_CREATE_TABLE_USE_SUFFIX)
|| HiveConf.getBoolVar(conf, ConfVars.HIVE_ACID_LOCKLESS_READS_ENABLED);
if (createTableUseSuffix
&& (tbl.getSd().getLocation() == null || tbl.getSd().getLocation().isEmpty())) {
tbl.setProperty(SOFT_DELETE_TABLE, Boolean.TRUE.toString());
}
tTbl.setTxnId(ss.getTxnMgr().getCurrentTxnId());
}
}
// Set table snapshot to api.Table to make it persistent. A transactional table being
// replicated may have a valid write Id copied from the source. Use that instead of
// crafting one on the replica.
if (tTbl.getWriteId() <= 0) {
TableSnapshot tableSnapshot = AcidUtils.getTableSnapshot(conf, tbl, true);
if (tableSnapshot != null) {
tTbl.setWriteId(tableSnapshot.getWriteId());
}
}
CreateTableRequest request = new CreateTableRequest(tTbl);
if (isIcebergTable(tbl)) {
EnvironmentContext envContext = new EnvironmentContext();
if (TableType.MANAGED_TABLE.equals(tbl.getTableType())) {
envContext.putToProperties(CTAS_LEGACY_CONFIG, Boolean.TRUE.toString());
}
if (isIcebergStatsSource(conf)) {
envContext.putToProperties(StatsSetupConst.DO_NOT_UPDATE_STATS, StatsSetupConst.TRUE);
}
request.setEnvContext(envContext);
}
request.setPrimaryKeys(primaryKeys);
request.setForeignKeys(foreignKeys);
request.setUniqueConstraints(uniqueConstraints);
request.setNotNullConstraints(notNullConstraints);
request.setDefaultConstraints(defaultConstraints);
request.setCheckConstraints(checkConstraints);
getMSC().createTable(request);
} catch (AlreadyExistsException e) {
if (!ifNotExists) {
throw new HiveException(e);
}
} catch (Exception e) {
throw new HiveException(e);
}
}
public void createTable(Table tbl, boolean ifNotExists) throws HiveException {
createTable(tbl, ifNotExists, null, null, null, null,
null, null);
}
public static List getFieldsFromDeserializerForMsStorage(
Table tbl, Deserializer deserializer, Configuration conf) throws SerDeException, MetaException {
List schema = HiveMetaStoreUtils.getFieldsFromDeserializer(tbl.getTableName(), deserializer, conf);
for (FieldSchema field : schema) {
field.setType(MetaStoreUtils.TYPE_FROM_DESERIALIZER);
}
return schema;
}
/**
* Drops table along with the data in it. If the table doesn't exist then it
* is a no-op. If ifPurge option is specified it is passed to the
* hdfs command that removes table data from warehouse to make it skip trash.
*
* @param tableName
* table to drop
* @param ifPurge
* completely purge the table (skipping trash) while removing data from warehouse
* @throws HiveException
* thrown if the drop fails
*/
public void dropTable(String tableName, boolean ifPurge) throws HiveException {
String[] names = Utilities.getDbTableName(tableName);
dropTable(names[0], names[1], true, true, ifPurge);
}
public void dropTable(Table table, boolean ifPurge) throws HiveException {
boolean tableWithSuffix = AcidUtils.isTableSoftDeleteEnabled(table, conf);
long txnId = Optional.ofNullable(SessionState.get())
.map(ss -> ss.getTxnMgr().getCurrentTxnId()).orElse(0L);
table.getTTable().setTxnId(txnId);
dropTable(table.getTTable(), !tableWithSuffix, true, ifPurge);
}
/**
* Drops table along with the data in it. If the table doesn't exist then it
* is a no-op
*
* @param tableName
* table to drop
* @throws HiveException
* thrown if the drop fails
*/
public void dropTable(String tableName) throws HiveException {
dropTable(tableName, false);
}
/**
* Drops table along with the data in it. If the table doesn't exist then it
* is a no-op
*
* @param dbName
* database where the table lives
* @param tableName
* table to drop
* @throws HiveException
* thrown if the drop fails
*/
public void dropTable(String dbName, String tableName) throws HiveException {
dropTable(dbName, tableName, true, true, false);
}
/**
* Drops the table.
*
* @param dbName
* @param tableName
* @param deleteData
* deletes the underlying data along with metadata
* @param ignoreUnknownTab
* an exception is thrown if this is false and the table doesn't exist
* @throws HiveException
*/
public void dropTable(String dbName, String tableName, boolean deleteData,
boolean ignoreUnknownTab) throws HiveException {
dropTable(dbName, tableName, deleteData, ignoreUnknownTab, false);
}
/**
* Drops the table.
*
* @param dbName
* @param tableName
* @param deleteData
* deletes the underlying data along with metadata
* @param ignoreUnknownTab
* an exception is thrown if this is false and the table doesn't exist
* @param ifPurge
* completely purge the table skipping trash while removing data from warehouse
* @throws HiveException
*/
public void dropTable(String dbName, String tableName, boolean deleteData,
boolean ignoreUnknownTab, boolean ifPurge) throws HiveException {
try {
getMSC().dropTable(dbName, tableName, deleteData, ignoreUnknownTab, ifPurge);
} catch (NoSuchObjectException e) {
if (!ignoreUnknownTab) {
throw new HiveException(e);
}
} catch (Exception e) {
throw new HiveException(e);
}
}
public void dropTable(org.apache.hadoop.hive.metastore.api.Table table,
boolean deleteData, boolean ignoreUnknownTab, boolean ifPurge) throws HiveException {
try {
getMSC().dropTable(table, deleteData, ignoreUnknownTab, ifPurge);
} catch (Exception e) {
throw new HiveException(e);
} finally {
AcidUtils.tryInvalidateDirCache(table);
}
}
/**
* Truncates the table/partition as per specifications. Just trash the data files
*
* @param dbDotTableName
* name of the table
* @throws HiveException
*/
public void truncateTable(String dbDotTableName, Map partSpec, Long writeId) throws HiveException {
try {
Table table = getTable(dbDotTableName, true);
AcidUtils.TableSnapshot snapshot = null;
if (AcidUtils.isTransactionalTable(table)) {
if (writeId <= 0) {
snapshot = AcidUtils.getTableSnapshot(conf, table, true);
} else {
String fullTableName = getFullTableName(table.getDbName(), table.getTableName());
ValidWriteIdList writeIdList = getMSC().getValidWriteIds(fullTableName, writeId);
snapshot = new TableSnapshot(writeId, writeIdList.writeToString());
}
}
// TODO: APIs with catalog names
List partNames = ((null == partSpec)
? null : getPartitionNames(table.getDbName(), table.getTableName(), partSpec, (short) -1));
if (snapshot == null) {
getMSC().truncateTable(table.getDbName(), table.getTableName(), partNames);
} else {
boolean truncateUseBase = HiveConf.getBoolVar(conf, HiveConf.ConfVars.HIVE_ACID_TRUNCATE_USE_BASE)
|| HiveConf.getBoolVar(conf, HiveConf.ConfVars.HIVE_ACID_LOCKLESS_READS_ENABLED);
getMSC().truncateTable(table.getDbName(), table.getTableName(), partNames,
snapshot.getValidWriteIdList(), snapshot.getWriteId(), !truncateUseBase);
}
} catch (Exception e) {
throw new HiveException(e);
}
}
public HiveConf getConf() {
return (conf);
}
/**
* Returns metadata for the table named tableName
* @param tableName the name of the table
* @return the table metadata
* @throws HiveException if there's an internal error or if the
* table doesn't exist
*/
public Table getTable(final String tableName) throws HiveException {
return this.getTable(tableName, true);
}
/**
* Returns metadata for the table named tableName
* @param tableName the name of the table
* @param throwException controls whether an exception is thrown or a returns a null
* @return the table metadata
* @throws HiveException if there's an internal error or if the
* table doesn't exist
*/
public Table getTable(final String tableName, boolean throwException) throws HiveException {
String[] nameParts = tableName.split("\\.");
if (nameParts.length == 3) {
Table table = this.getTable(nameParts[0], nameParts[1], nameParts[2], throwException);
return table;
} else {
String[] names = Utilities.getDbTableName(tableName);
Table table = this.getTable(names[0], names[1], null, throwException);
return table;
}
}
/**
* Returns metadata of the table
*
* @param dbName
* the name of the database
* @param tableName
* the name of the table
* @return the table
* @exception HiveException
* if there's an internal error or if the table doesn't exist
*/
public Table getTable(final String dbName, final String tableName) throws HiveException {
// TODO: catalog... etc everywhere
if (tableName.contains(".")) {
String[] names = Utilities.getDbTableName(tableName);
return this.getTable(names[0], names[1], null, true);
} else {
return this.getTable(dbName, tableName, null, true);
}
}
/**
* Returns metadata of the table
*
* @param tableName
* the tableName object
* @return the table
* @exception HiveException
* if there's an internal error or if the table doesn't exist
*/
public Table getTable(TableName tableName) throws HiveException {
return this.getTable(ObjectUtils.firstNonNull(tableName.getDb(), SessionState.get().getCurrentDatabase()),
tableName.getTable(), tableName.getTableMetaRef(), true);
}
/**
* Returns metadata of the table
*
* @param dbName
* the name of the database
* @param tableName
* the name of the table
* @param tableMetaRef
* the name of the table meta ref, e.g. iceberg metadata table or branch
* @param throwException
* controls whether an exception is thrown or a returns a null
* @return the table or if throwException is false a null value.
* @throws HiveException
*/
public Table getTable(final String dbName, final String tableName,
final String tableMetaRef, boolean throwException) throws HiveException {
return this.getTable(dbName, tableName, tableMetaRef, throwException, false);
}
/**
* Returns metadata of the table
*
* @param dbName
* the name of the database
* @param tableName
* the name of the table
* @param throwException
* controls whether an exception is thrown or a returns a null
* @return the table or if throwException is false a null value.
* @throws HiveException
*/
public Table getTable(final String dbName, final String tableName, boolean throwException) throws HiveException {
return this.getTable(dbName, tableName, null, throwException);
}
/**
* Returns metadata of the table.
*
* @param dbName
* the name of the database
* @param tableName
* the name of the table
* @param throwException
* controls whether an exception is thrown or a returns a null
* @param checkTransactional
* checks whether the metadata table stats are valid (or
* compilant with the snapshot isolation of) for the current transaction.
* @return the table or if throwException is false a null value.
* @throws HiveException
*/
public Table getTable(final String dbName, final String tableName, boolean throwException, boolean checkTransactional)
throws HiveException {
return getTable(dbName, tableName, null, throwException, checkTransactional, false);
}
/**
* Returns metadata of the table.
*
* @param dbName
* the name of the database
* @param tableName
* the name of the table
* @param tableMetaRef
* the name of the table meta ref, e.g. iceberg metadata table or branch
* @param throwException
* controls whether an exception is thrown or a returns a null
* @param checkTransactional
* checks whether the metadata table stats are valid (or
* compilant with the snapshot isolation of) for the current transaction.
* @return the table or if throwException is false a null value.
* @throws HiveException
*/
public Table getTable(final String dbName, final String tableName, String tableMetaRef, boolean throwException,
boolean checkTransactional) throws HiveException {
return getTable(dbName, tableName, tableMetaRef, throwException, checkTransactional, false);
}
/**
* Returns metadata of the table.
*
* @param dbName
* the name of the database
* @param tableName
* the name of the table
* @param tableMetaRef
* the name of the table meta ref, e.g. iceberg metadata table or branch
* @param throwException
* controls whether an exception is thrown or a returns a null
* @param checkTransactional
* checks whether the metadata table stats are valid (or
* compilant with the snapshot isolation of) for the current transaction.
* @param getColumnStats
* get column statistics if available
* @return the table or if throwException is false a null value.
* @throws HiveException
*/
public Table getTable(final String dbName, final String tableName, String tableMetaRef, boolean throwException,
boolean checkTransactional, boolean getColumnStats) throws HiveException {
if (tableName == null || tableName.equals("")) {
throw new HiveException("empty table creation??");
}
// Get the table from metastore
org.apache.hadoop.hive.metastore.api.Table tTable = null;
try {
// Note: this is currently called w/true from StatsOptimizer only.
GetTableRequest request = new GetTableRequest(dbName, tableName);
request.setCatName(getDefaultCatalog(conf));
request.setGetColumnStats(getColumnStats);
request.setEngine(Constants.HIVE_ENGINE);
if (checkTransactional) {
ValidWriteIdList validWriteIdList = null;
long txnId = SessionState.get() != null && SessionState.get().getTxnMgr() != null ?
SessionState.get().getTxnMgr().getCurrentTxnId() : 0;
if (txnId > 0) {
validWriteIdList = AcidUtils.getTableValidWriteIdListWithTxnList(conf, dbName, tableName);
}
request.setValidWriteIdList(validWriteIdList != null ? validWriteIdList.toString() : null);
}
tTable = getMSC().getTable(request);
} catch (NoSuchObjectException e) {
if (throwException) {
throw new InvalidTableException(tableName);
}
return null;
} catch (Exception e) {
throw new HiveException("Unable to fetch table " + tableName + ". " + e.getMessage(), e);
}
// For non-views, we need to do some extra fixes
if (!TableType.VIRTUAL_VIEW.toString().equals(tTable.getTableType())) {
// Fix the non-printable chars
Map parameters = tTable.getSd().getParameters();
String sf = parameters!=null?parameters.get(SERIALIZATION_FORMAT) : null;
if (sf != null) {
char[] b = sf.toCharArray();
if ((b.length == 1) && (b[0] < 10)) { // ^A, ^B, ^C, ^D, \t
parameters.put(SERIALIZATION_FORMAT, Integer.toString(b[0]));
}
}
// Use LazySimpleSerDe for MetadataTypedColumnsetSerDe.
// NOTE: LazySimpleSerDe does not support tables with a single column of
// col
// of type "array". This happens when the table is created using
// an
// earlier version of Hive.
if (org.apache.hadoop.hive.serde2.MetadataTypedColumnsetSerDe.class
.getName().equals(
tTable.getSd().getSerdeInfo().getSerializationLib())
&& tTable.getSd().getColsSize() > 0
&& tTable.getSd().getCols().get(0).getType().indexOf('<') == -1) {
tTable.getSd().getSerdeInfo().setSerializationLib(
org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe.class.getName());
}
}
Table t = new Table(tTable);
if (tableMetaRef != null) {
if (t.getStorageHandler() == null || !t.getStorageHandler().isTableMetaRefSupported()) {
throw new SemanticException(ErrorMsg.TABLE_META_REF_NOT_SUPPORTED, t.getTableName());
}
t = t.getStorageHandler().checkAndSetTableMetaRef(t, tableMetaRef);
}
return t;
}
/**
* Get ValidWriteIdList for the current transaction.
* This fetches the ValidWriteIdList from the metastore for a given table if txnManager has an open transaction.
*
* @param dbName
* @param tableName
* @return
* @throws LockException
*/
private ValidWriteIdList getValidWriteIdList(String dbName, String tableName) throws LockException {
ValidWriteIdList validWriteIdList = null;
SessionState sessionState = SessionState.get();
HiveTxnManager txnMgr = sessionState != null? sessionState.getTxnMgr() : null;
long txnId = txnMgr != null ? txnMgr.getCurrentTxnId() : 0;
if (txnId > 0) {
validWriteIdList = AcidUtils.getTableValidWriteIdListWithTxnList(conf, dbName, tableName);
} else {
String fullTableName = getFullTableName(dbName, tableName);
validWriteIdList = new ValidReaderWriteIdList(fullTableName, new long[0], new BitSet(), Long.MAX_VALUE);
}
return validWriteIdList;
}
/**
* Get all table names for the current database.
* @return List of table names
* @throws HiveException
*/
public List getAllTables() throws HiveException {
return getTablesByType(SessionState.get().getCurrentDatabase(), null, null);
}
/**
* Get all table names for the specified database.
* @param dbName
* @return List of table names
* @throws HiveException
*/
public List getAllTables(String dbName) throws HiveException {
return getTablesByType(dbName, ".*", null);
}
/**
* Get all tables for the specified database.
* @param dbName
* @return List of all tables
* @throws HiveException
*/
public List
getAllTableObjects(String dbName) throws HiveException {
return getTableObjects(dbName, ".*", null);
}
/**
* Get all materialized view names for the specified database.
* @param dbName
* @return List of materialized view table names
* @throws HiveException
*/
public List getAllMaterializedViews(String dbName) throws HiveException {
return getTablesByType(dbName, ".*", TableType.MATERIALIZED_VIEW);
}
/**
* Get all materialized views for the specified database.
* @param dbName
* @return List of materialized view table objects
* @throws HiveException
*/
public List
getAllMaterializedViewObjects(String dbName) throws HiveException {
return getTableObjects(dbName, ".*", TableType.MATERIALIZED_VIEW);
}
/**
* Get materialized views for the specified database that match the provided regex pattern.
* @param dbName
* @param pattern
* @return List of materialized view table objects
* @throws HiveException
*/
public List
getMaterializedViewObjectsByPattern(String dbName, String pattern) throws HiveException {
return getTableObjects(dbName, pattern, TableType.MATERIALIZED_VIEW);
}
public List
getTableObjects(String dbName, String pattern, TableType tableType) throws HiveException {
try {
return Lists.transform(getMSC().getTableObjectsByName(dbName, getTablesByType(dbName, pattern, tableType)),
new com.google.common.base.Function() {
@Override
public Table apply(org.apache.hadoop.hive.metastore.api.Table table) {
return new Table(table);
}
}
);
} catch (Exception e) {
throw new HiveException(e);
}
}
/**
* Returns all existing tables from default database which match the given
* pattern. The matching occurs as per Java regular expressions
*
* @param tablePattern
* java re pattern
* @return list of table names
* @throws HiveException
*/
public List getTablesByPattern(String tablePattern) throws HiveException {
return getTablesByType(SessionState.get().getCurrentDatabase(),
tablePattern, null);
}
/**
* Returns all existing tables from the specified database which match the given
* pattern. The matching occurs as per Java regular expressions.
* @param dbName
* @param tablePattern
* @return list of table names
* @throws HiveException
*/
public List getTablesByPattern(String dbName, String tablePattern) throws HiveException {
return getTablesByType(dbName, tablePattern, null);
}
/**
* Returns all existing tables from the given database which match the given
* pattern. The matching occurs as per Java regular expressions
*
* @param database
* the database name
* @param tablePattern
* java re pattern
* @return list of table names
* @throws HiveException
*/
public List getTablesForDb(String database, String tablePattern)
throws HiveException {
return getTablesByType(database, tablePattern, null);
}
/**
* Returns all existing tables of a type (VIRTUAL_VIEW|EXTERNAL_TABLE|MANAGED_TABLE) from the specified
* database which match the given pattern. The matching occurs as per Java regular expressions.
* @param dbName Database name to find the tables in. if null, uses the current database in this session.
* @param pattern A pattern to match for the table names.If null, returns all names from this DB.
* @param type The type of tables to return. VIRTUAL_VIEWS for views. If null, returns all tables and views.
* @return list of table names that match the pattern.
* @throws HiveException
*/
public List getTablesByType(String dbName, String pattern, TableType type)
throws HiveException {
PerfLogger perfLogger = SessionState.getPerfLogger();
perfLogger.perfLogBegin(CLASS_NAME, PerfLogger.HIVE_GET_TABLE);
if (dbName == null) {
dbName = SessionState.get().getCurrentDatabase();
}
try {
List result;
if (type != null) {
if (pattern != null) {
result = getMSC().getTables(dbName, pattern, type);
} else {
result = getMSC().getTables(dbName, ".*", type);
}
} else {
if (pattern != null) {
result = getMSC().getTables(dbName, pattern);
} else {
result = getMSC().getTables(dbName, ".*");
}
}
return result;
} catch (Exception e) {
throw new HiveException(e);
} finally {
perfLogger.perfLogEnd(CLASS_NAME, PerfLogger.HIVE_GET_TABLE, "HS2-cache");
}
}
/**
* Get the materialized views that have been enabled for rewriting from the
* cache (registry). It will preprocess them to discard those that are
* outdated and augment those that need to be augmented, e.g., if incremental
* rewriting is enabled.
*
* @return the list of materialized views available for rewriting from the registry
* @throws HiveException
*/
public List getPreprocessedMaterializedViewsFromRegistry(
Set tablesUsed, HiveTxnManager txnMgr) throws HiveException {
// From cache
List materializedViews =
HiveMaterializedViewsRegistry.get().getRewritingMaterializedViews();
if (materializedViews.isEmpty()) {
return Collections.emptyList();
}
// Add to final result
return filterAugmentMaterializedViews(materializedViews, tablesUsed, txnMgr);
}
private List filterAugmentMaterializedViews(List materializedViews,
Set tablesUsed, HiveTxnManager txnMgr) throws HiveException {
final String validTxnsList = conf.get(ValidTxnList.VALID_TXNS_KEY);
final boolean tryIncrementalRewriting =
HiveConf.getBoolVar(conf, HiveConf.ConfVars.HIVE_MATERIALIZED_VIEW_REWRITING_INCREMENTAL);
try {
// Final result
List result = new ArrayList<>();
for (HiveRelOptMaterialization materialization : materializedViews) {
final Table materializedViewTable = extractTable(materialization);
final Boolean outdated = isOutdatedMaterializedView(
materializedViewTable, tablesUsed, false, txnMgr);
if (outdated == null) {
continue;
}
if (outdated) {
// The MV is outdated, see whether we should consider it for rewriting or not
if (!tryIncrementalRewriting) {
LOG.debug("Materialized view " + materializedViewTable.getFullyQualifiedName() +
" ignored for rewriting as its contents are outdated");
continue;
}
// We will rewrite it to include the filters on transaction list
// so we can produce partial rewritings.
// This would be costly since we are doing it for every materialized view
// that is outdated, but it only happens for more than one materialized view
// if rewriting with outdated materialized views is enabled (currently
// disabled by default).
materialization = HiveMaterializedViewUtils.augmentMaterializationWithTimeInformation(
materialization, validTxnsList, materializedViewTable.getMVMetadata().getSnapshot());
}
result.addAll(HiveMaterializedViewUtils.deriveGroupingSetsMaterializedViews(materialization));
}
return result;
} catch (Exception e) {
throw new HiveException(e);
}
}
/**
* Utility method that returns whether a materialized view is outdated (true), not outdated
* (false), or it cannot be determined (null). The latest case may happen e.g. when the
* materialized view definition uses external tables.
* This method checks invalidation time window defined in materialization.
*/
public Boolean isOutdatedMaterializedView(
Table materializedViewTable, Set tablesUsed,
boolean forceMVContentsUpToDate, HiveTxnManager txnMgr) throws HiveException {
String validTxnsList = conf.get(ValidTxnList.VALID_TXNS_KEY);
if (validTxnsList == null) {
return null;
}
long defaultTimeWindow = HiveConf.getTimeVar(conf,
HiveConf.ConfVars.HIVE_MATERIALIZED_VIEW_REWRITING_TIME_WINDOW, TimeUnit.MILLISECONDS);
// Check if materialization defined its own invalidation time window
String timeWindowString = materializedViewTable.getProperty(MATERIALIZED_VIEW_REWRITING_TIME_WINDOW);
long timeWindow = org.apache.commons.lang3.StringUtils.isEmpty(timeWindowString) ? defaultTimeWindow :
HiveConf.toTime(timeWindowString,
HiveConf.getDefaultTimeUnit(HiveConf.ConfVars.HIVE_MATERIALIZED_VIEW_REWRITING_TIME_WINDOW),
TimeUnit.MILLISECONDS);
MaterializedViewMetadata mvMetadata = materializedViewTable.getMVMetadata();
boolean outdated = false;
if (timeWindow < 0L) {
// We only consider the materialized view to be outdated if forceOutdated = true, i.e.,
// if it is a rebuild. Otherwise, it passed the test and we use it as it is.
outdated = forceMVContentsUpToDate;
} else {
// Check whether the materialized view is invalidated
if (forceMVContentsUpToDate || timeWindow == 0L ||
mvMetadata.getMaterializationTime() < System.currentTimeMillis() - timeWindow) {
return HiveMaterializedViewUtils.isOutdatedMaterializedView(
validTxnsList, txnMgr, this, tablesUsed, materializedViewTable);
}
}
return outdated;
}
/**
* Utility method that returns whether a materialized view is outdated (true), not outdated
* (false), or it cannot be determined (null). The latest case may happen e.g. when the
* materialized view definition uses external tables.
*/
public Boolean isOutdatedMaterializedView(HiveTxnManager txnManager, Table table) throws HiveException {
String validTxnsList = conf.get(ValidTxnList.VALID_TXNS_KEY);
if (validTxnsList == null) {
return null;
}
return HiveMaterializedViewUtils.isOutdatedMaterializedView(
validTxnsList, txnManager, this, table.getMVMetadata().getSourceTableNames(), table);
}
/**
* Validate that the materialized views retrieved from registry are still up-to-date.
* For those that are not, the method loads them from the metastore into the registry.
*
* @return true if they are up-to-date, otherwise false
* @throws HiveException
*/
public boolean validateMaterializedViewsFromRegistry(List
cachedMaterializedViewTables,
Set tablesUsed, HiveTxnManager txnMgr) throws HiveException {
try {
// Final result
boolean result = true;
for (Table cachedMaterializedViewTable : cachedMaterializedViewTables) {
// Retrieve the materialized view table from the metastore
final Table materializedViewTable = getTable(
cachedMaterializedViewTable.getDbName(), cachedMaterializedViewTable.getTableName());
if (materializedViewTable == null || !materializedViewTable.isRewriteEnabled()) {
// This could happen if materialized view has been deleted or rewriting has been disabled.
// We remove it from the registry and set result to false.
HiveMaterializedViewsRegistry.get().dropMaterializedView(cachedMaterializedViewTable);
result = false;
} else {
final Boolean outdated = isOutdatedMaterializedView(cachedMaterializedViewTable, tablesUsed, false, txnMgr);
if (outdated == null) {
result = false;
continue;
}
// If the cached materialized view was not outdated wrt the query snapshot,
// then we know that the metastore version should be either the same or
// more recent. If it is more recent, snapshot isolation will shield us
// from the reading its contents after snapshot was acquired, but we will
// update the registry so we have most recent version.
// On the other hand, if the materialized view in the cache was outdated,
// we can only use it if the version that was in the cache is the same one
// that we can find in the metastore.
if (outdated) {
if (!cachedMaterializedViewTable.equals(materializedViewTable)) {
// We ignore and update the registry
HiveMaterializedViewsRegistry.get().refreshMaterializedView(conf, cachedMaterializedViewTable, materializedViewTable);
result = false;
} else {
// Obtain additional information if we should try incremental rewriting / rebuild
// We will not try partial rewriting if there were update/delete/compaction operations on source tables
Materialization invalidationInfo = getMaterializationInvalidationInfo(materializedViewTable.getMVMetadata());
if (invalidationInfo == null || invalidationInfo.isSourceTablesUpdateDeleteModified() ||
invalidationInfo.isSourceTablesCompacted()) {
// We ignore (as it did not meet the requirements), but we do not need to update it in the
// registry, since it is up-to-date
result = false;
}
}
} else if (!cachedMaterializedViewTable.equals(materializedViewTable)) {
// Update the registry
HiveMaterializedViewsRegistry.get().refreshMaterializedView(conf, cachedMaterializedViewTable, materializedViewTable);
}
}
}
return result;
} catch (Exception e) {
throw new HiveException(e);
}
}
private Materialization getMaterializationInvalidationInfo(MaterializedViewMetadata metadata)
throws TException, HiveException {
Optional first = metadata.getSourceTables().stream().findFirst();
if (!first.isPresent()) {
// This is unexpected: all MV must have at least one source
Materialization materialization = new Materialization();
materialization.setSourceTablesCompacted(true);
materialization.setSourceTablesUpdateDeleteModified(true);
return new Materialization();
} else {
Table table = getTable(first.get().getTable().getDbName(), first.get().getTable().getTableName());
if (!(table.isNonNative() && table.getStorageHandler().areSnapshotsSupported())) {
// Mixing native and non-native acid source tables are not supported. If the first source is native acid
// the rest is expected to be native acid
return getMSC().getMaterializationInvalidationInfo(
metadata.creationMetadata, conf.get(ValidTxnList.VALID_TXNS_KEY));
}
}
MaterializationSnapshot mvSnapshot = MaterializationSnapshot.fromJson(metadata.creationMetadata.getValidTxnList());
boolean hasAppendsOnly = true;
for (SourceTable sourceTable : metadata.getSourceTables()) {
Table table = getTable(sourceTable.getTable().getDbName(), sourceTable.getTable().getTableName());
HiveStorageHandler storageHandler = table.getStorageHandler();
if (storageHandler == null) {
Materialization materialization = new Materialization();
materialization.setSourceTablesCompacted(true);
return materialization;
}
Boolean b = storageHandler.hasAppendsOnly(
table, mvSnapshot.getTableSnapshots().get(table.getFullyQualifiedName()));
if (b == null) {
Materialization materialization = new Materialization();
materialization.setSourceTablesCompacted(true);
return materialization;
} else if (!b) {
hasAppendsOnly = false;
break;
}
}
Materialization materialization = new Materialization();
// TODO: delete operations are not supported yet.
// Set setSourceTablesCompacted to false when delete is supported
materialization.setSourceTablesCompacted(!hasAppendsOnly);
materialization.setSourceTablesUpdateDeleteModified(!hasAppendsOnly);
return materialization;
}
/**
* Get the materialized views that have been enabled for rewriting from the
* metastore. If the materialized view is in the cache, we do not need to
* parse it to generate a logical plan for the rewriting. Instead, we
* return the version present in the cache. Further, information provided
* by the invalidation cache is useful to know whether a materialized view
* can be used for rewriting or not.
*
* @return the list of materialized views available for rewriting
* @throws HiveException
*/
public List getPreprocessedMaterializedViews(
Set tablesUsed, HiveTxnManager txnMgr)
throws HiveException {
// From metastore
List
materializedViewTables =
getAllMaterializedViewObjectsForRewriting();
if (materializedViewTables.isEmpty()) {
return Collections.emptyList();
}
// Return final result
return getValidMaterializedViews(materializedViewTables, tablesUsed, false, true, txnMgr, EnumSet.of(CALCITE));
}
/**
* Get the target materialized view from the metastore. Although it may load the plan
* from the registry, it is guaranteed that it will always return an up-to-date version
* wrt metastore.
*
* @return the materialized view for rebuild
* @throws HiveException
*/
public HiveRelOptMaterialization getMaterializedViewForRebuild(String dbName, String materializedViewName,
Set tablesUsed, HiveTxnManager txnMgr) throws HiveException {
List validMaterializedViews = getValidMaterializedViews(
ImmutableList.of(getTable(dbName, materializedViewName)), tablesUsed, true, false, txnMgr, ALL);
if (validMaterializedViews.isEmpty()) {
return null;
}
Preconditions.checkState(validMaterializedViews.size() == 1,
"Returned more than a materialized view for rebuild");
return validMaterializedViews.get(0);
}
private List getValidMaterializedViews(List
materializedViewTables,
Set tablesUsed, boolean forceMVContentsUpToDate, boolean expandGroupingSets,
HiveTxnManager txnMgr, EnumSet scope)
throws HiveException {
final String validTxnsList = conf.get(ValidTxnList.VALID_TXNS_KEY);
final boolean tryIncrementalRewriting =
HiveConf.getBoolVar(conf, HiveConf.ConfVars.HIVE_MATERIALIZED_VIEW_REWRITING_INCREMENTAL);
final boolean tryIncrementalRebuild =
HiveConf.getBoolVar(conf, HiveConf.ConfVars.HIVE_MATERIALIZED_VIEW_REBUILD_INCREMENTAL);
try {
// Final result
List result = new ArrayList<>();
for (Table materializedViewTable : materializedViewTables) {
final Boolean outdated = isOutdatedMaterializedView(
materializedViewTable, tablesUsed, forceMVContentsUpToDate, txnMgr);
if (outdated == null) {
continue;
}
final MaterializedViewMetadata metadata = materializedViewTable.getMVMetadata();
Materialization invalidationInfo = null;
if (outdated) {
// The MV is outdated, see whether we should consider it for rewriting or not
boolean ignore;
if (forceMVContentsUpToDate && !tryIncrementalRebuild) {
// We will not try partial rewriting for rebuild if incremental rebuild is disabled
ignore = true;
} else if (!forceMVContentsUpToDate && !tryIncrementalRewriting) {
// We will not try partial rewriting for non-rebuild if incremental rewriting is disabled
ignore = true;
} else {
// Obtain additional information if we should try incremental rewriting / rebuild
// We will not try partial rewriting if there were update/delete/compaction operations on source tables
invalidationInfo = getMaterializationInvalidationInfo(metadata);
ignore = invalidationInfo == null || invalidationInfo.isSourceTablesCompacted();
}
if (ignore) {
LOG.debug("Materialized view " + materializedViewTable.getFullyQualifiedName() +
" ignored for rewriting as its contents are outdated");
continue;
}
}
// It passed the test, load
HiveRelOptMaterialization relOptMaterialization =
HiveMaterializedViewsRegistry.get().getRewritingMaterializedView(
materializedViewTable.getDbName(), materializedViewTable.getTableName(), scope);
if (relOptMaterialization != null) {
Table cachedMaterializedViewTable = extractTable(relOptMaterialization);
if (cachedMaterializedViewTable.equals(materializedViewTable)) {
// It is in the cache and up to date
if (outdated) {
// We will rewrite it to include the filters on transaction list
// so we can produce partial rewritings
relOptMaterialization = HiveMaterializedViewUtils.augmentMaterializationWithTimeInformation(
relOptMaterialization, validTxnsList, metadata.getSnapshot());
}
addToMaterializationList(expandGroupingSets, invalidationInfo, relOptMaterialization, result);
continue;
}
}
// It was not present in the cache (maybe because it was added by another HS2)
// or it is not up to date. We need to add it
if (LOG.isDebugEnabled()) {
LOG.debug("Materialized view " + materializedViewTable.getFullyQualifiedName() +
" was not in the cache or it is not supported by specified rewrite algorithm {}", scope);
}
HiveRelOptMaterialization hiveRelOptMaterialization =
HiveMaterializedViewsRegistry.get().createMaterialization(conf, materializedViewTable);
if (hiveRelOptMaterialization != null && hiveRelOptMaterialization.isSupported(scope)) {
relOptMaterialization = hiveRelOptMaterialization;
HiveMaterializedViewsRegistry.get().refreshMaterializedView(conf, null, materializedViewTable);
if (outdated) {
// We will rewrite it to include the filters on transaction list
// so we can produce partial rewritings
relOptMaterialization = HiveMaterializedViewUtils.augmentMaterializationWithTimeInformation(
hiveRelOptMaterialization, validTxnsList, metadata.getSnapshot());
}
addToMaterializationList(expandGroupingSets, invalidationInfo, relOptMaterialization, result);
}
}
return result;
} catch (Exception e) {
throw new HiveException(e);
}
}
private void addToMaterializationList(
boolean expandGroupingSets, Materialization invalidationInfo, HiveRelOptMaterialization relOptMaterialization,
List result) {
if (expandGroupingSets) {
List hiveRelOptMaterializationList =
HiveMaterializedViewUtils.deriveGroupingSetsMaterializedViews(relOptMaterialization);
if (invalidationInfo != null) {
for (HiveRelOptMaterialization materialization : hiveRelOptMaterializationList) {
result.add(materialization.updateInvalidation(invalidationInfo));
}
} else {
result.addAll(hiveRelOptMaterializationList);
}
} else {
result.add(invalidationInfo == null ? relOptMaterialization : relOptMaterialization.updateInvalidation(invalidationInfo));
}
}
public List
getAllMaterializedViewObjectsForRewriting() throws HiveException {
try {
return getMSC().getAllMaterializedViewObjectsForRewriting()
.stream()
.map(Table::new)
.collect(Collectors.toList());
} catch (Exception e) {
throw new HiveException(e);
}
}
/**
* Get the materialized views from the metastore or from the registry which has the same query definition as the
* specified sql query text. It is guaranteed that it will always return an up-to-date version wrt metastore.
* This method filters out outdated Materialized views. It compares the transaction ids of the passed usedTables and
* the materialized view using the txnMgr.
* @param tablesUsed List of tables to verify whether materialized view is outdated
* @param txnMgr Transaction manager to get open transactions affects used tables.
* @return List of materialized views has matching query definition with querySql
* @throws HiveException - an exception is thrown during validation or unable to pull transaction ids
*/
public List getMaterializedViewsByAST(
ASTNode astNode, Set tablesUsed, HiveTxnManager txnMgr) throws HiveException {
List materializedViews =
HiveMaterializedViewsRegistry.get().getRewritingMaterializedViews(astNode);
if (materializedViews.isEmpty()) {
return Collections.emptyList();
}
try {
// Final result
List result = new ArrayList<>();
for (HiveRelOptMaterialization materialization : materializedViews) {
Table materializedViewTable = extractTable(materialization);
final Boolean outdated = isOutdatedMaterializedView(materializedViewTable, tablesUsed, false, txnMgr);
if (outdated == null) {
LOG.debug("Unable to determine if Materialized view " + materializedViewTable.getFullyQualifiedName() +
" contents are outdated. It may uses external tables?");
continue;
}
if (outdated) {
LOG.debug("Materialized view " + materializedViewTable.getFullyQualifiedName() +
" ignored for rewriting as its contents are outdated");
continue;
}
result.add(materialization);
}
return result;
} catch (Exception e) {
throw new HiveException(e);
}
}
/**
* Get all existing database names.
*
* @return List of database names.
* @throws HiveException
*/
public List getAllDatabases() throws HiveException {
try {
return getMSC().getAllDatabases();
} catch (Exception e) {
throw new HiveException(e);
}
}
/**
* Get all existing databases that match the given
* pattern. The matching occurs as per Java regular expressions
*
* @param databasePattern
* java re pattern
* @return list of database names
* @throws HiveException
*/
public List getDatabasesByPattern(String databasePattern) throws HiveException {
try {
return getMSC().getDatabases(databasePattern);
} catch (Exception e) {
throw new HiveException(e);
}
}
public boolean grantPrivileges(PrivilegeBag privileges)
throws HiveException {
try {
return getMSC().grant_privileges(privileges);
} catch (Exception e) {
throw new HiveException(e);
}
}
/**
* @param privileges
* a bag of privileges
* @return true on success
* @throws HiveException
*/
public boolean revokePrivileges(PrivilegeBag privileges, boolean grantOption)
throws HiveException {
try {
return getMSC().revoke_privileges(privileges, grantOption);
} catch (Exception e) {
throw new HiveException(e);
}
}
public void validateDatabaseExists(String databaseName) throws SemanticException {
boolean exists;
try {
exists = databaseExists(databaseName);
} catch (HiveException e) {
throw new SemanticException(ErrorMsg.DATABASE_NOT_EXISTS.getMsg(databaseName), e);
}
if (!exists) {
throw new SemanticException(ErrorMsg.DATABASE_NOT_EXISTS.getMsg(databaseName));
}
}
/**
* Query metadata to see if a database with the given name already exists.
*
* @param dbName
* @return true if a database with the given name already exists, false if
* does not exist.
* @throws HiveException
*/
public boolean databaseExists(String dbName) throws HiveException {
return getDatabase(dbName) != null;
}
/**
* Get the database by name.
* @param dbName the name of the database.
* @return a Database object if this database exists, null otherwise.
* @throws HiveException
*/
public Database getDatabase(String dbName) throws HiveException {
PerfLogger perfLogger = SessionState.getPerfLogger();
perfLogger.perfLogBegin(CLASS_NAME, PerfLogger.HIVE_GET_DATABASE);
try {
return getMSC().getDatabase(dbName);
} catch (NoSuchObjectException e) {
return null;
} catch (Exception e) {
throw new HiveException(e);
} finally {
perfLogger.perfLogEnd(CLASS_NAME, PerfLogger.HIVE_GET_DATABASE, "HS2-cache");
}
}
/**
* Get the database by name.
* @param catName catalog name
* @param dbName the name of the database.
* @return a Database object if this database exists, null otherwise.
* @throws HiveException
*/
public Database getDatabase(String catName, String dbName) throws HiveException {
PerfLogger perfLogger = SessionState.getPerfLogger();
perfLogger.perfLogBegin(CLASS_NAME, PerfLogger.HIVE_GET_DATABASE_2);
try {
return getMSC().getDatabase(catName, dbName);
} catch (NoSuchObjectException e) {
return null;
} catch (Exception e) {
throw new HiveException(e);
} finally {
perfLogger.perfLogEnd(CLASS_NAME, PerfLogger.HIVE_GET_DATABASE_2, "HS2-cache");
}
}
/**
* Get the Database object for current database
* @return a Database object if this database exists, null otherwise.
* @throws HiveException
*/
public Database getDatabaseCurrent() throws HiveException {
String currentDb = SessionState.get().getCurrentDatabase();
return getDatabase(currentDb);
}
private TableSnapshot getTableSnapshot(Table tbl, Long writeId) throws LockException {
TableSnapshot tableSnapshot = null;
if ((writeId != null) && (writeId > 0)) {
ValidWriteIdList writeIds = AcidUtils.getTableValidWriteIdListWithTxnList(
conf, tbl.getDbName(), tbl.getTableName());
tableSnapshot = new TableSnapshot(writeId, writeIds.writeToString());
} else {
// Make sure we pass in the names, so we can get the correct snapshot for rename table.
tableSnapshot = AcidUtils.getTableSnapshot(conf, tbl, tbl.getDbName(), tbl.getTableName(),
true);
}
return tableSnapshot;
}
/**
* Load a directory into a Hive Table Partition - Alters existing content of
* the partition with the contents of loadPath. - If the partition does not
* exist - one is created - files in loadPath are moved into Hive. But the
* directory itself is not removed.
*
* @param loadPath
* Directory containing files to load into Table
* @param tbl
* name of table to be loaded.
* @param partSpec
* defines which partition needs to be loaded
* @param loadFileType
* if REPLACE_ALL - replace files in the table,
* otherwise add files to table (KEEP_EXISTING, OVERWRITE_EXISTING)
* @param inheritTableSpecs if true, on [re]creating the partition, take the
* location/inputformat/outputformat/serde details from table spec
* @param isSrcLocal
* If the source directory is LOCAL
* @param isAcidIUDoperation
* true if this is an ACID operation Insert/Update/Delete operation
* @param resetStatistics
* if true, reset the statistics. If false, do not reset statistics.
* @param writeId write ID allocated for the current load operation
* @param stmtId statement ID of the current load statement
* @param isInsertOverwrite
* @return Partition object being loaded with data
*/
public Partition loadPartition(Path loadPath, Table tbl, Map partSpec,
LoadFileType loadFileType, boolean inheritTableSpecs,
boolean inheritLocation,
boolean isSkewedStoreAsSubdir,
boolean isSrcLocal, boolean isAcidIUDoperation,
boolean resetStatistics, Long writeId,
int stmtId, boolean isInsertOverwrite, boolean isDirectInsert) throws HiveException {
PerfLogger perfLogger = SessionState.getPerfLogger();
perfLogger.perfLogBegin("MoveTask", PerfLogger.LOAD_PARTITION);
// Get the partition object if it already exists
Partition oldPart = getPartition(tbl, partSpec, false);
boolean isTxnTable = AcidUtils.isTransactionalTable(tbl);
// If config is set, table is not temporary and partition being inserted exists, capture
// the list of files added. For not yet existing partitions (insert overwrite to new partition
// or dynamic partition inserts), the add partition event will capture the list of files added.
List newFiles = null;
if (conf.getBoolVar(ConfVars.FIRE_EVENTS_FOR_DML) && !tbl.isTemporary()) {
newFiles = Collections.synchronizedList(new ArrayList<>());
}
Partition newTPart = loadPartitionInternal(loadPath, tbl, partSpec, oldPart,
loadFileType, inheritTableSpecs,
inheritLocation, isSkewedStoreAsSubdir, isSrcLocal, isAcidIUDoperation,
resetStatistics, writeId, stmtId, isInsertOverwrite, isTxnTable, newFiles, isDirectInsert);
AcidUtils.TableSnapshot tableSnapshot = isTxnTable ? getTableSnapshot(tbl, writeId) : null;
if (tableSnapshot != null) {
newTPart.getTPartition().setWriteId(tableSnapshot.getWriteId());
}
if (oldPart == null) {
addPartitionToMetastore(newTPart, resetStatistics, tbl, tableSnapshot);
// For acid table, add the acid_write event with file list at the time of load itself. But
// it should be done after partition is created.
if (isTxnTable && (null != newFiles)) {
addWriteNotificationLog(tbl, partSpec, newFiles, writeId, null);
}
} else {
try {
setStatsPropAndAlterPartition(resetStatistics, tbl, newTPart, tableSnapshot);
} catch (TException e) {
LOG.error("Error loading partitions", e);
throw new HiveException(e);
}
}
perfLogger.perfLogEnd("MoveTask", PerfLogger.LOAD_PARTITION);
return newTPart;
}
/**
* Move all the files from loadPath into Hive. If the partition
* does not exist - one is created - files in loadPath are moved into Hive. But the
* directory itself is not removed.
*
* @param loadPath
* Directory containing files to load into Table
* @param tbl
* name of table to be loaded.
* @param partSpec
* defines which partition needs to be loaded
* @param oldPart
* already existing partition object, can be null
* @param loadFileType
* if REPLACE_ALL - replace files in the table,
* otherwise add files to table (KEEP_EXISTING, OVERWRITE_EXISTING)
* @param inheritTableSpecs if true, on [re]creating the partition, take the
* location/inputformat/outputformat/serde details from table spec
* @param inheritLocation
* if true, partition path is generated from table
* @param isSkewedStoreAsSubdir
* if true, skewed is stored as sub-directory
* @param isSrcLocal
* If the source directory is LOCAL
* @param isAcidIUDoperation
* true if this is an ACID operation Insert/Update/Delete operation
* @param resetStatistics
* if true, reset the statistics. Do not reset statistics if false.
* @param writeId
* write ID allocated for the current load operation
* @param stmtId
* statement ID of the current load statement
* @param isInsertOverwrite
* @param isTxnTable
*
* @return Partition object being loaded with data
* @throws HiveException
*/
private Partition loadPartitionInternal(Path loadPath, Table tbl, Map partSpec,
Partition oldPart, LoadFileType loadFileType, boolean inheritTableSpecs,
boolean inheritLocation, boolean isSkewedStoreAsSubdir,
boolean isSrcLocal, boolean isAcidIUDoperation, boolean resetStatistics,
Long writeId, int stmtId, boolean isInsertOverwrite,
boolean isTxnTable, List newFiles, boolean isDirectInsert) throws HiveException {
Path tblDataLocationPath = tbl.getDataLocation();
boolean isMmTableWrite = AcidUtils.isInsertOnlyTable(tbl.getParameters());
assert tbl.getPath() != null : "null==getPath() for " + tbl.getTableName();
boolean isFullAcidTable = AcidUtils.isFullAcidTable(tbl);
List newFileStatuses = null;
try {
PerfLogger perfLogger = SessionState.getPerfLogger();
/**
* Move files before creating the partition since down stream processes
* check for existence of partition in metadata before accessing the data.
* If partition is created before data is moved, downstream waiting
* processes might move forward with partial data
*/
Path oldPartPath = (oldPart != null) ? oldPart.getDataLocation() : null;
Path newPartPath = null;
if (inheritLocation) {
newPartPath = genPartPathFromTable(tbl, partSpec, tblDataLocationPath);
if(oldPart != null) {
/*
* If we are moving the partition across filesystem boundaries
* inherit from the table properties. Otherwise (same filesystem) use the
* original partition location.
*
* See: HIVE-1707 and HIVE-2117 for background
*/
FileSystem oldPartPathFS = oldPartPath.getFileSystem(getConf());
FileSystem tblPathFS = tblDataLocationPath.getFileSystem(getConf());
if (FileUtils.isEqualFileSystemAndSameOzoneBucket(oldPartPathFS, tblPathFS, oldPartPath, tblDataLocationPath)) {
newPartPath = oldPartPath;
}
}
} else {
newPartPath = oldPartPath == null
? genPartPathFromTable(tbl, partSpec, tblDataLocationPath) : oldPartPath;
}
perfLogger.perfLogBegin("MoveTask", PerfLogger.FILE_MOVES);
// Note: the stats for ACID tables do not have any coordination with either Hive ACID logic
// like txn commits, time outs, etc.; nor the lower level sync in metastore pertaining
// to ACID updates. So the are not themselves ACID.
// Note: this assumes both paths are qualified; which they are, currently.
if (((isMmTableWrite || isDirectInsert || isFullAcidTable) && loadPath.equals(newPartPath)) ||
(loadFileType == LoadFileType.IGNORE)) {
// MM insert query or direct insert; move itself is a no-op.
if (Utilities.FILE_OP_LOGGER.isTraceEnabled()) {
Utilities.FILE_OP_LOGGER.trace("not moving " + loadPath + " to " + newPartPath + " (MM = " + isMmTableWrite
+ ", Direct insert = " + isDirectInsert + ")");
}
if (newFiles != null) {
if (!newFiles.isEmpty()) {
newFileStatuses = new ArrayList<>();
newFileStatuses.addAll(newFiles);
} else {
newFileStatuses = listFilesCreatedByQuery(loadPath, writeId, stmtId);
newFiles.addAll(newFileStatuses);
}
}
if (Utilities.FILE_OP_LOGGER.isTraceEnabled()) {
Utilities.FILE_OP_LOGGER.trace("maybe deleting stuff from " + oldPartPath
+ " (new " + newPartPath + ") for replace");
}
} else {
// Either a non-MM query, or a load into MM table from an external source.
Path destPath = newPartPath;
if (isMmTableWrite) {
// We will load into MM directory, and hide previous directories if needed.
destPath = new Path(destPath, isInsertOverwrite
? AcidUtils.baseDir(writeId) : AcidUtils.deltaSubdir(writeId, writeId, stmtId));
}
if (!isAcidIUDoperation && isFullAcidTable) {
destPath = fixFullAcidPathForLoadData(loadFileType, destPath, writeId, stmtId, tbl);
}
if (Utilities.FILE_OP_LOGGER.isTraceEnabled()) {
Utilities.FILE_OP_LOGGER.trace("moving " + loadPath + " to " + destPath);
}
boolean isManaged = tbl.getTableType() == TableType.MANAGED_TABLE;
// TODO: why is "&& !isAcidIUDoperation" needed here?
if (!isTxnTable && ((loadFileType == LoadFileType.REPLACE_ALL) || (oldPart == null && !isAcidIUDoperation))) {
//for fullAcid tables we don't delete files for commands with OVERWRITE - we create a new
// base_x. (there is Insert Overwrite and Load Data Overwrite)
boolean isSkipTrash = MetaStoreUtils.isSkipTrash(tbl.getParameters());
boolean needRecycle = !tbl.isTemporary()
&& ReplChangeManager.shouldEnableCm(getDatabase(tbl.getDbName()), tbl.getTTable());
replaceFiles(tbl.getPath(), loadPath, destPath, oldPartPath, getConf(), isSrcLocal,
isSkipTrash, newFiles, FileUtils.HIDDEN_FILES_PATH_FILTER, needRecycle, isManaged, isInsertOverwrite);
} else {
FileSystem fs = destPath.getFileSystem(conf);
copyFiles(conf, loadPath, destPath, fs, isSrcLocal, isAcidIUDoperation,
(loadFileType == LoadFileType.OVERWRITE_EXISTING), newFiles,
tbl.getNumBuckets() > 0, isFullAcidTable, isManaged, false);
}
}
perfLogger.perfLogEnd("MoveTask", PerfLogger.FILE_MOVES);
Partition newTPart = oldPart != null ? oldPart : new Partition(tbl, partSpec, newPartPath);
alterPartitionSpecInMemory(tbl, partSpec, newTPart.getTPartition(), inheritTableSpecs, newPartPath.toString());
validatePartition(newTPart);
// If config is set, table is not temporary and partition being inserted exists, capture
// the list of files added. For not yet existing partitions (insert overwrite to new partition
// or dynamic partition inserts), the add partition event will capture the list of files added.
// Generate an insert event only if inserting into an existing partition
// When inserting into a new partition, the add partition event takes care of insert event
if ((null != oldPart) && (null != newFiles)) {
if (isTxnTable) {
addWriteNotificationLog(tbl, partSpec, newFiles, writeId, null);
} else {
fireInsertEvent(tbl, partSpec, (loadFileType == LoadFileType.REPLACE_ALL), newFiles);
}
} else {
LOG.debug("No new files were created, and is not a replace, or we're inserting into a "
+ "partition that does not exist yet. Skipping generating INSERT event.");
}
// recreate the partition if it existed before
if (isSkewedStoreAsSubdir) {
org.apache.hadoop.hive.metastore.api.Partition newCreatedTpart = newTPart.getTPartition();
SkewedInfo skewedInfo = newCreatedTpart.getSd().getSkewedInfo();
/* Construct list bucketing location mappings from sub-directory name. */
Map, String> skewedColValueLocationMaps = constructListBucketingLocationMap(
newPartPath, skewedInfo);
/* Add list bucketing location mappings. */
skewedInfo.setSkewedColValueLocationMaps(skewedColValueLocationMaps);
newCreatedTpart.getSd().setSkewedInfo(skewedInfo);
}
// If there is no column stats gather stage present in the plan. So we don't know the accuracy of the stats or
// auto gather stats is turn off explicitly. We need to reset the stats in both cases.
if (resetStatistics || !this.getConf().getBoolVar(HiveConf.ConfVars.HIVESTATSAUTOGATHER)) {
LOG.debug(
"Clear partition column statistics by setting basic stats to false for " + newTPart.getCompleteName());
StatsSetupConst.setBasicStatsState(newTPart.getParameters(), StatsSetupConst.FALSE);
}
if (oldPart == null) {
newTPart.getTPartition().setParameters(new HashMap());
if (this.getConf().getBoolVar(HiveConf.ConfVars.HIVESTATSAUTOGATHER)) {
StatsSetupConst.setStatsStateForCreateTable(newTPart.getParameters(),
MetaStoreUtils.getColumnNames(tbl.getCols()), StatsSetupConst.TRUE);
}
// Note: we are creating a brand new the partition, so this is going to be valid for ACID.
List filesForStats = null;
if (newFileStatuses != null && !newFileStatuses.isEmpty()) {
filesForStats = newFileStatuses;
} else {
if (isTxnTable) {
filesForStats = AcidUtils.getAcidFilesForStats(newTPart.getTable(), newPartPath, conf, null);
} else {
filesForStats = HiveStatsUtils.getFileStatusRecurse(newPartPath, -1, newPartPath.getFileSystem(conf));
}
}
if (filesForStats != null) {
MetaStoreServerUtils.populateQuickStats(filesForStats, newTPart.getParameters());
} else {
// The ACID state is probably absent. Warning is logged in the get method.
MetaStoreServerUtils.clearQuickStats(newTPart.getParameters());
}
}
return newTPart;
} catch (IOException | MetaException | InvalidOperationException e) {
LOG.error("Error in loadPartitionInternal", e);
throw new HiveException(e);
}
}
private void addPartitionToMetastore(Partition newTPart, boolean resetStatistics,
Table tbl, TableSnapshot tableSnapshot) throws HiveException{
try {
LOG.debug("Adding new partition " + newTPart.getSpec());
getSynchronizedMSC().add_partition(newTPart.getTPartition());
} catch (AlreadyExistsException aee) {
// With multiple users concurrently issuing insert statements on the same partition has
// a side effect that some queries may not see a partition at the time when they're issued,
// but will realize the partition is actually there when it is trying to add such partition
// to the metastore and thus get AlreadyExistsException, because some earlier query just
// created it (race condition).
// For example, imagine such a table is created:
// create table T (name char(50)) partitioned by (ds string);
// and the following two queries are launched at the same time, from different sessions:
// insert into table T partition (ds) values ('Bob', 'today'); -- creates the partition 'today'
// insert into table T partition (ds) values ('Joe', 'today'); -- will fail with AlreadyExistsException
// In that case, we want to retry with alterPartition.
LOG.debug("Caught AlreadyExistsException, trying to alter partition instead");
try {
setStatsPropAndAlterPartition(resetStatistics, tbl, newTPart, tableSnapshot);
} catch (TException e) {
LOG.error("Error setStatsPropAndAlterPartition", e);
throw new HiveException(e);
}
} catch (Exception e) {
try {
final FileSystem newPathFileSystem = newTPart.getPartitionPath().getFileSystem(this.getConf());
boolean isSkipTrash = MetaStoreUtils.isSkipTrash(tbl.getParameters());
final FileStatus status = newPathFileSystem.getFileStatus(newTPart.getPartitionPath());
Hive.trashFiles(newPathFileSystem, new FileStatus[]{status}, this.getConf(), isSkipTrash);
} catch (IOException io) {
LOG.error("Could not delete partition directory contents after failed partition creation: ", io);
}
LOG.error("Error addPartitionToMetastore", e);
throw new HiveException(e);
}
}
private void addPartitionsToMetastore(List partitions,
boolean resetStatistics, Table tbl,
List tableSnapshots)
throws HiveException {
try {
if (partitions.isEmpty() || tableSnapshots.isEmpty()) {
return;
}
if (LOG.isDebugEnabled()) {
StringBuffer debugMsg = new StringBuffer("Adding new partitions ");
partitions.forEach(partition -> debugMsg.append(partition.getSpec() + " "));
LOG.debug(debugMsg.toString());
}
getSynchronizedMSC().add_partitions(partitions.stream().map(Partition::getTPartition)
.collect(Collectors.toList()));
} catch(AlreadyExistsException aee) {
// With multiple users concurrently issuing insert statements on the same partition has
// a side effect that some queries may not see a partition at the time when they're issued,
// but will realize the partition is actually there when it is trying to add such partition
// to the metastore and thus get AlreadyExistsException, because some earlier query just
// created it (race condition).
// For example, imagine such a table is created:
// create table T (name char(50)) partitioned by (ds string);
// and the following two queries are launched at the same time, from different sessions:
// insert into table T partition (ds) values ('Bob', 'today'); -- creates the partition 'today'
// insert into table T partition (ds) values ('Joe', 'today'); -- will fail with AlreadyExistsException
// In that case, we want to retry with alterPartition.
LOG.debug("Caught AlreadyExistsException, trying to add partitions one by one.");
assert partitions.size() == tableSnapshots.size();
for (int i = 0; i < partitions.size(); i++) {
addPartitionToMetastore(partitions.get(i), resetStatistics, tbl,
tableSnapshots.get(i));
}
} catch (Exception e) {
try {
for (Partition partition : partitions) {
final FileSystem newPathFileSystem = partition.getPartitionPath().getFileSystem(this.getConf());
boolean isSkipTrash = MetaStoreUtils.isSkipTrash(tbl.getParameters());
final FileStatus status = newPathFileSystem.getFileStatus(partition.getPartitionPath());
Hive.trashFiles(newPathFileSystem, new FileStatus[]{status}, this.getConf(), isSkipTrash);
}
} catch (IOException io) {
LOG.error("Could not delete partition directory contents after failed partition creation: ", io);
}
LOG.error("Failed addPartitionsToMetastore", e);
throw new HiveException(e);
}
}
private static Path genPartPathFromTable(Table tbl, Map partSpec,
Path tblDataLocationPath) throws MetaException {
Path partPath = new Path(tbl.getDataLocation(), Warehouse.makePartPath(partSpec));
return new Path(tblDataLocationPath.toUri().getScheme(),
tblDataLocationPath.toUri().getAuthority(), partPath.toUri().getPath());
}
/**
* Load Data commands for fullAcid tables write to base_x (if there is overwrite clause) or
* delta_x_x directory - same as any other Acid write. This method modifies the destPath to add
* this path component.
* @param writeId - write id of the operated table from current transaction (in which this operation is running)
* @param stmtId - see {@link DbTxnManager#getStmtIdAndIncrement()}
* @return appropriately modified path
*/
private Path fixFullAcidPathForLoadData(LoadFileType loadFileType, Path destPath, long writeId, int stmtId, Table tbl) throws HiveException {
switch (loadFileType) {
case REPLACE_ALL:
destPath = new Path(destPath, AcidUtils.baseDir(writeId));
break;
case KEEP_EXISTING:
destPath = new Path(destPath, AcidUtils.deltaSubdir(writeId, writeId, stmtId));
break;
case OVERWRITE_EXISTING:
//should not happen here - this is for replication
default:
throw new IllegalArgumentException("Unexpected " + LoadFileType.class.getName() + " " + loadFileType);
}
try {
FileSystem fs = tbl.getDataLocation().getFileSystem(SessionState.getSessionConf());
if(!FileUtils.mkdir(fs, destPath, conf)) {
LOG.warn(destPath + " already exists?!?!");
}
} catch (IOException e) {
throw new HiveException("load: error while creating " + destPath + ";loadFileType=" + loadFileType, e);
}
return destPath;
}
public static void listFilesInsideAcidDirectory(Path acidDir, FileSystem srcFs, List newFiles, PathFilter filter)
throws IOException {
// list out all the files/directory in the path
FileStatus[] acidFiles = null;
if (filter != null) {
acidFiles = srcFs.listStatus(acidDir, filter);
} else {
acidFiles = srcFs.listStatus(acidDir);
}
if (acidFiles == null) {
LOG.debug("No files added by this query in: " + acidDir);
return;
}
LOG.debug("Listing files under " + acidDir);
for (FileStatus acidFile : acidFiles) {
// need to list out only files, ignore folders.
if (!acidFile.isDirectory()) {
newFiles.add(acidFile.getPath());
} else {
listFilesInsideAcidDirectory(acidFile.getPath(), srcFs, newFiles, null);
}
}
}
private List listFilesCreatedByQuery(Path loadPath, long writeId, int stmtId) throws HiveException {
try {
FileSystem srcFs = loadPath.getFileSystem(conf);
PathFilter filter = new AcidUtils.IdFullPathFiler(writeId, stmtId, loadPath);
return HdfsUtils.listLocatedFileStatus(srcFs, loadPath, filter, true);
} catch (FileNotFoundException e) {
LOG.info("directory does not exist: " + loadPath);
} catch (IOException e) {
LOG.error("Error listing files", e);
throw new HiveException(e);
}
return Collections.EMPTY_LIST;
}
private void setStatsPropAndAlterPartition(boolean resetStatistics, Table tbl,
Partition newTPart, TableSnapshot tableSnapshot) throws TException {
EnvironmentContext ec = new EnvironmentContext();
if (!resetStatistics) {
ec.putToProperties(StatsSetupConst.DO_NOT_UPDATE_STATS, StatsSetupConst.TRUE);
}
LOG.debug("Altering existing partition " + newTPart.getSpec());
getSynchronizedMSC().alter_partition(tbl.getCatName(),
tbl.getDbName(), tbl.getTableName(), newTPart.getTPartition(), new EnvironmentContext(),
tableSnapshot == null ? null : tableSnapshot.getValidWriteIdList());
}
private void setStatsPropAndAlterPartitions(boolean resetStatistics, Table tbl,
List partitions,
AcidUtils.TableSnapshot tableSnapshot)
throws TException {
if (partitions.isEmpty() || conf.getBoolVar(ConfVars.HIVESTATSAUTOGATHER)) {
return;
}
EnvironmentContext ec = new EnvironmentContext();
if (!resetStatistics) {
ec.putToProperties(StatsSetupConst.DO_NOT_UPDATE_STATS, StatsSetupConst.TRUE);
}
if (LOG.isDebugEnabled()) {
StringBuilder sb = new StringBuilder("Altering existing partitions ");
partitions.forEach(p -> sb.append(p.getSpec()));
LOG.debug(sb.toString());
}
String validWriteIdList = null;
long writeId = 0L;
if (tableSnapshot != null) {
validWriteIdList = tableSnapshot.getValidWriteIdList();
writeId = tableSnapshot.getWriteId();
}
getSynchronizedMSC().alter_partitions(tbl.getCatName(), tbl.getDbName(), tbl.getTableName(),
partitions.stream().map(Partition::getTPartition).collect(Collectors.toList()),
ec, validWriteIdList, writeId);
}
/**
* Walk through sub-directory tree to construct list bucketing location map.
*
* @param fSta
* @param fSys
* @param skewedColValueLocationMaps
* @param newPartPath
* @param skewedInfo
* @throws IOException
*/
private void walkDirTree(FileStatus fSta, FileSystem fSys,
Map, String> skewedColValueLocationMaps, Path newPartPath, SkewedInfo skewedInfo)
throws IOException {
/* Base Case. It's leaf. */
if (!fSta.isDir()) {
if (Utilities.FILE_OP_LOGGER.isTraceEnabled()) {
Utilities.FILE_OP_LOGGER.trace("Processing LB leaf " + fSta.getPath());
}
/* construct one location map if not exists. */
constructOneLBLocationMap(fSta, skewedColValueLocationMaps, newPartPath, skewedInfo);
return;
}
/* dfs. */
FileStatus[] children = fSys.listStatus(fSta.getPath(), FileUtils.HIDDEN_FILES_PATH_FILTER);
if (children != null) {
if (Utilities.FILE_OP_LOGGER.isTraceEnabled()) {
Utilities.FILE_OP_LOGGER.trace("Processing LB dir " + fSta.getPath());
}
for (FileStatus child : children) {
walkDirTree(child, fSys, skewedColValueLocationMaps, newPartPath, skewedInfo);
}
}
}
/**
* Construct a list bucketing location map
* @param fSta
* @param skewedColValueLocationMaps
* @param newPartPath
* @param skewedInfo
*/
private void constructOneLBLocationMap(FileStatus fSta,
Map, String> skewedColValueLocationMaps,
Path newPartPath, SkewedInfo skewedInfo) {
Path lbdPath = fSta.getPath().getParent();
List skewedValue = new ArrayList();
String lbDirName = FileUtils.unescapePathName(lbdPath.toString());
String partDirName = FileUtils.unescapePathName(newPartPath.toString());
String lbDirSuffix = lbDirName.replace(partDirName, ""); // TODO: should it rather do a prefix?
if (lbDirSuffix.startsWith(Path.SEPARATOR)) {
lbDirSuffix = lbDirSuffix.substring(1);
}
String[] dirNames = lbDirSuffix.split(Path.SEPARATOR);
int keysFound = 0, dirsToTake = 0;
int colCount = skewedInfo.getSkewedColNames().size();
while (dirsToTake < dirNames.length && keysFound < colCount) {
String dirName = dirNames[dirsToTake++];
// Construct skewed-value to location map except default directory.
// why? query logic knows default-dir structure and don't need to get from map
if (dirName.equalsIgnoreCase(ListBucketingPrunerUtils.HIVE_LIST_BUCKETING_DEFAULT_DIR_NAME)) {
++keysFound;
} else {
String[] kv = dirName.split("=");
if (kv.length == 2) {
skewedValue.add(kv[1]);
++keysFound;
} else {
// TODO: we should really probably throw. Keep the existing logic for now.
LOG.warn("Skipping unknown directory " + dirName
+ " when expecting LB keys or default directory (from " + lbDirName + ")");
}
}
}
for (int i = 0; i < (dirNames.length - dirsToTake); ++i) {
lbdPath = lbdPath.getParent();
}
if (Utilities.FILE_OP_LOGGER.isTraceEnabled()) {
Utilities.FILE_OP_LOGGER.trace("Saving LB location " + lbdPath + " based on "
+ colCount + " keys and " + fSta.getPath());
}
if ((skewedValue.size() > 0) && (skewedValue.size() == colCount)
&& !skewedColValueLocationMaps.containsKey(skewedValue)) {
skewedColValueLocationMaps.put(skewedValue, lbdPath.toString());
}
}
/**
* Construct location map from path
*
* @param newPartPath
* @param skewedInfo
* @return
* @throws IOException
* @throws FileNotFoundException
*/
private Map, String> constructListBucketingLocationMap(Path newPartPath,
SkewedInfo skewedInfo) throws IOException, FileNotFoundException {
Map, String> skewedColValueLocationMaps = new HashMap, String>();
FileSystem fSys = newPartPath.getFileSystem(conf);
walkDirTree(fSys.getFileStatus(newPartPath),
fSys, skewedColValueLocationMaps, newPartPath, skewedInfo);
return skewedColValueLocationMaps;
}
/**
* Given a source directory name of the load path, load all dynamically generated partitions
* into the specified table and return a list of strings that represent the dynamic partition
* paths.
* @param tbd table descriptor
* @param numLB number of buckets
* @param isAcid true if this is an ACID operation
* @param writeId writeId, can be 0 unless isAcid == true
* @param stmtId statementId
* @param resetStatistics if true, reset statistics. Do not reset statistics otherwise.
* @param operation ACID operation type
* @param partitionDetailsMap full dynamic partition specification
* @return partition map details (PartitionSpec and Partition)
* @throws HiveException
*/
public Map