| /* |
| * 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.metastore; |
| |
| import com.codahale.metrics.Counter; |
| import com.facebook.fb303.FacebookBase; |
| import com.facebook.fb303.fb_status; |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.Preconditions; |
| import com.google.common.base.Splitter; |
| import com.google.common.base.Supplier; |
| import com.google.common.base.Suppliers; |
| import com.google.common.collect.Lists; |
| import com.google.common.util.concurrent.Striped; |
| import com.google.common.util.concurrent.ThreadFactoryBuilder; |
| import org.apache.commons.collections.CollectionUtils; |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.fs.FileStatus; |
| import org.apache.hadoop.fs.FileSystem; |
| import org.apache.hadoop.fs.Path; |
| import org.apache.hadoop.hive.common.AcidConstants; |
| import org.apache.hadoop.hive.common.AcidMetaDataFile; |
| import org.apache.hadoop.hive.common.AcidMetaDataFile.DataFormat; |
| 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.ValidWriteIdList; |
| import org.apache.hadoop.hive.common.repl.ReplConst; |
| import org.apache.hadoop.hive.metastore.api.*; |
| import org.apache.hadoop.hive.metastore.api.Package; |
| import org.apache.hadoop.hive.metastore.conf.MetastoreConf; |
| import org.apache.hadoop.hive.metastore.conf.MetastoreConf.ConfVars; |
| import org.apache.hadoop.hive.metastore.dataconnector.DataConnectorProviderFactory; |
| import org.apache.hadoop.hive.metastore.events.*; |
| import org.apache.hadoop.hive.metastore.leader.HouseKeepingTasks; |
| import org.apache.hadoop.hive.metastore.leader.LeaderElectionContext; |
| import org.apache.hadoop.hive.metastore.messaging.EventMessage; |
| import org.apache.hadoop.hive.metastore.messaging.EventMessage.EventType; |
| import org.apache.hadoop.hive.metastore.metrics.Metrics; |
| import org.apache.hadoop.hive.metastore.metrics.MetricsConstants; |
| import org.apache.hadoop.hive.metastore.metrics.PerfLogger; |
| import org.apache.hadoop.hive.metastore.model.MTable; |
| import org.apache.hadoop.hive.metastore.partition.spec.PartitionSpecProxy; |
| import org.apache.hadoop.hive.metastore.properties.PropertyException; |
| import org.apache.hadoop.hive.metastore.properties.PropertyManager; |
| import org.apache.hadoop.hive.metastore.properties.PropertyMap; |
| import org.apache.hadoop.hive.metastore.properties.PropertyStore; |
| import org.apache.hadoop.hive.metastore.txn.*; |
| import org.apache.hadoop.hive.metastore.utils.FileUtils; |
| import org.apache.hadoop.hive.metastore.utils.FilterUtils; |
| import org.apache.hadoop.hive.metastore.utils.HdfsUtils; |
| import org.apache.hadoop.hive.metastore.utils.JavaUtils; |
| import org.apache.hadoop.hive.metastore.utils.MetaStoreServerUtils; |
| import org.apache.hadoop.hive.metastore.utils.MetaStoreUtils; |
| import org.apache.hadoop.hive.metastore.utils.MetastoreVersionInfo; |
| import org.apache.hadoop.hive.metastore.utils.SecurityUtils; |
| import org.apache.hadoop.security.UserGroupInformation; |
| import org.apache.hadoop.util.ReflectionUtils; |
| import org.apache.thrift.TException; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import javax.jdo.JDOException; |
| import java.io.IOException; |
| import java.lang.reflect.UndeclaredThrowableException; |
| import java.nio.ByteBuffer; |
| import java.security.PrivilegedExceptionAction; |
| import java.util.*; |
| 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.atomic.AtomicBoolean; |
| import java.util.concurrent.locks.Lock; |
| import java.util.regex.Pattern; |
| |
| import static org.apache.commons.lang3.StringUtils.isBlank; |
| import static org.apache.commons.lang3.StringUtils.join; |
| import static org.apache.hadoop.hive.common.AcidConstants.SOFT_DELETE_PATH_SUFFIX; |
| import static org.apache.hadoop.hive.common.AcidConstants.SOFT_DELETE_TABLE_PATTERN; |
| import static org.apache.hadoop.hive.common.AcidConstants.SOFT_DELETE_TABLE; |
| import static org.apache.hadoop.hive.common.AcidConstants.DELTA_DIGITS; |
| |
| import static org.apache.hadoop.hive.common.repl.ReplConst.REPL_RESUME_STARTED_AFTER_FAILOVER; |
| import static org.apache.hadoop.hive.common.repl.ReplConst.REPL_TARGET_DATABASE_PROPERTY; |
| import static org.apache.hadoop.hive.metastore.HiveMetaStoreClient.RENAME_PARTITION_MAKE_COPY; |
| import static org.apache.hadoop.hive.metastore.HiveMetaStoreClient.TRUNCATE_SKIP_DATA_DELETION; |
| import static org.apache.hadoop.hive.metastore.api.hive_metastoreConstants.TABLE_IS_CTAS; |
| import static org.apache.hadoop.hive.metastore.ExceptionHandler.handleException; |
| import static org.apache.hadoop.hive.metastore.ExceptionHandler.newMetaException; |
| import static org.apache.hadoop.hive.metastore.ExceptionHandler.rethrowException; |
| import static org.apache.hadoop.hive.metastore.ExceptionHandler.throwMetaException; |
| import static org.apache.hadoop.hive.metastore.Warehouse.DEFAULT_CATALOG_NAME; |
| import static org.apache.hadoop.hive.metastore.Warehouse.DEFAULT_DATABASE_COMMENT; |
| import static org.apache.hadoop.hive.metastore.Warehouse.DEFAULT_DATABASE_NAME; |
| import static org.apache.hadoop.hive.metastore.Warehouse.getCatalogQualifiedTableName; |
| import static org.apache.hadoop.hive.metastore.api.hive_metastoreConstants.TABLE_IS_CTLT; |
| import static org.apache.hadoop.hive.metastore.conf.MetastoreConf.ConfVars.HIVE_IN_TEST; |
| import static org.apache.hadoop.hive.metastore.utils.MetaStoreUtils.CAT_NAME; |
| import static org.apache.hadoop.hive.metastore.utils.MetaStoreUtils.DB_NAME; |
| import static org.apache.hadoop.hive.metastore.utils.MetaStoreUtils.getDefaultCatalog; |
| import static org.apache.hadoop.hive.metastore.utils.MetaStoreUtils.parseDbName; |
| import static org.apache.hadoop.hive.metastore.utils.MetaStoreUtils.prependCatalogToDbName; |
| import static org.apache.hadoop.hive.metastore.utils.MetaStoreUtils.prependNotNullCatToDbName; |
| import static org.apache.hadoop.hive.metastore.utils.StringUtils.normalizeIdentifier; |
| |
| /** |
| * Default handler for all Hive Metastore methods. Implements methods defined in hive_metastore.thrift. |
| */ |
| public class HMSHandler extends FacebookBase implements IHMSHandler { |
| public static final Logger LOG = LoggerFactory.getLogger(HMSHandler.class); |
| private final Configuration conf; // stores datastore (jpox) properties, |
| // right now they come from jpox.properties |
| |
| // Flag to control that always threads are initialized only once |
| // instead of multiple times |
| private final static AtomicBoolean alwaysThreadsInitialized = |
| new AtomicBoolean(false); |
| |
| private static String currentUrl; |
| private FileMetadataManager fileMetadataManager; |
| private PartitionExpressionProxy expressionProxy; |
| private StorageSchemaReader storageSchemaReader; |
| private IMetaStoreMetadataTransformer transformer; |
| private static DataConnectorProviderFactory dataconnectorFactory = null; |
| |
| public static final String PARTITION_NUMBER_EXCEED_LIMIT_MSG = |
| "Number of partitions scanned (=%d) on table '%s' exceeds limit (=%d). This is controlled on the metastore server by %s."; |
| |
| // Used for testing to simulate method timeout. |
| @VisibleForTesting |
| static boolean testTimeoutEnabled = false; |
| @VisibleForTesting |
| static long testTimeoutValue = -1; |
| |
| public static final String ADMIN = "admin"; |
| public static final String PUBLIC = "public"; |
| |
| static final String NO_FILTER_STRING = ""; |
| static final int UNLIMITED_MAX_PARTITIONS = -1; |
| |
| private Warehouse wh; // hdfs warehouse |
| private static Striped<Lock> tablelocks; |
| |
| public static RawStore getRawStore() { |
| return HMSHandlerContext.getRawStore().orElse(null); |
| } |
| |
| static void cleanupHandlerContext() { |
| AtomicBoolean cleanedRawStore = new AtomicBoolean(false); |
| try { |
| HMSHandlerContext.getRawStore().ifPresent(rs -> { |
| logAndAudit("Cleaning up thread local RawStore..."); |
| rs.shutdown(); |
| cleanedRawStore.set(true); |
| }); |
| } finally { |
| HMSHandlerContext.getHMSHandler().ifPresent(HMSHandler::notifyMetaListenersOnShutDown); |
| if (cleanedRawStore.get()) { |
| logAndAudit("Done cleaning up thread local RawStore"); |
| } |
| HMSHandlerContext.clear(); |
| } |
| } |
| |
| private static ExecutorService threadPool; |
| |
| static final Logger auditLog = LoggerFactory.getLogger( |
| HiveMetaStore.class.getName() + ".audit"); |
| |
| private static void logAuditEvent(String cmd) { |
| if (cmd == null) { |
| return; |
| } |
| |
| UserGroupInformation ugi; |
| try { |
| ugi = SecurityUtils.getUGI(); |
| } catch (Exception ex) { |
| throw new RuntimeException(ex); |
| } |
| |
| String address = getIPAddress(); |
| if (address == null) { |
| address = "unknown-ip-addr"; |
| } |
| |
| auditLog.info("ugi={} ip={} cmd={} ", ugi.getUserName(), address, cmd); |
| } |
| |
| public static String getIPAddress() { |
| if (HiveMetaStore.useSasl) { |
| if (HiveMetaStore.saslServer != null && HiveMetaStore.saslServer.getRemoteAddress() != null) { |
| return HiveMetaStore.saslServer.getRemoteAddress().getHostAddress(); |
| } |
| } else { |
| // if kerberos is not enabled |
| return getThreadLocalIpAddress(); |
| } |
| return null; |
| } |
| |
| /** |
| * Internal function to notify listeners for meta config change events |
| */ |
| private void notifyMetaListeners(String key, String oldValue, String newValue) throws MetaException { |
| for (MetaStoreEventListener listener : listeners) { |
| listener.onConfigChange(new ConfigChangeEvent(this, key, oldValue, newValue)); |
| } |
| |
| if (transactionalListeners.size() > 0) { |
| // All the fields of this event are final, so no reason to create a new one for each |
| // listener |
| ConfigChangeEvent cce = new ConfigChangeEvent(this, key, oldValue, newValue); |
| for (MetaStoreEventListener transactionalListener : transactionalListeners) { |
| transactionalListener.onConfigChange(cce); |
| } |
| } |
| } |
| |
| /** |
| * Internal function to notify listeners to revert back to old values of keys |
| * that were modified during setMetaConf. This would get called from HiveMetaStore#cleanupHandlerContext |
| */ |
| private void notifyMetaListenersOnShutDown() { |
| try { |
| Map<String, String> modifiedConf = HMSHandlerContext.getModifiedConfig(); |
| Configuration conf = HMSHandlerContext.getConfiguration() |
| .orElseThrow(() -> new MetaException("Unexpected: modifiedConf is non-null but conf is null")); |
| // Notify listeners of the changed value |
| for (Map.Entry<String, String> entry : modifiedConf.entrySet()) { |
| String key = entry.getKey(); |
| // curr value becomes old and vice-versa |
| String currVal = entry.getValue(); |
| String oldVal = conf.get(key); |
| if (!Objects.equals(oldVal, currVal)) { |
| notifyMetaListeners(key, oldVal, currVal); |
| } |
| } |
| logAndAudit("Meta listeners shutdown notification completed."); |
| } catch (MetaException e) { |
| LOG.error("Failed to notify meta listeners on shutdown: ", e); |
| } |
| } |
| |
| static void setThreadLocalIpAddress(String ipAddress) { |
| HMSHandlerContext.setIpAddress(ipAddress); |
| } |
| |
| // This will return null if the metastore is not being accessed from a metastore Thrift server, |
| // or if the TTransport being used to connect is not an instance of TSocket, or if kereberos |
| // is used |
| static String getThreadLocalIpAddress() { |
| return HMSHandlerContext.getIpAddress().orElse(null); |
| } |
| |
| // Make it possible for tests to check that the right type of PartitionExpressionProxy was |
| // instantiated. |
| @VisibleForTesting |
| PartitionExpressionProxy getExpressionProxy() { |
| return expressionProxy; |
| } |
| |
| public HMSHandler(String name, Configuration conf) { |
| super(name); |
| this.conf = conf; |
| isInTest = MetastoreConf.getBoolVar(this.conf, HIVE_IN_TEST); |
| if (threadPool == null) { |
| synchronized (HMSHandler.class) { |
| if (threadPool == null) { |
| int numThreads = MetastoreConf.getIntVar(conf, ConfVars.FS_HANDLER_THREADS_COUNT); |
| threadPool = Executors.newFixedThreadPool(numThreads, |
| new ThreadFactoryBuilder().setDaemon(true).setNameFormat("HMSHandler #%d").build()); |
| int numTableLocks = MetastoreConf.getIntVar(conf, ConfVars.METASTORE_NUM_STRIPED_TABLE_LOCKS); |
| tablelocks = Striped.lock(numTableLocks); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Use {@link #getConf()} instead. |
| * @return Configuration object |
| */ |
| @Deprecated |
| public Configuration getHiveConf() { |
| return conf; |
| } |
| |
| private AlterHandler alterHandler; |
| private List<MetaStorePreEventListener> preListeners; |
| private List<MetaStoreEventListener> listeners; |
| private List<TransactionalMetaStoreEventListener> transactionalListeners; |
| private List<MetaStoreEndFunctionListener> endFunctionListeners; |
| private List<MetaStoreInitListener> initListeners; |
| private MetaStoreFilterHook filterHook; |
| private boolean isServerFilterEnabled = false; |
| |
| private Pattern partitionValidationPattern; |
| private final boolean isInTest; |
| |
| @Override |
| public List<TransactionalMetaStoreEventListener> getTransactionalListeners() { |
| return transactionalListeners; |
| } |
| |
| @Override |
| public List<MetaStoreEventListener> getListeners() { |
| return listeners; |
| } |
| |
| @Override |
| public void init() throws MetaException { |
| Metrics.initialize(conf); |
| initListeners = MetaStoreServerUtils.getMetaStoreListeners( |
| MetaStoreInitListener.class, conf, MetastoreConf.getVar(conf, ConfVars.INIT_HOOKS)); |
| for (MetaStoreInitListener singleInitListener: initListeners) { |
| MetaStoreInitContext context = new MetaStoreInitContext(); |
| singleInitListener.onInit(context); |
| } |
| |
| String alterHandlerName = MetastoreConf.getVar(conf, ConfVars.ALTER_HANDLER); |
| alterHandler = ReflectionUtils.newInstance(JavaUtils.getClass( |
| alterHandlerName, AlterHandler.class), conf); |
| wh = new Warehouse(conf); |
| |
| synchronized (HMSHandler.class) { |
| if (currentUrl == null || !currentUrl.equals(MetaStoreInit.getConnectionURL(conf))) { |
| createDefaultDB(); |
| createDefaultRoles(); |
| addAdminUsers(); |
| currentUrl = MetaStoreInit.getConnectionURL(conf); |
| updateMetrics(); |
| } |
| } |
| |
| preListeners = MetaStoreServerUtils.getMetaStoreListeners(MetaStorePreEventListener.class, |
| conf, MetastoreConf.getVar(conf, ConfVars.PRE_EVENT_LISTENERS)); |
| preListeners.add(0, new TransactionalValidationListener(conf)); |
| listeners = MetaStoreServerUtils.getMetaStoreListeners(MetaStoreEventListener.class, conf, |
| MetastoreConf.getVar(conf, ConfVars.EVENT_LISTENERS)); |
| listeners.add(new SessionPropertiesListener(conf)); |
| transactionalListeners = new ArrayList() {{ |
| add(new AcidEventListener(conf)); |
| }}; |
| transactionalListeners.addAll(MetaStoreServerUtils.getMetaStoreListeners( |
| TransactionalMetaStoreEventListener.class, conf, |
| MetastoreConf.getVar(conf, ConfVars.TRANSACTIONAL_EVENT_LISTENERS))); |
| if (Metrics.getRegistry() != null) { |
| listeners.add(new HMSMetricsListener(conf)); |
| } |
| |
| boolean canCachedStoreCanUseEvent = false; |
| for (MetaStoreEventListener listener : transactionalListeners) { |
| if (listener.doesAddEventsToNotificationLogTable()) { |
| canCachedStoreCanUseEvent = true; |
| break; |
| } |
| } |
| if (conf.getBoolean(ConfVars.METASTORE_CACHE_CAN_USE_EVENT.getVarname(), false) && |
| !canCachedStoreCanUseEvent) { |
| throw new MetaException("CahcedStore can not use events for invalidation as there is no " + |
| " TransactionalMetaStoreEventListener to add events to notification table"); |
| } |
| |
| endFunctionListeners = MetaStoreServerUtils.getMetaStoreListeners( |
| MetaStoreEndFunctionListener.class, conf, MetastoreConf.getVar(conf, ConfVars.END_FUNCTION_LISTENERS)); |
| |
| String partitionValidationRegex = |
| MetastoreConf.getVar(conf, ConfVars.PARTITION_NAME_WHITELIST_PATTERN); |
| if (partitionValidationRegex != null && !partitionValidationRegex.isEmpty()) { |
| partitionValidationPattern = Pattern.compile(partitionValidationRegex); |
| } |
| |
| // We only initialize once the tasks that need to be run periodically. For remote metastore |
| // these threads are started along with the other housekeeping threads only in the leader |
| // HMS. |
| if (!HiveMetaStore.isMetaStoreRemote()) { |
| startAlwaysTaskThreads(conf, this); |
| } |
| expressionProxy = PartFilterExprUtil.createExpressionProxy(conf); |
| fileMetadataManager = new FileMetadataManager(this.getMS(), conf); |
| |
| isServerFilterEnabled = getIfServerFilterenabled(); |
| filterHook = isServerFilterEnabled ? loadFilterHooks() : null; |
| |
| String className = MetastoreConf.getVar(conf, MetastoreConf.ConfVars.METASTORE_METADATA_TRANSFORMER_CLASS); |
| if (className != null && !className.trim().isEmpty()) { |
| try { |
| transformer = JavaUtils.newInstance(JavaUtils.getClass(className.trim(), IMetaStoreMetadataTransformer.class), |
| new Class[] {IHMSHandler.class}, new Object[] {this}); |
| } catch (Exception e) { |
| LOG.error("Unable to create instance of class " + className, e); |
| throw new IllegalArgumentException(e); |
| } |
| } |
| dataconnectorFactory = DataConnectorProviderFactory.getInstance(this); |
| } |
| |
| static void startAlwaysTaskThreads(Configuration conf, IHMSHandler handler) throws MetaException { |
| if (alwaysThreadsInitialized.compareAndSet(false, true)) { |
| try { |
| LeaderElectionContext context = new LeaderElectionContext.ContextBuilder(conf) |
| .setTType(LeaderElectionContext.TTYPE.ALWAYS_TASKS) |
| .addListener(new HouseKeepingTasks(conf, false)) |
| .setHMSHandler(handler).build(); |
| context.start(); |
| } catch (Exception e) { |
| throw newMetaException(e); |
| } |
| } |
| } |
| |
| /** |
| * |
| * Filter is actually enabled only when the configured filter hook is configured, not default, and |
| * enabled in configuration |
| * @return |
| */ |
| private boolean getIfServerFilterenabled() throws MetaException{ |
| boolean isEnabled = MetastoreConf.getBoolVar(conf, ConfVars.METASTORE_SERVER_FILTER_ENABLED); |
| |
| if (!isEnabled) { |
| LOG.info("HMS server filtering is disabled by configuration"); |
| return false; |
| } |
| |
| String filterHookClassName = MetastoreConf.getVar(conf, ConfVars.FILTER_HOOK); |
| |
| if (isBlank(filterHookClassName)) { |
| throw new MetaException("HMS server filtering is enabled but no filter hook is configured"); |
| } |
| |
| if (filterHookClassName.trim().equalsIgnoreCase(DefaultMetaStoreFilterHookImpl.class.getName())) { |
| throw new MetaException("HMS server filtering is enabled but the filter hook is DefaultMetaStoreFilterHookImpl, which does no filtering"); |
| } |
| |
| LOG.info("HMS server filtering is enabled. The filter class is " + filterHookClassName); |
| return true; |
| } |
| |
| private MetaStoreFilterHook loadFilterHooks() throws IllegalStateException { |
| String errorMsg = "Unable to load filter hook at HMS server. "; |
| |
| String filterHookClassName = MetastoreConf.getVar(conf, ConfVars.FILTER_HOOK); |
| Preconditions.checkState(!isBlank(filterHookClassName)); |
| |
| try { |
| return (MetaStoreFilterHook)Class.forName( |
| filterHookClassName.trim(), true, JavaUtils.getClassLoader()).getConstructor( |
| Configuration.class).newInstance(conf); |
| } catch (Exception e) { |
| LOG.error(errorMsg, e); |
| throw new IllegalStateException(errorMsg + e.getMessage(), e); |
| } |
| } |
| |
| /** |
| * Check if user can access the table associated with the partition. If not, then throw exception |
| * so user cannot access partitions associated with this table |
| * We are not calling Pre event listener for authorization because it requires getting the |
| * table object from DB, more overhead. Instead ,we call filter hook to filter out table if user |
| * has no access. Filter hook only requires table name, not table object. That saves DB access for |
| * table object, and still achieve the same purpose: checking if user can access the specified |
| * table |
| * |
| * @param catName catalog name of the table |
| * @param dbName database name of the table |
| * @param tblName table name |
| * @throws NoSuchObjectException |
| * @throws MetaException |
| */ |
| private void authorizeTableForPartitionMetadata( |
| final String catName, final String dbName, final String tblName) |
| throws NoSuchObjectException, MetaException { |
| |
| FilterUtils.checkDbAndTableFilters( |
| isServerFilterEnabled, filterHook, catName, dbName, tblName); |
| } |
| |
| @Override |
| public void setConf(Configuration conf) { |
| HMSHandlerContext.setConfiguration(conf); |
| // reload if DS related configuration is changed |
| HMSHandlerContext.getRawStore().ifPresent(ms -> ms.setConf(conf)); |
| } |
| |
| @Override |
| public Configuration getConf() { |
| Configuration conf = HMSHandlerContext.getConfiguration() |
| .orElseGet(() -> { |
| Configuration configuration = new Configuration(this.conf); |
| HMSHandlerContext.setConfiguration(configuration); |
| return configuration; |
| }); |
| return conf; |
| } |
| |
| @Override |
| public Warehouse getWh() { |
| return wh; |
| } |
| |
| @Override |
| public void setMetaConf(String key, String value) throws MetaException { |
| ConfVars confVar = MetastoreConf.getMetaConf(key); |
| if (confVar == null) { |
| throw new MetaException("Invalid configuration key " + key); |
| } |
| try { |
| confVar.validate(value); |
| } catch (IllegalArgumentException e) { |
| throw new MetaException("Invalid configuration value " + value + " for key " + key + |
| " by " + e.getMessage()); |
| } |
| Configuration configuration = getConf(); |
| String oldValue = MetastoreConf.get(configuration, key); |
| // Save prev val of the key on threadLocal |
| Map<String, String> modifiedConf = HMSHandlerContext.getModifiedConfig(); |
| if (!modifiedConf.containsKey(key)) { |
| modifiedConf.put(key, oldValue); |
| } |
| // Set invoking HMSHandler on threadLocal, this will be used later to notify |
| // metaListeners in HiveMetaStore#cleanupHandlerContext |
| if (!HMSHandlerContext.getHMSHandler().isPresent()) { |
| HMSHandlerContext.setHMSHandler(this); |
| } |
| configuration.set(key, value); |
| notifyMetaListeners(key, oldValue, value); |
| |
| if (ConfVars.TRY_DIRECT_SQL == confVar) { |
| HMSHandler.LOG.info("Direct SQL optimization = {}", value); |
| } |
| } |
| |
| @Override |
| public String getMetaConf(String key) throws MetaException { |
| ConfVars confVar = MetastoreConf.getMetaConf(key); |
| if (confVar == null) { |
| throw new MetaException("Invalid configuration key " + key); |
| } |
| return getConf().get(key, confVar.getDefaultVal().toString()); |
| } |
| |
| /** |
| * Get a cached RawStore. |
| * |
| * @return the cached RawStore |
| * @throws MetaException |
| */ |
| @Override |
| public RawStore getMS() throws MetaException { |
| Configuration conf = getConf(); |
| return getMSForConf(conf); |
| } |
| |
| public static synchronized RawStore getMSForConf(Configuration conf) throws MetaException { |
| RawStore ms = getRawStore(); |
| if (ms == null) { |
| ms = newRawStoreForConf(conf); |
| try { |
| ms.verifySchema(); |
| } catch (MetaException e) { |
| ms.shutdown(); |
| throw e; |
| } |
| HMSHandlerContext.setRawStore(ms); |
| LOG.info("Created RawStore: {}", ms); |
| } |
| return ms; |
| } |
| |
| @Override |
| public TxnStore getTxnHandler() { |
| return HMSHandlerContext.getTxnStore(conf); |
| } |
| |
| static RawStore newRawStoreForConf(Configuration conf) throws MetaException { |
| Configuration newConf = new Configuration(conf); |
| String rawStoreClassName = MetastoreConf.getVar(newConf, ConfVars.RAW_STORE_IMPL); |
| LOG.info("Opening raw store with implementation class: {}", rawStoreClassName); |
| return RawStoreProxy.getProxy(newConf, conf, rawStoreClassName); |
| } |
| |
| @VisibleForTesting |
| public static void createDefaultCatalog(RawStore ms, Warehouse wh) throws MetaException, |
| InvalidOperationException { |
| try { |
| Catalog defaultCat = ms.getCatalog(DEFAULT_CATALOG_NAME); |
| // Null check because in some test cases we get a null from ms.getCatalog. |
| if (defaultCat !=null && defaultCat.getLocationUri().equals("TBD")) { |
| // One time update issue. When the new 'hive' catalog is created in an upgrade the |
| // script does not know the location of the warehouse. So we need to update it. |
| LOG.info("Setting location of default catalog, as it hasn't been done after upgrade"); |
| defaultCat.setLocationUri(wh.getWhRoot().toString()); |
| ms.alterCatalog(defaultCat.getName(), defaultCat); |
| } |
| |
| } catch (NoSuchObjectException e) { |
| Catalog cat = new Catalog(DEFAULT_CATALOG_NAME, wh.getWhRoot().toString()); |
| long time = System.currentTimeMillis() / 1000; |
| cat.setCreateTime((int) time); |
| cat.setDescription(Warehouse.DEFAULT_CATALOG_COMMENT); |
| ms.createCatalog(cat); |
| } |
| } |
| |
| private void createDefaultDB_core(RawStore ms) throws MetaException, InvalidObjectException { |
| try { |
| ms.getDatabase(DEFAULT_CATALOG_NAME, DEFAULT_DATABASE_NAME); |
| } catch (NoSuchObjectException e) { |
| LOG.info("Started creating a default database with name: "+DEFAULT_DATABASE_NAME); |
| Database db = new Database(DEFAULT_DATABASE_NAME, DEFAULT_DATABASE_COMMENT, |
| wh.getDefaultDatabasePath(DEFAULT_DATABASE_NAME, true).toString(), null); |
| db.setOwnerName(PUBLIC); |
| db.setOwnerType(PrincipalType.ROLE); |
| db.setCatalogName(DEFAULT_CATALOG_NAME); |
| long time = System.currentTimeMillis() / 1000; |
| db.setCreateTime((int) time); |
| db.setType(DatabaseType.NATIVE); |
| ms.createDatabase(db); |
| LOG.info("Successfully created a default database with name: "+DEFAULT_DATABASE_NAME); |
| } |
| } |
| |
| /** |
| * create default database if it doesn't exist. |
| * |
| * This is a potential contention when HiveServer2 using embedded metastore and Metastore |
| * Server try to concurrently invoke createDefaultDB. If one failed, JDOException was caught |
| * for one more time try, if failed again, simply ignored by warning, which meant another |
| * succeeds. |
| * |
| * @throws MetaException |
| */ |
| private void createDefaultDB() throws MetaException { |
| try { |
| RawStore ms = getMS(); |
| createDefaultCatalog(ms, wh); |
| createDefaultDB_core(ms); |
| } catch (JDOException e) { |
| LOG.warn("Retrying creating default database after error: " + e.getMessage(), e); |
| try { |
| RawStore ms = getMS(); |
| createDefaultCatalog(ms, wh); |
| createDefaultDB_core(ms); |
| } catch (InvalidObjectException | InvalidOperationException e1) { |
| throw new MetaException(e1.getMessage()); |
| } |
| } catch (InvalidObjectException|InvalidOperationException e) { |
| throw new MetaException(e.getMessage()); |
| } |
| } |
| |
| /** |
| * create default roles if they don't exist. |
| * |
| * This is a potential contention when HiveServer2 using embedded metastore and Metastore |
| * Server try to concurrently invoke createDefaultRoles. If one failed, JDOException was caught |
| * for one more time try, if failed again, simply ignored by warning, which meant another |
| * succeeds. |
| * |
| * @throws MetaException |
| */ |
| private void createDefaultRoles() throws MetaException { |
| try { |
| createDefaultRoles_core(); |
| } catch (JDOException e) { |
| LOG.warn("Retrying creating default roles after error: " + e.getMessage(), e); |
| createDefaultRoles_core(); |
| } |
| } |
| |
| private void createDefaultRoles_core() throws MetaException { |
| |
| RawStore ms = getMS(); |
| try { |
| ms.addRole(ADMIN, ADMIN); |
| } catch (InvalidObjectException e) { |
| LOG.debug(ADMIN +" role already exists",e); |
| } catch (NoSuchObjectException e) { |
| // This should never be thrown. |
| LOG.warn("Unexpected exception while adding " +ADMIN+" roles" , e); |
| } |
| LOG.info("Added "+ ADMIN+ " role in metastore"); |
| try { |
| ms.addRole(PUBLIC, PUBLIC); |
| } catch (InvalidObjectException e) { |
| LOG.debug(PUBLIC + " role already exists",e); |
| } catch (NoSuchObjectException e) { |
| // This should never be thrown. |
| LOG.warn("Unexpected exception while adding "+PUBLIC +" roles" , e); |
| } |
| LOG.info("Added "+PUBLIC+ " role in metastore"); |
| // now grant all privs to admin |
| PrivilegeBag privs = new PrivilegeBag(); |
| privs.addToPrivileges(new HiveObjectPrivilege( new HiveObjectRef(HiveObjectType.GLOBAL, null, |
| null, null, null), ADMIN, PrincipalType.ROLE, new PrivilegeGrantInfo("All", 0, ADMIN, |
| PrincipalType.ROLE, true), "SQL")); |
| try { |
| ms.grantPrivileges(privs); |
| } catch (InvalidObjectException e) { |
| // Surprisingly these privs are already granted. |
| LOG.debug("Failed while granting global privs to admin", e); |
| } catch (NoSuchObjectException e) { |
| // Unlikely to be thrown. |
| LOG.warn("Failed while granting global privs to admin", e); |
| } |
| } |
| |
| /** |
| * add admin users if they don't exist. |
| * |
| * This is a potential contention when HiveServer2 using embedded metastore and Metastore |
| * Server try to concurrently invoke addAdminUsers. If one failed, JDOException was caught for |
| * one more time try, if failed again, simply ignored by warning, which meant another succeeds. |
| * |
| * @throws MetaException |
| */ |
| private void addAdminUsers() throws MetaException { |
| try { |
| addAdminUsers_core(); |
| } catch (JDOException e) { |
| LOG.warn("Retrying adding admin users after error: " + e.getMessage(), e); |
| addAdminUsers_core(); |
| } |
| } |
| |
| private void addAdminUsers_core() throws MetaException { |
| |
| // now add pre-configured users to admin role |
| String userStr = MetastoreConf.getVar(conf,ConfVars.USERS_IN_ADMIN_ROLE,"").trim(); |
| if (userStr.isEmpty()) { |
| LOG.info("No user is added in admin role, since config is empty"); |
| return; |
| } |
| // Since user names need to be valid unix user names, per IEEE Std 1003.1-2001 they cannot |
| // contain comma, so we can safely split above string on comma. |
| |
| Iterator<String> users = Splitter.on(",").trimResults().omitEmptyStrings().split(userStr).iterator(); |
| if (!users.hasNext()) { |
| LOG.info("No user is added in admin role, since config value "+ userStr + |
| " is in incorrect format. We accept comma separated list of users."); |
| return; |
| } |
| Role adminRole; |
| RawStore ms = getMS(); |
| try { |
| adminRole = ms.getRole(ADMIN); |
| } catch (NoSuchObjectException e) { |
| LOG.error("Failed to retrieve just added admin role",e); |
| return; |
| } |
| while (users.hasNext()) { |
| String userName = users.next(); |
| try { |
| ms.grantRole(adminRole, userName, PrincipalType.USER, ADMIN, PrincipalType.ROLE, true); |
| LOG.info("Added " + userName + " to admin role"); |
| } catch (NoSuchObjectException e) { |
| LOG.error("Failed to add "+ userName + " in admin role",e); |
| } catch (InvalidObjectException e) { |
| LOG.debug(userName + " already in admin role", e); |
| } |
| } |
| } |
| |
| private static void logAndAudit(final String m) { |
| LOG.debug(m); |
| logAuditEvent(m); |
| } |
| |
| private String startFunction(String function, String extraLogInfo) { |
| incrementCounter(function); |
| logAndAudit((getThreadLocalIpAddress() == null ? "" : "source:" + getThreadLocalIpAddress() + " ") + |
| function + extraLogInfo); |
| com.codahale.metrics.Timer timer = |
| Metrics.getOrCreateTimer(MetricsConstants.API_PREFIX + function); |
| if (timer != null) { |
| // Timer will be null we aren't using the metrics |
| HMSHandlerContext.getTimerContexts().put(function, timer.time()); |
| } |
| Counter counter = Metrics.getOrCreateCounter(MetricsConstants.ACTIVE_CALLS + function); |
| if (counter != null) { |
| counter.inc(); |
| } |
| return function; |
| } |
| |
| private String startFunction(String function) { |
| return startFunction(function, ""); |
| } |
| |
| private void startTableFunction(String function, String catName, String db, String tbl) { |
| startFunction(function, " : tbl=" + |
| TableName.getQualified(catName, db, tbl)); |
| } |
| |
| private void startMultiTableFunction(String function, String db, List<String> tbls) { |
| String tableNames = join(tbls, ","); |
| startFunction(function, " : db=" + db + " tbls=" + tableNames); |
| } |
| |
| private void startPartitionFunction(String function, String cat, String db, String tbl, |
| List<String> partVals) { |
| startFunction(function, " : tbl=" + |
| TableName.getQualified(cat, db, tbl) + "[" + join(partVals, ",") + "]"); |
| } |
| |
| private void startPartitionFunction(String function, String catName, String db, String tbl, |
| Map<String, String> partName) { |
| startFunction(function, " : tbl=" + |
| TableName.getQualified(catName, db, tbl) + "partition=" + partName); |
| } |
| |
| private void endFunction(String function, boolean successful, Exception e) { |
| endFunction(function, successful, e, null); |
| } |
| private void endFunction(String function, boolean successful, Exception e, |
| String inputTableName) { |
| endFunction(function, new MetaStoreEndFunctionContext(successful, e, inputTableName)); |
| } |
| |
| private void endFunction(String function, MetaStoreEndFunctionContext context) { |
| com.codahale.metrics.Timer.Context timerContext = HMSHandlerContext.getTimerContexts().remove(function); |
| if (timerContext != null) { |
| long timeTaken = timerContext.stop(); |
| LOG.debug((getThreadLocalIpAddress() == null ? "" : "source:" + getThreadLocalIpAddress() + " ") + |
| function + "time taken(ns): " + timeTaken); |
| } |
| Counter counter = Metrics.getOrCreateCounter(MetricsConstants.ACTIVE_CALLS + function); |
| if (counter != null) { |
| counter.dec(); |
| } |
| |
| for (MetaStoreEndFunctionListener listener : endFunctionListeners) { |
| listener.onEndFunction(function, context); |
| } |
| } |
| |
| @Override |
| public fb_status getStatus() { |
| return fb_status.ALIVE; |
| } |
| |
| @Override |
| public void shutdown() { |
| cleanupHandlerContext(); |
| PerfLogger.getPerfLogger(false).cleanupPerfLogMetrics(); |
| } |
| |
| @Override |
| public AbstractMap<String, Long> getCounters() { |
| AbstractMap<String, Long> counters = super.getCounters(); |
| |
| // Allow endFunctionListeners to add any counters they have collected |
| if (endFunctionListeners != null) { |
| for (MetaStoreEndFunctionListener listener : endFunctionListeners) { |
| listener.exportCounters(counters); |
| } |
| } |
| |
| return counters; |
| } |
| |
| @Override |
| public void create_catalog(CreateCatalogRequest rqst) |
| throws AlreadyExistsException, InvalidObjectException, MetaException { |
| Catalog catalog = rqst.getCatalog(); |
| startFunction("create_catalog", ": " + catalog.toString()); |
| boolean success = false; |
| Exception ex = null; |
| try { |
| try { |
| getMS().getCatalog(catalog.getName()); |
| throw new AlreadyExistsException("Catalog " + catalog.getName() + " already exists"); |
| } catch (NoSuchObjectException e) { |
| // expected |
| } |
| |
| if (!MetaStoreUtils.validateName(catalog.getName(), null)) { |
| throw new InvalidObjectException(catalog.getName() + " is not a valid catalog name"); |
| } |
| |
| if (catalog.getLocationUri() == null) { |
| throw new InvalidObjectException("You must specify a path for the catalog"); |
| } |
| |
| RawStore ms = getMS(); |
| Path catPath = new Path(catalog.getLocationUri()); |
| boolean madeDir = false; |
| Map<String, String> transactionalListenersResponses = Collections.emptyMap(); |
| try { |
| firePreEvent(new PreCreateCatalogEvent(this, catalog)); |
| if (!wh.isDir(catPath)) { |
| if (!wh.mkdirs(catPath)) { |
| throw new MetaException("Unable to create catalog path " + catPath + |
| ", failed to create catalog " + catalog.getName()); |
| } |
| madeDir = true; |
| } |
| // set the create time of catalog |
| long time = System.currentTimeMillis() / 1000; |
| catalog.setCreateTime((int) time); |
| ms.openTransaction(); |
| ms.createCatalog(catalog); |
| |
| // Create a default database inside the catalog |
| Database db = new Database(DEFAULT_DATABASE_NAME, |
| "Default database for catalog " + catalog.getName(), catalog.getLocationUri(), |
| Collections.emptyMap()); |
| db.setCatalogName(catalog.getName()); |
| create_database_core(ms, db); |
| |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenersResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.CREATE_CATALOG, |
| new CreateCatalogEvent(true, this, catalog)); |
| } |
| |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| if (madeDir) { |
| wh.deleteDir(catPath, true, false, false); |
| } |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.CREATE_CATALOG, |
| new CreateCatalogEvent(success, this, catalog), |
| null, |
| transactionalListenersResponses, ms); |
| } |
| } |
| success = true; |
| } catch (AlreadyExistsException|InvalidObjectException|MetaException e) { |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("create_catalog", success, ex); |
| } |
| } |
| |
| @Override |
| public void alter_catalog(AlterCatalogRequest rqst) throws TException { |
| startFunction("alter_catalog " + rqst.getName()); |
| boolean success = false; |
| Exception ex = null; |
| RawStore ms = getMS(); |
| Map<String, String> transactionalListenersResponses = Collections.emptyMap(); |
| GetCatalogResponse oldCat = null; |
| |
| try { |
| oldCat = get_catalog(new GetCatalogRequest(rqst.getName())); |
| // Above should have thrown NoSuchObjectException if there is no such catalog |
| assert oldCat != null && oldCat.getCatalog() != null; |
| firePreEvent(new PreAlterCatalogEvent(oldCat.getCatalog(), rqst.getNewCat(), this)); |
| |
| ms.openTransaction(); |
| ms.alterCatalog(rqst.getName(), rqst.getNewCat()); |
| |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenersResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.ALTER_CATALOG, |
| new AlterCatalogEvent(oldCat.getCatalog(), rqst.getNewCat(), true, this)); |
| } |
| |
| success = ms.commitTransaction(); |
| } catch (MetaException|NoSuchObjectException e) { |
| ex = e; |
| throw e; |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| |
| if ((null != oldCat) && (!listeners.isEmpty())) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ALTER_CATALOG, |
| new AlterCatalogEvent(oldCat.getCatalog(), rqst.getNewCat(), success, this), |
| null, transactionalListenersResponses, ms); |
| } |
| endFunction("alter_catalog", success, ex); |
| } |
| |
| } |
| |
| @Override |
| public GetCatalogResponse get_catalog(GetCatalogRequest rqst) |
| throws NoSuchObjectException, TException { |
| String catName = rqst.getName(); |
| startFunction("get_catalog", ": " + catName); |
| Catalog cat = null; |
| Exception ex = null; |
| try { |
| cat = getMS().getCatalog(catName); |
| firePreEvent(new PreReadCatalogEvent(this, cat)); |
| return new GetCatalogResponse(cat); |
| } catch (MetaException|NoSuchObjectException e) { |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_catalog", cat != null, ex); |
| } |
| } |
| |
| @Override |
| public GetCatalogsResponse get_catalogs() throws MetaException { |
| startFunction("get_catalogs"); |
| |
| List<String> ret = null; |
| Exception ex = null; |
| try { |
| ret = getMS().getCatalogs(); |
| } catch (Exception e) { |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_catalog", ret != null, ex); |
| } |
| return new GetCatalogsResponse(ret == null ? Collections.emptyList() : ret); |
| |
| } |
| |
| @Override |
| public void drop_catalog(DropCatalogRequest rqst) |
| throws NoSuchObjectException, InvalidOperationException, MetaException { |
| String catName = rqst.getName(); |
| startFunction("drop_catalog", ": " + catName); |
| if (DEFAULT_CATALOG_NAME.equalsIgnoreCase(catName)) { |
| endFunction("drop_catalog", false, null); |
| throw new MetaException("Can not drop " + DEFAULT_CATALOG_NAME + " catalog"); |
| } |
| |
| boolean success = false; |
| Exception ex = null; |
| try { |
| dropCatalogCore(catName); |
| success = true; |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e) |
| .throwIfInstance(NoSuchObjectException.class, InvalidOperationException.class, MetaException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("drop_catalog", success, ex); |
| } |
| |
| } |
| |
| private void dropCatalogCore(String catName) |
| throws MetaException, NoSuchObjectException, InvalidOperationException { |
| boolean success = false; |
| Catalog cat = null; |
| Map<String, String> transactionalListenerResponses = Collections.emptyMap(); |
| RawStore ms = getMS(); |
| try { |
| ms.openTransaction(); |
| cat = ms.getCatalog(catName); |
| |
| firePreEvent(new PreDropCatalogEvent(this, cat)); |
| |
| List<String> allDbs = get_databases(prependNotNullCatToDbName(catName, null)); |
| if (allDbs != null && !allDbs.isEmpty()) { |
| // It might just be the default, in which case we can drop that one if it's empty |
| if (allDbs.size() == 1 && allDbs.get(0).equals(DEFAULT_DATABASE_NAME)) { |
| try { |
| DropDatabaseRequest req = new DropDatabaseRequest(); |
| req.setName(DEFAULT_DATABASE_NAME); |
| req.setCatalogName(catName); |
| req.setDeleteData(true); |
| req.setCascade(false); |
| |
| drop_database_core(ms, req); |
| } catch (InvalidOperationException e) { |
| // This means there are tables of something in the database |
| throw new InvalidOperationException("There are still objects in the default " + |
| "database for catalog " + catName); |
| } catch (InvalidObjectException|IOException|InvalidInputException e) { |
| MetaException me = new MetaException("Error attempt to drop default database for " + |
| "catalog " + catName); |
| me.initCause(e); |
| throw me; |
| } |
| } else { |
| throw new InvalidOperationException("There are non-default databases in the catalog " + |
| catName + " so it cannot be dropped."); |
| } |
| } |
| |
| ms.dropCatalog(catName) ; |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenerResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.DROP_CATALOG, |
| new DropCatalogEvent(true, this, cat)); |
| } |
| |
| success = ms.commitTransaction(); |
| } finally { |
| if (success) { |
| wh.deleteDir(wh.getDnsPath(new Path(cat.getLocationUri())), false, false, false); |
| } else { |
| ms.rollbackTransaction(); |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.DROP_CATALOG, |
| new DropCatalogEvent(success, this, cat), |
| null, |
| transactionalListenerResponses, ms); |
| } |
| } |
| } |
| |
| static boolean isDbReplicationTarget(Database db) { |
| String dbCkptStatus = (db.getParameters() == null) ? null : db.getParameters().get(ReplConst.REPL_TARGET_DB_PROPERTY); |
| return dbCkptStatus != null && !dbCkptStatus.trim().isEmpty(); |
| } |
| |
| // Assumes that the catalog has already been set. |
| private void create_database_core(RawStore ms, final Database db) |
| throws AlreadyExistsException, InvalidObjectException, MetaException { |
| if (!MetaStoreUtils.validateName(db.getName(), conf)) { |
| throw new InvalidObjectException(db.getName() + " is not a valid database name"); |
| } |
| |
| Catalog cat = null; |
| try { |
| cat = getMS().getCatalog(db.getCatalogName()); |
| } catch (NoSuchObjectException e) { |
| LOG.error("No such catalog " + db.getCatalogName()); |
| throw new InvalidObjectException("No such catalog " + db.getCatalogName()); |
| } |
| boolean skipAuthorization = false; |
| String passedInURI = db.getLocationUri(); |
| String passedInManagedURI = db.getManagedLocationUri(); |
| if (passedInURI == null && passedInManagedURI == null) { |
| skipAuthorization = true; |
| } |
| final Path defaultDbExtPath = wh.getDefaultDatabasePath(db.getName(), true); |
| final Path defaultDbMgdPath = wh.getDefaultDatabasePath(db.getName(), false); |
| final Path dbExtPath = (passedInURI != null) ? wh.getDnsPath(new Path(passedInURI)) : wh.determineDatabasePath(cat, db); |
| final Path dbMgdPath = (passedInManagedURI != null) ? wh.getDnsPath(new Path(passedInManagedURI)) : null; |
| |
| if ((defaultDbExtPath.equals(dbExtPath) && defaultDbMgdPath.equals(dbMgdPath)) && |
| ((dbMgdPath == null) || dbMgdPath.equals(defaultDbMgdPath))) { |
| skipAuthorization = true; |
| } |
| |
| if ( skipAuthorization ) { |
| //null out to skip authorizer URI check |
| db.setLocationUri(null); |
| db.setManagedLocationUri(null); |
| }else{ |
| db.setLocationUri(dbExtPath.toString()); |
| if (dbMgdPath != null) { |
| db.setManagedLocationUri(dbMgdPath.toString()); |
| } |
| } |
| if (db.getOwnerName() == null){ |
| try { |
| db.setOwnerName(SecurityUtils.getUGI().getShortUserName()); |
| }catch (Exception e){ |
| LOG.warn("Failed to get owner name for create database operation.", e); |
| } |
| } |
| long time = System.currentTimeMillis()/1000; |
| db.setCreateTime((int) time); |
| boolean success = false; |
| boolean madeManagedDir = false; |
| boolean madeExternalDir = false; |
| boolean isReplicated = isDbReplicationTarget(db); |
| Map<String, String> transactionalListenersResponses = Collections.emptyMap(); |
| try { |
| firePreEvent(new PreCreateDatabaseEvent(db, this)); |
| //reinstate location uri for metastore db. |
| if (skipAuthorization == true){ |
| db.setLocationUri(dbExtPath.toString()); |
| if (dbMgdPath != null) { |
| db.setManagedLocationUri(dbMgdPath.toString()); |
| } |
| } |
| if (db.getCatalogName() != null && !db.getCatalogName(). |
| equals(Warehouse.DEFAULT_CATALOG_NAME)) { |
| if (!wh.isDir(dbExtPath)) { |
| LOG.debug("Creating database path " + dbExtPath); |
| if (!wh.mkdirs(dbExtPath)) { |
| throw new MetaException("Unable to create database path " + dbExtPath + |
| ", failed to create database " + db.getName()); |
| } |
| madeExternalDir = true; |
| } |
| } else { |
| if (dbMgdPath != null) { |
| try { |
| // Since this may be done as random user (if doAs=true) he may not have access |
| // to the managed directory. We run this as an admin user |
| madeManagedDir = UserGroupInformation.getLoginUser().doAs(new PrivilegedExceptionAction<Boolean>() { |
| @Override public Boolean run() throws MetaException { |
| if (!wh.isDir(dbMgdPath)) { |
| LOG.info("Creating database path in managed directory " + dbMgdPath); |
| if (!wh.mkdirs(dbMgdPath)) { |
| throw new MetaException("Unable to create database managed path " + dbMgdPath + ", failed to create database " + db.getName()); |
| } |
| return true; |
| } |
| return false; |
| } |
| }); |
| if (madeManagedDir) { |
| LOG.info("Created database path in managed directory " + dbMgdPath); |
| } else if (!isInTest || !isDbReplicationTarget(db)) { // Hive replication tests doesn't drop the db after each test |
| throw new MetaException( |
| "Unable to create database managed directory " + dbMgdPath + ", failed to create database " + db.getName()); |
| } |
| } catch (IOException | InterruptedException e) { |
| throw new MetaException( |
| "Unable to create database managed directory " + dbMgdPath + ", failed to create database " + db.getName() + ":" + e.getMessage()); |
| } |
| } |
| if (dbExtPath != null) { |
| try { |
| madeExternalDir = UserGroupInformation.getCurrentUser().doAs(new PrivilegedExceptionAction<Boolean>() { |
| @Override public Boolean run() throws MetaException { |
| if (!wh.isDir(dbExtPath)) { |
| LOG.info("Creating database path in external directory " + dbExtPath); |
| return wh.mkdirs(dbExtPath); |
| } |
| return false; |
| } |
| }); |
| if (madeExternalDir) { |
| LOG.info("Created database path in external directory " + dbExtPath); |
| } else { |
| LOG.warn("Failed to create external path " + dbExtPath + " for database " + db.getName() + ". This may result in access not being allowed if the " |
| + "StorageBasedAuthorizationProvider is enabled"); |
| } |
| } catch (IOException | InterruptedException | UndeclaredThrowableException e) { |
| throw new MetaException("Failed to create external path " + dbExtPath + " for database " + db.getName() + ". This may result in access not being allowed if the " |
| + "StorageBasedAuthorizationProvider is enabled: " + e.getMessage()); |
| } |
| } else { |
| LOG.info("Database external path won't be created since the external warehouse directory is not defined"); |
| } |
| } |
| |
| ms.openTransaction(); |
| ms.createDatabase(db); |
| |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenersResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.CREATE_DATABASE, |
| new CreateDatabaseEvent(db, true, this, isReplicated)); |
| } |
| |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| |
| if (db.getCatalogName() != null && !db.getCatalogName(). |
| equals(Warehouse.DEFAULT_CATALOG_NAME)) { |
| if (madeManagedDir && dbMgdPath != null) { |
| wh.deleteDir(dbMgdPath, true, db); |
| } |
| } else { |
| if (madeManagedDir && dbMgdPath != null) { |
| try { |
| UserGroupInformation.getLoginUser().doAs(new PrivilegedExceptionAction<Void>() { |
| @Override public Void run() throws Exception { |
| wh.deleteDir(dbMgdPath, true, db); |
| return null; |
| } |
| }); |
| } catch (IOException | InterruptedException e) { |
| LOG.error( |
| "Couldn't delete managed directory " + dbMgdPath + " after " + "it was created for database " + db.getName() + " " + e.getMessage()); |
| } |
| } |
| |
| if (madeExternalDir && dbExtPath != null) { |
| try { |
| UserGroupInformation.getCurrentUser().doAs(new PrivilegedExceptionAction<Void>() { |
| @Override public Void run() throws Exception { |
| wh.deleteDir(dbExtPath, true, db); |
| return null; |
| } |
| }); |
| } catch (IOException | InterruptedException e) { |
| LOG.error("Couldn't delete external directory " + dbExtPath + " after " + "it was created for database " |
| + db.getName() + " " + e.getMessage()); |
| } |
| } |
| } |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.CREATE_DATABASE, |
| new CreateDatabaseEvent(db, success, this, isReplicated), |
| null, |
| transactionalListenersResponses, ms); |
| } |
| } |
| } |
| |
| @Override |
| public void create_database(final Database db) |
| throws AlreadyExistsException, InvalidObjectException, MetaException { |
| startFunction("create_database", ": " + db.toString()); |
| boolean success = false; |
| Exception ex = null; |
| if (!db.isSetCatalogName()) { |
| db.setCatalogName(getDefaultCatalog(conf)); |
| } |
| try { |
| try { |
| if (null != get_database_core(db.getCatalogName(), db.getName())) { |
| throw new AlreadyExistsException("Database " + db.getName() + " already exists"); |
| } |
| } catch (NoSuchObjectException e) { |
| // expected |
| } |
| |
| if (testTimeoutEnabled) { |
| try { |
| Thread.sleep(testTimeoutValue); |
| } catch (InterruptedException e) { |
| // do nothing |
| } |
| Deadline.checkTimeout(); |
| } |
| create_database_core(getMS(), db); |
| success = true; |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e) |
| .throwIfInstance(MetaException.class, InvalidObjectException.class, AlreadyExistsException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("create_database", success, ex); |
| } |
| } |
| |
| @Override |
| public Database get_database(final String name) |
| throws NoSuchObjectException, MetaException { |
| GetDatabaseRequest request = new GetDatabaseRequest(); |
| String[] parsedDbName = parseDbName(name, conf); |
| request.setName(parsedDbName[DB_NAME]); |
| if (parsedDbName[CAT_NAME] != null) { |
| request.setCatalogName(parsedDbName[CAT_NAME]); |
| } |
| return get_database_req(request); |
| } |
| |
| @Override |
| public Database get_database_core(String catName, final String name) throws NoSuchObjectException, MetaException { |
| Database db = null; |
| if (name == null) { |
| throw new MetaException("Database name cannot be null."); |
| } |
| try { |
| db = getMS().getDatabase(catName, name); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class, NoSuchObjectException.class) |
| .defaultRuntimeException(); |
| } |
| return db; |
| } |
| |
| @Override |
| public Database get_database_req(GetDatabaseRequest request) throws NoSuchObjectException, MetaException { |
| startFunction("get_database", ": " + request.getName()); |
| Database db = null; |
| Exception ex = null; |
| if (request.getName() == null) { |
| throw new MetaException("Database name cannot be null."); |
| } |
| List<String> processorCapabilities = request.getProcessorCapabilities(); |
| String processorId = request.getProcessorIdentifier(); |
| try { |
| db = getMS().getDatabase(request.getCatalogName(), request.getName()); |
| firePreEvent(new PreReadDatabaseEvent(db, this)); |
| if (transformer != null) { |
| db = transformer.transformDatabase(db, processorCapabilities, processorId); |
| } |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, NoSuchObjectException.class) |
| .defaultRuntimeException(); |
| } finally { |
| endFunction("get_database", db != null, ex); |
| } |
| return db; |
| } |
| |
| @Override |
| public void alter_database(final String dbName, final Database newDB) throws TException { |
| startFunction("alter_database " + dbName); |
| boolean success = false; |
| Exception ex = null; |
| RawStore ms = getMS(); |
| Database oldDB = null; |
| Map<String, String> transactionalListenersResponses = Collections.emptyMap(); |
| |
| // Perform the same URI normalization as create_database_core. |
| if (newDB.getLocationUri() != null) { |
| newDB.setLocationUri(wh.getDnsPath(new Path(newDB.getLocationUri())).toString()); |
| } |
| |
| String[] parsedDbName = parseDbName(dbName, conf); |
| |
| // We can replicate into an empty database, in which case newDB will have indication that |
| // it's target of replication but not oldDB. But replication flow will never alter a |
| // database so that oldDB indicates that it's target or replication but not the newDB. So, |
| // relying solely on newDB to check whether the database is target of replication works. |
| boolean isReplicated = isDbReplicationTarget(newDB); |
| try { |
| oldDB = get_database_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME]); |
| if (oldDB == null) { |
| throw new MetaException("Could not alter database \"" + parsedDbName[DB_NAME] + |
| "\". Could not retrieve old definition."); |
| } |
| |
| // Add replication target event id. |
| if (isReplicationEventIdUpdate(oldDB, newDB)) { |
| Map<String, String> oldParams = new LinkedHashMap<>(newDB.getParameters()); |
| String currentNotificationLogID = Long.toString(ms.getCurrentNotificationEventId().getEventId()); |
| oldParams.put(REPL_TARGET_DATABASE_PROPERTY, currentNotificationLogID); |
| LOG.debug("Adding the {} property for database {} with event id {}", REPL_TARGET_DATABASE_PROPERTY, |
| newDB.getName(), currentNotificationLogID); |
| newDB.setParameters(oldParams); |
| } |
| firePreEvent(new PreAlterDatabaseEvent(oldDB, newDB, this)); |
| |
| ms.openTransaction(); |
| ms.alterDatabase(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], newDB); |
| |
| if (!transactionalListeners.isEmpty()) { |
| AlterDatabaseEvent event = new AlterDatabaseEvent(oldDB, newDB, true, this, isReplicated); |
| if (!event.shouldSkipCapturing()) { |
| transactionalListenersResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, EventType.ALTER_DATABASE, event); |
| } |
| } |
| |
| success = ms.commitTransaction(); |
| } catch (MetaException|NoSuchObjectException e) { |
| ex = e; |
| throw e; |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| |
| if ((null != oldDB) && (!listeners.isEmpty())) { |
| AlterDatabaseEvent event = new AlterDatabaseEvent(oldDB, newDB, success, this, isReplicated); |
| if (!event.shouldSkipCapturing()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ALTER_DATABASE, event, null, transactionalListenersResponses, ms); |
| } |
| } |
| endFunction("alter_database", success, ex); |
| } |
| } |
| |
| /** |
| * Checks whether the repl.last.id is being updated. |
| * @param oldDb the old db object |
| * @param newDb the new db object |
| * @return true if repl.last.id is being changed. |
| */ |
| private boolean isReplicationEventIdUpdate(Database oldDb, Database newDb) { |
| Map<String, String> oldDbProp = oldDb.getParameters(); |
| Map<String, String> newDbProp = newDb.getParameters(); |
| if (newDbProp == null || newDbProp.isEmpty() || |
| Boolean.parseBoolean(newDbProp.get(REPL_RESUME_STARTED_AFTER_FAILOVER))) { |
| return false; |
| } |
| String newReplId = newDbProp.get(ReplConst.REPL_TARGET_TABLE_PROPERTY); |
| String oldReplId = oldDbProp != null ? oldDbProp.get(ReplConst.REPL_TARGET_TABLE_PROPERTY) : null; |
| return newReplId != null && !newReplId.equalsIgnoreCase(oldReplId); |
| } |
| |
| private void drop_database_core(RawStore ms, DropDatabaseRequest req) throws NoSuchObjectException, |
| InvalidOperationException, MetaException, IOException, InvalidObjectException, InvalidInputException { |
| boolean success = false; |
| Database db = null; |
| List<Path> tablePaths = new ArrayList<>(); |
| List<Path> partitionPaths = new ArrayList<>(); |
| Map<String, String> transactionalListenerResponses = Collections.emptyMap(); |
| if (req.getName() == null) { |
| throw new MetaException("Database name cannot be null."); |
| } |
| boolean isReplicated = false; |
| try { |
| ms.openTransaction(); |
| db = ms.getDatabase(req.getCatalogName(), req.getName()); |
| if (db.getType() == DatabaseType.REMOTE) { |
| success = drop_remote_database_core(ms, db); |
| return; |
| } |
| isReplicated = isDbReplicationTarget(db); |
| |
| if (!isInTest && ReplChangeManager.isSourceOfReplication(db)) { |
| throw new InvalidOperationException("can not drop a database which is a source of replication"); |
| } |
| |
| firePreEvent(new PreDropDatabaseEvent(db, this)); |
| String catPrependedName = MetaStoreUtils.prependCatalogToDbName(req.getCatalogName(), req.getName(), conf); |
| |
| Set<String> uniqueTableNames = new HashSet<>(get_all_tables(catPrependedName)); |
| List<String> allFunctions = get_functions(catPrependedName, "*"); |
| ListStoredProcedureRequest request = new ListStoredProcedureRequest(req.getCatalogName()); |
| request.setDbName(req.getName()); |
| List<String> allProcedures = get_all_stored_procedures(request); |
| ListPackageRequest pkgRequest = new ListPackageRequest(req.getCatalogName()); |
| pkgRequest.setDbName(req.getName()); |
| List<String> allPackages = get_all_packages(pkgRequest); |
| |
| if (!req.isCascade()) { |
| if (!uniqueTableNames.isEmpty()) { |
| throw new InvalidOperationException( |
| "Database " + db.getName() + " is not empty. One or more tables exist."); |
| } |
| if (!allFunctions.isEmpty()) { |
| throw new InvalidOperationException( |
| "Database " + db.getName() + " is not empty. One or more functions exist."); |
| } |
| if (!allProcedures.isEmpty()) { |
| throw new InvalidOperationException( |
| "Database " + db.getName() + " is not empty. One or more stored procedures exist."); |
| } |
| if (!allPackages.isEmpty()) { |
| throw new InvalidOperationException( |
| "Database " + db.getName() + " is not empty. One or more packages exist."); |
| } |
| } |
| Path path = new Path(db.getLocationUri()).getParent(); |
| if (!wh.isWritable(path)) { |
| throw new MetaException("Database not dropped since its external warehouse location " + path + " is not writable by " + |
| SecurityUtils.getUser()); |
| } |
| path = wh.getDatabaseManagedPath(db).getParent(); |
| if (!wh.isWritable(path)) { |
| throw new MetaException("Database not dropped since its managed warehouse location " + path + " is not writable by " + |
| SecurityUtils.getUser()); |
| } |
| |
| Path databasePath = wh.getDnsPath(wh.getDatabasePath(db)); |
| |
| // drop any functions before dropping db |
| for (String funcName : allFunctions) { |
| drop_function(catPrependedName, funcName); |
| } |
| |
| for (String procName : allProcedures) { |
| drop_stored_procedure(new StoredProcedureRequest(req.getCatalogName(), req.getName(), procName)); |
| } |
| for (String pkgName : allPackages) { |
| drop_package(new DropPackageRequest(req.getCatalogName(), req.getName(), pkgName)); |
| } |
| |
| final int tableBatchSize = MetastoreConf.getIntVar(conf, |
| ConfVars.BATCH_RETRIEVE_MAX); |
| |
| // First pass will drop the materialized views |
| List<String> materializedViewNames = getTablesByTypeCore(req.getCatalogName(), req.getName(), ".*", |
| TableType.MATERIALIZED_VIEW.toString()); |
| int startIndex = 0; |
| // retrieve the tables from the metastore in batches to alleviate memory constraints |
| while (startIndex < materializedViewNames.size()) { |
| int endIndex = Math.min(startIndex + tableBatchSize, materializedViewNames.size()); |
| |
| List<Table> materializedViews; |
| try { |
| materializedViews = ms.getTableObjectsByName(req.getCatalogName(), req.getName(), materializedViewNames.subList(startIndex, endIndex)); |
| } catch (UnknownDBException e) { |
| throw new MetaException(e.getMessage()); |
| } |
| |
| if (materializedViews != null && !materializedViews.isEmpty()) { |
| for (Table materializedView : materializedViews) { |
| boolean isSoftDelete = TxnUtils.isTableSoftDeleteEnabled(materializedView, req.isSoftDelete()); |
| |
| if (materializedView.getSd().getLocation() != null && !isSoftDelete) { |
| Path materializedViewPath = wh.getDnsPath(new Path(materializedView.getSd().getLocation())); |
| |
| if (!FileUtils.isSubdirectory(databasePath.toString(), materializedViewPath.toString()) || req.isSoftDelete()) { |
| if (!wh.isWritable(materializedViewPath.getParent())) { |
| throw new MetaException("Database metadata not deleted since table: " + |
| materializedView.getTableName() + " has a parent location " + materializedViewPath.getParent() + |
| " which is not writable by " + SecurityUtils.getUser()); |
| } |
| tablePaths.add(materializedViewPath); |
| } |
| } |
| EnvironmentContext context = null; |
| if (isSoftDelete) { |
| context = new EnvironmentContext(); |
| context.putToProperties(hive_metastoreConstants.TXN_ID, String.valueOf(req.getTxnId())); |
| } |
| // Drop the materialized view but not its data |
| drop_table_with_environment_context( |
| req.getName(), materializedView.getTableName(), false, context); |
| |
| // Remove from all tables |
| uniqueTableNames.remove(materializedView.getTableName()); |
| } |
| } |
| startIndex = endIndex; |
| } |
| |
| // drop tables before dropping db |
| List<String> allTables = new ArrayList<>(uniqueTableNames); |
| startIndex = 0; |
| |
| // retrieve the tables from the metastore in batches to alleviate memory constraints |
| while (startIndex < allTables.size()) { |
| int endIndex = Math.min(startIndex + tableBatchSize, allTables.size()); |
| |
| List<Table> tables; |
| try { |
| tables = ms.getTableObjectsByName(req.getCatalogName(), req.getName(), allTables.subList(startIndex, endIndex)); |
| } catch (UnknownDBException e) { |
| throw new MetaException(e.getMessage()); |
| } |
| |
| if (tables != null && !tables.isEmpty()) { |
| for (Table table : tables) { |
| // If the table is not external and it might not be in a subdirectory of the database |
| // add it's locations to the list of paths to delete |
| boolean isSoftDelete = TxnUtils.isTableSoftDeleteEnabled(table, req.isSoftDelete()); |
| Path tablePath = null; |
| |
| boolean tableDataShouldBeDeleted = checkTableDataShouldBeDeleted(table, req.isDeleteData()) |
| && !isSoftDelete; |
| |
| boolean isManagedTable = table.getTableType().equals(TableType.MANAGED_TABLE.toString()); |
| if (table.getSd().getLocation() != null && tableDataShouldBeDeleted) { |
| tablePath = wh.getDnsPath(new Path(table.getSd().getLocation())); |
| if (!isManagedTable || req.isSoftDelete()) { |
| if (!wh.isWritable(tablePath.getParent())) { |
| throw new MetaException( |
| "Database metadata not deleted since table: " + table.getTableName() + " has a parent location " |
| + tablePath.getParent() + " which is not writable by " + SecurityUtils.getUser()); |
| } |
| tablePaths.add(tablePath); |
| } |
| } |
| // For each partition in each table, drop the partitions and get a list of |
| // partitions' locations which might need to be deleted |
| partitionPaths = dropPartitionsAndGetLocations(ms, req.getCatalogName(), req.getName(), table.getTableName(), |
| tablePath, tableDataShouldBeDeleted); |
| |
| EnvironmentContext context = null; |
| if (isSoftDelete) { |
| context = new EnvironmentContext(); |
| context.putToProperties(hive_metastoreConstants.TXN_ID, String.valueOf(req.getTxnId())); |
| req.setDeleteManagedDir(false); |
| } |
| // Drop the table but not its data |
| drop_table_with_environment_context( |
| MetaStoreUtils.prependCatalogToDbName(table.getCatName(), table.getDbName(), conf), |
| table.getTableName(), false, context, false); |
| } |
| } |
| startIndex = endIndex; |
| } |
| |
| if (ms.dropDatabase(req.getCatalogName(), req.getName())) { |
| if (!transactionalListeners.isEmpty()) { |
| DropDatabaseEvent dropEvent = new DropDatabaseEvent(db, true, this, isReplicated); |
| EnvironmentContext context = null; |
| if (!req.isDeleteManagedDir()) { |
| context = new EnvironmentContext(); |
| context.putToProperties(hive_metastoreConstants.TXN_ID, String.valueOf(req.getTxnId())); |
| } |
| dropEvent.setEnvironmentContext(context); |
| transactionalListenerResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.DROP_DATABASE, dropEvent); |
| } |
| success = ms.commitTransaction(); |
| } |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } else if (req.isDeleteData()) { |
| // Delete the data in the partitions which have other locations |
| deletePartitionData(partitionPaths, false, db); |
| // Delete the data in the tables which have other locations or soft-delete is enabled |
| for (Path tablePath : tablePaths) { |
| deleteTableData(tablePath, false, db); |
| } |
| final Database dbFinal = db; |
| final Path path = (dbFinal.getManagedLocationUri() != null) ? |
| new Path(dbFinal.getManagedLocationUri()) : wh.getDatabaseManagedPath(dbFinal); |
| if (req.isDeleteManagedDir()) { |
| try { |
| Boolean deleted = UserGroupInformation.getLoginUser().doAs((PrivilegedExceptionAction<Boolean>) |
| () -> wh.deleteDir(path, true, dbFinal)); |
| if (!deleted) { |
| LOG.error("Failed to delete database's managed warehouse directory: " + path); |
| } |
| } catch (Exception e) { |
| LOG.error("Failed to delete database's managed warehouse directory: " + path + " " + e.getMessage()); |
| } |
| } |
| try { |
| Boolean deleted = UserGroupInformation.getCurrentUser().doAs((PrivilegedExceptionAction<Boolean>) |
| () -> wh.deleteDir(new Path(dbFinal.getLocationUri()), true, dbFinal)); |
| if (!deleted) { |
| LOG.error("Failed to delete database external warehouse directory " + db.getLocationUri()); |
| } |
| } catch (IOException | InterruptedException | UndeclaredThrowableException e) { |
| LOG.error("Failed to delete the database external warehouse directory: " + db.getLocationUri() + " " + e |
| .getMessage()); |
| } |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.DROP_DATABASE, |
| new DropDatabaseEvent(db, success, this, isReplicated), |
| null, |
| transactionalListenerResponses, ms); |
| } |
| } |
| } |
| |
| private boolean drop_remote_database_core(RawStore ms, final Database db) throws MetaException, NoSuchObjectException { |
| boolean success = false; |
| firePreEvent(new PreDropDatabaseEvent(db, this)); |
| |
| if (ms.dropDatabase(db.getCatalogName(), db.getName())) { |
| success = ms.commitTransaction(); |
| } |
| return success; |
| } |
| |
| @Override |
| public void drop_database(final String dbName, final boolean deleteData, final boolean cascade) |
| throws NoSuchObjectException, InvalidOperationException, MetaException { |
| String[] parsedDbName = parseDbName(dbName, conf); |
| |
| DropDatabaseRequest req = new DropDatabaseRequest(); |
| req.setName(parsedDbName[DB_NAME]); |
| req.setCatalogName(parsedDbName[CAT_NAME]); |
| req.setDeleteData(deleteData); |
| req.setCascade(cascade); |
| drop_database_req(req); |
| } |
| |
| @Override |
| public void drop_database_req(final DropDatabaseRequest req) |
| throws NoSuchObjectException, InvalidOperationException, MetaException { |
| startFunction("drop_database", ": " + req.getName()); |
| |
| if (DEFAULT_CATALOG_NAME.equalsIgnoreCase(req.getCatalogName()) |
| && DEFAULT_DATABASE_NAME.equalsIgnoreCase(req.getName())) { |
| endFunction("drop_database", false, null); |
| throw new MetaException("Can not drop " + DEFAULT_DATABASE_NAME + " database in catalog " |
| + DEFAULT_CATALOG_NAME); |
| } |
| boolean success = false; |
| Exception ex = null; |
| try { |
| drop_database_core(getMS(), req); |
| success = true; |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e) |
| .throwIfInstance(NoSuchObjectException.class, InvalidOperationException.class, MetaException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("drop_database", success, ex); |
| } |
| } |
| |
| @Override |
| public List<String> get_databases(final String pattern) throws MetaException { |
| startFunction("get_databases", ": " + pattern); |
| |
| String[] parsedDbNamed = parseDbName(pattern, conf); |
| List<String> ret = null; |
| Exception ex = null; |
| try { |
| if (parsedDbNamed[DB_NAME] == null) { |
| ret = getMS().getAllDatabases(parsedDbNamed[CAT_NAME]); |
| ret = FilterUtils.filterDbNamesIfEnabled(isServerFilterEnabled, filterHook, ret); |
| } else { |
| ret = getMS().getDatabases(parsedDbNamed[CAT_NAME], parsedDbNamed[DB_NAME]); |
| ret = FilterUtils.filterDbNamesIfEnabled(isServerFilterEnabled, filterHook, ret); |
| } |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_databases", ret != null, ex); |
| } |
| return ret; |
| } |
| |
| @Override |
| public List<String> get_all_databases() throws MetaException { |
| // get_databases filters results already. No need to filter here |
| return get_databases(MetaStoreUtils.prependCatalogToDbName(null, null, conf)); |
| } |
| |
| private void create_dataconnector_core(RawStore ms, final DataConnector connector) |
| throws AlreadyExistsException, InvalidObjectException, MetaException { |
| if (!MetaStoreUtils.validateName(connector.getName(), conf)) { |
| throw new InvalidObjectException(connector.getName() + " is not a valid dataconnector name"); |
| } |
| |
| if (connector.getOwnerName() == null){ |
| try { |
| connector.setOwnerName(SecurityUtils.getUGI().getShortUserName()); |
| }catch (Exception e){ |
| LOG.warn("Failed to get owner name for create dataconnector operation.", e); |
| } |
| } |
| long time = System.currentTimeMillis()/1000; |
| connector.setCreateTime((int) time); |
| boolean success = false; |
| Map<String, String> transactionalListenersResponses = Collections.emptyMap(); |
| try { |
| firePreEvent(new PreCreateDataConnectorEvent(connector, this)); |
| |
| ms.openTransaction(); |
| ms.createDataConnector(connector); |
| |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenersResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.CREATE_DATACONNECTOR, |
| new CreateDataConnectorEvent(connector, true, this)); |
| } |
| |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.CREATE_DATACONNECTOR, |
| new CreateDataConnectorEvent(connector, success, this), |
| null, |
| transactionalListenersResponses, ms); |
| } |
| } |
| } |
| |
| @Override |
| public void create_dataconnector(final DataConnector connector) |
| throws AlreadyExistsException, InvalidObjectException, MetaException { |
| startFunction("create_dataconnector", ": " + connector.toString()); |
| boolean success = false; |
| Exception ex = null; |
| try { |
| try { |
| if (null != get_dataconnector_core(connector.getName())) { |
| throw new AlreadyExistsException("DataConnector " + connector.getName() + " already exists"); |
| } |
| } catch (NoSuchObjectException e) { |
| // expected |
| } |
| if (testTimeoutEnabled) { |
| try { |
| Thread.sleep(testTimeoutValue); |
| } catch (InterruptedException e) { |
| // do nothing |
| } |
| Deadline.checkTimeout(); |
| } |
| create_dataconnector_core(getMS(), connector); |
| success = true; |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e) |
| .throwIfInstance(MetaException.class, InvalidObjectException.class, AlreadyExistsException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("create_connector", success, ex); |
| } |
| } |
| |
| @Override |
| public DataConnector get_dataconnector_core(final String name) throws NoSuchObjectException, MetaException { |
| DataConnector connector = null; |
| if (name == null) { |
| throw new MetaException("Data connector name cannot be null."); |
| } |
| try { |
| connector = getMS().getDataConnector(name); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class, NoSuchObjectException.class) |
| .defaultRuntimeException(); |
| } |
| return connector; |
| } |
| |
| @Override |
| public DataConnector get_dataconnector_req(GetDataConnectorRequest request) throws NoSuchObjectException, MetaException { |
| startFunction("get_dataconnector", ": " + request.getConnectorName()); |
| DataConnector connector = null; |
| Exception ex = null; |
| try { |
| connector = get_dataconnector_core(request.getConnectorName()); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, NoSuchObjectException.class) |
| .defaultRuntimeException(); |
| } finally { |
| endFunction("get_dataconnector", connector != null, ex); |
| } |
| return connector; |
| } |
| |
| @Override |
| public void alter_dataconnector(final String dcName, final DataConnector newDC) throws TException { |
| startFunction("alter_dataconnector " + dcName); |
| boolean success = false; |
| Exception ex = null; |
| RawStore ms = getMS(); |
| DataConnector oldDC = null; |
| Map<String, String> transactionalListenersResponses = Collections.emptyMap(); |
| |
| try { |
| oldDC = get_dataconnector_core(dcName); |
| if (oldDC == null) { |
| throw new MetaException("Could not alter dataconnector \"" + dcName + |
| "\". Could not retrieve old definition."); |
| } |
| firePreEvent(new PreAlterDataConnectorEvent(oldDC, newDC, this)); |
| |
| ms.openTransaction(); |
| ms.alterDataConnector(dcName, newDC); |
| |
| /* |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenersResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.ALTER_DATACONNECTOR, |
| new AlterDataConnectorEvent(oldDC, newDC, true, this)); |
| } |
| */ |
| |
| success = ms.commitTransaction(); |
| } catch (MetaException|NoSuchObjectException e) { |
| ex = e; |
| throw e; |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| /* |
| if ((null != oldDC) && (!listeners.isEmpty())) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ALTER_DATACONNECTOR, |
| new AlterDataConnectorEvent(oldDC, newDC, success, this), |
| null, |
| transactionalListenersResponses, ms); |
| } |
| */ |
| endFunction("alter_database", success, ex); |
| } |
| } |
| |
| @Override |
| public List<String> get_dataconnectors() throws MetaException { |
| startFunction("get_dataconnectors"); |
| |
| List<String> ret = null; |
| Exception ex = null; |
| try { |
| ret = getMS().getAllDataConnectorNames(); |
| ret = FilterUtils.filterDataConnectorsIfEnabled(isServerFilterEnabled, filterHook, ret); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_dataconnectors", ret != null, ex); |
| } |
| return ret; |
| } |
| |
| @Override |
| public void drop_dataconnector(final String dcName, boolean ifNotExists, boolean checkReferences) throws NoSuchObjectException, InvalidOperationException, MetaException { |
| startFunction("drop_dataconnector", ": " + dcName); |
| boolean success = false; |
| DataConnector connector = null; |
| Exception ex = null; |
| RawStore ms = getMS(); |
| try { |
| connector = getMS().getDataConnector(dcName); |
| } catch (NoSuchObjectException e) { |
| if (!ifNotExists) { |
| throw new NoSuchObjectException("DataConnector " + dcName + " doesn't exist"); |
| } else { |
| return; |
| } |
| } |
| try { |
| ms.openTransaction(); |
| // TODO find DBs with references to this connector |
| // if any existing references and checkReferences=true, do not drop |
| |
| firePreEvent(new PreDropDataConnectorEvent(connector, this)); |
| |
| if (!ms.dropDataConnector(dcName)) { |
| throw new MetaException("Unable to drop dataconnector " + dcName); |
| } else { |
| /* |
| // TODO |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenerResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.DROP_TABLE, |
| new DropTableEvent(tbl, true, deleteData, |
| this, isReplicated), |
| envContext); |
| } |
| */ |
| success = ms.commitTransaction(); |
| } |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| /* |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.DROP_TABLE, |
| new DropTableEvent(tbl, success, deleteData, this, isReplicated), |
| envContext, |
| transactionalListenerResponses, ms); |
| } |
| */ |
| endFunction("drop_dataconnector", success, ex); |
| } |
| } |
| |
| private void create_type_core(final RawStore ms, final Type type) |
| throws AlreadyExistsException, MetaException, InvalidObjectException { |
| if (!MetaStoreUtils.validateName(type.getName(), null)) { |
| throw new InvalidObjectException("Invalid type name"); |
| } |
| |
| boolean success = false; |
| try { |
| ms.openTransaction(); |
| if (is_type_exists(ms, type.getName())) { |
| throw new AlreadyExistsException("Type " + type.getName() + " already exists"); |
| } |
| ms.createType(type); |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| } |
| } |
| |
| @Override |
| public boolean create_type(final Type type) throws AlreadyExistsException, |
| MetaException, InvalidObjectException { |
| startFunction("create_type", ": " + type.toString()); |
| boolean success = false; |
| Exception ex = null; |
| try { |
| create_type_core(getMS(), type); |
| success = true; |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e) |
| .throwIfInstance(MetaException.class, InvalidObjectException.class, AlreadyExistsException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("create_type", success, ex); |
| } |
| |
| return success; |
| } |
| |
| @Override |
| public Type get_type(final String name) throws MetaException, NoSuchObjectException { |
| startFunction("get_type", ": " + name); |
| |
| Type ret = null; |
| Exception ex = null; |
| try { |
| ret = getMS().getType(name); |
| if (null == ret) { |
| throw new NoSuchObjectException("Type \"" + name + "\" not found."); |
| } |
| } catch (Exception e) { |
| ex = e; |
| throwMetaException(e); |
| } finally { |
| endFunction("get_type", ret != null, ex); |
| } |
| return ret; |
| } |
| |
| private boolean is_type_exists(RawStore ms, String typeName) |
| throws MetaException { |
| return (ms.getType(typeName) != null); |
| } |
| |
| @Override |
| public boolean drop_type(final String name) throws MetaException, NoSuchObjectException { |
| startFunction("drop_type", ": " + name); |
| |
| boolean success = false; |
| Exception ex = null; |
| try { |
| // TODO:pc validate that there are no types that refer to this |
| success = getMS().dropType(name); |
| } catch (Exception e) { |
| ex = e; |
| throwMetaException(e); |
| } finally { |
| endFunction("drop_type", success, ex); |
| } |
| return success; |
| } |
| |
| @Override |
| public Map<String, Type> get_type_all(String name) throws MetaException { |
| // TODO Auto-generated method stub |
| startFunction("get_type_all", ": " + name); |
| endFunction("get_type_all", false, null); |
| throw new MetaException("Not yet implemented"); |
| } |
| |
| @Override |
| public Table translate_table_dryrun(final CreateTableRequest req) throws AlreadyExistsException, |
| MetaException, InvalidObjectException, InvalidInputException { |
| Table transformedTbl = null; |
| Table tbl = req.getTable(); |
| List<String> processorCapabilities = req.getProcessorCapabilities(); |
| String processorId = req.getProcessorIdentifier(); |
| if (!tbl.isSetCatName()) { |
| tbl.setCatName(getDefaultCatalog(conf)); |
| } |
| if (transformer != null) { |
| transformedTbl = transformer.transformCreateTable(tbl, processorCapabilities, processorId); |
| } |
| return transformedTbl != null ? transformedTbl : tbl; |
| } |
| |
| private void create_table_core(final RawStore ms, final Table tbl, |
| final EnvironmentContext envContext) |
| throws AlreadyExistsException, MetaException, |
| InvalidObjectException, NoSuchObjectException, InvalidInputException { |
| CreateTableRequest req = new CreateTableRequest(tbl); |
| req.setEnvContext(envContext); |
| create_table_core(ms, req); |
| } |
| |
| private void create_table_core(final RawStore ms, final Table tbl, |
| final EnvironmentContext envContext, List<SQLPrimaryKey> primaryKeys, |
| List<SQLForeignKey> foreignKeys, List<SQLUniqueConstraint> uniqueConstraints, |
| List<SQLNotNullConstraint> notNullConstraints, List<SQLDefaultConstraint> defaultConstraints, |
| List<SQLCheckConstraint> checkConstraints, |
| List<String> processorCapabilities, String processorIdentifier) |
| throws AlreadyExistsException, MetaException, |
| InvalidObjectException, NoSuchObjectException, InvalidInputException { |
| CreateTableRequest req = new CreateTableRequest(tbl); |
| if (envContext != null) { |
| req.setEnvContext(envContext); |
| } |
| if (primaryKeys != null) { |
| req.setPrimaryKeys(primaryKeys); |
| } |
| if (foreignKeys != null) { |
| req.setForeignKeys(foreignKeys); |
| } |
| if (uniqueConstraints != null) { |
| req.setUniqueConstraints(uniqueConstraints); |
| } |
| if (notNullConstraints != null) { |
| req.setNotNullConstraints(notNullConstraints); |
| } |
| if (defaultConstraints != null) { |
| req.setDefaultConstraints(defaultConstraints); |
| } |
| if (checkConstraints != null) { |
| req.setCheckConstraints(checkConstraints); |
| } |
| if (processorCapabilities != null) { |
| req.setProcessorCapabilities(processorCapabilities); |
| req.setProcessorIdentifier(processorIdentifier); |
| } |
| create_table_core(ms, req); |
| } |
| |
| private void create_table_core(final RawStore ms, final CreateTableRequest req) |
| throws AlreadyExistsException, MetaException, |
| InvalidObjectException, NoSuchObjectException, InvalidInputException { |
| ColumnStatistics colStats = null; |
| Table tbl = req.getTable(); |
| EnvironmentContext envContext = req.getEnvContext(); |
| SQLAllTableConstraints constraints = new SQLAllTableConstraints(); |
| constraints.setPrimaryKeys(req.getPrimaryKeys()); |
| constraints.setForeignKeys(req.getForeignKeys()); |
| constraints.setUniqueConstraints(req.getUniqueConstraints()); |
| constraints.setDefaultConstraints(req.getDefaultConstraints()); |
| constraints.setCheckConstraints(req.getCheckConstraints()); |
| constraints.setNotNullConstraints(req.getNotNullConstraints()); |
| List<String> processorCapabilities = req.getProcessorCapabilities(); |
| String processorId = req.getProcessorIdentifier(); |
| |
| // To preserve backward compatibility throw MetaException in case of null database |
| if (tbl.getDbName() == null) { |
| throw new MetaException("Null database name is not allowed"); |
| } |
| |
| if (!MetaStoreUtils.validateName(tbl.getTableName(), conf)) { |
| throw new InvalidObjectException(tbl.getTableName() |
| + " is not a valid object name"); |
| } |
| |
| if (!MetaStoreUtils.validateTblStorage(tbl.getSd())) { |
| throw new InvalidObjectException(tbl.getTableName() |
| + " location must not be root path"); |
| } |
| |
| if (!tbl.isSetCatName()) { |
| tbl.setCatName(getDefaultCatalog(conf)); |
| } |
| |
| Database db = get_database_core(tbl.getCatName(), tbl.getDbName()); |
| if (db != null && db.getType().equals(DatabaseType.REMOTE)) { |
| // HIVE-24425: Create table in REMOTE db should fail |
| throw new MetaException("Create table in REMOTE database " + db.getName() + " is not allowed"); |
| } |
| |
| if (is_table_exists(ms, tbl.getCatName(), tbl.getDbName(), tbl.getTableName())) { |
| throw new AlreadyExistsException("Table " + getCatalogQualifiedTableName(tbl) |
| + " already exists"); |
| } |
| |
| tbl.setDbName(normalizeIdentifier(tbl.getDbName())); |
| tbl.setTableName(normalizeIdentifier(tbl.getTableName())); |
| |
| if (transformer != null) { |
| tbl = transformer.transformCreateTable(tbl, processorCapabilities, processorId); |
| } |
| |
| if (tbl.getParameters() != null) { |
| tbl.getParameters().remove(TABLE_IS_CTAS); |
| tbl.getParameters().remove(TABLE_IS_CTLT); |
| } |
| |
| // If the given table has column statistics, save it here. We will update it later. |
| // We don't want it to be part of the Table object being created, lest the create table |
| // event will also have the col stats which we don't want. |
| if (tbl.isSetColStats()) { |
| colStats = tbl.getColStats(); |
| tbl.unsetColStats(); |
| } |
| |
| String validate = MetaStoreServerUtils.validateTblColumns(tbl.getSd().getCols()); |
| if (validate != null) { |
| throw new InvalidObjectException("Invalid column " + validate); |
| } |
| if (tbl.getPartitionKeys() != null) { |
| validate = MetaStoreServerUtils.validateTblColumns(tbl.getPartitionKeys()); |
| if (validate != null) { |
| throw new InvalidObjectException("Invalid partition column " + validate); |
| } |
| } |
| if (tbl.isSetId()) { |
| LOG.debug("Id shouldn't be set but table {}.{} has the Id set to {}. Id is ignored.", tbl.getDbName(), |
| tbl.getTableName(), tbl.getId()); |
| tbl.unsetId(); |
| } |
| SkewedInfo skew = tbl.getSd().getSkewedInfo(); |
| if (skew != null) { |
| validate = MetaStoreServerUtils.validateSkewedColNames(skew.getSkewedColNames()); |
| if (validate != null) { |
| throw new InvalidObjectException("Invalid skew column " + validate); |
| } |
| validate = MetaStoreServerUtils.validateSkewedColNamesSubsetCol( |
| skew.getSkewedColNames(), tbl.getSd().getCols()); |
| if (validate != null) { |
| throw new InvalidObjectException("Invalid skew column " + validate); |
| } |
| } |
| |
| Map<String, String> transactionalListenerResponses = Collections.emptyMap(); |
| Path tblPath = null; |
| boolean success = false, madeDir = false; |
| boolean isReplicated = false; |
| try { |
| |
| ms.openTransaction(); |
| |
| db = ms.getDatabase(tbl.getCatName(), tbl.getDbName()); |
| isReplicated = isDbReplicationTarget(db); |
| |
| firePreEvent(new PreCreateTableEvent(tbl, db, this)); |
| |
| if (!TableType.VIRTUAL_VIEW.toString().equals(tbl.getTableType())) { |
| if (tbl.getSd().getLocation() == null |
| || tbl.getSd().getLocation().isEmpty()) { |
| tblPath = wh.getDefaultTablePath(db, tbl.getTableName() + getTableSuffix(tbl), isExternal(tbl)); |
| } else { |
| if (!isExternal(tbl) && !MetaStoreUtils.isNonNativeTable(tbl)) { |
| LOG.warn("Location: " + tbl.getSd().getLocation() |
| + " specified for non-external table:" + tbl.getTableName()); |
| } |
| tblPath = wh.getDnsPath(new Path(tbl.getSd().getLocation())); |
| // ignore suffix if it's already there (direct-write CTAS) |
| if (!tblPath.getName().matches("(.*)" + SOFT_DELETE_TABLE_PATTERN)) { |
| tblPath = new Path(tblPath + getTableSuffix(tbl)); |
| } |
| } |
| tbl.getSd().setLocation(tblPath.toString()); |
| } |
| |
| if (tblPath != null) { |
| if (!wh.isDir(tblPath)) { |
| if (!wh.mkdirs(tblPath)) { |
| throw new MetaException(tblPath |
| + " is not a directory or unable to create one"); |
| } |
| madeDir = true; |
| } |
| } |
| |
| MetaStoreServerUtils.updateTableStatsForCreateTable(wh, db, tbl, envContext, conf, tblPath, madeDir); |
| |
| // set create time |
| long time = System.currentTimeMillis() / 1000; |
| tbl.setCreateTime((int) time); |
| if (tbl.getParameters() == null || |
| tbl.getParameters().get(hive_metastoreConstants.DDL_TIME) == null) { |
| tbl.putToParameters(hive_metastoreConstants.DDL_TIME, Long.toString(time)); |
| } |
| |
| if (CollectionUtils.isEmpty(constraints.getPrimaryKeys()) && CollectionUtils.isEmpty(constraints.getForeignKeys()) |
| && CollectionUtils.isEmpty(constraints.getUniqueConstraints())&& CollectionUtils.isEmpty(constraints.getNotNullConstraints())&& CollectionUtils.isEmpty(constraints.getDefaultConstraints()) |
| && CollectionUtils.isEmpty(constraints.getCheckConstraints())) { |
| ms.createTable(tbl); |
| } else { |
| final String catName = tbl.getCatName(); |
| // Check that constraints have catalog name properly set first |
| if (CollectionUtils.isNotEmpty(constraints.getPrimaryKeys()) && !constraints.getPrimaryKeys().get(0).isSetCatName()) { |
| constraints.getPrimaryKeys().forEach(constraint -> constraint.setCatName(catName)); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getForeignKeys()) && !constraints.getForeignKeys().get(0).isSetCatName()) { |
| constraints.getForeignKeys().forEach(constraint -> constraint.setCatName(catName)); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getUniqueConstraints()) && !constraints.getUniqueConstraints().get(0).isSetCatName()) { |
| constraints.getUniqueConstraints().forEach(constraint -> constraint.setCatName(catName)); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getNotNullConstraints()) && !constraints.getNotNullConstraints().get(0).isSetCatName()) { |
| constraints.getNotNullConstraints().forEach(constraint -> constraint.setCatName(catName)); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getDefaultConstraints()) && !constraints.getDefaultConstraints().get(0).isSetCatName()) { |
| constraints.getDefaultConstraints().forEach(constraint -> constraint.setCatName(catName)); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getCheckConstraints()) && !constraints.getCheckConstraints().get(0).isSetCatName()) { |
| constraints.getCheckConstraints().forEach(constraint -> constraint.setCatName(catName)); |
| } |
| // Set constraint name if null before sending to listener |
| constraints = ms.createTableWithConstraints(tbl, constraints); |
| |
| } |
| |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenerResponses = MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.CREATE_TABLE, new CreateTableEvent(tbl, true, this, isReplicated), envContext); |
| if (CollectionUtils.isNotEmpty(constraints.getPrimaryKeys())) { |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, EventType.ADD_PRIMARYKEY, |
| new AddPrimaryKeyEvent(constraints.getPrimaryKeys(), true, this), envContext); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getForeignKeys())) { |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, EventType.ADD_FOREIGNKEY, |
| new AddForeignKeyEvent(constraints.getForeignKeys(), true, this), envContext); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getUniqueConstraints())) { |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, EventType.ADD_UNIQUECONSTRAINT, |
| new AddUniqueConstraintEvent(constraints.getUniqueConstraints(), true, this), envContext); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getNotNullConstraints())) { |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, EventType.ADD_NOTNULLCONSTRAINT, |
| new AddNotNullConstraintEvent(constraints.getNotNullConstraints(), true, this), envContext); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getCheckConstraints())) { |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, EventType.ADD_CHECKCONSTRAINT, |
| new AddCheckConstraintEvent(constraints.getCheckConstraints(), true, this), envContext); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getDefaultConstraints())) { |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, EventType.ADD_DEFAULTCONSTRAINT, |
| new AddDefaultConstraintEvent(constraints.getDefaultConstraints(), true, this), envContext); |
| } |
| } |
| |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| if (madeDir) { |
| wh.deleteDir(tblPath, true, false, ReplChangeManager.shouldEnableCm(db, tbl)); |
| } |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.CREATE_TABLE, |
| new CreateTableEvent(tbl, success, this, isReplicated), envContext, |
| transactionalListenerResponses, ms); |
| if (CollectionUtils.isNotEmpty(constraints.getPrimaryKeys())) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ADD_PRIMARYKEY, |
| new AddPrimaryKeyEvent(constraints.getPrimaryKeys(), success, this), envContext); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getForeignKeys())) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ADD_FOREIGNKEY, |
| new AddForeignKeyEvent(constraints.getForeignKeys(), success, this), envContext); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getUniqueConstraints())) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ADD_UNIQUECONSTRAINT, |
| new AddUniqueConstraintEvent(constraints.getUniqueConstraints(), success, this), envContext); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getNotNullConstraints())) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ADD_NOTNULLCONSTRAINT, |
| new AddNotNullConstraintEvent(constraints.getNotNullConstraints(), success, this), envContext); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getDefaultConstraints())) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ADD_DEFAULTCONSTRAINT, |
| new AddDefaultConstraintEvent(constraints.getDefaultConstraints(), success, this), envContext); |
| } |
| if (CollectionUtils.isNotEmpty(constraints.getCheckConstraints())) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ADD_CHECKCONSTRAINT, |
| new AddCheckConstraintEvent(constraints.getCheckConstraints(), success, this), envContext); |
| } |
| } |
| } |
| |
| // If the table has column statistics, update it into the metastore. We need a valid |
| // writeId list to update column statistics for a transactional table. But during bootstrap |
| // replication, where we use this feature, we do not have a valid writeId list which was |
| // used to update the stats. But we know for sure that the writeId associated with the |
| // stats was valid then (otherwise stats update would have failed on the source). So, craft |
| // a valid transaction list with only that writeId and use it to update the stats. |
| if (colStats != null) { |
| long writeId = tbl.getWriteId(); |
| String validWriteIds = null; |
| if (writeId > 0) { |
| ValidWriteIdList validWriteIdList = |
| new ValidReaderWriteIdList(TableName.getDbTable(tbl.getDbName(), |
| tbl.getTableName()), |
| new long[0], new BitSet(), writeId); |
| validWriteIds = validWriteIdList.toString(); |
| } |
| updateTableColumnStatsInternal(colStats, validWriteIds, tbl.getWriteId()); |
| } |
| } |
| |
| private String getTableSuffix(Table tbl) { |
| return tbl.isSetTxnId() && tbl.getParameters() != null |
| && Boolean.parseBoolean(tbl.getParameters().get(SOFT_DELETE_TABLE)) ? |
| SOFT_DELETE_PATH_SUFFIX + String.format(DELTA_DIGITS, tbl.getTxnId()) : ""; |
| } |
| |
| @Override |
| public void create_table(final Table tbl) throws AlreadyExistsException, |
| MetaException, InvalidObjectException, InvalidInputException { |
| create_table_with_environment_context(tbl, null); |
| } |
| |
| @Override |
| public void create_table_with_environment_context(final Table tbl, |
| final EnvironmentContext envContext) |
| throws AlreadyExistsException, MetaException, InvalidObjectException, |
| InvalidInputException { |
| startFunction("create_table", ": " + tbl.toString()); |
| boolean success = false; |
| Exception ex = null; |
| try { |
| create_table_core(getMS(), tbl, envContext); |
| success = true; |
| } catch (Exception e) { |
| LOG.warn("create_table_with_environment_context got ", e); |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, InvalidObjectException.class) |
| .throwIfInstance(AlreadyExistsException.class, InvalidInputException.class) |
| .convertIfInstance(NoSuchObjectException.class, InvalidObjectException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("create_table", success, ex, tbl.getTableName()); |
| } |
| } |
| |
| @Override |
| public void create_table_req(final CreateTableRequest req) |
| throws AlreadyExistsException, MetaException, InvalidObjectException, |
| InvalidInputException { |
| Table tbl = req.getTable(); |
| startFunction("create_table_req", ": " + tbl.toString()); |
| boolean success = false; |
| Exception ex = null; |
| try { |
| create_table_core(getMS(), req); |
| success = true; |
| } catch (Exception e) { |
| LOG.warn("create_table_req got ", e); |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, InvalidObjectException.class) |
| .throwIfInstance(AlreadyExistsException.class, InvalidInputException.class) |
| .convertIfInstance(NoSuchObjectException.class, InvalidObjectException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("create_table_req", success, ex, tbl.getTableName()); |
| } |
| } |
| |
| @Override |
| public void create_table_with_constraints(final Table tbl, |
| final List<SQLPrimaryKey> primaryKeys, final List<SQLForeignKey> foreignKeys, |
| List<SQLUniqueConstraint> uniqueConstraints, |
| List<SQLNotNullConstraint> notNullConstraints, |
| List<SQLDefaultConstraint> defaultConstraints, |
| List<SQLCheckConstraint> checkConstraints) |
| throws AlreadyExistsException, MetaException, InvalidObjectException, |
| InvalidInputException { |
| startFunction("create_table", ": " + tbl.toString()); |
| boolean success = false; |
| Exception ex = null; |
| try { |
| CreateTableRequest req = new CreateTableRequest(tbl); |
| req.setPrimaryKeys(primaryKeys); |
| req.setForeignKeys(foreignKeys); |
| req.setUniqueConstraints(uniqueConstraints); |
| req.setNotNullConstraints(notNullConstraints); |
| req.setDefaultConstraints(defaultConstraints); |
| req.setCheckConstraints(checkConstraints); |
| create_table_req(req); |
| success = true; |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, InvalidObjectException.class) |
| .throwIfInstance(AlreadyExistsException.class, InvalidInputException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("create_table_with_constraints", success, ex, tbl.getTableName()); |
| } |
| } |
| |
| @Override |
| public void drop_constraint(DropConstraintRequest req) |
| throws MetaException, InvalidObjectException { |
| String catName = req.isSetCatName() ? req.getCatName() : getDefaultCatalog(conf); |
| String dbName = req.getDbname(); |
| String tableName = req.getTablename(); |
| String constraintName = req.getConstraintname(); |
| startFunction("drop_constraint", ": " + constraintName); |
| boolean success = false; |
| Exception ex = null; |
| RawStore ms = getMS(); |
| try { |
| ms.openTransaction(); |
| ms.dropConstraint(catName, dbName, tableName, constraintName); |
| if (transactionalListeners.size() > 0) { |
| DropConstraintEvent dropConstraintEvent = new DropConstraintEvent(catName, dbName, |
| tableName, constraintName, true, this); |
| for (MetaStoreEventListener transactionalListener : transactionalListeners) { |
| transactionalListener.onDropConstraint(dropConstraintEvent); |
| } |
| } |
| success = ms.commitTransaction(); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class) |
| .convertIfInstance(NoSuchObjectException.class, InvalidObjectException.class) |
| .defaultMetaException(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } else { |
| for (MetaStoreEventListener listener : listeners) { |
| DropConstraintEvent dropConstraintEvent = new DropConstraintEvent(catName, dbName, |
| tableName, constraintName, true, this); |
| listener.onDropConstraint(dropConstraintEvent); |
| } |
| } |
| endFunction("drop_constraint", success, ex, constraintName); |
| } |
| } |
| |
| @Override |
| public void add_primary_key(AddPrimaryKeyRequest req) |
| throws MetaException, InvalidObjectException { |
| List<SQLPrimaryKey> primaryKeyCols = req.getPrimaryKeyCols(); |
| String constraintName = (CollectionUtils.isNotEmpty(primaryKeyCols)) ? |
| primaryKeyCols.get(0).getPk_name() : "null"; |
| startFunction("add_primary_key", ": " + constraintName); |
| boolean success = false; |
| Exception ex = null; |
| if (CollectionUtils.isNotEmpty(primaryKeyCols) && !primaryKeyCols.get(0).isSetCatName()) { |
| String defaultCat = getDefaultCatalog(conf); |
| primaryKeyCols.forEach(pk -> pk.setCatName(defaultCat)); |
| } |
| RawStore ms = getMS(); |
| try { |
| ms.openTransaction(); |
| List<SQLPrimaryKey> primaryKeys = ms.addPrimaryKeys(primaryKeyCols); |
| if (transactionalListeners.size() > 0) { |
| if (CollectionUtils.isNotEmpty(primaryKeys)) { |
| AddPrimaryKeyEvent addPrimaryKeyEvent = new AddPrimaryKeyEvent(primaryKeys, true, this); |
| for (MetaStoreEventListener transactionalListener : transactionalListeners) { |
| transactionalListener.onAddPrimaryKey(addPrimaryKeyEvent); |
| } |
| } |
| } |
| success = ms.commitTransaction(); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, InvalidObjectException.class) |
| .defaultMetaException(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } else if (primaryKeyCols != null && primaryKeyCols.size() > 0) { |
| for (MetaStoreEventListener listener : listeners) { |
| AddPrimaryKeyEvent addPrimaryKeyEvent = new AddPrimaryKeyEvent(primaryKeyCols, true, this); |
| listener.onAddPrimaryKey(addPrimaryKeyEvent); |
| } |
| } |
| endFunction("add_primary_key", success, ex, constraintName); |
| } |
| } |
| |
| @Override |
| public void add_foreign_key(AddForeignKeyRequest req) |
| throws MetaException, InvalidObjectException { |
| List<SQLForeignKey> foreignKeys = req.getForeignKeyCols(); |
| String constraintName = CollectionUtils.isNotEmpty(foreignKeys) ? |
| foreignKeys.get(0).getFk_name() : "null"; |
| startFunction("add_foreign_key", ": " + constraintName); |
| boolean success = false; |
| Exception ex = null; |
| if (CollectionUtils.isNotEmpty(foreignKeys) && !foreignKeys.get(0).isSetCatName()) { |
| String defaultCat = getDefaultCatalog(conf); |
| foreignKeys.forEach(pk -> pk.setCatName(defaultCat)); |
| } |
| RawStore ms = getMS(); |
| try { |
| ms.openTransaction(); |
| foreignKeys = ms.addForeignKeys(foreignKeys); |
| if (transactionalListeners.size() > 0) { |
| if (CollectionUtils.isNotEmpty(foreignKeys)) { |
| AddForeignKeyEvent addForeignKeyEvent = new AddForeignKeyEvent(foreignKeys, true, this); |
| for (MetaStoreEventListener transactionalListener : transactionalListeners) { |
| transactionalListener.onAddForeignKey(addForeignKeyEvent); |
| } |
| } |
| } |
| success = ms.commitTransaction(); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, InvalidObjectException.class) |
| .defaultMetaException(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } else if (CollectionUtils.isNotEmpty(foreignKeys)) { |
| for (MetaStoreEventListener listener : listeners) { |
| AddForeignKeyEvent addForeignKeyEvent = new AddForeignKeyEvent(foreignKeys, true, this); |
| listener.onAddForeignKey(addForeignKeyEvent); |
| } |
| } |
| endFunction("add_foreign_key", success, ex, constraintName); |
| } |
| } |
| |
| @Override |
| public void add_unique_constraint(AddUniqueConstraintRequest req) |
| throws MetaException, InvalidObjectException { |
| List<SQLUniqueConstraint> uniqueConstraints = req.getUniqueConstraintCols(); |
| String constraintName = (uniqueConstraints != null && uniqueConstraints.size() > 0) ? |
| uniqueConstraints.get(0).getUk_name() : "null"; |
| startFunction("add_unique_constraint", ": " + constraintName); |
| boolean success = false; |
| Exception ex = null; |
| if (!uniqueConstraints.isEmpty() && !uniqueConstraints.get(0).isSetCatName()) { |
| String defaultCat = getDefaultCatalog(conf); |
| uniqueConstraints.forEach(pk -> pk.setCatName(defaultCat)); |
| } |
| RawStore ms = getMS(); |
| try { |
| ms.openTransaction(); |
| uniqueConstraints = ms.addUniqueConstraints(uniqueConstraints); |
| if (transactionalListeners.size() > 0) { |
| if (CollectionUtils.isNotEmpty(uniqueConstraints)) { |
| AddUniqueConstraintEvent addUniqueConstraintEvent = new AddUniqueConstraintEvent(uniqueConstraints, true, this); |
| for (MetaStoreEventListener transactionalListener : transactionalListeners) { |
| transactionalListener.onAddUniqueConstraint(addUniqueConstraintEvent); |
| } |
| } |
| } |
| success = ms.commitTransaction(); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, InvalidObjectException.class) |
| .defaultMetaException(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } else if (CollectionUtils.isNotEmpty(uniqueConstraints)) { |
| for (MetaStoreEventListener listener : listeners) { |
| AddUniqueConstraintEvent addUniqueConstraintEvent = new AddUniqueConstraintEvent(uniqueConstraints, true, this); |
| listener.onAddUniqueConstraint(addUniqueConstraintEvent); |
| } |
| } |
| endFunction("add_unique_constraint", success, ex, constraintName); |
| } |
| } |
| |
| @Override |
| public void add_not_null_constraint(AddNotNullConstraintRequest req) |
| throws MetaException, InvalidObjectException { |
| List<SQLNotNullConstraint> notNullConstraints = req.getNotNullConstraintCols(); |
| String constraintName = (notNullConstraints != null && notNullConstraints.size() > 0) ? |
| notNullConstraints.get(0).getNn_name() : "null"; |
| startFunction("add_not_null_constraint", ": " + constraintName); |
| boolean success = false; |
| Exception ex = null; |
| if (!notNullConstraints.isEmpty() && !notNullConstraints.get(0).isSetCatName()) { |
| String defaultCat = getDefaultCatalog(conf); |
| notNullConstraints.forEach(pk -> pk.setCatName(defaultCat)); |
| } |
| RawStore ms = getMS(); |
| try { |
| ms.openTransaction(); |
| notNullConstraints = ms.addNotNullConstraints(notNullConstraints); |
| |
| if (transactionalListeners.size() > 0) { |
| if (CollectionUtils.isNotEmpty(notNullConstraints)) { |
| AddNotNullConstraintEvent addNotNullConstraintEvent = new AddNotNullConstraintEvent(notNullConstraints, true, this); |
| for (MetaStoreEventListener transactionalListener : transactionalListeners) { |
| transactionalListener.onAddNotNullConstraint(addNotNullConstraintEvent); |
| } |
| } |
| } |
| success = ms.commitTransaction(); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, InvalidObjectException.class).defaultMetaException(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } else if (CollectionUtils.isNotEmpty(notNullConstraints)) { |
| for (MetaStoreEventListener listener : listeners) { |
| AddNotNullConstraintEvent addNotNullConstraintEvent = new AddNotNullConstraintEvent(notNullConstraints, true, this); |
| listener.onAddNotNullConstraint(addNotNullConstraintEvent); |
| } |
| } |
| endFunction("add_not_null_constraint", success, ex, constraintName); |
| } |
| } |
| |
| @Override |
| public void add_default_constraint(AddDefaultConstraintRequest req) throws MetaException, InvalidObjectException { |
| List<SQLDefaultConstraint> defaultConstraints = req.getDefaultConstraintCols(); |
| String constraintName = |
| CollectionUtils.isNotEmpty(defaultConstraints) ? defaultConstraints.get(0).getDc_name() : "null"; |
| startFunction("add_default_constraint", ": " + constraintName); |
| boolean success = false; |
| Exception ex = null; |
| if (!defaultConstraints.isEmpty() && !defaultConstraints.get(0).isSetCatName()) { |
| String defaultCat = getDefaultCatalog(conf); |
| defaultConstraints.forEach(pk -> pk.setCatName(defaultCat)); |
| } |
| RawStore ms = getMS(); |
| try { |
| ms.openTransaction(); |
| defaultConstraints = ms.addDefaultConstraints(defaultConstraints); |
| if (transactionalListeners.size() > 0) { |
| if (CollectionUtils.isNotEmpty(defaultConstraints)) { |
| AddDefaultConstraintEvent addDefaultConstraintEvent = |
| new AddDefaultConstraintEvent(defaultConstraints, true, this); |
| for (MetaStoreEventListener transactionalListener : transactionalListeners) { |
| transactionalListener.onAddDefaultConstraint(addDefaultConstraintEvent); |
| } |
| } |
| } |
| success = ms.commitTransaction(); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, InvalidObjectException.class).defaultMetaException(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } else if (CollectionUtils.isNotEmpty(defaultConstraints)) { |
| for (MetaStoreEventListener listener : listeners) { |
| AddDefaultConstraintEvent addDefaultConstraintEvent = |
| new AddDefaultConstraintEvent(defaultConstraints, true, this); |
| listener.onAddDefaultConstraint(addDefaultConstraintEvent); |
| } |
| } |
| endFunction("add_default_constraint", success, ex, constraintName); |
| } |
| } |
| |
| @Override |
| public void add_check_constraint(AddCheckConstraintRequest req) |
| throws MetaException, InvalidObjectException { |
| List<SQLCheckConstraint> checkConstraints= req.getCheckConstraintCols(); |
| String constraintName = CollectionUtils.isNotEmpty(checkConstraints) ? |
| checkConstraints.get(0).getDc_name() : "null"; |
| startFunction("add_check_constraint", ": " + constraintName); |
| boolean success = false; |
| Exception ex = null; |
| if (!checkConstraints.isEmpty() && !checkConstraints.get(0).isSetCatName()) { |
| String defaultCat = getDefaultCatalog(conf); |
| checkConstraints.forEach(pk -> pk.setCatName(defaultCat)); |
| } |
| RawStore ms = getMS(); |
| try { |
| ms.openTransaction(); |
| checkConstraints = ms.addCheckConstraints(checkConstraints); |
| if (transactionalListeners.size() > 0) { |
| if (CollectionUtils.isNotEmpty(checkConstraints)) { |
| AddCheckConstraintEvent addcheckConstraintEvent = new AddCheckConstraintEvent(checkConstraints, true, this); |
| for (MetaStoreEventListener transactionalListener : transactionalListeners) { |
| transactionalListener.onAddCheckConstraint(addcheckConstraintEvent); |
| } |
| } |
| } |
| success = ms.commitTransaction(); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, InvalidObjectException.class).defaultMetaException(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } else if (CollectionUtils.isNotEmpty(checkConstraints)) { |
| for (MetaStoreEventListener listener : listeners) { |
| AddCheckConstraintEvent addCheckConstraintEvent = new AddCheckConstraintEvent(checkConstraints, true, this); |
| listener.onAddCheckConstraint(addCheckConstraintEvent); |
| } |
| } |
| endFunction("add_check_constraint", success, ex, constraintName); |
| } |
| } |
| |
| private boolean is_table_exists(RawStore ms, String catName, String dbname, String name) |
| throws MetaException { |
| return (ms.getTable(catName, dbname, name, null) != null); |
| } |
| |
| private boolean drop_table_core(final RawStore ms, final String catName, final String dbname, |
| final String name, final boolean deleteData, |
| final EnvironmentContext envContext, final String indexName, boolean dropPartitions) |
| throws TException, IOException { |
| boolean success = false; |
| boolean tableDataShouldBeDeleted = false; |
| Path tblPath = null; |
| List<Path> partPaths = null; |
| Table tbl = null; |
| boolean ifPurge = false; |
| Map<String, String> transactionalListenerResponses = Collections.emptyMap(); |
| Database db = null; |
| boolean isReplicated = false; |
| try { |
| ms.openTransaction(); |
| |
| // HIVE-25282: Drop/Alter table in REMOTE db should fail |
| db = ms.getDatabase(catName, dbname); |
| if (db.getType() == DatabaseType.REMOTE) { |
| throw new MetaException("Drop table in REMOTE database " + db.getName() + " is not allowed"); |
| } |
| isReplicated = isDbReplicationTarget(db); |
| |
| // drop any partitions |
| GetTableRequest req = new GetTableRequest(dbname,name); |
| req.setCatName(catName); |
| tbl = get_table_core(req); |
| if (tbl == null) { |
| throw new NoSuchObjectException(name + " doesn't exist"); |
| } |
| |
| // Check if table is part of a materialized view. |
| // If it is, it cannot be dropped. |
| List<String> isPartOfMV = ms.isPartOfMaterializedView(catName, dbname, name); |
| if (!isPartOfMV.isEmpty()) { |
| throw new MetaException(String.format("Cannot drop table as it is used in the following materialized" + |
| " views %s%n", isPartOfMV)); |
| } |
| |
| if (tbl.getSd() == null) { |
| throw new MetaException("Table metadata is corrupted"); |
| } |
| ifPurge = isMustPurge(envContext, tbl); |
| |
| firePreEvent(new PreDropTableEvent(tbl, deleteData, this)); |
| |
| tableDataShouldBeDeleted = checkTableDataShouldBeDeleted(tbl, deleteData); |
| if (tbl.getSd().getLocation() != null) { |
| tblPath = new Path(tbl.getSd().getLocation()); |
| if (!wh.isWritable(tblPath.getParent())) { |
| String target = indexName == null ? "Table" : "Index table"; |
| throw new MetaException(target + " metadata not deleted since " + |
| tblPath.getParent() + " is not writable by " + |
| SecurityUtils.getUser()); |
| } |
| } |
| |
| // Drop the partitions and get a list of locations which need to be deleted |
| // In case of drop database cascade we need not to drop the partitions, they are already dropped. |
| if (dropPartitions) { |
| partPaths = dropPartitionsAndGetLocations(ms, catName, dbname, name, tblPath, tableDataShouldBeDeleted); |
| } |
| // Drop any constraints on the table |
| ms.dropConstraint(catName, dbname, name, null, true); |
| |
| if (!ms.dropTable(catName, dbname, name)) { |
| String tableName = TableName.getQualified(catName, dbname, name); |
| throw new MetaException(indexName == null ? "Unable to drop table " + tableName: |
| "Unable to drop index table " + tableName + " for index " + indexName); |
| } else { |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenerResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.DROP_TABLE, |
| new DropTableEvent(tbl, true, deleteData, |
| this, isReplicated), |
| envContext); |
| } |
| success = ms.commitTransaction(); |
| } |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } else if (tableDataShouldBeDeleted) { |
| // Data needs deletion. Check if trash may be skipped. |
| // Delete the data in the partitions which have other locations |
| deletePartitionData(partPaths, ifPurge, ReplChangeManager.shouldEnableCm(db, tbl)); |
| // Delete the data in the table |
| deleteTableData(tblPath, ifPurge, ReplChangeManager.shouldEnableCm(db, tbl)); |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.DROP_TABLE, |
| new DropTableEvent(tbl, success, deleteData, this, isReplicated), |
| envContext, |
| transactionalListenerResponses, ms); |
| } |
| } |
| return success; |
| } |
| |
| private boolean checkTableDataShouldBeDeleted(Table tbl, boolean deleteData) { |
| if (deleteData && isExternal(tbl)) { |
| // External table data can be deleted if EXTERNAL_TABLE_PURGE is true |
| return isExternalTablePurge(tbl); |
| } |
| return deleteData; |
| } |
| |
| /** |
| * Deletes the data in a table's location, if it fails logs an error |
| * |
| * @param tablePath |
| * @param ifPurge completely purge the table (skipping trash) while removing |
| * data from warehouse |
| * @param shouldEnableCm If cm should be enabled |
| */ |
| private void deleteTableData(Path tablePath, boolean ifPurge, boolean shouldEnableCm) { |
| if (tablePath != null) { |
| deleteDataExcludeCmroot(tablePath, ifPurge, shouldEnableCm); |
| } |
| } |
| |
| /** |
| * Deletes the data in a table's location, if it fails logs an error. |
| * |
| * @param tablePath |
| * @param ifPurge completely purge the table (skipping trash) while removing |
| * data from warehouse |
| * @param db Database |
| */ |
| private void deleteTableData(Path tablePath, boolean ifPurge, Database db) { |
| if (tablePath != null) { |
| try { |
| wh.deleteDir(tablePath, true, ifPurge, db); |
| } catch (Exception e) { |
| LOG.error("Failed to delete table directory: " + tablePath + |
| " " + e.getMessage()); |
| } |
| } |
| } |
| |
| /** |
| * Give a list of partitions' locations, tries to delete each one |
| * and for each that fails logs an error. |
| * |
| * @param partPaths |
| * @param ifPurge completely purge the partition (skipping trash) while |
| * removing data from warehouse |
| * @param shouldEnableCm If cm should be enabled |
| */ |
| private void deletePartitionData(List<Path> partPaths, boolean ifPurge, boolean shouldEnableCm) { |
| if (partPaths != null && !partPaths.isEmpty()) { |
| for (Path partPath : partPaths) { |
| deleteDataExcludeCmroot(partPath, ifPurge, shouldEnableCm); |
| } |
| } |
| } |
| |
| /** |
| * Give a list of partitions' locations, tries to delete each one |
| * and for each that fails logs an error. |
| * |
| * @param partPaths |
| * @param ifPurge completely purge the partition (skipping trash) while |
| * removing data from warehouse |
| * @param db Database |
| */ |
| private void deletePartitionData(List<Path> partPaths, boolean ifPurge, Database db) { |
| if (partPaths != null && !partPaths.isEmpty()) { |
| for (Path partPath : partPaths) { |
| try { |
| wh.deleteDir(partPath, true, ifPurge, db); |
| } catch (Exception e) { |
| LOG.error("Failed to delete partition directory: " + partPath + |
| " " + e.getMessage()); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Delete data from path excluding cmdir |
| * and for each that fails logs an error. |
| * |
| * @param path |
| * @param ifPurge completely purge the partition (skipping trash) while |
| * removing data from warehouse |
| * @param shouldEnableCm If cm should be enabled |
| */ |
| private void deleteDataExcludeCmroot(Path path, boolean ifPurge, boolean shouldEnableCm) { |
| try { |
| if (shouldEnableCm) { |
| //Don't delete cmdir if its inside the partition path |
| FileStatus[] statuses = path.getFileSystem(conf).listStatus(path, |
| ReplChangeManager.CMROOT_PATH_FILTER); |
| for (final FileStatus status : statuses) { |
| wh.deleteDir(status.getPath(), true, ifPurge, shouldEnableCm); |
| } |
| //Check if table directory is empty, delete it |
| FileStatus[] statusWithoutFilter = path.getFileSystem(conf).listStatus(path); |
| if (statusWithoutFilter.length == 0) { |
| wh.deleteDir(path, true, ifPurge, shouldEnableCm); |
| } |
| } else { |
| //If no cm delete the complete table directory |
| wh.deleteDir(path, true, ifPurge, shouldEnableCm); |
| } |
| } catch (Exception e) { |
| LOG.error("Failed to delete directory: " + path + |
| " " + e.getMessage()); |
| } |
| } |
| |
| /** |
| * Deletes the partitions specified by catName, dbName, tableName. If checkLocation is true, for |
| * locations of partitions which may not be subdirectories of tablePath checks to make sure the |
| * locations are writable. |
| * |
| * Drops the metadata for each partition. |
| * |
| * Provides a list of locations of partitions which may not be subdirectories of tablePath. |
| * |
| * @param ms RawStore to use for metadata retrieval and delete |
| * @param catName The catName |
| * @param dbName The dbName |
| * @param tableName The tableName |
| * @param tablePath The tablePath of which subdirectories does not have to be checked |
| * @param checkLocation Should we check the locations at all |
| * @return The list of the Path objects to delete (only in case checkLocation is true) |
| * @throws MetaException |
| * @throws IOException |
| * @throws NoSuchObjectException |
| */ |
| private List<Path> dropPartitionsAndGetLocations(RawStore ms, String catName, String dbName, |
| String tableName, Path tablePath, boolean checkLocation) |
| throws MetaException, IOException, NoSuchObjectException { |
| int batchSize = MetastoreConf.getIntVar(conf, ConfVars.BATCH_RETRIEVE_OBJECTS_MAX); |
| String tableDnsPath = null; |
| if (tablePath != null) { |
| tableDnsPath = wh.getDnsPath(tablePath).toString(); |
| } |
| |
| List<Path> partPaths = new ArrayList<>(); |
| while (true) { |
| List<String> partNames; |
| if (checkLocation) { |
| Map<String, String> partitionLocations = ms.getPartitionLocations(catName, dbName, tableName, |
| tableDnsPath, batchSize); |
| partNames = new ArrayList<>(partitionLocations.keySet()); |
| for (String partName : partNames) { |
| String pathString = partitionLocations.get(partName); |
| if (pathString != null) { |
| Path partPath = wh.getDnsPath(new Path(pathString)); |
| // Double check here. Maybe Warehouse.getDnsPath revealed relationship between the |
| // path objects |
| if (tableDnsPath == null || |
| !FileUtils.isSubdirectory(tableDnsPath, partPath.toString())) { |
| if (!wh.isWritable(partPath.getParent())) { |
| throw new MetaException("Table metadata not deleted since the partition " |
| + partName + " has parent location " + partPath.getParent() |
| + " which is not writable by " + SecurityUtils.getUser()); |
| } |
| partPaths.add(partPath); |
| } |
| } |
| } |
| } else { |
| partNames = ms.listPartitionNames(catName, dbName, tableName, (short) batchSize); |
| } |
| |
| if (partNames == null || partNames.isEmpty()) { |
| // No more partitions left to drop. Return with the collected path list to delete. |
| return partPaths; |
| } |
| |
| for (MetaStoreEventListener listener : listeners) { |
| //No drop part listener events fired for public listeners historically, for drop table case. |
| //Limiting to internal listeners for now, to avoid unexpected calls for public listeners. |
| if (listener instanceof HMSMetricsListener) { |
| for (@SuppressWarnings("unused") String partName : partNames) { |
| listener.onDropPartition(null); |
| } |
| } |
| } |
| |
| ms.dropPartitions(catName, dbName, tableName, partNames); |
| } |
| } |
| |
| @Override |
| public void drop_table(final String dbname, final String name, final boolean deleteData) |
| throws NoSuchObjectException, MetaException { |
| drop_table_with_environment_context(dbname, name, deleteData, null); |
| } |
| |
| @Override |
| public void drop_table_with_environment_context(final String dbname, final String name, final boolean deleteData, |
| final EnvironmentContext envContext) throws NoSuchObjectException, MetaException { |
| drop_table_with_environment_context(dbname, name, deleteData, envContext, true); |
| } |
| |
| private void drop_table_with_environment_context(final String dbname, final String name, final boolean deleteData, |
| final EnvironmentContext envContext, boolean dropPartitions) throws MetaException, NoSuchObjectException { |
| String[] parsedDbName = parseDbName(dbname, conf); |
| startTableFunction("drop_table", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], name); |
| |
| boolean success = false; |
| Exception ex = null; |
| try { |
| success = |
| drop_table_core(getMS(), parsedDbName[CAT_NAME], parsedDbName[DB_NAME], name, deleteData, envContext, null, dropPartitions); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, NoSuchObjectException.class) |
| .convertIfInstance(IOException.class, MetaException.class).defaultMetaException(); |
| } finally { |
| endFunction("drop_table", success, ex, name); |
| } |
| } |
| |
| private void updateStatsForTruncate(Map<String,String> props, EnvironmentContext environmentContext) { |
| if (null == props) { |
| return; |
| } |
| for (String stat : StatsSetupConst.SUPPORTED_STATS) { |
| String statVal = props.get(stat); |
| if (statVal != null) { |
| //In the case of truncate table, we set the stats to be 0. |
| props.put(stat, "0"); |
| } |
| } |
| //first set basic stats to true |
| StatsSetupConst.setBasicStatsState(props, StatsSetupConst.TRUE); |
| environmentContext.putToProperties(StatsSetupConst.STATS_GENERATED, StatsSetupConst.TASK); |
| environmentContext.putToProperties(StatsSetupConst.DO_NOT_POPULATE_QUICK_STATS, StatsSetupConst.TRUE); |
| //then invalidate column stats |
| StatsSetupConst.clearColumnStatsState(props); |
| return; |
| } |
| |
| private void alterPartitionForTruncate(RawStore ms, String catName, String dbName, String tableName, |
| Table table, Partition partition, String validWriteIds, long writeId) throws Exception { |
| EnvironmentContext environmentContext = new EnvironmentContext(); |
| updateStatsForTruncate(partition.getParameters(), environmentContext); |
| |
| if (!transactionalListeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.ALTER_PARTITION, |
| new AlterPartitionEvent(partition, partition, table, true, true, |
| writeId, this)); |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ALTER_PARTITION, |
| new AlterPartitionEvent(partition, partition, table, true, true, |
| writeId, this)); |
| } |
| |
| if (writeId > 0) { |
| partition.setWriteId(writeId); |
| } |
| alterHandler.alterPartition(ms, wh, catName, dbName, tableName, null, partition, |
| environmentContext, this, validWriteIds); |
| } |
| |
| private void alterTableStatsForTruncate(RawStore ms, String catName, String dbName, |
| String tableName, Table table, List<String> partNames, |
| String validWriteIds, long writeId) throws Exception { |
| if (partNames == null) { |
| if (0 != table.getPartitionKeysSize()) { |
| for (Partition partition : ms.getPartitions(catName, dbName, tableName, -1)) { |
| alterPartitionForTruncate(ms, catName, dbName, tableName, table, partition, |
| validWriteIds, writeId); |
| } |
| } else { |
| EnvironmentContext environmentContext = new EnvironmentContext(); |
| updateStatsForTruncate(table.getParameters(), environmentContext); |
| |
| boolean isReplicated = isDbReplicationTarget(ms.getDatabase(catName, dbName)); |
| if (!transactionalListeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.ALTER_TABLE, |
| new AlterTableEvent(table, table, true, true, |
| writeId, this, isReplicated)); |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ALTER_TABLE, |
| new AlterTableEvent(table, table, true, true, |
| writeId, this, isReplicated)); |
| } |
| |
| // TODO: this should actually pass thru and set writeId for txn stats. |
| if (writeId > 0) { |
| table.setWriteId(writeId); |
| } |
| alterHandler.alterTable(ms, wh, catName, dbName, tableName, table, |
| environmentContext, this, validWriteIds); |
| } |
| } else { |
| for (Partition partition : ms.getPartitionsByNames(catName, dbName, tableName, partNames)) { |
| alterPartitionForTruncate(ms, catName, dbName, tableName, table, partition, |
| validWriteIds, writeId); |
| } |
| } |
| return; |
| } |
| |
| private List<Path> getLocationsForTruncate(final RawStore ms, |
| final String catName, |
| final String dbName, |
| final String tableName, |
| final Table table, |
| final List<String> partNames) throws Exception { |
| List<Path> locations = new ArrayList<>(); |
| if (partNames == null) { |
| if (0 != table.getPartitionKeysSize()) { |
| for (Partition partition : ms.getPartitions(catName, dbName, tableName, -1)) { |
| locations.add(new Path(partition.getSd().getLocation())); |
| } |
| } else { |
| locations.add(new Path(table.getSd().getLocation())); |
| } |
| } else { |
| for (Partition partition : ms.getPartitionsByNames(catName, dbName, tableName, partNames)) { |
| locations.add(new Path(partition.getSd().getLocation())); |
| } |
| } |
| return locations; |
| } |
| |
| @Override |
| public CmRecycleResponse cm_recycle(final CmRecycleRequest request) throws MetaException { |
| wh.recycleDirToCmPath(new Path(request.getDataPath()), request.isPurge()); |
| return new CmRecycleResponse(); |
| } |
| |
| @Override |
| public void truncate_table(final String dbName, final String tableName, List<String> partNames) |
| throws NoSuchObjectException, MetaException { |
| // Deprecated path, won't work for txn tables. |
| truncateTableInternal(dbName, tableName, partNames, null, -1, null); |
| } |
| |
| @Override |
| public TruncateTableResponse truncate_table_req(TruncateTableRequest req) |
| throws MetaException, TException { |
| truncateTableInternal(req.getDbName(), req.getTableName(), req.getPartNames(), |
| req.getValidWriteIdList(), req.getWriteId(), req.getEnvironmentContext()); |
| return new TruncateTableResponse(); |
| } |
| |
| private void truncateTableInternal(String dbName, String tableName, List<String> partNames, |
| String validWriteIds, long writeId, EnvironmentContext context) throws MetaException, NoSuchObjectException { |
| boolean isSkipTrash = false, needCmRecycle = false; |
| try { |
| String[] parsedDbName = parseDbName(dbName, conf); |
| Table tbl = get_table_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName); |
| |
| boolean skipDataDeletion = Optional.ofNullable(context) |
| .map(EnvironmentContext::getProperties) |
| .map(prop -> prop.get(TRUNCATE_SKIP_DATA_DELETION)) |
| .map(Boolean::parseBoolean) |
| .orElse(false); |
| |
| if (TxnUtils.isTransactionalTable(tbl) || !skipDataDeletion) { |
| if (!skipDataDeletion) { |
| isSkipTrash = MetaStoreUtils.isSkipTrash(tbl.getParameters()); |
| |
| Database db = get_database_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME]); |
| needCmRecycle = ReplChangeManager.shouldEnableCm(db, tbl); |
| } |
| // This is not transactional |
| for (Path location : getLocationsForTruncate(getMS(), parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName, |
| tbl, partNames)) { |
| if (!skipDataDeletion) { |
| truncateDataFiles(location, isSkipTrash, needCmRecycle); |
| } else { |
| // For Acid tables we don't need to delete the old files, only write an empty baseDir. |
| // Compaction and cleaner will take care of the rest |
| addTruncateBaseFile(location, writeId, conf, DataFormat.TRUNCATED); |
| } |
| } |
| } |
| |
| // Alter the table/partition stats and also notify truncate table event |
| alterTableStatsForTruncate(getMS(), parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tableName, tbl, partNames, validWriteIds, writeId); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class, NoSuchObjectException.class) |
| .convertIfInstance(IOException.class, MetaException.class) |
| .defaultMetaException(); |
| } |
| } |
| |
| /** |
| * Add an empty baseDir with a truncate metadatafile |
| * @param location partition or table directory |
| * @param writeId allocated writeId |
| * @throws MetaException |
| */ |
| static void addTruncateBaseFile(Path location, long writeId, Configuration conf, DataFormat dataFormat) |
| throws MetaException { |
| if (location == null) |
| return; |
| |
| Path basePath = new Path(location, AcidConstants.baseDir(writeId)); |
| try { |
| FileSystem fs = location.getFileSystem(conf); |
| fs.mkdirs(basePath); |
| // We can not leave the folder empty, otherwise it will be skipped at some file listing in AcidUtils |
| // No need for a data file, a simple metadata is enough |
| AcidMetaDataFile.writeToFile(fs, basePath, dataFormat); |
| } catch (Exception e) { |
| throw newMetaException(e); |
| } |
| } |
| |
| private void truncateDataFiles(Path location, boolean isSkipTrash, boolean needCmRecycle) |
| throws IOException, MetaException { |
| FileSystem fs = location.getFileSystem(getConf()); |
| |
| if (!HdfsUtils.isPathEncrypted(getConf(), fs.getUri(), location) && |
| !FileUtils.pathHasSnapshotSubDir(location, fs)) { |
| HdfsUtils.HadoopFileStatus status = new HdfsUtils.HadoopFileStatus(getConf(), fs, location); |
| FileStatus targetStatus = fs.getFileStatus(location); |
| String targetGroup = targetStatus == null ? null : targetStatus.getGroup(); |
| |
| wh.deleteDir(location, true, isSkipTrash, needCmRecycle); |
| fs.mkdirs(location); |
| HdfsUtils.setFullFileStatus(getConf(), status, targetGroup, fs, location, false); |
| } else { |
| FileStatus[] statuses = fs.listStatus(location, FileUtils.HIDDEN_FILES_PATH_FILTER); |
| if (statuses == null || statuses.length == 0) { |
| return; |
| } |
| for (final FileStatus status : statuses) { |
| wh.deleteDir(status.getPath(), true, isSkipTrash, needCmRecycle); |
| } |
| } |
| } |
| |
| /** |
| * Is this an external table? |
| * |
| * @param table |
| * Check if this table is external. |
| * @return True if the table is external, otherwise false. |
| */ |
| private boolean isExternal(Table table) { |
| return MetaStoreUtils.isExternalTable(table); |
| } |
| |
| private boolean isExternalTablePurge(Table table) { |
| return MetaStoreUtils.isExternalTablePurge(table); |
| } |
| |
| @Override |
| @Deprecated |
| public Table get_table(final String dbname, final String name) throws MetaException, |
| NoSuchObjectException { |
| String[] parsedDbName = parseDbName(dbname, conf); |
| GetTableRequest getTableRequest = new GetTableRequest(parsedDbName[DB_NAME],name); |
| getTableRequest.setCatName(parsedDbName[CAT_NAME]); |
| return getTableInternal(getTableRequest); |
| } |
| |
| @Override |
| public List<ExtendedTableInfo> get_tables_ext(final GetTablesExtRequest req) throws MetaException { |
| List<String> tables = new ArrayList<String>(); |
| List<ExtendedTableInfo> ret = new ArrayList<ExtendedTableInfo>(); |
| String catalog = req.getCatalog(); |
| String database = req.getDatabase(); |
| String pattern = req.getTableNamePattern(); |
| List<String> processorCapabilities = req.getProcessorCapabilities(); |
| int limit = req.getLimit(); |
| String processorId = req.getProcessorIdentifier(); |
| List<Table> tObjects = new ArrayList<>(); |
| |
| startTableFunction("get_tables_ext", catalog, database, pattern); |
| Exception ex = null; |
| try { |
| tables = getMS().getTables(catalog, database, pattern, null, limit); |
| LOG.debug("get_tables_ext:getTables() returned " + tables.size()); |
| tables = FilterUtils.filterTableNamesIfEnabled(isServerFilterEnabled, filterHook, |
| catalog, database, tables); |
| |
| if (tables.size() > 0) { |
| tObjects = getMS().getTableObjectsByName(catalog, database, tables); |
| LOG.debug("get_tables_ext:getTableObjectsByName() returned " + tObjects.size()); |
| if (processorCapabilities == null || processorCapabilities.size() == 0 || |
| processorCapabilities.contains("MANAGERAWMETADATA")) { |
| LOG.info("Skipping translation for processor with " + processorId); |
| } else { |
| if (transformer != null) { |
| Map<Table, List<String>> retMap = transformer.transform(tObjects, processorCapabilities, processorId); |
| |
| for (Map.Entry<Table, List<String>> entry : retMap.entrySet()) { |
| LOG.debug("Table " + entry.getKey().getTableName() + " requires " + Arrays.toString((entry.getValue()).toArray())); |
| ret.add(convertTableToExtendedTable(entry.getKey(), entry.getValue(), req.getRequestedFields())); |
| } |
| } else { |
| for (Table table : tObjects) { |
| ret.add(convertTableToExtendedTable(table, processorCapabilities, req.getRequestedFields())); |
| } |
| } |
| } |
| } |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_tables_ext", ret != null, ex); |
| } |
| return ret; |
| } |
| |
| private ExtendedTableInfo convertTableToExtendedTable (Table table, |
| List<String> processorCapabilities, int mask) { |
| ExtendedTableInfo extTable = new ExtendedTableInfo(table.getTableName()); |
| if ((mask & GetTablesExtRequestFields.ACCESS_TYPE.getValue()) == GetTablesExtRequestFields.ACCESS_TYPE.getValue()) { |
| extTable.setAccessType(table.getAccessType()); |
| } |
| |
| if ((mask & GetTablesExtRequestFields.PROCESSOR_CAPABILITIES.getValue()) |
| == GetTablesExtRequestFields.PROCESSOR_CAPABILITIES.getValue()) { |
| extTable.setRequiredReadCapabilities(table.getRequiredReadCapabilities()); |
| extTable.setRequiredWriteCapabilities(table.getRequiredWriteCapabilities()); |
| } |
| |
| return extTable; |
| } |
| |
| @Override |
| public GetTableResult get_table_req(GetTableRequest req) throws MetaException, |
| NoSuchObjectException { |
| req.setCatName(req.isSetCatName() ? req.getCatName() : getDefaultCatalog(conf)); |
| return new GetTableResult(getTableInternal(req)); |
| } |
| |
| /** |
| * This function retrieves table from metastore. If getColumnStats flag is true, |
| * then engine should be specified so the table is retrieve with the column stats |
| * for that engine. |
| */ |
| private Table getTableInternal(GetTableRequest getTableRequest) throws MetaException, NoSuchObjectException { |
| |
| Preconditions.checkArgument(!getTableRequest.isGetColumnStats() || getTableRequest.getEngine() != null, |
| "To retrieve column statistics with a table, engine parameter cannot be null"); |
| |
| if (isInTest) { |
| assertClientHasCapability(getTableRequest.getCapabilities(), ClientCapability.TEST_CAPABILITY, "Hive tests", |
| "get_table_req"); |
| } |
| |
| Table t = null; |
| startTableFunction("get_table", getTableRequest.getCatName(), getTableRequest.getDbName(), |
| getTableRequest.getTblName()); |
| Exception ex = null; |
| try { |
| t = get_table_core(getTableRequest); |
| if (MetaStoreUtils.isInsertOnlyTableParam(t.getParameters())) { |
| assertClientHasCapability(getTableRequest.getCapabilities(), ClientCapability.INSERT_ONLY_TABLES, |
| "insert-only tables", "get_table_req"); |
| } |
| |
| if (CollectionUtils.isEmpty(getTableRequest.getProcessorCapabilities()) || getTableRequest |
| .getProcessorCapabilities().contains("MANAGERAWMETADATA")) { |
| LOG.info("Skipping translation for processor with " + getTableRequest.getProcessorIdentifier()); |
| } else { |
| if (transformer != null) { |
| List<Table> tList = new ArrayList<>(); |
| tList.add(t); |
| Map<Table, List<String>> ret = transformer |
| .transform(tList, getTableRequest.getProcessorCapabilities(), getTableRequest.getProcessorIdentifier()); |
| if (ret.size() > 1) { |
| LOG.warn("Unexpected resultset size:" + ret.size()); |
| throw new MetaException("Unexpected result from metadata transformer:return list size is " + ret.size()); |
| } |
| t = ret.keySet().iterator().next(); |
| } |
| } |
| |
| firePreEvent(new PreReadTableEvent(t, this)); |
| } catch (MetaException | NoSuchObjectException e) { |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_table", t != null, ex, getTableRequest.getTblName()); |
| } |
| return t; |
| } |
| |
| @Override |
| public List<TableMeta> get_table_meta(String dbnames, String tblNames, List<String> tblTypes) |
| throws MetaException, NoSuchObjectException { |
| List<TableMeta> t = null; |
| String[] parsedDbName = parseDbName(dbnames, conf); |
| startTableFunction("get_table_metas", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tblNames); |
| Exception ex = null; |
| try { |
| t = getMS().getTableMeta(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tblNames, tblTypes); |
| t = FilterUtils.filterTableMetasIfEnabled(isServerFilterEnabled, filterHook, t); |
| t = filterReadableTables(parsedDbName[CAT_NAME], t); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_table_metas", t != null, ex); |
| } |
| return t; |
| } |
| |
| @Override |
| @Deprecated |
| public Table get_table_core( |
| final String catName, |
| final String dbname, |
| final String name) |
| throws MetaException, NoSuchObjectException { |
| GetTableRequest getTableRequest = new GetTableRequest(dbname,name); |
| getTableRequest.setCatName(catName); |
| return get_table_core(getTableRequest); |
| } |
| |
| @Override |
| @Deprecated |
| public Table get_table_core( |
| final String catName, |
| final String dbname, |
| final String name, |
| final String writeIdList) |
| throws MetaException, NoSuchObjectException { |
| GetTableRequest getTableRequest = new GetTableRequest(dbname,name); |
| getTableRequest.setCatName(catName); |
| getTableRequest.setValidWriteIdList(writeIdList); |
| return get_table_core(getTableRequest); |
| } |
| |
| /** |
| * This function retrieves table from metastore. If getColumnStats flag is true, |
| * then engine should be specified so the table is retrieve with the column stats |
| * for that engine. |
| */ |
| @Override |
| public Table get_table_core(GetTableRequest getTableRequest) throws MetaException, NoSuchObjectException { |
| Preconditions.checkArgument(!getTableRequest.isGetColumnStats() || getTableRequest.getEngine() != null, |
| "To retrieve column statistics with a table, engine parameter cannot be null"); |
| String catName = getTableRequest.getCatName(); |
| String dbName = getTableRequest.getDbName(); |
| String tblName = getTableRequest.getTblName(); |
| Database db = null; |
| Table t = null; |
| try { |
| db = get_database_core(catName, dbName); |
| } catch (Exception e) { /* appears exception is not thrown currently if db doesnt exist */ } |
| |
| if (db != null) { |
| if (db.getType().equals(DatabaseType.REMOTE)) { |
| t = DataConnectorProviderFactory.getDataConnectorProvider(db).getTable(tblName); |
| if (t == null) { |
| throw new NoSuchObjectException(TableName.getQualified(catName, dbName, tblName) + " table not found"); |
| } |
| t.setDbName(dbName); |
| return t; |
| } |
| } |
| |
| try { |
| t = getMS().getTable(catName, dbName, tblName, getTableRequest.getValidWriteIdList(), getTableRequest.getId()); |
| if (t == null) { |
| throw new NoSuchObjectException(TableName.getQualified(catName, dbName, tblName) + " table not found"); |
| } |
| |
| // If column statistics was requested and is valid fetch it. |
| if (getTableRequest.isGetColumnStats()) { |
| ColumnStatistics colStats = getMS().getTableColumnStatistics(catName, dbName, tblName, |
| StatsSetupConst.getColumnsHavingStats(t.getParameters()), getTableRequest.getEngine(), |
| getTableRequest.getValidWriteIdList()); |
| if (colStats != null) { |
| t.setColStats(colStats); |
| } |
| } |
| } catch (Exception e) { |
| throwMetaException(e); |
| } |
| return t; |
| } |
| |
| /** |
| * Gets multiple tables from the hive metastore. |
| * |
| * @param dbName |
| * The name of the database in which the tables reside |
| * @param tableNames |
| * The names of the tables to get. |
| * |
| * @return A list of tables whose names are in the the list "names" and |
| * are retrievable from the database specified by "dbnames." |
| * There is no guarantee of the order of the returned tables. |
| * If there are duplicate names, only one instance of the table will be returned. |
| * @throws MetaException |
| * @throws InvalidOperationException |
| * @throws UnknownDBException |
| */ |
| @Override |
| @Deprecated |
| public List<Table> get_table_objects_by_name(final String dbName, final List<String> tableNames) |
| throws MetaException, InvalidOperationException, UnknownDBException { |
| String[] parsedDbName = parseDbName(dbName, conf); |
| return getTableObjectsInternal(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableNames, null, null, null); |
| } |
| |
| @Override |
| public GetTablesResult get_table_objects_by_name_req(GetTablesRequest req) throws TException { |
| String catName = req.isSetCatName() ? req.getCatName() : getDefaultCatalog(conf); |
| if (isDatabaseRemote(req.getDbName())) { |
| return new GetTablesResult(getRemoteTableObjectsInternal(req.getDbName(), req.getTblNames(), req.getTablesPattern())); |
| } |
| return new GetTablesResult(getTableObjectsInternal(catName, req.getDbName(), |
| req.getTblNames(), req.getCapabilities(), req.getProjectionSpec(), req.getTablesPattern())); |
| } |
| |
| private List<Table> filterTablesByName(List<Table> tables, List<String> tableNames) { |
| List<Table> filteredTables = new ArrayList<>(); |
| for (Table table : tables) { |
| if (tableNames.contains(table.getTableName())) { |
| filteredTables.add(table); |
| } |
| } |
| return filteredTables; |
| } |
| |
| private List<Table> getRemoteTableObjectsInternal(String dbname, List<String> tableNames, String pattern) throws MetaException { |
| String[] parsedDbName = parseDbName(dbname, conf); |
| try { |
| // retrieve tables from remote database |
| Database db = get_database_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME]); |
| List<Table> tables = DataConnectorProviderFactory.getDataConnectorProvider(db).getTables(null); |
| |
| // filtered out undesired tables |
| if (tableNames != null) { |
| tables = filterTablesByName(tables, tableNames); |
| } |
| |
| // set remote tables' local hive database reference |
| for (Table table : tables) { |
| table.setDbName(dbname); |
| } |
| |
| return FilterUtils.filterTablesIfEnabled(isServerFilterEnabled, filterHook, tables); |
| } catch (Exception e) { |
| LOG.warn("Unexpected exception while getting table(s) in remote database " + dbname , e); |
| return new ArrayList<Table>(); |
| } |
| } |
| |
| private List<Table> getTableObjectsInternal(String catName, String dbName, |
| List<String> tableNames, |
| ClientCapabilities capabilities, |
| GetProjectionsSpec projectionsSpec, String tablePattern) |
| throws MetaException, InvalidOperationException, UnknownDBException { |
| if (isInTest) { |
| assertClientHasCapability(capabilities, ClientCapability.TEST_CAPABILITY, |
| "Hive tests", "get_table_objects_by_name_req"); |
| } |
| |
| if (projectionsSpec != null) { |
| if (!projectionsSpec.isSetFieldList() && (projectionsSpec.isSetIncludeParamKeyPattern() || |
| projectionsSpec.isSetExcludeParamKeyPattern())) { |
| throw new InvalidOperationException("Include and Exclude Param key are not supported."); |
| } |
| } |
| |
| List<Table> tables = new ArrayList<>(); |
| startMultiTableFunction("get_multi_table", dbName, tableNames); |
| Exception ex = null; |
| int tableBatchSize = MetastoreConf.getIntVar(conf, |
| ConfVars.BATCH_RETRIEVE_MAX); |
| |
| try { |
| if (dbName == null || dbName.isEmpty()) { |
| throw new UnknownDBException("DB name is null or empty"); |
| } |
| RawStore ms = getMS(); |
| if(tablePattern != null){ |
| tables = ms.getTableObjectsByName(catName, dbName, tableNames, projectionsSpec, tablePattern); |
| }else { |
| if (tableNames == null) { |
| throw new InvalidOperationException(dbName + " cannot find null tables"); |
| } |
| |
| // The list of table names could contain duplicates. RawStore.getTableObjectsByName() |
| // only guarantees returning no duplicate table objects in one batch. If we need |
| // to break into multiple batches, remove duplicates first. |
| List<String> distinctTableNames = tableNames; |
| if (distinctTableNames.size() > tableBatchSize) { |
| List<String> lowercaseTableNames = new ArrayList<>(); |
| for (String tableName : tableNames) { |
| lowercaseTableNames.add(normalizeIdentifier(tableName)); |
| } |
| distinctTableNames = new ArrayList<>(new HashSet<>(lowercaseTableNames)); |
| } |
| |
| int startIndex = 0; |
| // Retrieve the tables from the metastore in batches. Some databases like |
| // Oracle cannot have over 1000 expressions in a in-list |
| while (startIndex < distinctTableNames.size()) { |
| int endIndex = Math.min(startIndex + tableBatchSize, distinctTableNames.size()); |
| tables.addAll(ms.getTableObjectsByName(catName, dbName, distinctTableNames.subList( |
| startIndex, endIndex), projectionsSpec, tablePattern)); |
| startIndex = endIndex; |
| } |
| } |
| for (Table t : tables) { |
| if (t.getParameters() != null && MetaStoreUtils.isInsertOnlyTableParam(t.getParameters())) { |
| assertClientHasCapability(capabilities, ClientCapability.INSERT_ONLY_TABLES, |
| "insert-only tables", "get_table_req"); |
| } |
| } |
| |
| tables = FilterUtils.filterTablesIfEnabled(isServerFilterEnabled, filterHook, tables); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e) |
| .throwIfInstance(MetaException.class, InvalidOperationException.class, UnknownDBException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("get_multi_table", tables != null, ex, join(tableNames, ",")); |
| } |
| return tables; |
| } |
| |
| @Override |
| public Materialization get_materialization_invalidation_info(final CreationMetadata cm, final String validTxnList) throws MetaException { |
| return getTxnHandler().getMaterializationInvalidationInfo(cm, validTxnList); |
| } |
| |
| @Override |
| public void update_creation_metadata(String catName, final String dbName, final String tableName, CreationMetadata cm) throws MetaException { |
| getMS().updateCreationMetadata(catName, dbName, tableName, cm); |
| } |
| |
| private void assertClientHasCapability(ClientCapabilities client, |
| ClientCapability value, String what, String call) throws MetaException { |
| if (!doesClientHaveCapability(client, value)) { |
| throw new MetaException("Your client does not appear to support " + what + ". To skip" |
| + " capability checks, please set " + ConfVars.CAPABILITY_CHECK.toString() |
| + " to false. This setting can be set globally, or on the client for the current" |
| + " metastore session. Note that this may lead to incorrect results, data loss," |
| + " undefined behavior, etc. if your client is actually incompatible. You can also" |
| + " specify custom client capabilities via " + call + " API."); |
| } |
| } |
| |
| private boolean doesClientHaveCapability(ClientCapabilities client, ClientCapability value) { |
| if (!MetastoreConf.getBoolVar(getConf(), ConfVars.CAPABILITY_CHECK)) { |
| return true; |
| } |
| return (client != null && client.isSetValues() && client.getValues().contains(value)); |
| } |
| |
| @Override |
| public List<String> get_table_names_by_filter( |
| final String dbName, final String filter, final short maxTables) |
| throws MetaException, InvalidOperationException, UnknownDBException { |
| List<String> tables = null; |
| startFunction("get_table_names_by_filter", ": db = " + dbName + ", filter = " + filter); |
| Exception ex = null; |
| String[] parsedDbName = parseDbName(dbName, conf); |
| try { |
| if (parsedDbName[CAT_NAME] == null || parsedDbName[CAT_NAME].isEmpty() || |
| parsedDbName[DB_NAME] == null || parsedDbName[DB_NAME].isEmpty()) { |
| throw new UnknownDBException("DB name is null or empty"); |
| } |
| if (filter == null) { |
| throw new InvalidOperationException(filter + " cannot apply null filter"); |
| } |
| tables = getMS().listTableNamesByFilter(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], filter, maxTables); |
| tables = FilterUtils.filterTableNamesIfEnabled( |
| isServerFilterEnabled, filterHook, parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tables); |
| |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e) |
| .throwIfInstance(MetaException.class, InvalidOperationException.class, UnknownDBException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("get_table_names_by_filter", tables != null, ex, join(tables, ",")); |
| } |
| return tables; |
| } |
| |
| private Partition append_partition_common(RawStore ms, String catName, String dbName, |
| String tableName, List<String> part_vals, |
| EnvironmentContext envContext) |
| throws InvalidObjectException, AlreadyExistsException, MetaException, NoSuchObjectException { |
| |
| Partition part = new Partition(); |
| boolean success = false, madeDir = false; |
| Path partLocation = null; |
| Table tbl = null; |
| Map<String, String> transactionalListenerResponses = Collections.emptyMap(); |
| Database db = null; |
| try { |
| ms.openTransaction(); |
| part.setCatName(catName); |
| part.setDbName(dbName); |
| part.setTableName(tableName); |
| part.setValues(part_vals); |
| |
| MetaStoreServerUtils.validatePartitionNameCharacters(part_vals, partitionValidationPattern); |
| |
| tbl = ms.getTable(part.getCatName(), part.getDbName(), part.getTableName(), null); |
| if (tbl == null) { |
| throw new InvalidObjectException( |
| "Unable to add partition because table or database do not exist"); |
| } |
| if (tbl.getSd().getLocation() == null) { |
| throw new MetaException( |
| "Cannot append a partition to a view"); |
| } |
| |
| db = get_database_core(catName, dbName); |
| |
| firePreEvent(new PreAddPartitionEvent(tbl, part, this)); |
| |
| part.setSd(tbl.getSd().deepCopy()); |
| partLocation = new Path(tbl.getSd().getLocation(), Warehouse |
| .makePartName(tbl.getPartitionKeys(), part_vals)); |
| part.getSd().setLocation(partLocation.toString()); |
| |
| Partition old_part; |
| try { |
| old_part = ms.getPartition(part.getCatName(), part.getDbName(), part |
| .getTableName(), part.getValues()); |
| } catch (NoSuchObjectException e) { |
| // this means there is no existing partition |
| old_part = null; |
| } |
| if (old_part != null) { |
| throw new AlreadyExistsException("Partition already exists:" + part); |
| } |
| |
| if (!wh.isDir(partLocation)) { |
| if (!wh.mkdirs(partLocation)) { |
| throw new MetaException(partLocation |
| + " is not a directory or unable to create one"); |
| } |
| madeDir = true; |
| } |
| |
| // set create time |
| long time = System.currentTimeMillis() / 1000; |
| part.setCreateTime((int) time); |
| part.putToParameters(hive_metastoreConstants.DDL_TIME, Long.toString(time)); |
| |
| if (canUpdateStats(tbl)) { |
| MetaStoreServerUtils.updatePartitionStatsFast(part, tbl, wh, madeDir, false, envContext, true); |
| } |
| |
| if (ms.addPartition(part)) { |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenerResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.ADD_PARTITION, |
| new AddPartitionEvent(tbl, part, true, this), |
| envContext); |
| } |
| |
| success = ms.commitTransaction(); |
| } |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| if (madeDir) { |
| wh.deleteDir(partLocation, true, false, ReplChangeManager.shouldEnableCm(db, tbl)); |
| } |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ADD_PARTITION, |
| new AddPartitionEvent(tbl, part, success, this), |
| envContext, |
| transactionalListenerResponses, ms); |
| } |
| } |
| return part; |
| } |
| |
| private void firePreEvent(PreEventContext event) throws MetaException { |
| for (MetaStorePreEventListener listener : preListeners) { |
| try { |
| listener.onEvent(event); |
| } catch (NoSuchObjectException e) { |
| throw new MetaException(e.getMessage()); |
| } catch (InvalidOperationException e) { |
| throw new MetaException(e.getMessage()); |
| } |
| } |
| } |
| |
| @Override |
| public Partition append_partition(final String dbName, final String tableName, |
| final List<String> part_vals) throws InvalidObjectException, |
| AlreadyExistsException, MetaException { |
| return append_partition_with_environment_context(dbName, tableName, part_vals, null); |
| } |
| |
| @Override |
| public Partition append_partition_with_environment_context(final String dbName, |
| final String tableName, final List<String> part_vals, final EnvironmentContext envContext) |
| throws InvalidObjectException, AlreadyExistsException, MetaException { |
| if (part_vals == null || part_vals.isEmpty()) { |
| throw new MetaException("The partition values must not be null or empty."); |
| } |
| String[] parsedDbName = parseDbName(dbName, conf); |
| startPartitionFunction("append_partition", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName, part_vals); |
| if (LOG.isDebugEnabled()) { |
| for (String part : part_vals) { |
| LOG.debug(part); |
| } |
| } |
| |
| Partition ret = null; |
| Exception ex = null; |
| try { |
| ret = append_partition_common(getMS(), parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName, part_vals, envContext); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e) |
| .throwIfInstance(MetaException.class, InvalidObjectException.class, AlreadyExistsException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("append_partition", ret != null, ex, tableName); |
| } |
| return ret; |
| } |
| |
| private static class PartValEqWrapperLite { |
| List<String> values; |
| String location; |
| |
| PartValEqWrapperLite(Partition partition) { |
| this.values = partition.isSetValues()? partition.getValues() : null; |
| if (partition.getSd() != null) { |
| this.location = partition.getSd().getLocation(); |
| } |
| } |
| |
| @Override |
| public int hashCode() { |
| return values == null ? 0 : values.hashCode(); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null || !(obj instanceof PartValEqWrapperLite)) { |
| return false; |
| } |
| |
| List<String> lhsValues = this.values; |
| List<String> rhsValues = ((PartValEqWrapperLite)obj).values; |
| |
| if (lhsValues == null || rhsValues == null) { |
| return lhsValues == rhsValues; |
| } |
| |
| if (lhsValues.size() != rhsValues.size()) { |
| return false; |
| } |
| |
| for (int i=0; i<lhsValues.size(); ++i) { |
| String lhsValue = lhsValues.get(i); |
| String rhsValue = rhsValues.get(i); |
| |
| if ((lhsValue == null && rhsValue != null) |
| || (lhsValue != null && !lhsValue.equals(rhsValue))) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| } |
| |
| private List<Partition> add_partitions_core(final RawStore ms, String catName, |
| String dbName, String tblName, List<Partition> parts, final boolean ifNotExists) |
| throws TException { |
| logAndAudit("add_partitions"); |
| boolean success = false; |
| // Ensures that the list doesn't have dups, and keeps track of directories we have created. |
| final Map<PartValEqWrapperLite, Boolean> addedPartitions = new ConcurrentHashMap<>(); |
| final List<Partition> newParts = new ArrayList<>(); |
| final List<Partition> existingParts = new ArrayList<>(); |
| Table tbl = null; |
| Map<String, String> transactionalListenerResponses = Collections.emptyMap(); |
| Database db = null; |
| |
| List<ColumnStatistics> partsColStats = new ArrayList<>(parts.size()); |
| List<Long> partsWriteIds = new ArrayList<>(parts.size()); |
| |
| throwUnsupportedExceptionIfRemoteDB(dbName, "add_partitions"); |
| |
| Lock tableLock = getTableLockFor(dbName, tblName); |
| tableLock.lock(); |
| try { |
| ms.openTransaction(); |
| tbl = ms.getTable(catName, dbName, tblName, null); |
| if (tbl == null) { |
| throw new InvalidObjectException("Unable to add partitions because " |
| + TableName.getQualified(catName, dbName, tblName) + |
| " does not exist"); |
| } |
| MTable mTable = getMS().ensureGetMTable(catName, dbName, tblName); |
| db = ms.getDatabase(catName, dbName); |
| |
| if (!parts.isEmpty()) { |
| firePreEvent(new PreAddPartitionEvent(tbl, parts, this)); |
| } |
| |
| Set<PartValEqWrapperLite> partsToAdd = new HashSet<>(parts.size()); |
| List<Partition> partitionsToAdd = new ArrayList<>(parts.size()); |
| List<FieldSchema> partitionKeys = tbl.getPartitionKeys(); |
| for (final Partition part : parts) { |
| // Collect partition column stats to be updated if present. Partition objects passed down |
| // here at the time of replication may have statistics in them, which is required to be |
| // updated in the metadata. But we don't want it to be part of the Partition object when |
| // it's being created or altered, lest it becomes part of the notification event. |
| if (part.isSetColStats()) { |
| partsColStats.add(part.getColStats()); |
| part.unsetColStats(); |
| partsWriteIds.add(part.getWriteId()); |
| } |
| |
| // Iterate through the partitions and validate them. If one of the partitions is |
| // incorrect, an exception will be thrown before the threads which create the partition |
| // folders are submitted. This way we can be sure that no partition and no partition |
| // folder will be created if the list contains an invalid partition. |
| if (validatePartition(part, catName, tblName, dbName, partsToAdd, ms, ifNotExists, |
| partitionKeys)) { |
| partitionsToAdd.add(part); |
| } else { |
| existingParts.add(part); |
| } |
| } |
| |
| newParts.addAll(createPartitionFolders(partitionsToAdd, tbl, addedPartitions)); |
| |
| if (!newParts.isEmpty()) { |
| ms.addPartitions(catName, dbName, tblName, newParts); |
| } |
| |
| // Notification is generated for newly created partitions only. The subset of partitions |
| // that already exist (existingParts), will not generate notifications. |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenerResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.ADD_PARTITION, |
| new AddPartitionEvent(tbl, newParts, true, this)); |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ADD_PARTITION, |
| new AddPartitionEvent(tbl, newParts, true, this), |
| null, |
| transactionalListenerResponses, ms); |
| |
| if (!existingParts.isEmpty()) { |
| // The request has succeeded but we failed to add these partitions. |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ADD_PARTITION, |
| new AddPartitionEvent(tbl, existingParts, false, this), |
| null, null, ms); |
| } |
| } |
| |
| // Update partition column statistics if available. We need a valid writeId list to |
| // update column statistics for a transactional table. But during bootstrap replication, |
| // where we use this feature, we do not have a valid writeId list which was used to |
| // update the stats. But we know for sure that the writeId associated with the stats was |
| // valid then (otherwise stats update would have failed on the source). So, craft a valid |
| // transaction list with only that writeId and use it to update the stats. |
| int cnt = 0; |
| for (ColumnStatistics partColStats: partsColStats) { |
| long writeId = partsWriteIds.get(cnt++); |
| String validWriteIds = null; |
| if (writeId > 0) { |
| ValidWriteIdList validWriteIdList = |
| new ValidReaderWriteIdList(TableName.getDbTable(tbl.getDbName(), |
| tbl.getTableName()), |
| new long[0], new BitSet(), writeId); |
| validWriteIds = validWriteIdList.toString(); |
| } |
| updatePartitonColStatsInternal(tbl, mTable, partColStats, validWriteIds, writeId); |
| } |
| |
| success = ms.commitTransaction(); |
| } finally { |
| try { |
| if (!success) { |
| ms.rollbackTransaction(); |
| cleanupPartitionFolders(addedPartitions, db); |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ADD_PARTITION, |
| new AddPartitionEvent(tbl, parts, false, this), |
| null, null, ms); |
| } |
| } |
| } finally { |
| tableLock.unlock(); |
| } |
| } |
| |
| return newParts; |
| } |
| |
| private Lock getTableLockFor(String dbName, String tblName) { |
| return tablelocks.get(dbName + "." + tblName); |
| } |
| |
| /** |
| * Remove the newly created partition folders. The values in the addedPartitions map indicates |
| * whether or not the location of the partition was newly created. If the value is false, the |
| * partition folder will not be removed. |
| * @param addedPartitions |
| * @throws MetaException |
| * @throws IllegalArgumentException |
| */ |
| private void cleanupPartitionFolders(final Map<PartValEqWrapperLite, Boolean> addedPartitions, |
| Database db) throws MetaException, IllegalArgumentException { |
| for (Map.Entry<PartValEqWrapperLite, Boolean> e : addedPartitions.entrySet()) { |
| if (e.getValue()) { |
| // we just created this directory - it's not a case of pre-creation, so we nuke. |
| wh.deleteDir(new Path(e.getKey().location), true, db); |
| } |
| } |
| } |
| |
| /** |
| * Validate a partition before creating it. The validation checks |
| * <ul> |
| * <li>if the database and table names set in the partition are not null and they are matching |
| * with the expected values set in the tblName and dbName parameters.</li> |
| * <li>if the partition values are set.</li> |
| * <li>if none of the partition values is null.</li> |
| * <li>if the partition values are matching with the pattern set in the |
| * 'metastore.partition.name.whitelist.pattern' configuration property.</li> |
| * <li>if the partition doesn't already exist. If the partition already exists, an exception |
| * will be thrown if the ifNotExists parameter is false, otherwise it will be just ignored.</li> |
| * <li>if the partsToAdd set doesn't contain the partition. The partsToAdd set contains the |
| * partitions which are already validated. If the set contains the current partition, it means |
| * that the partition is tried to be added multiple times in the same batch. Please note that |
| * the set will be updated with the current partition if the validation was successful.</li> |
| * </ul> |
| * @param part |
| * @param catName |
| * @param tblName |
| * @param dbName |
| * @param partsToAdd |
| * @param ms |
| * @param ifNotExists |
| * @return |
| * @throws MetaException |
| * @throws TException |
| */ |
| private boolean validatePartition(final Partition part, final String catName, |
| final String tblName, final String dbName, final Set<PartValEqWrapperLite> partsToAdd, |
| final RawStore ms, final boolean ifNotExists, List<FieldSchema> partitionKeys) throws MetaException, TException { |
| |
| if (part.getDbName() == null || part.getTableName() == null) { |
| throw new MetaException("The database and table name must be set in the partition."); |
| } |
| |
| if (!part.getTableName().equalsIgnoreCase(tblName) |
| || !part.getDbName().equalsIgnoreCase(dbName)) { |
| String errorMsg = String.format( |
| "Partition does not belong to target table %s. It belongs to the table %s.%s : %s", |
| TableName.getQualified(catName, dbName, tblName), part.getDbName(), |
| part.getTableName(), part.toString()); |
| throw new MetaException(errorMsg); |
| } |
| |
| if (part.getValues() == null || part.getValues().isEmpty()) { |
| throw new MetaException("The partition values cannot be null or empty."); |
| } |
| |
| if (part.getValues().contains(null)) { |
| throw new MetaException("Partition value cannot be null."); |
| } |
| |
| boolean shouldAdd = startAddPartition(ms, part, partitionKeys, ifNotExists); |
| if (!shouldAdd) { |
| LOG.info("Not adding partition {} as it already exists", part); |
| return false; |
| } |
| |
| if (!partsToAdd.add(new PartValEqWrapperLite(part))) { |
| // Technically, for ifNotExists case, we could insert one and discard the other |
| // because the first one now "exists", but it seems better to report the problem |
| // upstream as such a command doesn't make sense. |
| throw new MetaException("Duplicate partitions in the list: " + part); |
| } |
| return true; |
| } |
| |
| /** |
| * Create the location folders for the partitions. For each partition a separate thread will be |
| * started to create the folder. The method will wait until all threads are finished and returns |
| * the partitions whose folders were created successfully. If an error occurs during the |
| * execution of a thread, a MetaException will be thrown. |
| * @param partitionsToAdd |
| * @param table |
| * @param addedPartitions |
| * @return |
| * @throws MetaException |
| */ |
| private List<Partition> createPartitionFolders(final List<Partition> partitionsToAdd, |
| final Table table, final Map<PartValEqWrapperLite, Boolean> addedPartitions) |
| throws MetaException { |
| |
| final AtomicBoolean failureOccurred = new AtomicBoolean(false); |
| final List<Future<Partition>> partFutures = new ArrayList<>(partitionsToAdd.size()); |
| final Map<PartValEqWrapperLite, Boolean> addedParts = new ConcurrentHashMap<>(); |
| |
| final UserGroupInformation ugi; |
| try { |
| ugi = UserGroupInformation.getCurrentUser(); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| |
| for (final Partition partition : partitionsToAdd) { |
| initializePartitionParameters(table, partition); |
| |
| partFutures.add(threadPool.submit(() -> { |
| if (failureOccurred.get()) { |
| return null; |
| } |
| ugi.doAs((PrivilegedExceptionAction<Partition>) () -> { |
| try { |
| boolean madeDir = createLocationForAddedPartition(table, partition); |
| addedParts.put(new PartValEqWrapperLite(partition), madeDir); |
| initializeAddedPartition(table, partition, madeDir, null); |
| } catch (MetaException e) { |
| throw new IOException(e.getMessage(), e); |
| } |
| return null; |
| }); |
| return partition; |
| })); |
| } |
| |
| List<Partition> newParts = new ArrayList<>(partitionsToAdd.size()); |
| String errorMessage = null; |
| for (Future<Partition> partFuture : partFutures) { |
| try { |
| Partition part = partFuture.get(); |
| if (part != null && !failureOccurred.get()) { |
| newParts.add(part); |
| } |
| } catch (ExecutionException e) { |
| // If an exception is thrown in the execution of a task, set the failureOccurred flag to |
| // true. This flag is visible in the tasks and if its value is true, the partition |
| // folders won't be created. |
| // Then iterate through the remaining tasks and wait for them to finish. The tasks which |
| // are started before the flag got set will then finish creating the partition folders. |
| // The tasks which are started after the flag got set, won't create the partition |
| // folders, to avoid unnecessary work. |
| // This way it is sure that all tasks are finished, when entering the finally part where |
| // the partition folders are cleaned up. It won't happen that a task is still running |
| // when cleaning up the folders, so it is sure we won't have leftover folders. |
| // Canceling the other tasks would be also an option but during testing it turned out |
| // that it is not a trustworthy solution to avoid leftover folders. |
| failureOccurred.compareAndSet(false, true); |
| errorMessage = e.getMessage(); |
| } catch (InterruptedException e) { |
| failureOccurred.compareAndSet(false, true); |
| errorMessage = e.getMessage(); |
| // Restore interruption status of the corresponding thread |
| Thread.currentThread().interrupt(); |
| } |
| } |
| |
| addedPartitions.putAll(addedParts); |
| if (failureOccurred.get()) { |
| throw new MetaException(errorMessage); |
| } |
| |
| return newParts; |
| } |
| |
| @Override |
| public AddPartitionsResult add_partitions_req(AddPartitionsRequest request) |
| throws TException { |
| AddPartitionsResult result = new AddPartitionsResult(); |
| if (request.getParts().isEmpty()) { |
| return result; |
| } |
| try { |
| if (!request.isSetCatName()) { |
| request.setCatName(getDefaultCatalog(conf)); |
| } |
| // Make sure all of the partitions have the catalog set as well |
| request.getParts().forEach(p -> { |
| if (!p.isSetCatName()) { |
| p.setCatName(getDefaultCatalog(conf)); |
| } |
| }); |
| List<Partition> parts = add_partitions_core(getMS(), request.getCatName(), request.getDbName(), |
| request.getTblName(), request.getParts(), request.isIfNotExists()); |
| if (request.isNeedResult()) { |
| result.setPartitions(parts); |
| } |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(TException.class).defaultMetaException(); |
| } |
| return result; |
| } |
| |
| @Override |
| public int add_partitions(final List<Partition> parts) throws MetaException, |
| InvalidObjectException, AlreadyExistsException { |
| startFunction("add_partition"); |
| if (parts == null) { |
| throw new MetaException("Partition list cannot be null."); |
| } |
| if (parts.isEmpty()) { |
| return 0; |
| } |
| |
| Integer ret = null; |
| Exception ex = null; |
| try { |
| // Old API assumed all partitions belong to the same table; keep the same assumption |
| if (!parts.get(0).isSetCatName()) { |
| String defaultCat = getDefaultCatalog(conf); |
| for (Partition p : parts) { |
| p.setCatName(defaultCat); |
| } |
| } |
| ret = add_partitions_core(getMS(), parts.get(0).getCatName(), parts.get(0).getDbName(), |
| parts.get(0).getTableName(), parts, false).size(); |
| assert ret == parts.size(); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e) |
| .throwIfInstance(MetaException.class, InvalidObjectException.class, AlreadyExistsException.class) |
| .defaultMetaException(); |
| } finally { |
| String tableName = parts.get(0).getTableName(); |
| endFunction("add_partition", ret != null, ex, tableName); |
| } |
| return ret; |
| } |
| |
| @Override |
| public int add_partitions_pspec(final List<PartitionSpec> partSpecs) |
| throws TException { |
| logAndAudit("add_partitions_pspec"); |
| |
| if (partSpecs.isEmpty()) { |
| return 0; |
| } |
| |
| String dbName = partSpecs.get(0).getDbName(); |
| String tableName = partSpecs.get(0).getTableName(); |
| // If the catalog name isn't set, we need to go through and set it. |
| String catName; |
| if (!partSpecs.get(0).isSetCatName()) { |
| catName = getDefaultCatalog(conf); |
| partSpecs.forEach(ps -> ps.setCatName(catName)); |
| } else { |
| catName = partSpecs.get(0).getCatName(); |
| } |
| |
| return add_partitions_pspec_core(getMS(), catName, dbName, tableName, partSpecs, false); |
| } |
| |
| private int add_partitions_pspec_core(RawStore ms, String catName, String dbName, |
| String tblName, List<PartitionSpec> partSpecs, |
| boolean ifNotExists) |
| throws TException { |
| boolean success = false; |
| if (dbName == null || tblName == null) { |
| throw new MetaException("The database and table name cannot be null."); |
| } |
| // Ensures that the list doesn't have dups, and keeps track of directories we have created. |
| final Map<PartValEqWrapperLite, Boolean> addedPartitions = new ConcurrentHashMap<>(); |
| PartitionSpecProxy partitionSpecProxy = PartitionSpecProxy.Factory.get(partSpecs); |
| final PartitionSpecProxy.PartitionIterator partitionIterator = partitionSpecProxy |
| .getPartitionIterator(); |
| Table tbl = null; |
| Map<String, String> transactionalListenerResponses = Collections.emptyMap(); |
| Database db = null; |
| Lock tableLock = getTableLockFor(dbName, tblName); |
| tableLock.lock(); |
| try { |
| ms.openTransaction(); |
| try { |
| db = ms.getDatabase(catName, dbName); |
| } catch (NoSuchObjectException notExists) { |
| throw new InvalidObjectException("Unable to add partitions because " |
| + "database or table " + dbName + "." + tblName + " does not exist"); |
| } |
| if (db.getType() == DatabaseType.REMOTE) { |
| throw new MetaException("Operation add_partitions_pspec not supported on tables in REMOTE database"); |
| } |
| tbl = ms.getTable(catName, dbName, tblName, null); |
| if (tbl == null) { |
| throw new InvalidObjectException("Unable to add partitions because " |
| + "database or table " + dbName + "." + tblName + " does not exist"); |
| } |
| firePreEvent(new PreAddPartitionEvent(tbl, partitionSpecProxy, this)); |
| Set<PartValEqWrapperLite> partsToAdd = new HashSet<>(partitionSpecProxy.size()); |
| List<Partition> partitionsToAdd = new ArrayList<>(partitionSpecProxy.size()); |
| List<FieldSchema> partitionKeys = tbl.getPartitionKeys(); |
| while (partitionIterator.hasNext()) { |
| // Iterate through the partitions and validate them. If one of the partitions is |
| // incorrect, an exception will be thrown before the threads which create the partition |
| // folders are submitted. This way we can be sure that no partition or partition folder |
| // will be created if the list contains an invalid partition. |
| final Partition part = partitionIterator.getCurrent(); |
| if (validatePartition(part, catName, tblName, dbName, partsToAdd, ms, ifNotExists, |
| partitionKeys)) { |
| partitionsToAdd.add(part); |
| } |
| partitionIterator.next(); |
| } |
| |
| createPartitionFolders(partitionsToAdd, tbl, addedPartitions); |
| |
| ms.addPartitions(catName, dbName, tblName, partitionSpecProxy, ifNotExists); |
| |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenerResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.ADD_PARTITION, |
| new AddPartitionEvent(tbl, partitionSpecProxy, true, this)); |
| } |
| |
| success = ms.commitTransaction(); |
| return addedPartitions.size(); |
| } finally { |
| try { |
| if (!success) { |
| ms.rollbackTransaction(); |
| cleanupPartitionFolders(addedPartitions, db); |
| } |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ADD_PARTITION, |
| new AddPartitionEvent(tbl, partitionSpecProxy, true, this), |
| null, |
| transactionalListenerResponses, ms); |
| } |
| } finally { |
| tableLock.unlock(); |
| } |
| } |
| } |
| |
| private boolean startAddPartition( |
| RawStore ms, Partition part, List<FieldSchema> partitionKeys, boolean ifNotExists) |
| throws TException { |
| MetaStoreServerUtils.validatePartitionNameCharacters(part.getValues(), |
| partitionValidationPattern); |
| boolean doesExist = ms.doesPartitionExist(part.getCatName(), |
| part.getDbName(), part.getTableName(), partitionKeys, part.getValues()); |
| if (doesExist && !ifNotExists) { |
| throw new AlreadyExistsException("Partition already exists: " + part); |
| } |
| return !doesExist; |
| } |
| |
| /** |
| * Handles the location for a partition being created. |
| * @param tbl Table. |
| * @param part Partition. |
| * @return Whether the partition SD location is set to a newly created directory. |
| */ |
| private boolean createLocationForAddedPartition( |
| final Table tbl, final Partition part) throws MetaException { |
| Path partLocation = null; |
| String partLocationStr = null; |
| if (part.getSd() != null) { |
| partLocationStr = part.getSd().getLocation(); |
| } |
| |
| if (partLocationStr == null || partLocationStr.isEmpty()) { |
| // set default location if not specified and this is |
| // a physical table partition (not a view) |
| if (tbl.getSd().getLocation() != null) { |
| partLocation = new Path(tbl.getSd().getLocation(), Warehouse |
| .makePartName(tbl.getPartitionKeys(), part.getValues())); |
| } |
| } else { |
| if (tbl.getSd().getLocation() == null) { |
| throw new MetaException("Cannot specify location for a view partition"); |
| } |
| partLocation = wh.getDnsPath(new Path(partLocationStr)); |
| } |
| |
| boolean result = false; |
| if (partLocation != null) { |
| part.getSd().setLocation(partLocation.toString()); |
| |
| // Check to see if the directory already exists before calling |
| // mkdirs() because if the file system is read-only, mkdirs will |
| // throw an exception even if the directory already exists. |
| if (!wh.isDir(partLocation)) { |
| if (!wh.mkdirs(partLocation)) { |
| throw new MetaException(partLocation |
| + " is not a directory or unable to create one"); |
| } |
| result = true; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Verify if update stats while altering partition(s) |
| * For the following three cases HMS will not update partition stats |
| * 1) Table property 'DO_NOT_UPDATE_STATS' = True |
| * 2) HMS configuration property 'STATS_AUTO_GATHER' = False |
| * 3) Is View |
| */ |
| private boolean canUpdateStats(Table tbl) { |
| Map<String,String> tblParams = tbl.getParameters(); |
| boolean updateStatsTbl = true; |
| if ((tblParams != null) && tblParams.containsKey(StatsSetupConst.DO_NOT_UPDATE_STATS)) { |
| updateStatsTbl = !Boolean.valueOf(tblParams.get(StatsSetupConst.DO_NOT_UPDATE_STATS)); |
| } |
| if (!MetastoreConf.getBoolVar(conf, ConfVars.STATS_AUTO_GATHER) || |
| MetaStoreUtils.isView(tbl) || |
| !updateStatsTbl) { |
| return false; |
| } |
| return true; |
| } |
| |
| private void initializeAddedPartition(final Table tbl, final Partition part, boolean madeDir, |
| EnvironmentContext environmentContext) throws MetaException { |
| initializeAddedPartition(tbl, |
| new PartitionSpecProxy.SimplePartitionWrapperIterator(part), madeDir, environmentContext); |
| } |
| |
| private void initializeAddedPartition( |
| final Table tbl, final PartitionSpecProxy.PartitionIterator part, boolean madeDir, |
| EnvironmentContext environmentContext) throws MetaException { |
| if (canUpdateStats(tbl)) { |
| MetaStoreServerUtils.updatePartitionStatsFast(part, tbl, wh, madeDir, |
| false, environmentContext, true); |
| } |
| |
| // set create time |
| long time = System.currentTimeMillis() / 1000; |
| part.setCreateTime((int) time); |
| if (part.getParameters() == null || |
| part.getParameters().get(hive_metastoreConstants.DDL_TIME) == null) { |
| part.putToParameters(hive_metastoreConstants.DDL_TIME, Long.toString(time)); |
| } |
| } |
| |
| private void initializePartitionParameters(final Table tbl, final Partition part) |
| throws MetaException { |
| initializePartitionParameters(tbl, |
| new PartitionSpecProxy.SimplePartitionWrapperIterator(part)); |
| } |
| |
| private void initializePartitionParameters(final Table tbl, |
| final PartitionSpecProxy.PartitionIterator part) throws MetaException { |
| |
| // Inherit table properties into partition properties. |
| Map<String, String> tblParams = tbl.getParameters(); |
| String inheritProps = MetastoreConf.getVar(conf, ConfVars.PART_INHERIT_TBL_PROPS).trim(); |
| // Default value is empty string in which case no properties will be inherited. |
| // * implies all properties needs to be inherited |
| Set<String> inheritKeys = new HashSet<>(Arrays.asList(inheritProps.split(","))); |
| if (inheritKeys.contains("*")) { |
| inheritKeys = tblParams.keySet(); |
| } |
| |
| for (String key : inheritKeys) { |
| String paramVal = tblParams.get(key); |
| if (null != paramVal) { // add the property only if it exists in table properties |
| part.putToParameters(key, paramVal); |
| } |
| } |
| } |
| |
| private Partition add_partition_core(final RawStore ms, |
| final Partition part, final EnvironmentContext envContext) |
| throws TException { |
| boolean success = false; |
| Table tbl = null; |
| Map<String, String> transactionalListenerResponses = Collections.emptyMap(); |
| if (!part.isSetCatName()) { |
| part.setCatName(getDefaultCatalog(conf)); |
| } |
| try { |
| ms.openTransaction(); |
| tbl = ms.getTable(part.getCatName(), part.getDbName(), part.getTableName(), null); |
| if (tbl == null) { |
| throw new InvalidObjectException( |
| "Unable to add partition because table or database do not exist"); |
| } |
| |
| firePreEvent(new PreAddPartitionEvent(tbl, part, this)); |
| |
| if (part.getValues() == null || part.getValues().isEmpty()) { |
| throw new MetaException("The partition values cannot be null or empty."); |
| } |
| boolean shouldAdd = startAddPartition(ms, part, tbl.getPartitionKeys(), false); |
| assert shouldAdd; // start would throw if it already existed here |
| boolean madeDir = createLocationForAddedPartition(tbl, part); |
| try { |
| initializeAddedPartition(tbl, part, madeDir, envContext); |
| initializePartitionParameters(tbl, part); |
| success = ms.addPartition(part); |
| } finally { |
| if (!success && madeDir) { |
| wh.deleteDir(new Path(part.getSd().getLocation()), true, false, |
| ReplChangeManager.shouldEnableCm(ms.getDatabase(part.getCatName(), part.getDbName()), tbl)); |
| } |
| } |
| |
| // Setting success to false to make sure that if the listener fails, rollback happens. |
| success = false; |
| |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenerResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.ADD_PARTITION, |
| new AddPartitionEvent(tbl, Arrays.asList(part), true, this), |
| envContext); |
| |
| } |
| |
| // we proceed only if we'd actually succeeded anyway, otherwise, |
| // we'd have thrown an exception |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ADD_PARTITION, |
| new AddPartitionEvent(tbl, Arrays.asList(part), success, this), |
| envContext, |
| transactionalListenerResponses, ms); |
| |
| } |
| } |
| return part; |
| } |
| |
| @Override |
| public Partition add_partition(final Partition part) |
| throws InvalidObjectException, AlreadyExistsException, MetaException { |
| return add_partition_with_environment_context(part, null); |
| } |
| |
| @Override |
| public Partition add_partition_with_environment_context( |
| final Partition part, EnvironmentContext envContext) |
| throws InvalidObjectException, AlreadyExistsException, |
| MetaException { |
| if (part == null) { |
| throw new MetaException("Partition cannot be null."); |
| } |
| startTableFunction("add_partition", |
| part.getCatName(), part.getDbName(), part.getTableName()); |
| Partition ret = null; |
| Exception ex = null; |
| try { |
| ret = add_partition_core(getMS(), part, envContext); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e) |
| .throwIfInstance(MetaException.class, InvalidObjectException.class, AlreadyExistsException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("add_partition", ret != null, ex, part != null ? part.getTableName(): null); |
| } |
| return ret; |
| } |
| |
| @Override |
| public Partition exchange_partition(Map<String, String> partitionSpecs, |
| String sourceDbName, String sourceTableName, String destDbName, |
| String destTableName) throws TException { |
| exchange_partitions(partitionSpecs, sourceDbName, sourceTableName, destDbName, destTableName); |
| // Wouldn't it make more sense to return the first element of the list returned by the |
| // previous call? |
| return new Partition(); |
| } |
| |
| @Override |
| public List<Partition> exchange_partitions(Map<String, String> partitionSpecs, |
| String sourceDbName, String sourceTableName, String destDbName, |
| String destTableName) throws TException { |
| String[] parsedDestDbName = parseDbName(destDbName, conf); |
| String[] parsedSourceDbName = parseDbName(sourceDbName, conf); |
| // No need to check catalog for null as parseDbName() will never return null for the catalog. |
| if (partitionSpecs == null || parsedSourceDbName[DB_NAME] == null || sourceTableName == null |
| || parsedDestDbName[DB_NAME] == null || destTableName == null) { |
| throw new MetaException("The DB and table name for the source and destination tables," |
| + " and the partition specs must not be null."); |
| } |
| if (!parsedDestDbName[CAT_NAME].equals(parsedSourceDbName[CAT_NAME])) { |
| throw new MetaException("You cannot move a partition across catalogs"); |
| } |
| |
| boolean success = false; |
| boolean pathCreated = false; |
| RawStore ms = getMS(); |
| ms.openTransaction(); |
| |
| Table destinationTable = |
| ms.getTable( |
| parsedDestDbName[CAT_NAME], parsedDestDbName[DB_NAME], destTableName, null); |
| if (destinationTable == null) { |
| throw new MetaException( "The destination table " + |
| TableName.getQualified(parsedDestDbName[CAT_NAME], |
| parsedDestDbName[DB_NAME], destTableName) + " not found"); |
| } |
| Table sourceTable = |
| ms.getTable( |
| parsedSourceDbName[CAT_NAME], parsedSourceDbName[DB_NAME], sourceTableName, null); |
| if (sourceTable == null) { |
| throw new MetaException("The source table " + |
| TableName.getQualified(parsedSourceDbName[CAT_NAME], |
| parsedSourceDbName[DB_NAME], sourceTableName) + " not found"); |
| } |
| |
| List<String> partVals = MetaStoreUtils.getPvals(sourceTable.getPartitionKeys(), |
| partitionSpecs); |
| List<String> partValsPresent = new ArrayList<> (); |
| List<FieldSchema> partitionKeysPresent = new ArrayList<> (); |
| int i = 0; |
| for (FieldSchema fs: sourceTable.getPartitionKeys()) { |
| String partVal = partVals.get(i); |
| if (partVal != null && !partVal.equals("")) { |
| partValsPresent.add(partVal); |
| partitionKeysPresent.add(fs); |
| } |
| i++; |
| } |
| // Passed the unparsed DB name here, as get_partitions_ps expects to parse it |
| List<Partition> partitionsToExchange = get_partitions_ps(sourceDbName, sourceTableName, |
| partVals, (short)-1); |
| if (partitionsToExchange == null || partitionsToExchange.isEmpty()) { |
| throw new MetaException("No partition is found with the values " + partitionSpecs |
| + " for the table " + sourceTableName); |
| } |
| boolean sameColumns = MetaStoreUtils.compareFieldColumns( |
| sourceTable.getSd().getCols(), destinationTable.getSd().getCols()); |
| boolean samePartitions = MetaStoreUtils.compareFieldColumns( |
| sourceTable.getPartitionKeys(), destinationTable.getPartitionKeys()); |
| if (!sameColumns || !samePartitions) { |
| throw new MetaException("The tables have different schemas." + |
| " Their partitions cannot be exchanged."); |
| } |
| Path sourcePath = new Path(sourceTable.getSd().getLocation(), |
| Warehouse.makePartName(partitionKeysPresent, partValsPresent)); |
| Path destPath = new Path(destinationTable.getSd().getLocation(), |
| Warehouse.makePartName(partitionKeysPresent, partValsPresent)); |
| List<Partition> destPartitions = new ArrayList<>(); |
| |
| Map<String, String> transactionalListenerResponsesForAddPartition = Collections.emptyMap(); |
| List<Map<String, String>> transactionalListenerResponsesForDropPartition = |
| Lists.newArrayListWithCapacity(partitionsToExchange.size()); |
| |
| // Check if any of the partitions already exists in destTable. |
| List<String> destPartitionNames = ms.listPartitionNames(parsedDestDbName[CAT_NAME], |
| parsedDestDbName[DB_NAME], destTableName, (short) -1); |
| if (destPartitionNames != null && !destPartitionNames.isEmpty()) { |
| for (Partition partition : partitionsToExchange) { |
| String partToExchangeName = |
| Warehouse.makePartName(destinationTable.getPartitionKeys(), partition.getValues()); |
| if (destPartitionNames.contains(partToExchangeName)) { |
| throw new MetaException("The partition " + partToExchangeName |
| + " already exists in the table " + destTableName); |
| } |
| } |
| } |
| |
| Database srcDb = ms.getDatabase(parsedSourceDbName[CAT_NAME], parsedSourceDbName[DB_NAME]); |
| Database destDb = ms.getDatabase(parsedDestDbName[CAT_NAME], parsedDestDbName[DB_NAME]); |
| if (!HiveMetaStore.isRenameAllowed(srcDb, destDb)) { |
| throw new MetaException("Exchange partition not allowed for " + |
| TableName.getQualified(parsedSourceDbName[CAT_NAME], |
| parsedSourceDbName[DB_NAME], sourceTableName) + " Dest db : " + destDbName); |
| } |
| try { |
| for (Partition partition: partitionsToExchange) { |
| Partition destPartition = new Partition(partition); |
| destPartition.setDbName(parsedDestDbName[DB_NAME]); |
| destPartition.setTableName(destinationTable.getTableName()); |
| Path destPartitionPath = new Path(destinationTable.getSd().getLocation(), |
| Warehouse.makePartName(destinationTable.getPartitionKeys(), partition.getValues())); |
| destPartition.getSd().setLocation(destPartitionPath.toString()); |
| ms.addPartition(destPartition); |
| destPartitions.add(destPartition); |
| ms.dropPartition(parsedSourceDbName[CAT_NAME], partition.getDbName(), sourceTable.getTableName(), |
| Warehouse.makePartName(sourceTable.getPartitionKeys(), partition.getValues())); |
| } |
| Path destParentPath = destPath.getParent(); |
| if (!wh.isDir(destParentPath)) { |
| if (!wh.mkdirs(destParentPath)) { |
| throw new MetaException("Unable to create path " + destParentPath); |
| } |
| } |
| /* |
| * TODO: Use the hard link feature of hdfs |
| * once https://issues.apache.org/jira/browse/HDFS-3370 is done |
| */ |
| pathCreated = wh.renameDir(sourcePath, destPath, false); |
| |
| // Setting success to false to make sure that if the listener fails, rollback happens. |
| success = false; |
| |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenerResponsesForAddPartition = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.ADD_PARTITION, |
| new AddPartitionEvent(destinationTable, destPartitions, true, this)); |
| |
| for (Partition partition : partitionsToExchange) { |
| DropPartitionEvent dropPartitionEvent = |
| new DropPartitionEvent(sourceTable, partition, true, true, this); |
| transactionalListenerResponsesForDropPartition.add( |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.DROP_PARTITION, |
| dropPartitionEvent)); |
| } |
| } |
| |
| success = ms.commitTransaction(); |
| return destPartitions; |
| } finally { |
| if (!success || !pathCreated) { |
| ms.rollbackTransaction(); |
| if (pathCreated) { |
| wh.renameDir(destPath, sourcePath, false); |
| } |
| } |
| |
| if (!listeners.isEmpty()) { |
| AddPartitionEvent addPartitionEvent = new AddPartitionEvent(destinationTable, destPartitions, success, this); |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ADD_PARTITION, |
| addPartitionEvent, |
| null, |
| transactionalListenerResponsesForAddPartition, ms); |
| |
| i = 0; |
| for (Partition partition : partitionsToExchange) { |
| DropPartitionEvent dropPartitionEvent = |
| new DropPartitionEvent(sourceTable, partition, success, true, this); |
| Map<String, String> parameters = |
| (transactionalListenerResponsesForDropPartition.size() > i) |
| ? transactionalListenerResponsesForDropPartition.get(i) |
| : null; |
| |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.DROP_PARTITION, |
| dropPartitionEvent, |
| null, |
| parameters, ms); |
| i++; |
| } |
| } |
| } |
| } |
| |
| private boolean drop_partition_common(RawStore ms, String catName, String db_name, String tbl_name, |
| List<String> part_vals, boolean deleteData, final EnvironmentContext envContext) |
| throws MetaException, NoSuchObjectException, IOException, InvalidObjectException, InvalidInputException { |
| Path partPath = null; |
| boolean isArchived = false; |
| Path archiveParentDir = null; |
| boolean success = false; |
| |
| Table tbl = null; |
| Partition part = null; |
| boolean mustPurge = false; |
| long writeId = 0; |
| |
| Map<String, String> transactionalListenerResponses = Collections.emptyMap(); |
| boolean needsCm = false; |
| |
| if (db_name == null) { |
| throw new MetaException("The DB name cannot be null."); |
| } |
| if (tbl_name == null) { |
| throw new MetaException("The table name cannot be null."); |
| } |
| if (part_vals == null) { |
| throw new MetaException("The partition values cannot be null."); |
| } |
| |
| try { |
| ms.openTransaction(); |
| |
| part = ms.getPartition(catName, db_name, tbl_name, part_vals); |
| GetTableRequest request = new GetTableRequest(db_name,tbl_name); |
| request.setCatName(catName); |
| tbl = get_table_core(request); |
| firePreEvent(new PreDropPartitionEvent(tbl, part, deleteData, this)); |
| |
| mustPurge = isMustPurge(envContext, tbl); |
| writeId = getWriteId(envContext); |
| |
| if (part == null) { |
| throw new NoSuchObjectException("Partition doesn't exist. " + part_vals); |
| } |
| isArchived = MetaStoreUtils.isArchived(part); |
| if (isArchived) { |
| archiveParentDir = MetaStoreUtils.getOriginalLocation(part); |
| verifyIsWritablePath(archiveParentDir); |
| } |
| |
| if ((part.getSd() != null) && (part.getSd().getLocation() != null)) { |
| partPath = new Path(part.getSd().getLocation()); |
| verifyIsWritablePath(partPath); |
| } |
| |
| String partName = Warehouse.makePartName(tbl.getPartitionKeys(), part_vals); |
| ms.dropPartition(catName, db_name, tbl_name, partName); |
| |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenerResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.DROP_PARTITION, |
| new DropPartitionEvent(tbl, part, true, deleteData, this), |
| envContext); |
| } |
| needsCm = ReplChangeManager.shouldEnableCm(ms.getDatabase(catName, db_name), tbl); |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } else if (checkTableDataShouldBeDeleted(tbl, deleteData) && |
| (partPath != null || archiveParentDir != null)) { |
| |
| LOG.info(mustPurge ? |
| "dropPartition() will purge " + partPath + " directly, skipping trash." : |
| "dropPartition() will move " + partPath + " to trash-directory."); |
| |
| // Archived partitions have har:/to_har_file as their location. |
| // The original directory was saved in params |
| if (isArchived) { |
| wh.deleteDir(archiveParentDir, true, mustPurge, needsCm); |
| } else { |
| wh.deleteDir(partPath, true, mustPurge, needsCm); |
| deleteParentRecursive(partPath.getParent(), part_vals.size() - 1, mustPurge, needsCm); |
| } |
| // ok even if the data is not deleted |
| } else if (TxnUtils.isTransactionalTable(tbl) && writeId > 0) { |
| addTruncateBaseFile(partPath, writeId, conf, DataFormat.DROPPED); |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.DROP_PARTITION, |
| new DropPartitionEvent(tbl, part, success, deleteData, this), |
| envContext, |
| transactionalListenerResponses, ms); |
| } |
| } |
| return true; |
| } |
| |
| static boolean isMustPurge(EnvironmentContext envContext, Table tbl) { |
| // Data needs deletion. Check if trash may be skipped. |
| // Trash may be skipped iff: |
| // 1. deleteData == true, obviously. |
| // 2. tbl is external. |
| // 3. Either |
| // 3.1. User has specified PURGE from the commandline, and if not, |
| // 3.2. User has set the table to auto-purge. |
| return ((envContext != null) && Boolean.parseBoolean(envContext.getProperties().get("ifPurge"))) |
| || MetaStoreUtils.isSkipTrash(tbl.getParameters()); |
| } |
| |
| static long getWriteId(EnvironmentContext context){ |
| return Optional.ofNullable(context) |
| .map(EnvironmentContext::getProperties) |
| .map(prop -> prop.get(hive_metastoreConstants.WRITE_ID)) |
| .map(Long::parseLong) |
| .orElse(0L); |
| } |
| |
| private void throwUnsupportedExceptionIfRemoteDB(String dbName, String operationName) throws MetaException { |
| if (isDatabaseRemote(dbName)) { |
| throw new MetaException("Operation " + operationName + " not supported for REMOTE database " + dbName); |
| } |
| } |
| |
| private boolean isDatabaseRemote(String name) { |
| try { |
| String[] parsedDbName = parseDbName(name, conf); |
| Database db = get_database_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME]); |
| if (db != null && db.getType() != null && db.getType() == DatabaseType.REMOTE) { |
| return true; |
| } |
| } catch (Exception e) { |
| return false; |
| } |
| return false; |
| } |
| |
| private void deleteParentRecursive(Path parent, int depth, boolean mustPurge, boolean needRecycle) |
| throws IOException, MetaException { |
| if (depth > 0 && parent != null && wh.isWritable(parent) && wh.isEmptyDir(parent)) { |
| wh.deleteDir(parent, true, mustPurge, needRecycle); |
| deleteParentRecursive(parent.getParent(), depth - 1, mustPurge, needRecycle); |
| } |
| } |
| |
| @Override |
| public boolean drop_partition(final String db_name, final String tbl_name, |
| final List<String> part_vals, final boolean deleteData) |
| throws TException { |
| return drop_partition_with_environment_context(db_name, tbl_name, part_vals, deleteData, |
| null); |
| } |
| |
| /** Stores a path and its size. */ |
| private static class PathAndDepth implements Comparable<PathAndDepth> { |
| final Path path; |
| final int depth; |
| |
| public PathAndDepth(Path path, int depth) { |
| this.path = path; |
| this.depth = depth; |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(path.hashCode(), depth); |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (o == null || getClass() != o.getClass()) { |
| return false; |
| } |
| PathAndDepth that = (PathAndDepth) o; |
| return depth == that.depth && Objects.equals(path, that.path); |
| } |
| |
| /** The largest {@code depth} is processed first in a {@link PriorityQueue}. */ |
| @Override |
| public int compareTo(PathAndDepth o) { |
| return o.depth - depth; |
| } |
| } |
| |
| @Override |
| public DropPartitionsResult drop_partitions_req( |
| DropPartitionsRequest request) throws TException { |
| RawStore ms = getMS(); |
| String dbName = request.getDbName(), tblName = request.getTblName(); |
| String catName = request.isSetCatName() ? request.getCatName() : getDefaultCatalog(conf); |
| |
| boolean ifExists = request.isSetIfExists() && request.isIfExists(); |
| boolean deleteData = request.isSetDeleteData() && request.isDeleteData(); |
| boolean ignoreProtection = request.isSetIgnoreProtection() && request.isIgnoreProtection(); |
| boolean needResult = !request.isSetNeedResult() || request.isNeedResult(); |
| |
| List<PathAndDepth> dirsToDelete = new ArrayList<>(); |
| List<Path> archToDelete = new ArrayList<>(); |
| EnvironmentContext envContext = |
| request.isSetEnvironmentContext() ? request.getEnvironmentContext() : null; |
| boolean success = false; |
| |
| Table tbl = null; |
| List<Partition> parts = null; |
| boolean mustPurge = false; |
| long writeId = 0; |
| |
| Map<String, String> transactionalListenerResponses = null; |
| boolean needsCm = false; |
| |
| try { |
| ms.openTransaction(); |
| // We need Partition-s for firing events and for result; DN needs MPartition-s to drop. |
| // Great... Maybe we could bypass fetching MPartitions by issuing direct SQL deletes. |
| tbl = get_table_core(catName, dbName, tblName); |
| mustPurge = isMustPurge(envContext, tbl); |
| writeId = getWriteId(envContext); |
| |
| int minCount = 0; |
| RequestPartsSpec spec = request.getParts(); |
| List<String> partNames = null; |
| |
| if (spec.isSetExprs()) { |
| // Dropping by expressions. |
| parts = new ArrayList<>(spec.getExprs().size()); |
| for (DropPartitionsExpr expr : spec.getExprs()) { |
| ++minCount; // At least one partition per expression, if not ifExists |
| List<Partition> result = new ArrayList<>(); |
| boolean hasUnknown = ms.getPartitionsByExpr( |
| catName, dbName, tblName, expr.getExpr(), null, (short)-1, result); |
| if (hasUnknown) { |
| // Expr is built by DDLSA, it should only contain part cols and simple ops |
| throw new MetaException("Unexpected unknown partitions to drop"); |
| } |
| // this is to prevent dropping archived partition which is archived in a |
| // different level the drop command specified. |
| if (!ignoreProtection && expr.isSetPartArchiveLevel()) { |
| for (Partition part : parts) { |
| if (MetaStoreUtils.isArchived(part) |
| && MetaStoreUtils.getArchivingLevel(part) < expr.getPartArchiveLevel()) { |
| throw new MetaException("Cannot drop a subset of partitions " |
| + " in an archive, partition " + part); |
| } |
| } |
| } |
| parts.addAll(result); |
| } |
| } else if (spec.isSetNames()) { |
| partNames = spec.getNames(); |
| minCount = partNames.size(); |
| parts = ms.getPartitionsByNames(catName, dbName, tblName, partNames); |
| } else { |
| throw new MetaException("Partition spec is not set"); |
| } |
| |
| if ((parts.size() < minCount) && !ifExists) { |
| throw new NoSuchObjectException("Some partitions to drop are missing"); |
| } |
| |
| List<String> colNames = null; |
| if (partNames == null) { |
| partNames = new ArrayList<>(parts.size()); |
| colNames = new ArrayList<>(tbl.getPartitionKeys().size()); |
| for (FieldSchema col : tbl.getPartitionKeys()) { |
| colNames.add(col.getName()); |
| } |
| } |
| |
| for (Partition part : parts) { |
| // TODO - we need to speed this up for the normal path where all partitions are under |
| // the table and we don't have to stat every partition |
| |
| firePreEvent(new PreDropPartitionEvent(tbl, part, deleteData, this)); |
| if (colNames != null) { |
| partNames.add(FileUtils.makePartName(colNames, part.getValues())); |
| } |
| // Preserve the old behavior of failing when we cannot write, even w/o deleteData, |
| // and even if the table is external. That might not make any sense. |
| if (MetaStoreUtils.isArchived(part)) { |
| Path archiveParentDir = MetaStoreUtils.getOriginalLocation(part); |
| verifyIsWritablePath(archiveParentDir); |
| archToDelete.add(archiveParentDir); |
| } |
| if ((part.getSd() != null) && (part.getSd().getLocation() != null)) { |
| Path partPath = new Path(part.getSd().getLocation()); |
| verifyIsWritablePath(partPath); |
| dirsToDelete.add(new PathAndDepth(partPath, part.getValues().size())); |
| } |
| } |
| |
| ms.dropPartitions(catName, dbName, tblName, partNames); |
| if (!parts.isEmpty() && !transactionalListeners.isEmpty()) { |
| transactionalListenerResponses = MetaStoreListenerNotifier |
| .notifyEvent(transactionalListeners, EventType.DROP_PARTITION, |
| new DropPartitionEvent(tbl, parts, true, deleteData, this), envContext); |
| } |
| success = ms.commitTransaction(); |
| needsCm = ReplChangeManager.shouldEnableCm(ms.getDatabase(catName, dbName), tbl); |
| |
| DropPartitionsResult result = new DropPartitionsResult(); |
| if (needResult) { |
| result.setPartitions(parts); |
| } |
| return result; |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } else if (checkTableDataShouldBeDeleted(tbl, deleteData)) { |
| LOG.info(mustPurge ? |
| "dropPartition() will purge partition-directories directly, skipping trash." |
| : "dropPartition() will move partition-directories to trash-directory."); |
| // Archived partitions have har:/to_har_file as their location. |
| // The original directory was saved in params |
| for (Path path : archToDelete) { |
| wh.deleteDir(path, true, mustPurge, needsCm); |
| } |
| |
| // Uses a priority queue to delete the parents of deleted directories if empty. |
| // Parents with the deepest path are always processed first. It guarantees that the emptiness |
| // of a parent won't be changed once it has been processed. So duplicated processing can be |
| // avoided. |
| PriorityQueue<PathAndDepth> parentsToDelete = new PriorityQueue<>(); |
| for (PathAndDepth p : dirsToDelete) { |
| wh.deleteDir(p.path, true, mustPurge, needsCm); |
| addParentForDel(parentsToDelete, p); |
| } |
| |
| HashSet<PathAndDepth> processed = new HashSet<>(); |
| while (!parentsToDelete.isEmpty()) { |
| try { |
| PathAndDepth p = parentsToDelete.poll(); |
| if (processed.contains(p)) { |
| continue; |
| } |
| processed.add(p); |
| |
| Path path = p.path; |
| if (wh.isWritable(path) && wh.isEmptyDir(path)) { |
| wh.deleteDir(path, true, mustPurge, needsCm); |
| addParentForDel(parentsToDelete, p); |
| } |
| } catch (IOException ex) { |
| LOG.warn("Error from recursive parent deletion", ex); |
| throw new MetaException("Failed to delete parent: " + ex.getMessage()); |
| } |
| } |
| } else if (TxnUtils.isTransactionalTable(tbl) && writeId > 0) { |
| for (Partition part : parts) { |
| if ((part.getSd() != null) && (part.getSd().getLocation() != null)) { |
| Path partPath = new Path(part.getSd().getLocation()); |
| verifyIsWritablePath(partPath); |
| |
| addTruncateBaseFile(partPath, writeId, conf, DataFormat.DROPPED); |
| } |
| } |
| } |
| |
| if (parts != null) { |
| if (!parts.isEmpty() && !listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.DROP_PARTITION, |
| new DropPartitionEvent(tbl, parts, success, deleteData, this), |
| envContext, |
| transactionalListenerResponses, ms); |
| } |
| } |
| } |
| } |
| |
| private static void addParentForDel(PriorityQueue<PathAndDepth> parentsToDelete, PathAndDepth p) { |
| Path parent = p.path.getParent(); |
| if (parent != null && p.depth - 1 > 0) { |
| parentsToDelete.add(new PathAndDepth(parent, p.depth - 1)); |
| } |
| } |
| |
| private void verifyIsWritablePath(Path dir) throws MetaException { |
| try { |
| if (!wh.isWritable(dir.getParent())) { |
| throw new MetaException("Table partition not deleted since " + dir.getParent() |
| + " is not writable by " + SecurityUtils.getUser()); |
| } |
| } catch (IOException ex) { |
| LOG.warn("Error from isWritable", ex); |
| throw new MetaException("Table partition not deleted since " + dir.getParent() |
| + " access cannot be checked: " + ex.getMessage()); |
| } |
| } |
| |
| @Override |
| public boolean drop_partition_with_environment_context(final String db_name, |
| final String tbl_name, final List<String> part_vals, final boolean deleteData, |
| final EnvironmentContext envContext) |
| throws TException { |
| String[] parsedDbName = parseDbName(db_name, conf); |
| startPartitionFunction("drop_partition", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name, part_vals); |
| LOG.info("Partition values:" + part_vals); |
| |
| boolean ret = false; |
| Exception ex = null; |
| try { |
| ret = drop_partition_common(getMS(), parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name, part_vals, deleteData, envContext); |
| } catch (Exception e) { |
| ex = e; |
| handleException(e).convertIfInstance(IOException.class, MetaException.class) |
| .rethrowException(e); |
| } finally { |
| endFunction("drop_partition", ret, ex, tbl_name); |
| } |
| return ret; |
| |
| } |
| |
| /** |
| * Use {@link #get_partition_req(GetPartitionRequest)} ()} instead. |
| * |
| */ |
| @Override |
| @Deprecated |
| public Partition get_partition(final String db_name, final String tbl_name, |
| final List<String> part_vals) throws MetaException, NoSuchObjectException { |
| String[] parsedDbName = parseDbName(db_name, conf); |
| startPartitionFunction("get_partition", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name, part_vals); |
| |
| Partition ret = null; |
| Exception ex = null; |
| try { |
| authorizeTableForPartitionMetadata(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| fireReadTablePreEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| ret = getMS().getPartition(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name, part_vals); |
| ret = FilterUtils.filterPartitionIfEnabled(isServerFilterEnabled, filterHook, ret); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, NoSuchObjectException.class).defaultMetaException(); |
| } finally { |
| endFunction("get_partition", ret != null, ex, tbl_name); |
| } |
| return ret; |
| } |
| |
| @Override |
| public GetPartitionResponse get_partition_req(GetPartitionRequest req) |
| throws MetaException, NoSuchObjectException, TException { |
| // TODO Move the logic from get_partition to here, as that method is getting deprecated |
| String dbName = MetaStoreUtils.prependCatalogToDbName(req.getCatName(), req.getDbName(), conf); |
| Partition p = get_partition(dbName, req.getTblName(), req.getPartVals()); |
| GetPartitionResponse res = new GetPartitionResponse(); |
| res.setPartition(p); |
| return res; |
| } |
| |
| /** |
| * Fire a pre-event for read table operation, if there are any |
| * pre-event listeners registered |
| */ |
| private void fireReadTablePreEvent(String catName, String dbName, String tblName) |
| throws MetaException, NoSuchObjectException { |
| if(preListeners.size() > 0) { |
| Supplier<Table> tableSupplier = Suppliers.memoize(new Supplier<Table>() { |
| @Override public Table get() { |
| try { |
| Table t = getMS().getTable(catName, dbName, tblName, null); |
| if (t == null) { |
| throw new NoSuchObjectException(TableName.getQualified(catName, dbName, tblName) |
| + " table not found"); |
| } |
| return t; |
| } catch(MetaException | NoSuchObjectException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| }); |
| firePreEvent(new PreReadTableEvent(tableSupplier, this)); |
| } |
| } |
| |
| /** |
| * filters out the table meta for which read database access is not granted |
| * @param catName catalog name |
| * @param tableMetas list of table metas |
| * @return filtered list of table metas |
| * @throws RuntimeException |
| * @throws NoSuchObjectException |
| */ |
| private List<TableMeta> filterReadableTables(String catName, List<TableMeta> tableMetas) |
| throws RuntimeException, NoSuchObjectException { |
| List<TableMeta> finalT = new ArrayList<>(); |
| Map<String, Boolean> databaseNames = new HashMap(); |
| for (TableMeta tableMeta : tableMetas) { |
| String fullDbName = prependCatalogToDbName(catName, tableMeta.getDbName(), conf); |
| if (databaseNames.get(fullDbName) == null) { |
| boolean isExecptionThrown = false; |
| try { |
| fireReadDatabasePreEvent(fullDbName); |
| } catch (MetaException e) { |
| isExecptionThrown = true; |
| } |
| databaseNames.put(fullDbName, isExecptionThrown); |
| } |
| if (!databaseNames.get(fullDbName)) { |
| finalT.add(tableMeta); |
| } |
| } |
| return finalT; |
| } |
| |
| /** |
| * Fire a pre-event for read database operation, if there are any |
| * pre-event listeners registered |
| */ |
| private void fireReadDatabasePreEvent(final String name) |
| throws MetaException, RuntimeException, NoSuchObjectException { |
| if(preListeners.size() > 0) { |
| String[] parsedDbName = parseDbName(name, conf); |
| Database db = null; |
| try { |
| db = get_database_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME]); |
| if (db == null) { |
| throw new NoSuchObjectException("Database: " + name + " not found"); |
| } |
| } catch(MetaException | NoSuchObjectException e) { |
| throw new RuntimeException(e); |
| } |
| firePreEvent(new PreReadDatabaseEvent(db, this)); |
| } |
| } |
| |
| @Override |
| @Deprecated |
| public Partition get_partition_with_auth(final String db_name, |
| final String tbl_name, final List<String> part_vals, |
| final String user_name, final List<String> group_names) |
| throws TException { |
| String[] parsedDbName = parseDbName(db_name, conf); |
| startPartitionFunction("get_partition_with_auth", parsedDbName[CAT_NAME], |
| parsedDbName[DB_NAME], tbl_name, part_vals); |
| fireReadTablePreEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| Partition ret = null; |
| Exception ex = null; |
| try { |
| authorizeTableForPartitionMetadata(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| |
| ret = getMS().getPartitionWithAuth(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name, part_vals, user_name, group_names); |
| ret = FilterUtils.filterPartitionIfEnabled(isServerFilterEnabled, filterHook, ret); |
| } catch (Exception e) { |
| ex = e; |
| handleException(e).convertIfInstance(InvalidObjectException.class, NoSuchObjectException.class) |
| .rethrowException(e); |
| } finally { |
| endFunction("get_partition_with_auth", ret != null, ex, tbl_name); |
| } |
| return ret; |
| } |
| |
| /** |
| * Use {@link #get_partitions_req(PartitionsRequest)} ()} instead. |
| * |
| */ |
| @Override |
| @Deprecated |
| public List<Partition> get_partitions(final String db_name, final String tbl_name, |
| final short max_parts) throws NoSuchObjectException, MetaException { |
| String[] parsedDbName = parseDbName(db_name, conf); |
| startTableFunction("get_partitions", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| fireReadTablePreEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| List<Partition> ret = null; |
| Exception ex = null; |
| try { |
| checkLimitNumberOfPartitionsByFilter(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name, NO_FILTER_STRING, max_parts); |
| |
| authorizeTableForPartitionMetadata(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| |
| ret = getMS().getPartitions(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name, |
| max_parts); |
| ret = FilterUtils.filterPartitionsIfEnabled(isServerFilterEnabled, filterHook, ret); |
| } catch (Exception e) { |
| ex = e; |
| throwMetaException(e); |
| } finally { |
| endFunction("get_partitions", ret != null, ex, tbl_name); |
| } |
| return ret; |
| |
| } |
| |
| @Override |
| public PartitionsResponse get_partitions_req(PartitionsRequest req) |
| throws NoSuchObjectException, MetaException, TException { |
| String dbName = MetaStoreUtils.prependCatalogToDbName(req.getCatName(), req.getDbName(), conf); |
| List<Partition> partitions = get_partitions(dbName, req.getTblName(), req.getMaxParts()); |
| PartitionsResponse res = new PartitionsResponse(); |
| res.setPartitions(partitions); |
| return res; |
| } |
| |
| @Override |
| @Deprecated |
| public List<Partition> get_partitions_with_auth(final String dbName, |
| final String tblName, final short maxParts, final String userName, |
| final List<String> groupNames) throws TException { |
| String[] parsedDbName = parseDbName(dbName, conf); |
| startTableFunction("get_partitions_with_auth", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tblName); |
| |
| List<Partition> ret = null; |
| Exception ex = null; |
| try { |
| checkLimitNumberOfPartitionsByFilter(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tblName, NO_FILTER_STRING, maxParts); |
| |
| authorizeTableForPartitionMetadata(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tblName); |
| |
| ret = getMS().getPartitionsWithAuth(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tblName, |
| maxParts, userName, groupNames); |
| ret = FilterUtils.filterPartitionsIfEnabled(isServerFilterEnabled, filterHook, ret); |
| } catch (Exception e) { |
| ex = e; |
| handleException(e).convertIfInstance(InvalidObjectException.class, NoSuchObjectException.class) |
| .rethrowException(e); |
| } finally { |
| endFunction("get_partitions_with_auth", ret != null, ex, tblName); |
| } |
| return ret; |
| |
| } |
| |
| private void checkLimitNumberOfPartitionsByFilter(String catName, String dbName, |
| String tblName, String filterString, |
| int maxParts) throws TException { |
| if (isPartitionLimitEnabled()) { |
| checkLimitNumberOfPartitions(tblName, get_num_partitions_by_filter(prependCatalogToDbName( |
| catName, dbName, conf), tblName, filterString), maxParts); |
| } |
| } |
| |
| private void checkLimitNumberOfPartitionsByExpr(String catName, String dbName, String tblName, |
| byte[] filterExpr, int maxParts) |
| throws TException { |
| if (isPartitionLimitEnabled()) { |
| checkLimitNumberOfPartitions(tblName, get_num_partitions_by_expr(catName, dbName, tblName, |
| filterExpr), maxParts); |
| } |
| } |
| |
| private void checkLimitNumberOfPartitionsByPs(String catName, String dbName, String tblName, |
| List<String> partVals, int maxParts) |
| throws TException { |
| if (isPartitionLimitEnabled()) { |
| checkLimitNumberOfPartitions(tblName, getNumPartitionsByPs(catName, dbName, tblName, |
| partVals), maxParts); |
| } |
| } |
| |
| private boolean isPartitionLimitEnabled() { |
| int partitionLimit = MetastoreConf.getIntVar(conf, ConfVars.LIMIT_PARTITION_REQUEST); |
| return partitionLimit > -1; |
| } |
| |
| private void checkLimitNumberOfPartitions(String tblName, int numPartitions, int maxToFetch) throws MetaException { |
| if (isPartitionLimitEnabled()) { |
| int partitionLimit = MetastoreConf.getIntVar(conf, ConfVars.LIMIT_PARTITION_REQUEST); |
| int partitionRequest = (maxToFetch < 0) ? numPartitions : maxToFetch; |
| if (partitionRequest > partitionLimit) { |
| String configName = ConfVars.LIMIT_PARTITION_REQUEST.toString(); |
| throw new MetaException(String.format(PARTITION_NUMBER_EXCEED_LIMIT_MSG, partitionRequest, |
| tblName, partitionLimit, configName)); |
| } |
| } |
| } |
| |
| @Override |
| @Deprecated |
| public List<PartitionSpec> get_partitions_pspec(final String db_name, final String tbl_name, final int max_parts) |
| throws NoSuchObjectException, MetaException { |
| |
| String[] parsedDbName = parseDbName(db_name, conf); |
| String tableName = tbl_name.toLowerCase(); |
| |
| startTableFunction("get_partitions_pspec", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName); |
| |
| List<PartitionSpec> partitionSpecs = null; |
| try { |
| Table table = get_table_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName); |
| // get_partitions will parse out the catalog and db names itself |
| List<Partition> partitions = get_partitions(db_name, tableName, (short) max_parts); |
| |
| if (is_partition_spec_grouping_enabled(table)) { |
| partitionSpecs = MetaStoreServerUtils |
| .getPartitionspecsGroupedByStorageDescriptor(table, partitions); |
| } |
| else { |
| PartitionSpec pSpec = new PartitionSpec(); |
| pSpec.setPartitionList(new PartitionListComposingSpec(partitions)); |
| pSpec.setCatName(parsedDbName[CAT_NAME]); |
| pSpec.setDbName(parsedDbName[DB_NAME]); |
| pSpec.setTableName(tableName); |
| pSpec.setRootPath(table.getSd().getLocation()); |
| partitionSpecs = Arrays.asList(pSpec); |
| } |
| |
| return partitionSpecs; |
| } |
| finally { |
| endFunction("get_partitions_pspec", partitionSpecs != null && !partitionSpecs.isEmpty(), null, tbl_name); |
| } |
| } |
| |
| @Override |
| public GetPartitionsResponse get_partitions_with_specs(GetPartitionsRequest request) |
| throws MetaException, TException { |
| String catName = null; |
| if (request.isSetCatName()) { |
| catName = request.getCatName(); |
| } |
| String[] parsedDbName = parseDbName(request.getDbName(), conf); |
| String tableName = request.getTblName(); |
| if (catName == null) { |
| // if catName is not provided in the request use the catName parsed from the dbName |
| catName = parsedDbName[CAT_NAME]; |
| } |
| startTableFunction("get_partitions_with_specs", catName, parsedDbName[DB_NAME], |
| tableName); |
| GetPartitionsResponse response = null; |
| Exception ex = null; |
| try { |
| Table table = get_table_core(catName, parsedDbName[DB_NAME], tableName); |
| List<Partition> partitions = getMS() |
| .getPartitionSpecsByFilterAndProjection(table, request.getProjectionSpec(), |
| request.getFilterSpec()); |
| List<String> processorCapabilities = request.getProcessorCapabilities(); |
| String processorId = request.getProcessorIdentifier(); |
| if (processorCapabilities == null || processorCapabilities.size() == 0 || |
| processorCapabilities.contains("MANAGERAWMETADATA")) { |
| LOG.info("Skipping translation for processor with " + processorId); |
| } else { |
| if (transformer != null) { |
| partitions = transformer.transformPartitions(partitions, table, processorCapabilities, processorId); |
| } |
| } |
| List<PartitionSpec> partitionSpecs = |
| MetaStoreServerUtils.getPartitionspecsGroupedByStorageDescriptor(table, partitions); |
| response = new GetPartitionsResponse(); |
| response.setPartitionSpec(partitionSpecs); |
| } catch (Exception e) { |
| ex = e; |
| rethrowException(e); |
| } finally { |
| endFunction("get_partitions_with_specs", response != null, ex, tableName); |
| } |
| return response; |
| } |
| |
| private static boolean is_partition_spec_grouping_enabled(Table table) { |
| |
| Map<String, String> parameters = table.getParameters(); |
| return parameters.containsKey("hive.hcatalog.partition.spec.grouping.enabled") |
| && parameters.get("hive.hcatalog.partition.spec.grouping.enabled").equalsIgnoreCase("true"); |
| } |
| |
| @Override |
| @Deprecated |
| public List<String> get_partition_names(final String db_name, final String tbl_name, |
| final short max_parts) throws NoSuchObjectException, MetaException { |
| String[] parsedDbName = parseDbName(db_name, conf); |
| startTableFunction("get_partition_names", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| fireReadTablePreEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| List<String> ret = null; |
| Exception ex = null; |
| try { |
| authorizeTableForPartitionMetadata(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| ret = getMS().listPartitionNames(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name, |
| max_parts); |
| ret = FilterUtils.filterPartitionNamesIfEnabled(isServerFilterEnabled, |
| filterHook, parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name, ret); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_partition_names", ret != null, ex, tbl_name); |
| } |
| return ret; |
| } |
| |
| @Override |
| public PartitionValuesResponse get_partition_values(PartitionValuesRequest request) |
| throws MetaException { |
| String catName = request.isSetCatName() ? request.getCatName() : getDefaultCatalog(conf); |
| String dbName = request.getDbName(); |
| String tblName = request.getTblName(); |
| |
| try { |
| authorizeTableForPartitionMetadata(catName, dbName, tblName); |
| |
| // This is serious black magic, as the following 2 lines do nothing AFAICT but without them |
| // the subsequent call to listPartitionValues fails. |
| List<FieldSchema> partCols = new ArrayList<FieldSchema>(); |
| partCols.add(request.getPartitionKeys().get(0)); |
| return getMS().listPartitionValues(catName, dbName, tblName, request.getPartitionKeys(), |
| request.isApplyDistinct(), request.getFilter(), request.isAscending(), |
| request.getPartitionOrder(), request.getMaxParts()); |
| } catch (NoSuchObjectException e) { |
| LOG.error(String.format("Unable to get partition for %s.%s.%s", catName, dbName, tblName), e); |
| throw new MetaException(e.getMessage()); |
| } |
| } |
| |
| @Deprecated |
| @Override |
| public void alter_partition(final String db_name, final String tbl_name, |
| final Partition new_part) |
| throws TException { |
| rename_partition(db_name, tbl_name, null, new_part); |
| } |
| |
| @Deprecated |
| @Override |
| public void alter_partition_with_environment_context(final String dbName, |
| final String tableName, final Partition newPartition, |
| final EnvironmentContext envContext) |
| throws TException { |
| String[] parsedDbName = parseDbName(dbName, conf); |
| // TODO: this method name is confusing, it actually does full alter (sortof) |
| rename_partition(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName, null, newPartition, |
| envContext, null); |
| } |
| |
| @Deprecated |
| @Override |
| public void rename_partition(final String db_name, final String tbl_name, |
| final List<String> part_vals, final Partition new_part) |
| throws TException { |
| // Call rename_partition without an environment context. |
| String[] parsedDbName = parseDbName(db_name, conf); |
| rename_partition(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name, part_vals, new_part, |
| null, null); |
| } |
| |
| @Override |
| public RenamePartitionResponse rename_partition_req(RenamePartitionRequest req) throws TException { |
| EnvironmentContext context = new EnvironmentContext(); |
| context.putToProperties(RENAME_PARTITION_MAKE_COPY, String.valueOf(req.isClonePart())); |
| context.putToProperties(hive_metastoreConstants.TXN_ID, String.valueOf(req.getTxnId())); |
| |
| rename_partition(req.getCatName(), req.getDbName(), req.getTableName(), req.getPartVals(), |
| req.getNewPart(), context, req.getValidWriteIdList()); |
| return new RenamePartitionResponse(); |
| }; |
| |
| private void rename_partition(String catName, String db_name, String tbl_name, |
| List<String> part_vals, Partition new_part, EnvironmentContext envContext, |
| String validWriteIds) throws TException { |
| startTableFunction("alter_partition", catName, db_name, tbl_name); |
| |
| if (LOG.isInfoEnabled()) { |
| LOG.info("New partition values:" + new_part.getValues()); |
| if (part_vals != null && part_vals.size() > 0) { |
| LOG.info("Old Partition values:" + part_vals); |
| } |
| } |
| |
| // Adds the missing scheme/authority for the new partition location |
| if (new_part.getSd() != null) { |
| String newLocation = new_part.getSd().getLocation(); |
| if (org.apache.commons.lang3.StringUtils.isNotEmpty(newLocation)) { |
| Path tblPath = wh.getDnsPath(new Path(newLocation)); |
| new_part.getSd().setLocation(tblPath.toString()); |
| } |
| } |
| |
| // Make sure the new partition has the catalog value set |
| if (!new_part.isSetCatName()) { |
| new_part.setCatName(catName); |
| } |
| |
| Partition oldPart = null; |
| Exception ex = null; |
| try { |
| Table table = null; |
| table = getMS().getTable(catName, db_name, tbl_name, null); |
| |
| firePreEvent(new PreAlterPartitionEvent(db_name, tbl_name, table, part_vals, new_part, this)); |
| if (part_vals != null && !part_vals.isEmpty()) { |
| MetaStoreServerUtils.validatePartitionNameCharacters(new_part.getValues(), |
| partitionValidationPattern); |
| } |
| |
| oldPart = alterHandler.alterPartition(getMS(), wh, catName, db_name, tbl_name, |
| part_vals, new_part, envContext, this, validWriteIds); |
| |
| // Only fetch the table if we actually have a listener |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ALTER_PARTITION, |
| new AlterPartitionEvent(oldPart, new_part, table, false, |
| true, new_part.getWriteId(), this), |
| envContext); |
| } |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, InvalidOperationException.class) |
| .convertIfInstance(InvalidObjectException.class, InvalidOperationException.class) |
| .convertIfInstance(AlreadyExistsException.class, InvalidOperationException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("alter_partition", oldPart != null, ex, tbl_name); |
| } |
| } |
| |
| @Override |
| public void alter_partitions(final String db_name, final String tbl_name, |
| final List<Partition> new_parts) |
| throws TException { |
| String[] o = parseDbName(db_name, conf); |
| alter_partitions_with_environment_context(o[0], o[1], |
| tbl_name, new_parts, null, null, -1); |
| } |
| |
| @Override |
| public AlterPartitionsResponse alter_partitions_req(AlterPartitionsRequest req) throws TException { |
| alter_partitions_with_environment_context(req.getCatName(), |
| req.getDbName(), req.getTableName(), req.getPartitions(), req.getEnvironmentContext(), |
| req.isSetValidWriteIdList() ? req.getValidWriteIdList() : null, |
| req.isSetWriteId() ? req.getWriteId() : -1); |
| return new AlterPartitionsResponse(); |
| } |
| |
| // The old API we are keeping for backward compat. Not used within Hive. |
| @Deprecated |
| @Override |
| public void alter_partitions_with_environment_context(final String db_name, final String tbl_name, |
| final List<Partition> new_parts, EnvironmentContext environmentContext) |
| throws TException { |
| String[] o = parseDbName(db_name, conf); |
| alter_partitions_with_environment_context(o[0], o[1], tbl_name, new_parts, environmentContext, |
| null, -1); |
| } |
| |
| private void alter_partitions_with_environment_context(String catName, String db_name, final String tbl_name, |
| final List<Partition> new_parts, EnvironmentContext environmentContext, |
| String writeIdList, long writeId) |
| throws TException { |
| if (environmentContext == null) { |
| environmentContext = new EnvironmentContext(); |
| } |
| if (catName == null) { |
| catName = getDefaultCatalog(conf); |
| } |
| |
| startTableFunction("alter_partitions", catName, db_name, tbl_name); |
| |
| if (LOG.isInfoEnabled()) { |
| for (Partition tmpPart : new_parts) { |
| LOG.info("New partition values:" + tmpPart.getValues()); |
| } |
| } |
| // all partitions are altered atomically |
| // all prehooks are fired together followed by all post hooks |
| List<Partition> oldParts = null; |
| Exception ex = null; |
| Lock tableLock = getTableLockFor(db_name, tbl_name); |
| tableLock.lock(); |
| try { |
| |
| Table table = null; |
| table = getMS().getTable(catName, db_name, tbl_name, null); |
| |
| for (Partition tmpPart : new_parts) { |
| // Make sure the catalog name is set in the new partition |
| if (!tmpPart.isSetCatName()) { |
| tmpPart.setCatName(getDefaultCatalog(conf)); |
| } |
| firePreEvent(new PreAlterPartitionEvent(db_name, tbl_name, table, null, tmpPart, this)); |
| } |
| oldParts = alterHandler.alterPartitions(getMS(), wh, |
| catName, db_name, tbl_name, new_parts, environmentContext, writeIdList, writeId, this); |
| Iterator<Partition> olditr = oldParts.iterator(); |
| |
| for (Partition tmpPart : new_parts) { |
| Partition oldTmpPart; |
| if (olditr.hasNext()) { |
| oldTmpPart = olditr.next(); |
| } |
| else { |
| throw new InvalidOperationException("failed to alterpartitions"); |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.ALTER_PARTITION, |
| new AlterPartitionEvent(oldTmpPart, tmpPart, table, false, |
| true, writeId, this)); |
| } |
| } |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, InvalidOperationException.class) |
| .convertIfInstance(InvalidObjectException.class, InvalidOperationException.class) |
| .convertIfInstance(AlreadyExistsException.class, InvalidOperationException.class) |
| .defaultMetaException(); |
| } finally { |
| tableLock.unlock(); |
| endFunction("alter_partitions", oldParts != null, ex, tbl_name); |
| } |
| } |
| |
| @Override |
| public String getVersion() throws TException { |
| String version = MetastoreVersionInfo.getVersion(); |
| endFunction(startFunction("getVersion"), version != null, null); |
| return version; |
| } |
| |
| @Override |
| public void alter_table(final String dbname, final String name, |
| final Table newTable) |
| throws InvalidOperationException, MetaException { |
| // Do not set an environment context. |
| String[] parsedDbName = parseDbName(dbname, conf); |
| alter_table_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], name, newTable, |
| null, null, null, null, null, null); |
| } |
| |
| @Override |
| public void alter_table_with_cascade(final String dbname, final String name, |
| final Table newTable, final boolean cascade) |
| throws InvalidOperationException, MetaException { |
| EnvironmentContext envContext = null; |
| if (cascade) { |
| envContext = new EnvironmentContext(); |
| envContext.putToProperties(StatsSetupConst.CASCADE, StatsSetupConst.TRUE); |
| } |
| String[] parsedDbName = parseDbName(dbname, conf); |
| alter_table_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], name, newTable, |
| envContext, null, null, null, null, null); |
| } |
| |
| @Override |
| public AlterTableResponse alter_table_req(AlterTableRequest req) |
| throws InvalidOperationException, MetaException, TException { |
| alter_table_core(req.getCatName(), req.getDbName(), req.getTableName(), |
| req.getTable(), req.getEnvironmentContext(), req.getValidWriteIdList(), |
| req.getProcessorCapabilities(), req.getProcessorIdentifier(), |
| req.getExpectedParameterKey(), req.getExpectedParameterValue()); |
| return new AlterTableResponse(); |
| } |
| |
| @Override |
| public void alter_table_with_environment_context(final String dbname, |
| final String name, final Table newTable, |
| final EnvironmentContext envContext) |
| throws InvalidOperationException, MetaException { |
| String[] parsedDbName = parseDbName(dbname, conf); |
| alter_table_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| name, newTable, envContext, null, null, null, null, null); |
| } |
| |
| private void alter_table_core(String catName, String dbname, String name, Table newTable, |
| EnvironmentContext envContext, String validWriteIdList, List<String> processorCapabilities, |
| String processorId, String expectedPropertyKey, String expectedPropertyValue) |
| throws InvalidOperationException, MetaException { |
| startFunction("alter_table", ": " + TableName.getQualified(catName, dbname, name) |
| + " newtbl=" + newTable.getTableName()); |
| if (envContext == null) { |
| envContext = new EnvironmentContext(); |
| } |
| // Set the values to the envContext, so we do not have to change the HiveAlterHandler API |
| if (expectedPropertyKey != null) { |
| envContext.putToProperties(hive_metastoreConstants.EXPECTED_PARAMETER_KEY, expectedPropertyKey); |
| } |
| if (expectedPropertyValue != null) { |
| envContext.putToProperties(hive_metastoreConstants.EXPECTED_PARAMETER_VALUE, expectedPropertyValue); |
| } |
| |
| if (catName == null) { |
| catName = getDefaultCatalog(conf); |
| } |
| |
| // HIVE-25282: Drop/Alter table in REMOTE db should fail |
| try { |
| Database db = get_database_core(catName, dbname); |
| if (db != null && db.getType().equals(DatabaseType.REMOTE)) { |
| throw new MetaException("Alter table in REMOTE database " + db.getName() + " is not allowed"); |
| } |
| } catch (NoSuchObjectException e) { |
| throw new InvalidOperationException("Alter table in REMOTE database is not allowed"); |
| } |
| |
| // Update the time if it hasn't been specified. |
| if (newTable.getParameters() == null || |
| newTable.getParameters().get(hive_metastoreConstants.DDL_TIME) == null) { |
| newTable.putToParameters(hive_metastoreConstants.DDL_TIME, Long.toString(System |
| .currentTimeMillis() / 1000)); |
| } |
| |
| // Adds the missing scheme/authority for the new table location |
| if (newTable.getSd() != null) { |
| String newLocation = newTable.getSd().getLocation(); |
| if (org.apache.commons.lang3.StringUtils.isNotEmpty(newLocation)) { |
| Path tblPath = wh.getDnsPath(new Path(newLocation)); |
| newTable.getSd().setLocation(tblPath.toString()); |
| } |
| } |
| // Set the catalog name if it hasn't been set in the new table |
| if (!newTable.isSetCatName()) { |
| newTable.setCatName(catName); |
| } |
| |
| boolean success = false; |
| Exception ex = null; |
| try { |
| GetTableRequest request = new GetTableRequest(dbname, name); |
| request.setCatName(catName); |
| Table oldt = get_table_core(request); |
| if (transformer != null) { |
| newTable = transformer.transformAlterTable(oldt, newTable, processorCapabilities, processorId); |
| } |
| firePreEvent(new PreAlterTableEvent(oldt, newTable, this)); |
| alterHandler.alterTable(getMS(), wh, catName, dbname, name, newTable, |
| envContext, this, validWriteIdList); |
| success = true; |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(MetaException.class, InvalidOperationException.class) |
| .convertIfInstance(NoSuchObjectException.class, InvalidOperationException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("alter_table", success, ex, name); |
| } |
| } |
| |
| @Override |
| public List<String> get_tables(final String dbname, final String pattern) |
| throws MetaException { |
| startFunction("get_tables", ": db=" + dbname + " pat=" + pattern); |
| |
| List<String> ret = null; |
| Exception ex = null; |
| String[] parsedDbName = parseDbName(dbname, conf); |
| try { |
| if (isDatabaseRemote(dbname)) { |
| Database db = get_database_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME]); |
| return DataConnectorProviderFactory.getDataConnectorProvider(db).getTableNames(); |
| } |
| } catch (Exception e) { /* appears we return empty set instead of throwing an exception */ } |
| |
| try { |
| ret = getMS().getTables(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], pattern); |
| if(ret != null && !ret.isEmpty()) { |
| List<Table> tableInfo = new ArrayList<>(); |
| tableInfo = getMS().getTableObjectsByName(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], ret); |
| tableInfo = FilterUtils.filterTablesIfEnabled(isServerFilterEnabled, filterHook, tableInfo);// tableInfo object has the owner information of the table which is being passed to FilterUtils. |
| ret = new ArrayList<>(); |
| for (Table tbl : tableInfo) { |
| ret.add(tbl.getTableName()); |
| } |
| } |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_tables", ret != null, ex); |
| } |
| return ret; |
| } |
| |
| @Override |
| public List<String> get_tables_by_type(final String dbname, final String pattern, final String tableType) |
| throws MetaException { |
| startFunction("get_tables_by_type", ": db=" + dbname + " pat=" + pattern + ",type=" + tableType); |
| |
| List<String> ret = null; |
| Exception ex = null; |
| String[] parsedDbName = parseDbName(dbname, conf); |
| try { |
| ret = getTablesByTypeCore(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], pattern, tableType); |
| ret = FilterUtils.filterTableNamesIfEnabled(isServerFilterEnabled, filterHook, |
| parsedDbName[CAT_NAME], parsedDbName[DB_NAME], ret); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_tables_by_type", ret != null, ex); |
| } |
| return ret; |
| } |
| |
| private List<String> getTablesByTypeCore(final String catName, final String dbname, |
| final String pattern, final String tableType) throws MetaException { |
| startFunction("getTablesByTypeCore", ": catName=" + catName + |
| ": db=" + dbname + " pat=" + pattern + ",type=" + tableType); |
| |
| List<String> ret = null; |
| Exception ex = null; |
| Database db = null; |
| try { |
| db = get_database_core(catName, dbname); |
| if (db != null) { |
| if (db.getType().equals(DatabaseType.REMOTE)) { |
| return DataConnectorProviderFactory.getDataConnectorProvider(db).getTableNames(); |
| } |
| } |
| } catch (Exception e) { /* ignore */ } |
| |
| try { |
| ret = getMS().getTables(catName, dbname, pattern, TableType.valueOf(tableType), -1); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("getTablesByTypeCore", ret != null, ex); |
| } |
| return ret; |
| } |
| |
| @Override |
| public List<Table> get_all_materialized_view_objects_for_rewriting() |
| throws MetaException { |
| startFunction("get_all_materialized_view_objects_for_rewriting"); |
| |
| List<Table> ret = null; |
| Exception ex = null; |
| try { |
| ret = getMS().getAllMaterializedViewObjectsForRewriting(DEFAULT_CATALOG_NAME); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_all_materialized_view_objects_for_rewriting", ret != null, ex); |
| } |
| return ret; |
| } |
| |
| @Override |
| public List<String> get_materialized_views_for_rewriting(final String dbname) |
| throws MetaException { |
| startFunction("get_materialized_views_for_rewriting", ": db=" + dbname); |
| |
| List<String> ret = null; |
| Exception ex = null; |
| String[] parsedDbName = parseDbName(dbname, conf); |
| try { |
| ret = getMS().getMaterializedViewsForRewriting(parsedDbName[CAT_NAME], parsedDbName[DB_NAME]); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_materialized_views_for_rewriting", ret != null, ex); |
| } |
| return ret; |
| } |
| |
| @Override |
| public List<String> get_all_tables(final String dbname) throws MetaException { |
| startFunction("get_all_tables", ": db=" + dbname); |
| |
| List<String> ret = null; |
| Exception ex = null; |
| String[] parsedDbName = parseDbName(dbname, conf); |
| try { |
| if (isDatabaseRemote(dbname)) { |
| Database db = get_database_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME]); |
| return DataConnectorProviderFactory.getDataConnectorProvider(db).getTableNames(); |
| } |
| } catch (Exception e) { /* ignore */ } |
| |
| try { |
| ret = getMS().getAllTables(parsedDbName[CAT_NAME], parsedDbName[DB_NAME]); |
| ret = FilterUtils.filterTableNamesIfEnabled(isServerFilterEnabled, filterHook, |
| parsedDbName[CAT_NAME], parsedDbName[DB_NAME], ret); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_all_tables", ret != null, ex); |
| } |
| return ret; |
| } |
| |
| /** |
| * Use {@link #get_fields_req(GetFieldsRequest)} ()} instead. |
| * |
| */ |
| @Override |
| @Deprecated |
| public List<FieldSchema> get_fields(String db, String tableName) |
| throws MetaException, UnknownTableException, UnknownDBException { |
| return get_fields_with_environment_context(db, tableName, null); |
| } |
| |
| @Override |
| @Deprecated |
| public List<FieldSchema> get_fields_with_environment_context(String db, String tableName, |
| final EnvironmentContext envContext) |
| throws MetaException, UnknownTableException, UnknownDBException { |
| startFunction("get_fields_with_environment_context", ": db=" + db + "tbl=" + tableName); |
| String[] names = tableName.split("\\."); |
| String base_table_name = names[0]; |
| String[] parsedDbName = parseDbName(db, conf); |
| |
| Table tbl; |
| List<FieldSchema> ret = null; |
| Exception ex = null; |
| try { |
| try { |
| tbl = get_table_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], base_table_name); |
| firePreEvent(new PreReadTableEvent(tbl, this)); |
| } catch (NoSuchObjectException e) { |
| throw new UnknownTableException(e.getMessage()); |
| } |
| if (null == tbl.getSd().getSerdeInfo().getSerializationLib() || |
| MetastoreConf.getStringCollection(conf, |
| ConfVars.SERDES_USING_METASTORE_FOR_SCHEMA).contains( |
| tbl.getSd().getSerdeInfo().getSerializationLib())) { |
| ret = tbl.getSd().getCols(); |
| } else { |
| StorageSchemaReader schemaReader = getStorageSchemaReader(); |
| ret = schemaReader.readSchema(tbl, envContext, getConf()); |
| } |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(UnknownTableException.class, MetaException.class).defaultMetaException(); |
| } finally { |
| endFunction("get_fields_with_environment_context", ret != null, ex, tableName); |
| } |
| |
| return ret; |
| } |
| |
| @Override |
| public GetFieldsResponse get_fields_req(GetFieldsRequest req) |
| throws MetaException, UnknownTableException, UnknownDBException, TException { |
| String dbName = MetaStoreUtils.prependCatalogToDbName(req.getCatName(), req.getDbName(), conf); |
| List<FieldSchema> fields = get_fields_with_environment_context( |
| dbName, req.getTblName(), req.getEnvContext()); |
| GetFieldsResponse res = new GetFieldsResponse(); |
| res.setFields(fields); |
| return res; |
| } |
| |
| private StorageSchemaReader getStorageSchemaReader() throws MetaException { |
| if (storageSchemaReader == null) { |
| String className = |
| MetastoreConf.getVar(conf, MetastoreConf.ConfVars.STORAGE_SCHEMA_READER_IMPL); |
| Class<? extends StorageSchemaReader> readerClass = |
| JavaUtils.getClass(className, StorageSchemaReader.class); |
| try { |
| storageSchemaReader = readerClass.newInstance(); |
| } catch (InstantiationException|IllegalAccessException e) { |
| LOG.error("Unable to instantiate class " + className, e); |
| throw new MetaException(e.getMessage()); |
| } |
| } |
| return storageSchemaReader; |
| } |
| |
| /** |
| * Use {@link #get_schema_req(GetSchemaRequest)} ()} instead. |
| * |
| */ |
| @Override |
| @Deprecated |
| public List<FieldSchema> get_schema(String db, String tableName) |
| throws MetaException, UnknownTableException, UnknownDBException { |
| return get_schema_with_environment_context(db,tableName, null); |
| } |
| |
| /** |
| * Return the schema of the table. This function includes partition columns |
| * in addition to the regular columns. |
| * |
| * @param db |
| * Name of the database |
| * @param tableName |
| * Name of the table |
| * @param envContext |
| * Store session based properties |
| * @return List of columns, each column is a FieldSchema structure |
| * @throws MetaException |
| * @throws UnknownTableException |
| * @throws UnknownDBException |
| */ |
| @Override |
| @Deprecated |
| public List<FieldSchema> get_schema_with_environment_context(String db, String tableName, |
| final EnvironmentContext envContext) |
| throws MetaException, UnknownTableException, UnknownDBException { |
| startFunction("get_schema_with_environment_context", ": db=" + db + "tbl=" + tableName); |
| boolean success = false; |
| Exception ex = null; |
| try { |
| String[] names = tableName.split("\\."); |
| String base_table_name = names[0]; |
| String[] parsedDbName = parseDbName(db, conf); |
| |
| Table tbl; |
| try { |
| tbl = get_table_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], base_table_name); |
| } catch (NoSuchObjectException e) { |
| throw new UnknownTableException(e.getMessage()); |
| } |
| // Pass unparsed db name here |
| List<FieldSchema> fieldSchemas = get_fields_with_environment_context(db, base_table_name, |
| envContext); |
| |
| if (tbl == null || fieldSchemas == null) { |
| throw new UnknownTableException(tableName + " doesn't exist"); |
| } |
| |
| if (tbl.getPartitionKeys() != null) { |
| // Combine the column field schemas and the partition keys to create the |
| // whole schema |
| fieldSchemas.addAll(tbl.getPartitionKeys()); |
| } |
| success = true; |
| return fieldSchemas; |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e) |
| .throwIfInstance(UnknownDBException.class, UnknownTableException.class, MetaException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("get_schema_with_environment_context", success, ex, tableName); |
| } |
| } |
| |
| @Override |
| public GetSchemaResponse get_schema_req(GetSchemaRequest req) |
| throws MetaException, UnknownTableException, UnknownDBException, TException { |
| String dbName = MetaStoreUtils.prependCatalogToDbName(req.getCatName(), req.getDbName(), conf); |
| List<FieldSchema> fields = get_schema_with_environment_context( |
| dbName, req.getTblName(), req.getEnvContext()); |
| GetSchemaResponse res = new GetSchemaResponse(); |
| res.setFields(fields); |
| return res; |
| } |
| |
| @Override |
| public String getCpuProfile(int profileDurationInSec) throws TException { |
| return ""; |
| } |
| |
| /** |
| * Returns the value of the given configuration variable name. If the |
| * configuration variable with the given name doesn't exist, or if there |
| * were an exception thrown while retrieving the variable, or if name is |
| * null, defaultValue is returned. |
| */ |
| @Override |
| public String get_config_value(String name, String defaultValue) |
| throws TException { |
| startFunction("get_config_value", ": name=" + name + " defaultValue=" |
| + defaultValue); |
| boolean success = false; |
| Exception ex = null; |
| try { |
| if (name == null) { |
| success = true; |
| return defaultValue; |
| } |
| // Allow only keys that start with hive.*, hdfs.*, mapred.* for security |
| // i.e. don't allow access to db password |
| if (!Pattern.matches("(hive|hdfs|mapred|metastore).*", name)) { |
| throw new ConfigValSecurityException("For security reasons, the " |
| + "config key " + name + " cannot be accessed"); |
| } |
| |
| String toReturn = defaultValue; |
| try { |
| toReturn = MetastoreConf.get(conf, name); |
| if (toReturn == null) { |
| toReturn = defaultValue; |
| } |
| } catch (RuntimeException e) { |
| LOG.error("RuntimeException thrown in get_config_value - msg: " |
| + e.getMessage() + " cause: " + e.getCause()); |
| } |
| success = true; |
| return toReturn; |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(TException.class).defaultMetaException(); |
| } finally { |
| endFunction("get_config_value", success, ex); |
| } |
| } |
| |
| public static List<String> getPartValsFromName(Table t, String partName) |
| throws MetaException, InvalidObjectException { |
| Preconditions.checkArgument(t != null, "Table can not be null"); |
| // Unescape the partition name |
| LinkedHashMap<String, String> hm = Warehouse.makeSpecFromName(partName); |
| |
| List<String> partVals = new ArrayList<>(); |
| for (FieldSchema field : t.getPartitionKeys()) { |
| String key = field.getName(); |
| String val = hm.get(key); |
| if (val == null) { |
| throw new InvalidObjectException("incomplete partition name - missing " + key); |
| } |
| partVals.add(val); |
| } |
| return partVals; |
| } |
| |
| private List<String> getPartValsFromName(RawStore ms, String catName, String dbName, |
| String tblName, String partName) |
| throws MetaException, InvalidObjectException { |
| Table t = ms.getTable(catName, dbName, tblName, null); |
| if (t == null) { |
| throw new InvalidObjectException(dbName + "." + tblName |
| + " table not found"); |
| } |
| return getPartValsFromName(t, partName); |
| } |
| |
| private Partition get_partition_by_name_core(final RawStore ms, final String catName, |
| final String db_name, final String tbl_name, |
| final String part_name) throws TException { |
| fireReadTablePreEvent(catName, db_name, tbl_name); |
| List<String> partVals; |
| try { |
| partVals = getPartValsFromName(ms, catName, db_name, tbl_name, part_name); |
| } catch (InvalidObjectException e) { |
| throw new NoSuchObjectException(e.getMessage()); |
| } |
| Partition p = ms.getPartition(catName, db_name, tbl_name, partVals); |
| p = FilterUtils.filterPartitionIfEnabled(isServerFilterEnabled, filterHook, p); |
| |
| if (p == null) { |
| throw new NoSuchObjectException(TableName.getQualified(catName, db_name, tbl_name) |
| + " partition (" + part_name + ") not found"); |
| } |
| return p; |
| } |
| |
| @Override |
| @Deprecated |
| public Partition get_partition_by_name(final String db_name, final String tbl_name, |
| final String part_name) throws TException { |
| |
| String[] parsedDbName = parseDbName(db_name, conf); |
| startFunction("get_partition_by_name", ": tbl=" + |
| TableName.getQualified(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name) |
| + " part=" + part_name); |
| Partition ret = null; |
| Exception ex = null; |
| try { |
| ret = get_partition_by_name_core(getMS(), parsedDbName[CAT_NAME], |
| parsedDbName[DB_NAME], tbl_name, part_name); |
| ret = FilterUtils.filterPartitionIfEnabled(isServerFilterEnabled, filterHook, ret); |
| } catch (Exception e) { |
| ex = e; |
| rethrowException(e); |
| } finally { |
| endFunction("get_partition_by_name", ret != null, ex, tbl_name); |
| } |
| return ret; |
| } |
| |
| @Override |
| public Partition append_partition_by_name(final String db_name, final String tbl_name, |
| final String part_name) throws TException { |
| return append_partition_by_name_with_environment_context(db_name, tbl_name, part_name, null); |
| } |
| |
| @Override |
| public Partition append_partition_by_name_with_environment_context(final String db_name, |
| final String tbl_name, final String part_name, final EnvironmentContext env_context) |
| throws TException { |
| String[] parsedDbName = parseDbName(db_name, conf); |
| startFunction("append_partition_by_name", ": tbl=" |
| + TableName.getQualified(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name) + " part=" + part_name); |
| |
| Partition ret = null; |
| Exception ex = null; |
| try { |
| RawStore ms = getMS(); |
| List<String> partVals = getPartValsFromName(ms, parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name, part_name); |
| ret = append_partition_common(ms, parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name, partVals, env_context); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e) |
| .throwIfInstance(InvalidObjectException.class, AlreadyExistsException.class, MetaException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("append_partition_by_name", ret != null, ex, tbl_name); |
| } |
| return ret; |
| } |
| |
| private boolean drop_partition_by_name_core(final RawStore ms, final String catName, |
| final String db_name, final String tbl_name, |
| final String part_name, final boolean deleteData, |
| final EnvironmentContext envContext) |
| throws TException, IOException { |
| |
| List<String> partVals; |
| try { |
| partVals = getPartValsFromName(ms, catName, db_name, tbl_name, part_name); |
| } catch (InvalidObjectException e) { |
| throw new NoSuchObjectException(e.getMessage()); |
| } |
| |
| return drop_partition_common(ms, catName, db_name, tbl_name, partVals, deleteData, envContext); |
| } |
| |
| @Override |
| public boolean drop_partition_by_name(final String db_name, final String tbl_name, |
| final String part_name, final boolean deleteData) throws TException { |
| return drop_partition_by_name_with_environment_context(db_name, tbl_name, part_name, |
| deleteData, null); |
| } |
| |
| @Override |
| public boolean drop_partition_by_name_with_environment_context(final String db_name, |
| final String tbl_name, final String part_name, final boolean deleteData, |
| final EnvironmentContext envContext) throws TException { |
| String[] parsedDbName = parseDbName(db_name, conf); |
| startFunction("drop_partition_by_name", ": tbl=" + |
| TableName.getQualified(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name) |
| + " part=" + part_name); |
| |
| boolean ret = false; |
| Exception ex = null; |
| try { |
| ret = drop_partition_by_name_core(getMS(), parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name, part_name, deleteData, envContext); |
| } catch (Exception e) { |
| ex = e; |
| handleException(e).convertIfInstance(IOException.class, MetaException.class).rethrowException(e); |
| } finally { |
| endFunction("drop_partition_by_name", ret, ex, tbl_name); |
| } |
| |
| return ret; |
| } |
| |
| @Override |
| @Deprecated |
| public List<Partition> get_partitions_ps(final String db_name, |
| final String tbl_name, final List<String> part_vals, |
| final short max_parts) throws TException { |
| String[] parsedDbName = parseDbName(db_name, conf); |
| startPartitionFunction("get_partitions_ps", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name, part_vals); |
| |
| List<Partition> ret = null; |
| Exception ex = null; |
| try { |
| authorizeTableForPartitionMetadata(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| // Don't send the parsedDbName, as this method will parse itself. |
| ret = get_partitions_ps_with_auth(db_name, tbl_name, part_vals, |
| max_parts, null, null); |
| ret = FilterUtils.filterPartitionsIfEnabled(isServerFilterEnabled, filterHook, ret); |
| } catch (Exception e) { |
| ex = e; |
| rethrowException(e); |
| } finally { |
| endFunction("get_partitions_ps", ret != null, ex, tbl_name); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Use {@link #get_partitions_ps_with_auth_req(GetPartitionsPsWithAuthRequest)} ()} instead. |
| * |
| */ |
| @Override |
| @Deprecated |
| public List<Partition> get_partitions_ps_with_auth(final String db_name, |
| final String tbl_name, final List<String> part_vals, |
| final short max_parts, final String userName, |
| final List<String> groupNames) throws TException { |
| String[] parsedDbName = parseDbName(db_name, conf); |
| startPartitionFunction("get_partitions_ps_with_auth", parsedDbName[CAT_NAME], |
| parsedDbName[DB_NAME], tbl_name, part_vals); |
| fireReadTablePreEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| List<Partition> ret = null; |
| Exception ex = null; |
| try { |
| checkLimitNumberOfPartitionsByPs(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name, part_vals, max_parts); |
| authorizeTableForPartitionMetadata(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| ret = getMS().listPartitionsPsWithAuth(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name, part_vals, max_parts, userName, groupNames); |
| ret = FilterUtils.filterPartitionsIfEnabled(isServerFilterEnabled, filterHook, ret); |
| } catch (Exception e) { |
| ex = e; |
| handleException(e).convertIfInstance(InvalidObjectException.class, MetaException.class).rethrowException(e); |
| } finally { |
| endFunction("get_partitions_ps_with_auth", ret != null, ex, tbl_name); |
| } |
| return ret; |
| } |
| |
| @Override |
| public GetPartitionsPsWithAuthResponse get_partitions_ps_with_auth_req(GetPartitionsPsWithAuthRequest req) |
| throws MetaException, NoSuchObjectException, TException { |
| String dbName = MetaStoreUtils.prependCatalogToDbName(req.getCatName(), req.getDbName(), conf); |
| List<Partition> partitions = null; |
| if (req.getPartVals() == null) { |
| partitions = get_partitions_with_auth(dbName, req.getTblName(), req.getMaxParts(), req.getUserName(), |
| req.getGroupNames()); |
| } else { |
| partitions = |
| get_partitions_ps_with_auth(dbName, req.getTblName(), req.getPartVals(), req.getMaxParts(), |
| req.getUserName(), req.getGroupNames()); |
| } |
| GetPartitionsPsWithAuthResponse res = new GetPartitionsPsWithAuthResponse(); |
| res.setPartitions(partitions); |
| return res; |
| } |
| |
| /** |
| * Use {@link #get_partition_names_ps_req(GetPartitionNamesPsRequest)} ()} instead. |
| * |
| */ |
| @Override |
| @Deprecated |
| public List<String> get_partition_names_ps(final String db_name, |
| final String tbl_name, final List<String> part_vals, final short max_parts) |
| throws TException { |
| String[] parsedDbName = parseDbName(db_name, conf); |
| startPartitionFunction("get_partitions_names_ps", parsedDbName[CAT_NAME], |
| parsedDbName[DB_NAME], tbl_name, part_vals); |
| fireReadTablePreEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| List<String> ret = null; |
| Exception ex = null; |
| try { |
| authorizeTableForPartitionMetadata(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name); |
| ret = getMS().listPartitionNamesPs(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name, |
| part_vals, max_parts); |
| ret = FilterUtils.filterPartitionNamesIfEnabled(isServerFilterEnabled, |
| filterHook, parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name, ret); |
| } catch (Exception e) { |
| ex = e; |
| rethrowException(e); |
| } finally { |
| endFunction("get_partitions_names_ps", ret != null, ex, tbl_name); |
| } |
| return ret; |
| } |
| |
| @Override |
| public GetPartitionNamesPsResponse get_partition_names_ps_req(GetPartitionNamesPsRequest req) |
| throws MetaException, NoSuchObjectException, TException { |
| String dbName = MetaStoreUtils.prependCatalogToDbName(req.getCatName(), req.getDbName(), conf); |
| List<String> names = get_partition_names_ps(dbName, req.getTblName(), req.getPartValues(), |
| req.getMaxParts()); |
| GetPartitionNamesPsResponse res = new GetPartitionNamesPsResponse(); |
| res.setNames(names); |
| return res; |
| } |
| |
| @Override |
| public List<String> get_partition_names_req(PartitionsByExprRequest req) |
| throws MetaException, NoSuchObjectException, TException { |
| String catName = req.isSetCatName() ? req.getCatName() : getDefaultCatalog(conf); |
| String dbName = req.getDbName(), tblName = req.getTblName(); |
| startTableFunction("get_partition_names_req", catName, |
| dbName, tblName); |
| fireReadTablePreEvent(catName, dbName, tblName); |
| List<String> ret = null; |
| Exception ex = null; |
| try { |
| authorizeTableForPartitionMetadata(catName, dbName, tblName); |
| ret = getMS().listPartitionNames(catName, dbName, tblName, |
| req.getDefaultPartitionName(), req.getExpr(), req.getOrder(), req.getMaxParts()); |
| ret = FilterUtils.filterPartitionNamesIfEnabled(isServerFilterEnabled, |
| filterHook, catName, dbName, tblName, ret); |
| } catch (Exception e) { |
| ex = e; |
| rethrowException(e); |
| } finally { |
| endFunction("get_partition_names_req", ret != null, ex, tblName); |
| } |
| return ret; |
| } |
| |
| @Override |
| public List<String> partition_name_to_vals(String part_name) throws TException { |
| if (part_name.length() == 0) { |
| return Collections.emptyList(); |
| } |
| LinkedHashMap<String, String> map = Warehouse.makeSpecFromName(part_name); |
| return new ArrayList<>(map.values()); |
| } |
| |
| @Override |
| public Map<String, String> partition_name_to_spec(String part_name) throws TException { |
| if (part_name.length() == 0) { |
| return new HashMap<>(); |
| } |
| return Warehouse.makeSpecFromName(part_name); |
| } |
| |
| public static String lowerCaseConvertPartName(String partName) throws MetaException { |
| if (partName == null) { |
| return partName; |
| } |
| boolean isFirst = true; |
| Map<String, String> partSpec = Warehouse.makeEscSpecFromName(partName); |
| String convertedPartName = new String(); |
| |
| for (Map.Entry<String, String> entry : partSpec.entrySet()) { |
| String partColName = entry.getKey(); |
| String partColVal = entry.getValue(); |
| |
| if (!isFirst) { |
| convertedPartName += "/"; |
| } else { |
| isFirst = false; |
| } |
| convertedPartName += partColName.toLowerCase() + "=" + partColVal; |
| } |
| return convertedPartName; |
| } |
| |
| @Override |
| @Deprecated |
| public ColumnStatistics get_table_column_statistics(String dbName, String tableName, |
| String colName) throws TException { |
| String[] parsedDbName = parseDbName(dbName, conf); |
| parsedDbName[CAT_NAME] = parsedDbName[CAT_NAME].toLowerCase(); |
| parsedDbName[DB_NAME] = parsedDbName[DB_NAME].toLowerCase(); |
| tableName = tableName.toLowerCase(); |
| colName = colName.toLowerCase(); |
| startFunction("get_column_statistics_by_table", ": table=" + |
| TableName.getQualified(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tableName) + " column=" + colName); |
| ColumnStatistics statsObj = null; |
| try { |
| statsObj = getMS().getTableColumnStatistics( |
| parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName, Lists.newArrayList(colName), |
| "hive", null); |
| if (statsObj != null) { |
| assert statsObj.getStatsObjSize() <= 1; |
| } |
| return statsObj; |
| } finally { |
| endFunction("get_column_statistics_by_table", statsObj != null, null, tableName); |
| } |
| } |
| |
| @Override |
| public TableStatsResult get_table_statistics_req(TableStatsRequest request) throws TException { |
| String catName = request.isSetCatName() ? request.getCatName().toLowerCase() : |
| getDefaultCatalog(conf); |
| String dbName = request.getDbName().toLowerCase(); |
| String tblName = request.getTblName().toLowerCase(); |
| startFunction("get_table_statistics_req", ": table=" + |
| TableName.getQualified(catName, dbName, tblName)); |
| TableStatsResult result = null; |
| List<String> lowerCaseColNames = new ArrayList<>(request.getColNames().size()); |
| for (String colName : request.getColNames()) { |
| lowerCaseColNames.add(colName.toLowerCase()); |
| } |
| try { |
| ColumnStatistics cs = getMS().getTableColumnStatistics( |
| catName, dbName, tblName, lowerCaseColNames, |
| request.getEngine(), request.getValidWriteIdList()); |
| // Note: stats compliance is not propagated to the client; instead, we just return nothing |
| // if stats are not compliant for now. This won't work for stats merging, but that |
| // is currently only done on metastore size (see set_aggr...). |
| // For some optimizations we might make use of incorrect stats that are "better than |
| // nothing", so this may change in future. |
| result = new TableStatsResult((cs == null || cs.getStatsObj() == null |
| || (cs.isSetIsStatsCompliant() && !cs.isIsStatsCompliant())) |
| ? Lists.newArrayList() : cs.getStatsObj()); |
| } finally { |
| endFunction("get_table_statistics_req", result == null, null, tblName); |
| } |
| return result; |
| } |
| |
| @Override |
| @Deprecated |
| public ColumnStatistics get_partition_column_statistics(String dbName, String tableName, |
| String partName, String colName) throws TException { |
| // Note: this method appears to be unused within Hive. |
| // It doesn't take txn stats into account. |
| dbName = dbName.toLowerCase(); |
| String[] parsedDbName = parseDbName(dbName, conf); |
| tableName = tableName.toLowerCase(); |
| colName = colName.toLowerCase(); |
| String convertedPartName = lowerCaseConvertPartName(partName); |
| startFunction("get_column_statistics_by_partition", ": table=" + |
| TableName.getQualified(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tableName) + " partition=" + convertedPartName + " column=" + colName); |
| ColumnStatistics statsObj = null; |
| |
| try { |
| List<ColumnStatistics> list = getMS().getPartitionColumnStatistics( |
| parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName, |
| Lists.newArrayList(convertedPartName), Lists.newArrayList(colName), |
| "hive"); |
| if (list.isEmpty()) { |
| return null; |
| } |
| if (list.size() != 1) { |
| throw new MetaException(list.size() + " statistics for single column and partition"); |
| } |
| statsObj = list.get(0); |
| } finally { |
| endFunction("get_column_statistics_by_partition", statsObj != null, null, tableName); |
| } |
| return statsObj; |
| } |
| |
| @Override |
| public PartitionsStatsResult get_partitions_statistics_req(PartitionsStatsRequest request) |
| throws TException { |
| String catName = request.isSetCatName() ? request.getCatName().toLowerCase() : getDefaultCatalog(conf); |
| String dbName = request.getDbName().toLowerCase(); |
| String tblName = request.getTblName().toLowerCase(); |
| startFunction("get_partitions_statistics_req", ": table=" + |
| TableName.getQualified(catName, dbName, tblName)); |
| |
| PartitionsStatsResult result = null; |
| List<String> lowerCaseColNames = new ArrayList<>(request.getColNames().size()); |
| for (String colName : request.getColNames()) { |
| lowerCaseColNames.add(colName.toLowerCase()); |
| } |
| List<String> lowerCasePartNames = new ArrayList<>(request.getPartNames().size()); |
| for (String partName : request.getPartNames()) { |
| lowerCasePartNames.add(lowerCaseConvertPartName(partName)); |
| } |
| try { |
| List<ColumnStatistics> stats = getMS().getPartitionColumnStatistics( |
| catName, dbName, tblName, lowerCasePartNames, lowerCaseColNames, |
| request.getEngine(), request.isSetValidWriteIdList() ? request.getValidWriteIdList() : null); |
| Map<String, List<ColumnStatisticsObj>> map = new HashMap<>(); |
| if (stats != null) { |
| for (ColumnStatistics stat : stats) { |
| // Note: stats compliance is not propagated to the client; instead, we just return nothing |
| // if stats are not compliant for now. This won't work for stats merging, but that |
| // is currently only done on metastore size (see set_aggr...). |
| // For some optimizations we might make use of incorrect stats that are "better than |
| // nothing", so this may change in future. |
| if (stat.isSetIsStatsCompliant() && !stat.isIsStatsCompliant()) { |
| continue; |
| } |
| map.put(stat.getStatsDesc().getPartName(), stat.getStatsObj()); |
| } |
| } |
| result = new PartitionsStatsResult(map); |
| } finally { |
| endFunction("get_partitions_statistics_req", result == null, null, tblName); |
| } |
| return result; |
| } |
| |
| @Override |
| public boolean update_table_column_statistics(ColumnStatistics colStats) throws TException { |
| // Deprecated API, won't work for transactional tables |
| return updateTableColumnStatsInternal(colStats, null, -1); |
| } |
| |
| @Override |
| public SetPartitionsStatsResponse update_table_column_statistics_req( |
| SetPartitionsStatsRequest req) throws NoSuchObjectException, |
| InvalidObjectException, MetaException, InvalidInputException, |
| TException { |
| if (req.getColStatsSize() != 1) { |
| throw new InvalidInputException("Only one stats object expected"); |
| } |
| if (req.isNeedMerge()) { |
| throw new InvalidInputException("Merge is not supported for non-aggregate stats"); |
| } |
| ColumnStatistics colStats = req.getColStatsIterator().next(); |
| boolean ret = updateTableColumnStatsInternal(colStats, |
| req.getValidWriteIdList(), req.getWriteId()); |
| return new SetPartitionsStatsResponse(ret); |
| } |
| |
| private boolean updateTableColumnStatsInternal(ColumnStatistics colStats, |
| String validWriteIds, long writeId) |
| throws NoSuchObjectException, MetaException, InvalidObjectException, InvalidInputException { |
| normalizeColStatsInput(colStats); |
| |
| startFunction("write_column_statistics", ": table=" + TableName.getQualified( |
| colStats.getStatsDesc().getCatName(), colStats.getStatsDesc().getDbName(), |
| colStats.getStatsDesc().getTableName())); |
| |
| Map<String, String> parameters = null; |
| getMS().openTransaction(); |
| boolean committed = false; |
| try { |
| parameters = getMS().updateTableColumnStatistics(colStats, validWriteIds, writeId); |
| if (parameters != null) { |
| Table tableObj = getMS().getTable(colStats.getStatsDesc().getCatName(), |
| colStats.getStatsDesc().getDbName(), |
| colStats.getStatsDesc().getTableName(), validWriteIds); |
| if (transactionalListeners != null && !transactionalListeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.UPDATE_TABLE_COLUMN_STAT, |
| new UpdateTableColumnStatEvent(colStats, tableObj, parameters, |
| writeId, this)); |
| } |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.UPDATE_TABLE_COLUMN_STAT, |
| new UpdateTableColumnStatEvent(colStats, tableObj, parameters, |
| writeId,this)); |
| } |
| } |
| committed = getMS().commitTransaction(); |
| } finally { |
| if (!committed) { |
| getMS().rollbackTransaction(); |
| } |
| endFunction("write_column_statistics", parameters != null, null, |
| colStats.getStatsDesc().getTableName()); |
| } |
| |
| return parameters != null; |
| } |
| |
| private void normalizeColStatsInput(ColumnStatistics colStats) throws MetaException { |
| // TODO: is this really needed? this code is propagated from HIVE-1362 but most of it is useless. |
| ColumnStatisticsDesc statsDesc = colStats.getStatsDesc(); |
| statsDesc.setCatName(statsDesc.isSetCatName() ? statsDesc.getCatName().toLowerCase() : getDefaultCatalog(conf)); |
| statsDesc.setDbName(statsDesc.getDbName().toLowerCase()); |
| statsDesc.setTableName(statsDesc.getTableName().toLowerCase()); |
| statsDesc.setPartName(lowerCaseConvertPartName(statsDesc.getPartName())); |
| long time = System.currentTimeMillis() / 1000; |
| statsDesc.setLastAnalyzed(time); |
| |
| for (ColumnStatisticsObj statsObj : colStats.getStatsObj()) { |
| statsObj.setColName(statsObj.getColName().toLowerCase()); |
| statsObj.setColType(statsObj.getColType().toLowerCase()); |
| } |
| colStats.setStatsDesc(statsDesc); |
| colStats.setStatsObj(colStats.getStatsObj()); |
| } |
| |
| private boolean updatePartitonColStatsInternal(Table tbl, MTable mTable, ColumnStatistics colStats, |
| String validWriteIds, long writeId) |
| throws MetaException, InvalidObjectException, NoSuchObjectException, InvalidInputException { |
| normalizeColStatsInput(colStats); |
| |
| ColumnStatisticsDesc csd = colStats.getStatsDesc(); |
| String catName = csd.getCatName(), dbName = csd.getDbName(), tableName = csd.getTableName(); |
| startFunction("write_partition_column_statistics", ": db=" + dbName + " table=" + tableName |
| + " part=" + csd.getPartName()); |
| |
| boolean ret = false; |
| |
| Map<String, String> parameters; |
| List<String> partVals; |
| boolean committed = false; |
| getMS().openTransaction(); |
| |
| try { |
| tbl = Optional.ofNullable(tbl).orElse(getTable(catName, dbName, tableName)); |
| mTable = Optional.ofNullable(mTable).orElse(getMS().ensureGetMTable(catName, dbName, tableName)); |
| partVals = getPartValsFromName(tbl, csd.getPartName()); |
| parameters = getMS().updatePartitionColumnStatistics(tbl, mTable, colStats, partVals, validWriteIds, writeId); |
| if (parameters != null) { |
| if (transactionalListeners != null && !transactionalListeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.UPDATE_PARTITION_COLUMN_STAT, |
| new UpdatePartitionColumnStatEvent(colStats, partVals, parameters, tbl, |
| writeId, this)); |
| } |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.UPDATE_PARTITION_COLUMN_STAT, |
| new UpdatePartitionColumnStatEvent(colStats, partVals, parameters, tbl, |
| writeId, this)); |
| } |
| } |
| committed = getMS().commitTransaction(); |
| } finally { |
| if (!committed) { |
| getMS().rollbackTransaction(); |
| } |
| endFunction("write_partition_column_statistics", ret != false, null, tableName); |
| } |
| return parameters != null; |
| } |
| |
| private void updatePartitionColStatsForOneBatch(Table tbl, Map<String, ColumnStatistics> statsMap, |
| String validWriteIds, long writeId) |
| throws NoSuchObjectException, MetaException, InvalidObjectException, InvalidInputException { |
| Map<String, Map<String, String>> result = |
| getMS().updatePartitionColumnStatisticsInBatch(statsMap, tbl, transactionalListeners, validWriteIds, writeId); |
| if (result != null && result.size() != 0 && listeners != null) { |
| // The normal listeners, unlike transaction listeners are not using the same transactions used by the update |
| // operations. So there is no need of keeping them within the same transactions. If notification to one of |
| // the listeners failed, then even if we abort the transaction, we can not revert the notifications sent to the |
| // other listeners. |
| for (Map.Entry entry : result.entrySet()) { |
| Map<String, String> parameters = (Map<String, String>) entry.getValue(); |
| ColumnStatistics colStats = statsMap.get(entry.getKey()); |
| List<String> partVals = getPartValsFromName(tbl, colStats.getStatsDesc().getPartName()); |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventMessage.EventType.UPDATE_PARTITION_COLUMN_STAT, |
| new UpdatePartitionColumnStatEvent(colStats, partVals, parameters, |
| tbl, writeId, this)); |
| } |
| } |
| } |
| |
| private boolean updatePartitionColStatsInBatch(Table tbl, Map<String, ColumnStatistics> statsMap, |
| String validWriteIds, long writeId) |
| throws MetaException, InvalidObjectException, NoSuchObjectException, InvalidInputException { |
| |
| if (statsMap.size() == 0) { |
| return false; |
| } |
| |
| String catalogName = tbl.getCatName(); |
| String dbName = tbl.getDbName(); |
| String tableName = tbl.getTableName(); |
| |
| startFunction("updatePartitionColStatsInBatch", ": db=" + dbName + " table=" + tableName); |
| long start = System.currentTimeMillis(); |
| |
| Map<String, ColumnStatistics> newStatsMap = new HashMap<>(); |
| long numStats = 0; |
| long numStatsMax = MetastoreConf.getIntVar(conf, ConfVars.JDBC_MAX_BATCH_SIZE); |
| try { |
| for (Map.Entry entry : statsMap.entrySet()) { |
| ColumnStatistics colStats = (ColumnStatistics) entry.getValue(); |
| normalizeColStatsInput(colStats); |
| assert catalogName.equalsIgnoreCase(colStats.getStatsDesc().getCatName()); |
| assert dbName.equalsIgnoreCase(colStats.getStatsDesc().getDbName()); |
| assert tableName.equalsIgnoreCase(colStats.getStatsDesc().getTableName()); |
| newStatsMap.put((String) entry.getKey(), colStats); |
| numStats += colStats.getStatsObjSize(); |
| |
| if (newStatsMap.size() >= numStatsMax) { |
| updatePartitionColStatsForOneBatch(tbl, newStatsMap, validWriteIds, writeId); |
| newStatsMap.clear(); |
| numStats = 0; |
| } |
| } |
| if (numStats != 0) { |
| updatePartitionColStatsForOneBatch(tbl, newStatsMap, validWriteIds, writeId); |
| } |
| } finally { |
| endFunction("updatePartitionColStatsInBatch", true, null, tableName); |
| long end = System.currentTimeMillis(); |
| float sec = (end - start) / 1000F; |
| LOG.info("updatePartitionColStatsInBatch took " + sec + " seconds for " + statsMap.size() + " stats"); |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean update_partition_column_statistics(ColumnStatistics colStats) throws TException { |
| // Deprecated API. |
| return updatePartitonColStatsInternal(null, null, colStats, null, -1); |
| } |
| |
| |
| @Override |
| public SetPartitionsStatsResponse update_partition_column_statistics_req( |
| SetPartitionsStatsRequest req) throws NoSuchObjectException, |
| InvalidObjectException, MetaException, InvalidInputException, |
| TException { |
| if (req.getColStatsSize() != 1) { |
| throw new InvalidInputException("Only one stats object expected"); |
| } |
| if (req.isNeedMerge()) { |
| throw new InvalidInputException("Merge is not supported for non-aggregate stats"); |
| } |
| ColumnStatistics colStats = req.getColStatsIterator().next(); |
| boolean ret = updatePartitonColStatsInternal(null, null, colStats, |
| req.getValidWriteIdList(), req.getWriteId()); |
| return new SetPartitionsStatsResponse(ret); |
| } |
| |
| @Override |
| public boolean delete_partition_column_statistics(String dbName, String tableName, |
| String partName, String colName, String engine) throws TException { |
| dbName = dbName.toLowerCase(); |
| String[] parsedDbName = parseDbName(dbName, conf); |
| tableName = tableName.toLowerCase(); |
| if (colName != null) { |
| colName = colName.toLowerCase(); |
| } |
| String convertedPartName = lowerCaseConvertPartName(partName); |
| startFunction("delete_column_statistics_by_partition",": table=" + |
| TableName.getQualified(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName) + |
| " partition=" + convertedPartName + " column=" + colName); |
| boolean ret = false, committed = false; |
| |
| getMS().openTransaction(); |
| try { |
| List<String> partVals = getPartValsFromName(getMS(), parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName, convertedPartName); |
| Table table = getMS().getTable(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName); |
| // This API looks unused; if it were used we'd need to update stats state and write ID. |
| // We cannot just randomly nuke some txn stats. |
| if (TxnUtils.isTransactionalTable(table)) { |
| throw new MetaException("Cannot delete stats via this API for a transactional table"); |
| } |
| |
| ret = getMS().deletePartitionColumnStatistics(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName, |
| convertedPartName, partVals, colName, engine); |
| if (ret) { |
| if (transactionalListeners != null && !transactionalListeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.DELETE_PARTITION_COLUMN_STAT, |
| new DeletePartitionColumnStatEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName, |
| convertedPartName, partVals, colName, engine, this)); |
| } |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.DELETE_PARTITION_COLUMN_STAT, |
| new DeletePartitionColumnStatEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName, |
| convertedPartName, partVals, colName, engine, this)); |
| } |
| } |
| committed = getMS().commitTransaction(); |
| } finally { |
| if (!committed) { |
| getMS().rollbackTransaction(); |
| } |
| endFunction("delete_column_statistics_by_partition", ret != false, null, tableName); |
| } |
| return ret; |
| } |
| |
| @Override |
| public boolean delete_table_column_statistics(String dbName, String tableName, String colName, String engine) |
| throws TException { |
| dbName = dbName.toLowerCase(); |
| tableName = tableName.toLowerCase(); |
| |
| String[] parsedDbName = parseDbName(dbName, conf); |
| |
| if (colName != null) { |
| colName = colName.toLowerCase(); |
| } |
| startFunction("delete_column_statistics_by_table", ": table=" + |
| TableName.getQualified(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName) + " column=" + |
| colName); |
| |
| |
| boolean ret = false, committed = false; |
| getMS().openTransaction(); |
| try { |
| Table table = getMS().getTable(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName); |
| // This API looks unused; if it were used we'd need to update stats state and write ID. |
| // We cannot just randomly nuke some txn stats. |
| if (TxnUtils.isTransactionalTable(table)) { |
| throw new MetaException("Cannot delete stats via this API for a transactional table"); |
| } |
| |
| ret = getMS().deleteTableColumnStatistics(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tableName, colName, engine); |
| if (ret) { |
| if (transactionalListeners != null && !transactionalListeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.DELETE_TABLE_COLUMN_STAT, |
| new DeleteTableColumnStatEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tableName, colName, engine, this)); |
| } |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.DELETE_TABLE_COLUMN_STAT, |
| new DeleteTableColumnStatEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tableName, colName, engine, this)); |
| } |
| } |
| committed = getMS().commitTransaction(); |
| } finally { |
| if (!committed) { |
| getMS().rollbackTransaction(); |
| } |
| endFunction("delete_column_statistics_by_table", ret != false, null, tableName); |
| } |
| return ret; |
| } |
| |
| @Override |
| public void update_transaction_statistics(UpdateTransactionalStatsRequest req) throws TException { |
| getTxnHandler().updateTransactionStatistics(req); |
| } |
| |
| @Override |
| @Deprecated |
| public List<Partition> get_partitions_by_filter(final String dbName, final String tblName, |
| final String filter, final short maxParts) |
| throws TException { |
| String[] parsedDbName = parseDbName(dbName, conf); |
| startTableFunction("get_partitions_by_filter", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tblName); |
| fireReadTablePreEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tblName); |
| List<Partition> ret = null; |
| Exception ex = null; |
| try { |
| checkLimitNumberOfPartitionsByFilter(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tblName, filter, maxParts); |
| |
| authorizeTableForPartitionMetadata(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tblName); |
| |
| ret = getMS().getPartitionsByFilter(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tblName, |
| filter, maxParts); |
| ret = FilterUtils.filterPartitionsIfEnabled(isServerFilterEnabled, filterHook, ret); |
| } catch (Exception e) { |
| ex = e; |
| rethrowException(e); |
| } finally { |
| endFunction("get_partitions_by_filter", ret != null, ex, tblName); |
| } |
| return ret; |
| } |
| |
| @Override |
| @Deprecated |
| public List<PartitionSpec> get_part_specs_by_filter(final String dbName, final String tblName, |
| final String filter, final int maxParts) |
| throws TException { |
| |
| String[] parsedDbName = parseDbName(dbName, conf); |
| startTableFunction("get_partitions_by_filter_pspec", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tblName); |
| |
| List<PartitionSpec> partitionSpecs = null; |
| try { |
| Table table = get_table_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tblName); |
| // Don't pass the parsed db name, as get_partitions_by_filter will parse it itself |
| List<Partition> partitions = get_partitions_by_filter(dbName, tblName, filter, (short) maxParts); |
| |
| if (is_partition_spec_grouping_enabled(table)) { |
| partitionSpecs = MetaStoreServerUtils |
| .getPartitionspecsGroupedByStorageDescriptor(table, partitions); |
| } |
| else { |
| PartitionSpec pSpec = new PartitionSpec(); |
| pSpec.setPartitionList(new PartitionListComposingSpec(partitions)); |
| pSpec.setRootPath(table.getSd().getLocation()); |
| pSpec.setCatName(parsedDbName[CAT_NAME]); |
| pSpec.setDbName(parsedDbName[DB_NAME]); |
| pSpec.setTableName(tblName); |
| partitionSpecs = Arrays.asList(pSpec); |
| } |
| |
| return partitionSpecs; |
| } |
| finally { |
| endFunction("get_partitions_by_filter_pspec", partitionSpecs != null && !partitionSpecs.isEmpty(), null, tblName); |
| } |
| } |
| |
| @Override |
| public PartitionsSpecByExprResult get_partitions_spec_by_expr( |
| PartitionsByExprRequest req) throws TException { |
| String dbName = req.getDbName(), tblName = req.getTblName(); |
| String catName = req.isSetCatName() ? req.getCatName() : getDefaultCatalog(conf); |
| startTableFunction("get_partitions_spec_by_expr", catName, dbName, tblName); |
| fireReadTablePreEvent(catName, dbName, tblName); |
| PartitionsSpecByExprResult ret = null; |
| Exception ex = null; |
| try { |
| checkLimitNumberOfPartitionsByExpr(catName, dbName, tblName, req.getExpr(), UNLIMITED_MAX_PARTITIONS); |
| List<Partition> partitions = new LinkedList<>(); |
| boolean hasUnknownPartitions = getMS().getPartitionsByExpr(catName, dbName, tblName, |
| req.getExpr(), req.getDefaultPartitionName(), req.getMaxParts(), partitions); |
| Table table = get_table_core(catName, dbName, tblName); |
| List<PartitionSpec> partitionSpecs = |
| MetaStoreServerUtils.getPartitionspecsGroupedByStorageDescriptor(table, partitions); |
| ret = new PartitionsSpecByExprResult(partitionSpecs, hasUnknownPartitions); |
| } catch (Exception e) { |
| ex = e; |
| rethrowException(e); |
| } finally { |
| endFunction("get_partitions_spec_by_expr", ret != null, ex, tblName); |
| } |
| return ret; |
| } |
| |
| @Override |
| public PartitionsByExprResult get_partitions_by_expr( |
| PartitionsByExprRequest req) throws TException { |
| String dbName = req.getDbName(), tblName = req.getTblName(); |
| String catName = req.isSetCatName() ? req.getCatName() : getDefaultCatalog(conf); |
| startTableFunction("get_partitions_by_expr", catName, dbName, tblName); |
| fireReadTablePreEvent(catName, dbName, tblName); |
| PartitionsByExprResult ret = null; |
| Exception ex = null; |
| try { |
| checkLimitNumberOfPartitionsByExpr(catName, dbName, tblName, req.getExpr(), UNLIMITED_MAX_PARTITIONS); |
| List<Partition> partitions = new LinkedList<>(); |
| boolean hasUnknownPartitions = getMS().getPartitionsByExpr(catName, dbName, tblName, |
| req.getExpr(), req.getDefaultPartitionName(), req.getMaxParts(), partitions); |
| ret = new PartitionsByExprResult(partitions, hasUnknownPartitions); |
| } catch (Exception e) { |
| ex = e; |
| rethrowException(e); |
| } finally { |
| endFunction("get_partitions_by_expr", ret != null, ex, tblName); |
| } |
| return ret; |
| } |
| |
| @Override |
| @Deprecated |
| public int get_num_partitions_by_filter(final String dbName, |
| final String tblName, final String filter) |
| throws TException { |
| String[] parsedDbName = parseDbName(dbName, conf); |
| if (parsedDbName[DB_NAME] == null || tblName == null) { |
| throw new MetaException("The DB and table name cannot be null."); |
| } |
| startTableFunction("get_num_partitions_by_filter", parsedDbName[CAT_NAME], |
| parsedDbName[DB_NAME], tblName); |
| |
| int ret = -1; |
| Exception ex = null; |
| try { |
| ret = getMS().getNumPartitionsByFilter(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tblName, filter); |
| } catch (Exception e) { |
| ex = e; |
| rethrowException(e); |
| } finally { |
| endFunction("get_num_partitions_by_filter", ret != -1, ex, tblName); |
| } |
| return ret; |
| } |
| |
| private int get_num_partitions_by_expr(final String catName, final String dbName, |
| final String tblName, final byte[] expr) |
| throws TException { |
| int ret = -1; |
| Exception ex = null; |
| try { |
| ret = getMS().getNumPartitionsByExpr(catName, dbName, tblName, expr); |
| } catch (Exception e) { |
| ex = e; |
| rethrowException(e); |
| } finally { |
| endFunction("get_num_partitions_by_expr", ret != -1, ex, tblName); |
| } |
| return ret; |
| } |
| |
| private int getNumPartitionsByPs(final String catName, final String dbName, |
| final String tblName, List<String> partVals) |
| throws TException { |
| String[] parsedDbName = parseDbName(dbName, conf); |
| startTableFunction("getNumPartitionsByPs", parsedDbName[CAT_NAME], |
| parsedDbName[DB_NAME], tblName); |
| |
| int ret = -1; |
| Exception ex = null; |
| try { |
| ret = getMS().getNumPartitionsByPs(catName, dbName, tblName, partVals); |
| } catch (Exception e) { |
| ex = e; |
| rethrowException(e); |
| } finally { |
| endFunction("getNumPartitionsByPs", ret != -1, ex, tblName); |
| } |
| return ret; |
| } |
| |
| @Override |
| @Deprecated |
| public List<Partition> get_partitions_by_names(final String dbName, final String tblName, |
| final List<String> partNames) |
| throws TException { |
| return get_partitions_by_names(dbName, tblName, partNames, false, null, null); |
| } |
| |
| @Override |
| public GetPartitionsByNamesResult get_partitions_by_names_req(GetPartitionsByNamesRequest gpbnr) |
| throws TException { |
| List<Partition> partitions = get_partitions_by_names(gpbnr.getDb_name(), |
| gpbnr.getTbl_name(), gpbnr.getNames(), |
| gpbnr.isSetGet_col_stats() && gpbnr.isGet_col_stats(), gpbnr.getEngine(), |
| gpbnr.getProcessorCapabilities(), gpbnr.getProcessorIdentifier()); |
| return new GetPartitionsByNamesResult(partitions); |
| } |
| |
| public List<Partition> get_partitions_by_names(final String dbName, final String tblName, |
| final List<String> partNames, boolean getColStats, String engine, String validWriteIdList) |
| throws TException { |
| return get_partitions_by_names( |
| dbName, tblName, partNames, getColStats, engine, null, null); |
| } |
| |
| public List<Partition> get_partitions_by_names(final String dbName, final String tblName, |
| final List<String> partNames, boolean getColStats, String engine, |
| List<String> processorCapabilities, String processorId) throws TException { |
| |
| String[] dbNameParts = parseDbName(dbName, conf); |
| String parsedCatName = dbNameParts[CAT_NAME]; |
| String parsedDbName = dbNameParts[DB_NAME]; |
| List<Partition> ret = null; |
| Table table = null; |
| Exception ex = null; |
| boolean success = false; |
| startTableFunction("get_partitions_by_names", parsedCatName, parsedDbName, |
| tblName); |
| try { |
| getMS().openTransaction(); |
| authorizeTableForPartitionMetadata(parsedCatName, parsedDbName, tblName); |
| |
| fireReadTablePreEvent(parsedCatName, parsedDbName, tblName); |
| |
| ret = getMS().getPartitionsByNames(parsedCatName, parsedDbName, tblName, partNames); |
| ret = FilterUtils.filterPartitionsIfEnabled(isServerFilterEnabled, filterHook, ret); |
| table = getTable(parsedCatName, parsedDbName, tblName); |
| |
| // If requested add column statistics in each of the partition objects |
| if (getColStats) { |
| // Since each partition may have stats collected for different set of columns, we |
| // request them separately. |
| for (Partition part: ret) { |
| String partName = Warehouse.makePartName(table.getPartitionKeys(), part.getValues()); |
| List<ColumnStatistics> partColStatsList = |
| getMS().getPartitionColumnStatistics(parsedCatName, parsedDbName, tblName, |
| Collections.singletonList(partName), |
| StatsSetupConst.getColumnsHavingStats(part.getParameters()), |
| engine); |
| if (partColStatsList != null && !partColStatsList.isEmpty()) { |
| ColumnStatistics partColStats = partColStatsList.get(0); |
| if (partColStats != null) { |
| part.setColStats(partColStats); |
| } |
| } |
| } |
| } |
| |
| if (processorCapabilities == null || processorCapabilities.size() == 0 || |
| processorCapabilities.contains("MANAGERAWMETADATA")) { |
| LOG.info("Skipping translation for processor with " + processorId); |
| } else { |
| if (transformer != null) { |
| ret = transformer.transformPartitions(ret, table, processorCapabilities, processorId); |
| } |
| } |
| success = getMS().commitTransaction(); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e) |
| .throwIfInstance(MetaException.class, NoSuchObjectException.class, InvalidObjectException.class) |
| .defaultMetaException(); |
| } finally { |
| if (!success) { |
| getMS().rollbackTransaction(); |
| } |
| endFunction("get_partitions_by_names", ret != null, ex, tblName); |
| } |
| return ret; |
| } |
| |
| /** |
| * Creates an instance of property manager based on the (declared) namespace. |
| * @param ns the namespace |
| * @return the manager instance |
| * @throws TException |
| */ |
| private PropertyManager getPropertyManager(String ns) throws MetaException, NoSuchObjectException { |
| PropertyStore propertyStore = getMS().getPropertyStore(); |
| PropertyManager mgr = PropertyManager.create(ns, propertyStore); |
| return mgr; |
| } |
| @Override |
| public PropertyGetResponse get_properties(PropertyGetRequest req) throws TException { |
| try { |
| PropertyManager mgr = getPropertyManager(req.getNameSpace()); |
| Map<String, PropertyMap> selected = mgr.selectProperties(req.getMapPrefix(), req.getMapPredicate(), req.getMapSelection()); |
| PropertyGetResponse response = new PropertyGetResponse(); |
| Map<String, Map<String, String>> returned = new TreeMap<>(); |
| selected.forEach((k, v) -> { |
| returned.put(k, v.export()); |
| }); |
| response.setProperties(returned); |
| return response; |
| } catch(PropertyException exception) { |
| throw ExceptionHandler.newMetaException(exception); |
| } |
| } |
| |
| @Override |
| public boolean set_properties(PropertySetRequest req) throws TException { |
| try { |
| PropertyManager mgr = getPropertyManager(req.getNameSpace()); |
| mgr.setProperties((Map<String, Object>) (Map<?, ?>) req.getPropertyMap()); |
| mgr.commit(); |
| return true; |
| } catch(PropertyException exception) { |
| throw ExceptionHandler.newMetaException(exception); |
| } |
| } |
| |
| @Override |
| public PrincipalPrivilegeSet get_privilege_set(HiveObjectRef hiveObject, String userName, |
| List<String> groupNames) throws TException { |
| firePreEvent(new PreAuthorizationCallEvent(this)); |
| String catName = hiveObject.isSetCatName() ? hiveObject.getCatName() : getDefaultCatalog(conf); |
| HiveObjectType debug = hiveObject.getObjectType(); |
| if (hiveObject.getObjectType() == HiveObjectType.COLUMN) { |
| String partName = getPartName(hiveObject); |
| return this.get_column_privilege_set(catName, hiveObject.getDbName(), hiveObject |
| .getObjectName(), partName, hiveObject.getColumnName(), userName, |
| groupNames); |
| } else if (hiveObject.getObjectType() == HiveObjectType.PARTITION) { |
| String partName = getPartName(hiveObject); |
| return this.get_partition_privilege_set(catName, hiveObject.getDbName(), |
| hiveObject.getObjectName(), partName, userName, groupNames); |
| } else if (hiveObject.getObjectType() == HiveObjectType.DATABASE) { |
| return this.get_db_privilege_set(catName, hiveObject.getDbName(), userName, |
| groupNames); |
| } else if (hiveObject.getObjectType() == HiveObjectType.DATACONNECTOR) { |
| return this.get_connector_privilege_set(catName, hiveObject.getObjectName(), userName, groupNames); |
| } else if (hiveObject.getObjectType() == HiveObjectType.TABLE) { |
| return this.get_table_privilege_set(catName, hiveObject.getDbName(), hiveObject |
| .getObjectName(), userName, groupNames); |
| } else if (hiveObject.getObjectType() == HiveObjectType.GLOBAL) { |
| return this.get_user_privilege_set(userName, groupNames); |
| } |
| return null; |
| } |
| |
| private String getPartName(HiveObjectRef hiveObject) throws MetaException { |
| String partName = null; |
| List<String> partValue = hiveObject.getPartValues(); |
| if (partValue != null && partValue.size() > 0) { |
| try { |
| String catName = hiveObject.isSetCatName() ? hiveObject.getCatName() : |
| getDefaultCatalog(conf); |
| Table table = get_table_core(catName, hiveObject.getDbName(), hiveObject |
| .getObjectName()); |
| partName = Warehouse |
| .makePartName(table.getPartitionKeys(), partValue); |
| } catch (NoSuchObjectException e) { |
| throw new MetaException(e.getMessage()); |
| } |
| } |
| return partName; |
| } |
| |
| private PrincipalPrivilegeSet get_column_privilege_set(String catName, final String dbName, |
| final String tableName, final String partName, final String columnName, |
| final String userName, final List<String> groupNames) throws TException { |
| incrementCounter("get_column_privilege_set"); |
| |
| PrincipalPrivilegeSet ret; |
| try { |
| ret = getMS().getColumnPrivilegeSet( |
| catName, dbName, tableName, partName, columnName, userName, groupNames); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| return ret; |
| } |
| |
| private PrincipalPrivilegeSet get_db_privilege_set(String catName, final String dbName, |
| final String userName, final List<String> groupNames) throws TException { |
| incrementCounter("get_db_privilege_set"); |
| |
| PrincipalPrivilegeSet ret; |
| try { |
| ret = getMS().getDBPrivilegeSet(catName, dbName, userName, groupNames); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| return ret; |
| } |
| |
| private PrincipalPrivilegeSet get_connector_privilege_set(String catName, final String connectorName, |
| final String userName, final List<String> groupNames) throws TException { |
| incrementCounter("get_connector_privilege_set"); |
| |
| PrincipalPrivilegeSet ret; |
| try { |
| ret = getMS().getConnectorPrivilegeSet(catName, connectorName, userName, groupNames); |
| } catch (MetaException e) { |
| throw e; |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| return ret; |
| |
| } |
| |
| private PrincipalPrivilegeSet get_partition_privilege_set( |
| String catName, final String dbName, final String tableName, final String partName, |
| final String userName, final List<String> groupNames) |
| throws TException { |
| incrementCounter("get_partition_privilege_set"); |
| |
| PrincipalPrivilegeSet ret; |
| try { |
| ret = getMS().getPartitionPrivilegeSet(catName, dbName, tableName, partName, |
| userName, groupNames); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| return ret; |
| } |
| |
| private PrincipalPrivilegeSet get_table_privilege_set(String catName, final String dbName, |
| final String tableName, final String userName, |
| final List<String> groupNames) throws TException { |
| incrementCounter("get_table_privilege_set"); |
| |
| PrincipalPrivilegeSet ret; |
| try { |
| ret = getMS().getTablePrivilegeSet(catName, dbName, tableName, userName, |
| groupNames); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| return ret; |
| } |
| |
| @Override |
| public boolean grant_role(final String roleName, |
| final String principalName, final PrincipalType principalType, |
| final String grantor, final PrincipalType grantorType, final boolean grantOption) |
| throws TException { |
| incrementCounter("add_role_member"); |
| firePreEvent(new PreAuthorizationCallEvent(this)); |
| if (PUBLIC.equals(roleName)) { |
| throw new MetaException("No user can be added to " + PUBLIC +". Since all users implicitly" |
| + " belong to " + PUBLIC + " role."); |
| } |
| Boolean ret; |
| try { |
| RawStore ms = getMS(); |
| Role role = ms.getRole(roleName); |
| if(principalType == PrincipalType.ROLE){ |
| //check if this grant statement will end up creating a cycle |
| if(isNewRoleAParent(principalName, roleName)){ |
| throw new MetaException("Cannot grant role " + principalName + " to " + roleName + |
| " as " + roleName + " already belongs to the role " + principalName + |
| ". (no cycles allowed)"); |
| } |
| } |
| ret = ms.grantRole(role, principalName, principalType, grantor, grantorType, grantOption); |
| } catch (Exception e) { |
| String exInfo = "Got exception: " + e.getClass().getName() + " " + e.getMessage(); |
| LOG.error(exInfo, e); |
| throw handleException(e).throwIfInstance(MetaException.class) |
| .toMetaExceptionIfInstance(exInfo, InvalidObjectException.class, NoSuchObjectException.class) |
| .defaultTException(); |
| } |
| return ret; |
| } |
| |
| |
| |
| /** |
| * Check if newRole is in parent hierarchy of curRole |
| * @param newRole |
| * @param curRole |
| * @return true if newRole is curRole or present in its hierarchy |
| * @throws MetaException |
| */ |
| private boolean isNewRoleAParent(String newRole, String curRole) throws MetaException { |
| if(newRole.equals(curRole)){ |
| return true; |
| } |
| //do this check recursively on all the parent roles of curRole |
| List<Role> parentRoleMaps = getMS().listRoles(curRole, PrincipalType.ROLE); |
| for(Role parentRole : parentRoleMaps){ |
| if(isNewRoleAParent(newRole, parentRole.getRoleName())){ |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public List<Role> list_roles(final String principalName, |
| final PrincipalType principalType) throws TException { |
| incrementCounter("list_roles"); |
| firePreEvent(new PreAuthorizationCallEvent(this)); |
| return getMS().listRoles(principalName, principalType); |
| } |
| |
| @Override |
| public boolean create_role(final Role role) throws TException { |
| incrementCounter("create_role"); |
| firePreEvent(new PreAuthorizationCallEvent(this)); |
| if (PUBLIC.equals(role.getRoleName())) { |
| throw new MetaException(PUBLIC + " role implicitly exists. It can't be created."); |
| } |
| Boolean ret; |
| try { |
| ret = getMS().addRole(role.getRoleName(), role.getOwnerName()); |
| } catch (Exception e) { |
| String exInfo = "Got exception: " + e.getClass().getName() + " " + e.getMessage(); |
| LOG.error(exInfo, e); |
| throw handleException(e).throwIfInstance(MetaException.class) |
| .toMetaExceptionIfInstance(exInfo, InvalidObjectException.class, NoSuchObjectException.class) |
| .defaultTException(); |
| } |
| return ret; |
| } |
| |
| @Override |
| public boolean drop_role(final String roleName) throws TException { |
| incrementCounter("drop_role"); |
| firePreEvent(new PreAuthorizationCallEvent(this)); |
| if (ADMIN.equals(roleName) || PUBLIC.equals(roleName)) { |
| throw new MetaException(PUBLIC + "," + ADMIN + " roles can't be dropped."); |
| } |
| Boolean ret; |
| try { |
| ret = getMS().removeRole(roleName); |
| } catch (Exception e) { |
| String exInfo = "Got exception: " + e.getClass().getName() + " " + e.getMessage(); |
| LOG.error(exInfo, e); |
| throw handleException(e).throwIfInstance(MetaException.class) |
| .toMetaExceptionIfInstance(exInfo, NoSuchObjectException.class) |
| .defaultTException(); |
| } |
| return ret; |
| } |
| |
| @Override |
| public List<String> get_role_names() throws TException { |
| incrementCounter("get_role_names"); |
| firePreEvent(new PreAuthorizationCallEvent(this)); |
| List<String> ret; |
| try { |
| ret = getMS().listRoleNames(); |
| return ret; |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| } |
| |
| @Override |
| public boolean grant_privileges(final PrivilegeBag privileges) throws TException { |
| incrementCounter("grant_privileges"); |
| firePreEvent(new PreAuthorizationCallEvent(this)); |
| Boolean ret; |
| try { |
| ret = getMS().grantPrivileges(privileges); |
| } catch (Exception e) { |
| String exInfo = "Got exception: " + e.getClass().getName() + " " + e.getMessage(); |
| LOG.error(exInfo, e); |
| throw handleException(e).throwIfInstance(MetaException.class) |
| .toMetaExceptionIfInstance(exInfo, InvalidObjectException.class, NoSuchObjectException.class) |
| .defaultTException(); |
| } |
| return ret; |
| } |
| |
| @Override |
| public boolean revoke_role(final String roleName, final String userName, |
| final PrincipalType principalType) throws TException { |
| return revoke_role(roleName, userName, principalType, false); |
| } |
| |
| private boolean revoke_role(final String roleName, final String userName, |
| final PrincipalType principalType, boolean grantOption) throws TException { |
| incrementCounter("remove_role_member"); |
| firePreEvent(new PreAuthorizationCallEvent(this)); |
| if (PUBLIC.equals(roleName)) { |
| throw new MetaException(PUBLIC + " role can't be revoked."); |
| } |
| Boolean ret; |
| try { |
| RawStore ms = getMS(); |
| Role mRole = ms.getRole(roleName); |
| ret = ms.revokeRole(mRole, userName, principalType, grantOption); |
| } catch (Exception e) { |
| String exInfo = "Got exception: " + e.getClass().getName() + " " + e.getMessage(); |
| LOG.error(exInfo, e); |
| throw handleException(e).throwIfInstance(MetaException.class) |
| .toMetaExceptionIfInstance(exInfo, NoSuchObjectException.class) |
| .defaultTException(); |
| } |
| return ret; |
| } |
| |
| @Override |
| public GrantRevokeRoleResponse grant_revoke_role(GrantRevokeRoleRequest request) |
| throws TException { |
| GrantRevokeRoleResponse response = new GrantRevokeRoleResponse(); |
| boolean grantOption = false; |
| if (request.isSetGrantOption()) { |
| grantOption = request.isGrantOption(); |
| } |
| switch (request.getRequestType()) { |
| case GRANT: { |
| boolean result = grant_role(request.getRoleName(), |
| request.getPrincipalName(), request.getPrincipalType(), |
| request.getGrantor(), request.getGrantorType(), grantOption); |
| response.setSuccess(result); |
| break; |
| } |
| case REVOKE: { |
| boolean result = revoke_role(request.getRoleName(), request.getPrincipalName(), |
| request.getPrincipalType(), grantOption); |
| response.setSuccess(result); |
| break; |
| } |
| default: |
| throw new MetaException("Unknown request type " + request.getRequestType()); |
| } |
| |
| return response; |
| } |
| |
| @Override |
| public GrantRevokePrivilegeResponse grant_revoke_privileges(GrantRevokePrivilegeRequest request) |
| throws TException { |
| GrantRevokePrivilegeResponse response = new GrantRevokePrivilegeResponse(); |
| switch (request.getRequestType()) { |
| case GRANT: { |
| boolean result = grant_privileges(request.getPrivileges()); |
| response.setSuccess(result); |
| break; |
| } |
| case REVOKE: { |
| boolean revokeGrantOption = false; |
| if (request.isSetRevokeGrantOption()) { |
| revokeGrantOption = request.isRevokeGrantOption(); |
| } |
| boolean result = revoke_privileges(request.getPrivileges(), revokeGrantOption); |
| response.setSuccess(result); |
| break; |
| } |
| default: |
| throw new MetaException("Unknown request type " + request.getRequestType()); |
| } |
| |
| return response; |
| } |
| |
| @Override |
| public GrantRevokePrivilegeResponse refresh_privileges(HiveObjectRef objToRefresh, String authorizer, |
| GrantRevokePrivilegeRequest grantRequest) |
| throws TException { |
| incrementCounter("refresh_privileges"); |
| firePreEvent(new PreAuthorizationCallEvent(this)); |
| GrantRevokePrivilegeResponse response = new GrantRevokePrivilegeResponse(); |
| try { |
| boolean result = getMS().refreshPrivileges(objToRefresh, authorizer, grantRequest.getPrivileges()); |
| response.setSuccess(result); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| return response; |
| } |
| |
| @Override |
| public boolean revoke_privileges(final PrivilegeBag privileges) throws TException { |
| return revoke_privileges(privileges, false); |
| } |
| |
| public boolean revoke_privileges(final PrivilegeBag privileges, boolean grantOption) |
| throws TException { |
| incrementCounter("revoke_privileges"); |
| firePreEvent(new PreAuthorizationCallEvent(this)); |
| Boolean ret; |
| try { |
| ret = getMS().revokePrivileges(privileges, grantOption); |
| } catch (Exception e) { |
| String exInfo = "Got exception: " + e.getClass().getName() + " " + e.getMessage(); |
| LOG.error(exInfo, e); |
| throw handleException(e).throwIfInstance(MetaException.class) |
| .toMetaExceptionIfInstance(exInfo, InvalidObjectException.class, NoSuchObjectException.class) |
| .defaultTException(); |
| } |
| return ret; |
| } |
| |
| private PrincipalPrivilegeSet get_user_privilege_set(final String userName, |
| final List<String> groupNames) throws TException { |
| incrementCounter("get_user_privilege_set"); |
| PrincipalPrivilegeSet ret; |
| try { |
| ret = getMS().getUserPrivilegeSet(userName, groupNames); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| return ret; |
| } |
| |
| @Override |
| public List<HiveObjectPrivilege> list_privileges(String principalName, |
| PrincipalType principalType, HiveObjectRef hiveObject) |
| throws TException { |
| firePreEvent(new PreAuthorizationCallEvent(this)); |
| String catName = hiveObject.isSetCatName() ? hiveObject.getCatName() : getDefaultCatalog(conf); |
| if (hiveObject.getObjectType() == null) { |
| return getAllPrivileges(principalName, principalType, catName); |
| } |
| if (hiveObject.getObjectType() == HiveObjectType.GLOBAL) { |
| return list_global_privileges(principalName, principalType); |
| } |
| if (hiveObject.getObjectType() == HiveObjectType.DATABASE) { |
| return list_db_privileges(principalName, principalType, catName, hiveObject |
| .getDbName()); |
| } |
| if (hiveObject.getObjectType() == HiveObjectType.DATACONNECTOR) { |
| return list_dc_privileges(principalName, principalType, hiveObject |
| .getObjectName()); |
| } |
| if (hiveObject.getObjectType() == HiveObjectType.TABLE) { |
| return list_table_privileges(principalName, principalType, |
| catName, hiveObject.getDbName(), hiveObject.getObjectName()); |
| } |
| if (hiveObject.getObjectType() == HiveObjectType.PARTITION) { |
| return list_partition_privileges(principalName, principalType, |
| catName, hiveObject.getDbName(), hiveObject.getObjectName(), hiveObject |
| .getPartValues()); |
| } |
| if (hiveObject.getObjectType() == HiveObjectType.COLUMN) { |
| if (hiveObject.getPartValues() == null || hiveObject.getPartValues().isEmpty()) { |
| return list_table_column_privileges(principalName, principalType, |
| catName, hiveObject.getDbName(), hiveObject.getObjectName(), hiveObject.getColumnName()); |
| } |
| return list_partition_column_privileges(principalName, principalType, |
| catName, hiveObject.getDbName(), hiveObject.getObjectName(), hiveObject |
| .getPartValues(), hiveObject.getColumnName()); |
| } |
| return null; |
| } |
| |
| private List<HiveObjectPrivilege> getAllPrivileges(String principalName, |
| PrincipalType principalType, String catName) throws TException { |
| List<HiveObjectPrivilege> privs = new ArrayList<>(); |
| privs.addAll(list_global_privileges(principalName, principalType)); |
| privs.addAll(list_db_privileges(principalName, principalType, catName, null)); |
| privs.addAll(list_dc_privileges(principalName, principalType, null)); |
| privs.addAll(list_table_privileges(principalName, principalType, catName, null, null)); |
| privs.addAll(list_partition_privileges(principalName, principalType, catName, null, null, null)); |
| privs.addAll(list_table_column_privileges(principalName, principalType, catName, null, null, null)); |
| privs.addAll(list_partition_column_privileges(principalName, principalType, |
| catName, null, null, null, null)); |
| return privs; |
| } |
| |
| private List<HiveObjectPrivilege> list_table_column_privileges( |
| final String principalName, final PrincipalType principalType, String catName, |
| final String dbName, final String tableName, final String columnName) throws TException { |
| incrementCounter("list_table_column_privileges"); |
| |
| try { |
| if (dbName == null) { |
| return getMS().listPrincipalTableColumnGrantsAll(principalName, principalType); |
| } |
| if (principalName == null) { |
| return getMS().listTableColumnGrantsAll(catName, dbName, tableName, columnName); |
| } |
| return getMS().listPrincipalTableColumnGrants(principalName, principalType, |
| catName, dbName, tableName, columnName); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| } |
| |
| private List<HiveObjectPrivilege> list_partition_column_privileges( |
| final String principalName, final PrincipalType principalType, |
| String catName, final String dbName, final String tableName, final List<String> partValues, |
| final String columnName) throws TException { |
| incrementCounter("list_partition_column_privileges"); |
| |
| try { |
| if (dbName == null) { |
| return getMS().listPrincipalPartitionColumnGrantsAll(principalName, principalType); |
| } |
| Table tbl = get_table_core(catName, dbName, tableName); |
| String partName = Warehouse.makePartName(tbl.getPartitionKeys(), partValues); |
| if (principalName == null) { |
| return getMS().listPartitionColumnGrantsAll(catName, dbName, tableName, partName, columnName); |
| } |
| |
| return getMS().listPrincipalPartitionColumnGrants(principalName, principalType, catName, dbName, |
| tableName, partValues, partName, columnName); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| } |
| |
| private List<HiveObjectPrivilege> list_db_privileges(final String principalName, |
| final PrincipalType principalType, String catName, final String dbName) throws TException { |
| incrementCounter("list_security_db_grant"); |
| |
| try { |
| if (dbName == null) { |
| return getMS().listPrincipalDBGrantsAll(principalName, principalType); |
| } |
| if (principalName == null) { |
| return getMS().listDBGrantsAll(catName, dbName); |
| } else { |
| return getMS().listPrincipalDBGrants(principalName, principalType, catName, dbName); |
| } |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| } |
| |
| private List<HiveObjectPrivilege> list_dc_privileges(final String principalName, |
| final PrincipalType principalType, final String dcName) throws TException { |
| incrementCounter("list_security_dc_grant"); |
| |
| try { |
| if (dcName == null) { |
| return getMS().listPrincipalDCGrantsAll(principalName, principalType); |
| } |
| if (principalName == null) { |
| return getMS().listDCGrantsAll(dcName); |
| } else { |
| return getMS().listPrincipalDCGrants(principalName, principalType, dcName); |
| } |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| } |
| |
| private List<HiveObjectPrivilege> list_partition_privileges( |
| final String principalName, final PrincipalType principalType, |
| String catName, final String dbName, final String tableName, final List<String> partValues) |
| throws TException { |
| incrementCounter("list_security_partition_grant"); |
| |
| try { |
| if (dbName == null) { |
| return getMS().listPrincipalPartitionGrantsAll(principalName, principalType); |
| } |
| Table tbl = get_table_core(catName, dbName, tableName); |
| String partName = Warehouse.makePartName(tbl.getPartitionKeys(), partValues); |
| if (principalName == null) { |
| return getMS().listPartitionGrantsAll(catName, dbName, tableName, partName); |
| } |
| return getMS().listPrincipalPartitionGrants( |
| principalName, principalType, catName, dbName, tableName, partValues, partName); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| } |
| |
| private List<HiveObjectPrivilege> list_table_privileges( |
| final String principalName, final PrincipalType principalType, |
| String catName, final String dbName, final String tableName) throws TException { |
| incrementCounter("list_security_table_grant"); |
| |
| try { |
| if (dbName == null) { |
| return getMS().listPrincipalTableGrantsAll(principalName, principalType); |
| } |
| if (principalName == null) { |
| return getMS().listTableGrantsAll(catName, dbName, tableName); |
| } |
| return getMS().listAllTableGrants(principalName, principalType, catName, dbName, tableName); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| } |
| |
| private List<HiveObjectPrivilege> list_global_privileges( |
| final String principalName, final PrincipalType principalType) throws TException { |
| incrementCounter("list_security_user_grant"); |
| |
| try { |
| if (principalName == null) { |
| return getMS().listGlobalGrantsAll(); |
| } |
| return getMS().listPrincipalGlobalGrants(principalName, principalType); |
| } catch (Exception e) { |
| throw handleException(e).throwIfInstance(MetaException.class).defaultRuntimeException(); |
| } |
| } |
| |
| @Override |
| public void cancel_delegation_token(String token_str_form) throws TException { |
| startFunction("cancel_delegation_token"); |
| boolean success = false; |
| Exception ex = null; |
| try { |
| HiveMetaStore.cancelDelegationToken(token_str_form); |
| success = true; |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).convertIfInstance(IOException.class, MetaException.class).defaultMetaException(); |
| } finally { |
| endFunction("cancel_delegation_token", success, ex); |
| } |
| } |
| |
| @Override |
| public long renew_delegation_token(String token_str_form) throws TException { |
| startFunction("renew_delegation_token"); |
| Long ret = null; |
| Exception ex = null; |
| try { |
| ret = HiveMetaStore.renewDelegationToken(token_str_form); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).convertIfInstance(IOException.class, MetaException.class).defaultMetaException(); |
| } finally { |
| endFunction("renew_delegation_token", ret != null, ex); |
| } |
| return ret; |
| } |
| |
| @Override |
| public String get_delegation_token(String token_owner, String renewer_kerberos_principal_name) |
| throws TException { |
| startFunction("get_delegation_token"); |
| String ret = null; |
| Exception ex = null; |
| try { |
| ret = |
| HiveMetaStore.getDelegationToken(token_owner, |
| renewer_kerberos_principal_name, getIPAddress()); |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).convertIfInstance(IOException.class, MetaException.class) |
| .convertIfInstance(InterruptedException.class, MetaException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("get_delegation_token", ret != null, ex); |
| } |
| return ret; |
| } |
| |
| @Override |
| public boolean add_token(String token_identifier, String delegation_token) throws TException { |
| startFunction("add_token", ": " + token_identifier); |
| boolean ret = false; |
| Exception ex = null; |
| try { |
| ret = getMS().addToken(token_identifier, delegation_token); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("add_token", ret == true, ex); |
| } |
| return ret; |
| } |
| |
| @Override |
| public boolean remove_token(String token_identifier) throws TException { |
| startFunction("remove_token", ": " + token_identifier); |
| boolean ret = false; |
| Exception ex = null; |
| try { |
| ret = getMS().removeToken(token_identifier); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("remove_token", ret == true, ex); |
| } |
| return ret; |
| } |
| |
| @Override |
| public String get_token(String token_identifier) throws TException { |
| startFunction("get_token for", ": " + token_identifier); |
| String ret = null; |
| Exception ex = null; |
| try { |
| ret = getMS().getToken(token_identifier); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_token", ret != null, ex); |
| } |
| //Thrift cannot return null result |
| return ret == null ? "" : ret; |
| } |
| |
| @Override |
| public List<String> get_all_token_identifiers() throws TException { |
| startFunction("get_all_token_identifiers."); |
| List<String> ret; |
| Exception ex = null; |
| try { |
| ret = getMS().getAllTokenIdentifiers(); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_all_token_identifiers.", ex == null, ex); |
| } |
| return ret; |
| } |
| |
| @Override |
| public int add_master_key(String key) throws TException { |
| startFunction("add_master_key."); |
| int ret; |
| Exception ex = null; |
| try { |
| ret = getMS().addMasterKey(key); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("add_master_key.", ex == null, ex); |
| } |
| return ret; |
| } |
| |
| @Override |
| public void update_master_key(int seq_number, String key) throws TException { |
| startFunction("update_master_key."); |
| Exception ex = null; |
| try { |
| getMS().updateMasterKey(seq_number, key); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("update_master_key.", ex == null, ex); |
| } |
| } |
| |
| @Override |
| public boolean remove_master_key(int key_seq) throws TException { |
| startFunction("remove_master_key."); |
| Exception ex = null; |
| boolean ret; |
| try { |
| ret = getMS().removeMasterKey(key_seq); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("remove_master_key.", ex == null, ex); |
| } |
| return ret; |
| } |
| |
| @Override |
| public List<String> get_master_keys() throws TException { |
| startFunction("get_master_keys."); |
| Exception ex = null; |
| String [] ret = null; |
| try { |
| ret = getMS().getMasterKeys(); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_master_keys.", ret != null, ex); |
| } |
| return Arrays.asList(ret); |
| } |
| |
| @Override |
| public void markPartitionForEvent(final String db_name, final String tbl_name, |
| final Map<String, String> partName, final PartitionEventType evtType) throws TException { |
| |
| Table tbl = null; |
| Exception ex = null; |
| RawStore ms = getMS(); |
| boolean success = false; |
| try { |
| String[] parsedDbName = parseDbName(db_name, conf); |
| ms.openTransaction(); |
| startPartitionFunction("markPartitionForEvent", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name, partName); |
| firePreEvent(new PreLoadPartitionDoneEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name, partName, this)); |
| tbl = ms.markPartitionForEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], tbl_name, |
| partName, evtType); |
| if (null == tbl) { |
| throw new UnknownTableException("Table: " + tbl_name + " not found."); |
| } |
| |
| if (transactionalListeners.size() > 0) { |
| LoadPartitionDoneEvent lpde = new LoadPartitionDoneEvent(true, tbl, partName, this); |
| for (MetaStoreEventListener transactionalListener : transactionalListeners) { |
| transactionalListener.onLoadPartitionDone(lpde); |
| } |
| } |
| |
| success = ms.commitTransaction(); |
| for (MetaStoreEventListener listener : listeners) { |
| listener.onLoadPartitionDone(new LoadPartitionDoneEvent(true, tbl, partName, this)); |
| } |
| } catch (Exception original) { |
| ex = original; |
| LOG.error("Exception caught in mark partition event ", original); |
| throw handleException(original) |
| .throwIfInstance(UnknownTableException.class, InvalidPartitionException.class, MetaException.class) |
| .defaultMetaException(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| |
| endFunction("markPartitionForEvent", tbl != null, ex, tbl_name); |
| } |
| } |
| |
| @Override |
| public boolean isPartitionMarkedForEvent(final String db_name, final String tbl_name, |
| final Map<String, String> partName, final PartitionEventType evtType) throws TException { |
| |
| String[] parsedDbName = parseDbName(db_name, conf); |
| startPartitionFunction("isPartitionMarkedForEvent", parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name, partName); |
| Boolean ret = null; |
| Exception ex = null; |
| try { |
| ret = getMS().isPartitionMarkedForEvent(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], |
| tbl_name, partName, evtType); |
| } catch (Exception original) { |
| LOG.error("Exception caught for isPartitionMarkedForEvent ", original); |
| ex = original; |
| throw handleException(original).throwIfInstance(UnknownTableException.class, InvalidPartitionException.class) |
| .throwIfInstance(UnknownPartitionException.class, MetaException.class) |
| .defaultMetaException(); |
| } finally { |
| endFunction("isPartitionMarkedForEvent", ret != null, ex, tbl_name); |
| } |
| |
| return ret; |
| } |
| |
| @Override |
| public List<String> set_ugi(String username, List<String> groupNames) throws TException { |
| Collections.addAll(groupNames, username); |
| return groupNames; |
| } |
| |
| @Override |
| public boolean partition_name_has_valid_characters(List<String> part_vals, |
| boolean throw_exception) throws TException { |
| startFunction("partition_name_has_valid_characters"); |
| boolean ret; |
| Exception ex = null; |
| try { |
| if (throw_exception) { |
| MetaStoreServerUtils.validatePartitionNameCharacters(part_vals, partitionValidationPattern); |
| ret = true; |
| } else { |
| ret = MetaStoreServerUtils.partitionNameHasValidCharacters(part_vals, |
| partitionValidationPattern); |
| } |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("partition_name_has_valid_characters", true, ex); |
| } |
| return ret; |
| } |
| |
| private void validateFunctionInfo(Function func) throws InvalidObjectException, MetaException { |
| if (func == null) { |
| throw new MetaException("Function cannot be null."); |
| } |
| if (func.getFunctionName() == null) { |
| throw new MetaException("Function name cannot be null."); |
| } |
| if (func.getDbName() == null) { |
| throw new MetaException("Database name in Function cannot be null."); |
| } |
| if (!MetaStoreUtils.validateName(func.getFunctionName(), null)) { |
| throw new InvalidObjectException(func.getFunctionName() + " is not a valid object name"); |
| } |
| String className = func.getClassName(); |
| if (className == null) { |
| throw new InvalidObjectException("Function class name cannot be null"); |
| } |
| if (func.getOwnerType() == null) { |
| throw new MetaException("Function owner type cannot be null."); |
| } |
| if (func.getFunctionType() == null) { |
| throw new MetaException("Function type cannot be null."); |
| } |
| } |
| |
| @Override |
| public void create_function(Function func) throws TException { |
| validateFunctionInfo(func); |
| boolean success = false; |
| RawStore ms = getMS(); |
| Map<String, String> transactionalListenerResponses = Collections.emptyMap(); |
| try { |
| String catName = func.isSetCatName() ? func.getCatName() : getDefaultCatalog(conf); |
| if (!func.isSetOwnerName()) { |
| try { |
| func.setOwnerName(SecurityUtils.getUGI().getShortUserName()); |
| } catch (Exception ex) { |
| LOG.error("Cannot obtain username from the session to create a function", ex); |
| throw new TException(ex); |
| } |
| } |
| ms.openTransaction(); |
| Database db = ms.getDatabase(catName, func.getDbName()); |
| if (db == null) { |
| throw new NoSuchObjectException("The database " + func.getDbName() + " does not exist"); |
| } |
| |
| if (db.getType() == DatabaseType.REMOTE) { |
| throw new MetaException("Operation create_function not support for REMOTE database"); |
| } |
| |
| Function existingFunc = ms.getFunction(catName, func.getDbName(), func.getFunctionName()); |
| if (existingFunc != null) { |
| throw new AlreadyExistsException( |
| "Function " + func.getFunctionName() + " already exists"); |
| } |
| firePreEvent(new PreCreateFunctionEvent(func, this)); |
| long time = System.currentTimeMillis() / 1000; |
| func.setCreateTime((int) time); |
| ms.createFunction(func); |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenerResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.CREATE_FUNCTION, |
| new CreateFunctionEvent(func, true, this)); |
| } |
| |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.CREATE_FUNCTION, |
| new CreateFunctionEvent(func, success, this), |
| null, |
| transactionalListenerResponses, ms); |
| } |
| } |
| } |
| |
| @Override |
| public void drop_function(String dbName, String funcName) |
| throws NoSuchObjectException, MetaException, |
| InvalidObjectException, InvalidInputException { |
| if (funcName == null) { |
| throw new MetaException("Function name cannot be null."); |
| } |
| boolean success = false; |
| Function func = null; |
| RawStore ms = getMS(); |
| Map<String, String> transactionalListenerResponses = Collections.emptyMap(); |
| String[] parsedDbName = parseDbName(dbName, conf); |
| if (parsedDbName[DB_NAME] == null) { |
| throw new MetaException("Database name cannot be null."); |
| } |
| try { |
| ms.openTransaction(); |
| func = ms.getFunction(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], funcName); |
| if (func == null) { |
| throw new NoSuchObjectException("Function " + funcName + " does not exist"); |
| } |
| Boolean needsCm = |
| ReplChangeManager.isSourceOfReplication(get_database_core(parsedDbName[CAT_NAME], parsedDbName[DB_NAME])); |
| |
| // if copy of jar to change management fails we fail the metastore transaction, since the |
| // user might delete the jars on HDFS externally after dropping the function, hence having |
| // a copy is required to allow incremental replication to work correctly. |
| if (func.getResourceUris() != null && !func.getResourceUris().isEmpty()) { |
| for (ResourceUri uri : func.getResourceUris()) { |
| if (uri.getUri().toLowerCase().startsWith("hdfs:") && needsCm) { |
| wh.addToChangeManagement(new Path(uri.getUri())); |
| } |
| } |
| } |
| firePreEvent(new PreDropFunctionEvent(func, this)); |
| |
| // if the operation on metastore fails, we don't do anything in change management, but fail |
| // the metastore transaction, as having a copy of the jar in change management is not going |
| // to cause any problem, the cleaner thread will remove this when this jar expires. |
| ms.dropFunction(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], funcName); |
| if (transactionalListeners.size() > 0) { |
| transactionalListenerResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.DROP_FUNCTION, |
| new DropFunctionEvent(func, true, this)); |
| } |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| |
| if (listeners.size() > 0) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, |
| EventType.DROP_FUNCTION, |
| new DropFunctionEvent(func, success, this), |
| null, |
| transactionalListenerResponses, ms); |
| } |
| } |
| } |
| |
| @Override |
| public void alter_function(String dbName, String funcName, Function newFunc) throws TException { |
| String[] parsedDbName = parseDbName(dbName, conf); |
| validateForAlterFunction(parsedDbName[DB_NAME], funcName, newFunc); |
| boolean success = false; |
| RawStore ms = getMS(); |
| try { |
| firePreEvent(new PreCreateFunctionEvent(newFunc, this)); |
| ms.openTransaction(); |
| ms.alterFunction(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], funcName, newFunc); |
| success = ms.commitTransaction(); |
| } catch (InvalidObjectException e) { |
| // Throwing MetaException instead of InvalidObjectException as the InvalidObjectException |
| // is not defined for the alter_function method in the Thrift interface. |
| throwMetaException(e); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| } |
| } |
| |
| private void validateForAlterFunction(String dbName, String funcName, Function newFunc) |
| throws MetaException { |
| if (dbName == null || funcName == null) { |
| throw new MetaException("Database and function name cannot be null."); |
| } |
| try { |
| validateFunctionInfo(newFunc); |
| } catch (InvalidObjectException e) { |
| // The validateFunctionInfo method is used by the create and alter function methods as well |
| // and it can throw InvalidObjectException. But the InvalidObjectException is not defined |
| // for the alter_function method in the Thrift interface, therefore a TApplicationException |
| // will occur at the caller side. Re-throwing the InvalidObjectException as MetaException |
| // would eliminate the TApplicationException at caller side. |
| throw newMetaException(e); |
| } |
| } |
| |
| @Override |
| public List<String> get_functions(String dbName, String pattern) |
| throws MetaException { |
| startFunction("get_functions", ": db=" + dbName + " pat=" + pattern); |
| |
| RawStore ms = getMS(); |
| Exception ex = null; |
| List<String> funcNames = null; |
| String[] parsedDbName = parseDbName(dbName, conf); |
| |
| try { |
| funcNames = ms.getFunctions(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], pattern); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_functions", funcNames != null, ex); |
| } |
| |
| return funcNames; |
| } |
| |
| @Override |
| public GetAllFunctionsResponse get_all_functions() |
| throws MetaException { |
| GetAllFunctionsResponse response = new GetAllFunctionsResponse(); |
| startFunction("get_all_functions"); |
| RawStore ms = getMS(); |
| List<Function> allFunctions = null; |
| Exception ex = null; |
| try { |
| // Leaving this as the 'hive' catalog (rather than choosing the default from the |
| // configuration) because all the default UDFs are in that catalog, and I think that's |
| // would people really want here. |
| allFunctions = ms.getAllFunctions(DEFAULT_CATALOG_NAME); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_all_functions", allFunctions != null, ex); |
| } |
| response.setFunctions(allFunctions); |
| return response; |
| } |
| |
| @Override |
| public Function get_function(String dbName, String funcName) throws TException { |
| if (dbName == null || funcName == null) { |
| throw new MetaException("Database and function name cannot be null."); |
| } |
| startFunction("get_function", ": " + dbName + "." + funcName); |
| |
| RawStore ms = getMS(); |
| Function func = null; |
| Exception ex = null; |
| String[] parsedDbName = parseDbName(dbName, conf); |
| |
| try { |
| func = ms.getFunction(parsedDbName[CAT_NAME], parsedDbName[DB_NAME], funcName); |
| if (func == null) { |
| throw new NoSuchObjectException( |
| "Function " + dbName + "." + funcName + " does not exist"); |
| } |
| } catch (Exception e) { |
| ex = e; |
| throw handleException(e).throwIfInstance(NoSuchObjectException.class).defaultMetaException(); |
| } finally { |
| endFunction("get_function", func != null, ex); |
| } |
| |
| return func; |
| } |
| |
| // Transaction and locking methods |
| @Override |
| public GetOpenTxnsResponse get_open_txns() throws TException { |
| return getTxnHandler().getOpenTxns(); |
| } |
| |
| @Override |
| public GetOpenTxnsResponse get_open_txns_req(GetOpenTxnsRequest getOpenTxnsRequest) throws TException { |
| return getTxnHandler().getOpenTxns(getOpenTxnsRequest.getExcludeTxnTypes()); |
| } |
| |
| // Transaction and locking methods |
| @Override |
| public GetOpenTxnsInfoResponse get_open_txns_info() throws TException { |
| return getTxnHandler().getOpenTxnsInfo(); |
| } |
| |
| @Override |
| public OpenTxnsResponse open_txns(OpenTxnRequest rqst) throws TException { |
| OpenTxnsResponse response = getTxnHandler().openTxns(rqst); |
| List<Long> txnIds = response.getTxn_ids(); |
| boolean isHiveReplTxn = rqst.isSetReplPolicy() && TxnType.DEFAULT.equals(rqst.getTxn_type()); |
| if (txnIds != null && listeners != null && !listeners.isEmpty() && !isHiveReplTxn) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.OPEN_TXN, |
| new OpenTxnEvent(txnIds, this)); |
| } |
| return response; |
| } |
| |
| @Override |
| public void abort_txn(AbortTxnRequest rqst) throws TException { |
| getTxnHandler().abortTxn(rqst); |
| boolean isHiveReplTxn = rqst.isSetReplPolicy() && TxnType.DEFAULT.equals(rqst.getTxn_type()); |
| if (listeners != null && !listeners.isEmpty() && !isHiveReplTxn) { |
| // Not adding dbsUpdated to AbortTxnEvent because |
| // only DbNotificationListener cares about it, and this is already |
| // handled with transactional listeners in TxnHandler. |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ABORT_TXN, |
| new AbortTxnEvent(rqst.getTxnid(), this)); |
| } |
| } |
| |
| @Override |
| public void abort_txns(AbortTxnsRequest rqst) throws TException { |
| getTxnHandler().abortTxns(rqst); |
| if (listeners != null && !listeners.isEmpty()) { |
| for (Long txnId : rqst.getTxn_ids()) { |
| // See above abort_txn() note about not adding dbsUpdated. |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ABORT_TXN, |
| new AbortTxnEvent(txnId, this)); |
| } |
| } |
| } |
| |
| @Override |
| public AbortCompactResponse abort_Compactions(AbortCompactionRequest rqst) throws TException { |
| return getTxnHandler().abortCompactions(rqst); |
| } |
| |
| @Override |
| public long get_latest_txnid_in_conflict(long txnId) throws MetaException { |
| return getTxnHandler().getLatestTxnIdInConflict(txnId); |
| } |
| |
| @Override |
| public void commit_txn(CommitTxnRequest rqst) throws TException { |
| boolean isReplayedReplTxn = TxnType.REPL_CREATED.equals(rqst.getTxn_type()); |
| boolean isHiveReplTxn = rqst.isSetReplPolicy() && TxnType.DEFAULT.equals(rqst.getTxn_type()); |
| // in replication flow, the write notification log table will be updated here. |
| if (rqst.isSetWriteEventInfos() && isReplayedReplTxn) { |
| assert (rqst.isSetReplPolicy()); |
| long targetTxnId = getTxnHandler().getTargetTxnId(rqst.getReplPolicy(), rqst.getTxnid()); |
| if (targetTxnId < 0) { |
| //looks like a retry |
| return; |
| } |
| for (WriteEventInfo writeEventInfo : rqst.getWriteEventInfos()) { |
| String[] filesAdded = ReplChangeManager.getListFromSeparatedString(writeEventInfo.getFiles()); |
| List<String> partitionValue = null; |
| Partition ptnObj = null; |
| String root; |
| Table tbl = getTblObject(writeEventInfo.getDatabase(), writeEventInfo.getTable(), null); |
| |
| if (writeEventInfo.getPartition() != null && !writeEventInfo.getPartition().isEmpty()) { |
| partitionValue = Warehouse.getPartValuesFromPartName(writeEventInfo.getPartition()); |
| ptnObj = getPartitionObj(writeEventInfo.getDatabase(), writeEventInfo.getTable(), partitionValue, tbl); |
| root = ptnObj.getSd().getLocation(); |
| } else { |
| root = tbl.getSd().getLocation(); |
| } |
| |
| InsertEventRequestData insertData = new InsertEventRequestData(); |
| insertData.setReplace(true); |
| |
| // The files in the commit txn message during load will have files with path corresponding to source |
| // warehouse. Need to transform them to target warehouse using table or partition object location. |
| for (String file : filesAdded) { |
| String[] decodedPath = ReplChangeManager.decodeFileUri(file); |
| String name = (new Path(decodedPath[0])).getName(); |
| Path newPath = FileUtils.getTransformedPath(name, decodedPath[3], root); |
| insertData.addToFilesAdded(newPath.toUri().toString()); |
| insertData.addToSubDirectoryList(decodedPath[3]); |
| try { |
| insertData.addToFilesAddedChecksum(ReplChangeManager.checksumFor(newPath, newPath.getFileSystem(conf))); |
| } catch (IOException e) { |
| LOG.error("failed to get checksum for the file " + newPath + " with error: " + e.getMessage()); |
| throw new TException(e.getMessage()); |
| } |
| } |
| |
| WriteNotificationLogRequest wnRqst = new WriteNotificationLogRequest(targetTxnId, |
| writeEventInfo.getWriteId(), writeEventInfo.getDatabase(), writeEventInfo.getTable(), insertData); |
| if (partitionValue != null) { |
| wnRqst.setPartitionVals(partitionValue); |
| } |
| addTxnWriteNotificationLog(tbl, ptnObj, wnRqst); |
| } |
| } |
| getTxnHandler().commitTxn(rqst); |
| if (listeners != null && !listeners.isEmpty() && !isHiveReplTxn) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.COMMIT_TXN, |
| new CommitTxnEvent(rqst.getTxnid(), this)); |
| Optional<CompactionInfo> compactionInfo = getTxnHandler().getCompactionByTxnId(rqst.getTxnid()); |
| if (compactionInfo.isPresent()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.COMMIT_COMPACTION, |
| new CommitCompactionEvent(rqst.getTxnid(), compactionInfo.get(), this)); |
| } |
| } |
| } |
| |
| @Override |
| public void repl_tbl_writeid_state(ReplTblWriteIdStateRequest rqst) throws TException { |
| getTxnHandler().replTableWriteIdState(rqst); |
| } |
| |
| @Override |
| public GetValidWriteIdsResponse get_valid_write_ids(GetValidWriteIdsRequest rqst) throws TException { |
| return getTxnHandler().getValidWriteIds(rqst); |
| } |
| |
| @Override |
| public void add_write_ids_to_min_history(long txnId, Map<String, Long> validWriteIds) throws TException { |
| getTxnHandler().addWriteIdsToMinHistory(txnId, validWriteIds); |
| } |
| |
| @Override |
| public void set_hadoop_jobid(String jobId, long cqId) { |
| getTxnHandler().setHadoopJobId(jobId, cqId); |
| } |
| |
| @Deprecated |
| @Override |
| public OptionalCompactionInfoStruct find_next_compact(String workerId) throws MetaException{ |
| return CompactionInfo.compactionInfoToOptionalStruct( |
| getTxnHandler().findNextToCompact(workerId)); |
| } |
| |
| @Override |
| public OptionalCompactionInfoStruct find_next_compact2(FindNextCompactRequest rqst) throws MetaException{ |
| return CompactionInfo.compactionInfoToOptionalStruct( |
| getTxnHandler().findNextToCompact(rqst)); |
| } |
| |
| @Override |
| public void mark_cleaned(CompactionInfoStruct cr) throws MetaException { |
| getTxnHandler().markCleaned(CompactionInfo.compactionStructToInfo(cr), false); |
| } |
| |
| @Override |
| public void mark_compacted(CompactionInfoStruct cr) throws MetaException { |
| getTxnHandler().markCompacted(CompactionInfo.compactionStructToInfo(cr)); |
| } |
| |
| @Override |
| public void mark_failed(CompactionInfoStruct cr) throws MetaException { |
| getTxnHandler().markFailed(CompactionInfo.compactionStructToInfo(cr)); |
| } |
| |
| @Override |
| public void mark_refused(CompactionInfoStruct cr) throws MetaException { |
| getTxnHandler().markRefused(CompactionInfo.compactionStructToInfo(cr)); |
| } |
| |
| @Override |
| public boolean update_compaction_metrics_data(CompactionMetricsDataStruct struct) throws MetaException, TException { |
| return getTxnHandler().updateCompactionMetricsData(CompactionMetricsDataConverter.structToData(struct)); |
| } |
| |
| @Override |
| public void remove_compaction_metrics_data(CompactionMetricsDataRequest request) |
| throws MetaException, TException { |
| getTxnHandler().removeCompactionMetricsData(request.getDbName(), request.getTblName(), request.getPartitionName(), |
| CompactionMetricsDataConverter.thriftCompactionMetricType2DbType(request.getType())); |
| } |
| |
| @Override |
| public List<String> find_columns_with_stats(CompactionInfoStruct cr) throws MetaException { |
| return getTxnHandler().findColumnsWithStats(CompactionInfo.compactionStructToInfo(cr)); |
| } |
| |
| @Override |
| public void update_compactor_state(CompactionInfoStruct cr, long highWaterMark) throws MetaException { |
| getTxnHandler().updateCompactorState( |
| CompactionInfo.compactionStructToInfo(cr), highWaterMark); |
| } |
| |
| @Override |
| public GetLatestCommittedCompactionInfoResponse get_latest_committed_compaction_info( |
| GetLatestCommittedCompactionInfoRequest rqst) throws MetaException { |
| if (rqst.getDbname() == null || rqst.getTablename() == null) { |
| throw new MetaException("Database name and table name cannot be null."); |
| } |
| GetLatestCommittedCompactionInfoResponse response = getTxnHandler().getLatestCommittedCompactionInfo(rqst); |
| return FilterUtils.filterCommittedCompactionInfoStructIfEnabled(isServerFilterEnabled, filterHook, |
| getDefaultCatalog(conf), rqst.getDbname(), rqst.getTablename(), response); |
| } |
| |
| @Override |
| public AllocateTableWriteIdsResponse allocate_table_write_ids( |
| AllocateTableWriteIdsRequest rqst) throws TException { |
| AllocateTableWriteIdsResponse response = getTxnHandler().allocateTableWriteIds(rqst); |
| if (listeners != null && !listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ALLOC_WRITE_ID, |
| new AllocWriteIdEvent(response.getTxnToWriteIds(), rqst.getDbName(), |
| rqst.getTableName(), this)); |
| } |
| return response; |
| } |
| |
| @Override |
| public MaxAllocatedTableWriteIdResponse get_max_allocated_table_write_id(MaxAllocatedTableWriteIdRequest rqst) |
| throws MetaException { |
| return getTxnHandler().getMaxAllocatedTableWrited(rqst); |
| } |
| |
| @Override |
| public void seed_write_id(SeedTableWriteIdsRequest rqst) throws MetaException { |
| getTxnHandler().seedWriteId(rqst); |
| } |
| |
| @Override |
| public void seed_txn_id(SeedTxnIdRequest rqst) throws MetaException { |
| getTxnHandler().seedTxnId(rqst); |
| } |
| |
| private void addTxnWriteNotificationLog(Table tableObj, Partition ptnObj, WriteNotificationLogRequest rqst) |
| throws MetaException { |
| String partition = ""; //Empty string is an invalid partition name. Can be used for non partitioned table. |
| if (ptnObj != null) { |
| partition = Warehouse.makePartName(tableObj.getPartitionKeys(), rqst.getPartitionVals()); |
| } |
| AcidWriteEvent event = new AcidWriteEvent(partition, tableObj, ptnObj, rqst); |
| getTxnHandler().addWriteNotificationLog(event); |
| if (listeners != null && !listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ACID_WRITE, event); |
| } |
| } |
| |
| private Table getTblObject(String db, String table, String catalog) throws MetaException, NoSuchObjectException { |
| GetTableRequest req = new GetTableRequest(db, table); |
| if (catalog != null) { |
| req.setCatName(catalog); |
| } |
| req.setCapabilities(new ClientCapabilities(Lists.newArrayList(ClientCapability.TEST_CAPABILITY, ClientCapability.INSERT_ONLY_TABLES))); |
| return get_table_req(req).getTable(); |
| } |
| |
| private Partition getPartitionObj(String db, String table, List<String> partitionVals, Table tableObj) |
| throws MetaException, NoSuchObjectException { |
| if (tableObj.isSetPartitionKeys() && !tableObj.getPartitionKeys().isEmpty()) { |
| return get_partition(db, table, partitionVals); |
| } |
| return null; |
| } |
| |
| @Override |
| public WriteNotificationLogResponse add_write_notification_log(WriteNotificationLogRequest rqst) |
| throws TException { |
| Table tableObj = getTblObject(rqst.getDb(), rqst.getTable(), null); |
| Partition ptnObj = getPartitionObj(rqst.getDb(), rqst.getTable(), rqst.getPartitionVals(), tableObj); |
| addTxnWriteNotificationLog(tableObj, ptnObj, rqst); |
| return new WriteNotificationLogResponse(); |
| } |
| |
| @Override |
| public WriteNotificationLogBatchResponse add_write_notification_log_in_batch( |
| WriteNotificationLogBatchRequest batchRequest) throws TException { |
| if (batchRequest.getRequestList().size() == 0) { |
| return new WriteNotificationLogBatchResponse(); |
| } |
| |
| Table tableObj = getTblObject(batchRequest.getDb(), batchRequest.getTable(), batchRequest.getCatalog()); |
| BatchAcidWriteEvent event = new BatchAcidWriteEvent(); |
| List<String> partNameList = new ArrayList<>(); |
| List<Partition> ptnObjList; |
| |
| Map<String, WriteNotificationLogRequest> rqstMap = new HashMap<>(); |
| if (tableObj.getPartitionKeys().size() != 0) { |
| // partitioned table |
| for (WriteNotificationLogRequest rqst : batchRequest.getRequestList()) { |
| String partition = Warehouse.makePartName(tableObj.getPartitionKeys(), rqst.getPartitionVals()); |
| partNameList.add(partition); |
| // This is used to ignore those request for which the partition does not exists. |
| rqstMap.put(partition, rqst); |
| } |
| ptnObjList = getMS().getPartitionsByNames(tableObj.getCatName(), tableObj.getDbName(), |
| tableObj.getTableName(), partNameList); |
| } else { |
| ptnObjList = new ArrayList<>(); |
| for (WriteNotificationLogRequest ignored : batchRequest.getRequestList()) { |
| ptnObjList.add(null); |
| } |
| } |
| |
| int idx = 0; |
| for (Partition partObject : ptnObjList) { |
| String partition = ""; //Empty string is an invalid partition name. Can be used for non partitioned table. |
| WriteNotificationLogRequest request; |
| if (partObject != null) { |
| partition = Warehouse.makePartName(tableObj.getPartitionKeys(), partObject.getValues()); |
| request = rqstMap.get(partition); |
| } else { |
| // for non partitioned table, we can get serially from the list. |
| request = batchRequest.getRequestList().get(idx++); |
| } |
| event.addNotification(partition, tableObj, partObject, request); |
| if (listeners != null && !listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.BATCH_ACID_WRITE, |
| new BatchAcidWriteEvent(partition, tableObj, partObject, request)); |
| } |
| } |
| |
| getTxnHandler().addWriteNotificationLog(event); |
| return new WriteNotificationLogBatchResponse(); |
| } |
| |
| @Override |
| public LockResponse lock(LockRequest rqst) throws TException { |
| return getTxnHandler().lock(rqst); |
| } |
| |
| @Override |
| public LockResponse check_lock(CheckLockRequest rqst) throws TException { |
| return getTxnHandler().checkLock(rqst); |
| } |
| |
| @Override |
| public void unlock(UnlockRequest rqst) throws TException { |
| getTxnHandler().unlock(rqst); |
| } |
| |
| @Override |
| public ShowLocksResponse show_locks(ShowLocksRequest rqst) throws TException { |
| return getTxnHandler().showLocks(rqst); |
| } |
| |
| @Override |
| public void heartbeat(HeartbeatRequest ids) throws TException { |
| getTxnHandler().heartbeat(ids); |
| } |
| |
| @Override |
| public HeartbeatTxnRangeResponse heartbeat_txn_range(HeartbeatTxnRangeRequest rqst) |
| throws TException { |
| return getTxnHandler().heartbeatTxnRange(rqst); |
| } |
| @Deprecated |
| @Override |
| public void compact(CompactionRequest rqst) throws TException { |
| compact2(rqst); |
| } |
| @Override |
| public CompactionResponse compact2(CompactionRequest rqst) throws TException { |
| return getTxnHandler().compact(rqst); |
| } |
| |
| @Override |
| public ShowCompactResponse show_compact(ShowCompactRequest rqst) throws TException { |
| ShowCompactResponse response = getTxnHandler().showCompact(rqst); |
| response.setCompacts(FilterUtils.filterCompactionsIfEnabled(isServerFilterEnabled, |
| filterHook, getDefaultCatalog(conf), response.getCompacts())); |
| return response; |
| } |
| |
| @Override |
| public boolean submit_for_cleanup(CompactionRequest rqst, long highestWriteId, long txnId) |
| throws TException { |
| return getTxnHandler().submitForCleanup(rqst, highestWriteId, txnId); |
| } |
| |
| @Override |
| public void flushCache() throws TException { |
| getMS().flushCache(); |
| } |
| |
| @Override |
| public void add_dynamic_partitions(AddDynamicPartitions rqst) throws TException { |
| getTxnHandler().addDynamicPartitions(rqst); |
| } |
| |
| @Override |
| public GetPrincipalsInRoleResponse get_principals_in_role(GetPrincipalsInRoleRequest request) |
| throws TException { |
| |
| incrementCounter("get_principals_in_role"); |
| firePreEvent(new PreAuthorizationCallEvent(this)); |
| Exception ex = null; |
| GetPrincipalsInRoleResponse response = null; |
| try { |
| response = new GetPrincipalsInRoleResponse(getMS().listRoleMembers(request.getRoleName())); |
| } catch (Exception e) { |
| ex = e; |
| rethrowException(e); |
| } finally { |
| endFunction("get_principals_in_role", ex == null, ex); |
| } |
| return response; |
| } |
| |
| @Override |
| public GetRoleGrantsForPrincipalResponse get_role_grants_for_principal( |
| GetRoleGrantsForPrincipalRequest request) throws TException { |
| |
| incrementCounter("get_role_grants_for_principal"); |
| firePreEvent(new PreAuthorizationCallEvent(this)); |
| Exception ex = null; |
| List<RolePrincipalGrant> roleMaps = null; |
| try { |
| roleMaps = getMS().listRolesWithGrants(request.getPrincipal_name(), request.getPrincipal_type()); |
| } catch (Exception e) { |
| ex = e; |
| rethrowException(e); |
| } finally { |
| endFunction("get_role_grants_for_principal", ex == null, ex); |
| } |
| |
| //List<RolePrincipalGrant> roleGrantsList = getRolePrincipalGrants(roleMaps); |
| return new GetRoleGrantsForPrincipalResponse(roleMaps); |
| } |
| |
| @Override |
| public AggrStats get_aggr_stats_for(PartitionsStatsRequest request) throws TException { |
| String catName = request.isSetCatName() ? request.getCatName().toLowerCase() : |
| getDefaultCatalog(conf); |
| String dbName = request.getDbName().toLowerCase(); |
| String tblName = request.getTblName().toLowerCase(); |
| startFunction("get_aggr_stats_for", ": table=" + |
| TableName.getQualified(catName, dbName, tblName)); |
| |
| List<String> lowerCaseColNames = new ArrayList<>(request.getColNames().size()); |
| for (String colName : request.getColNames()) { |
| lowerCaseColNames.add(colName.toLowerCase()); |
| } |
| List<String> lowerCasePartNames = new ArrayList<>(request.getPartNames().size()); |
| for (String partName : request.getPartNames()) { |
| lowerCasePartNames.add(lowerCaseConvertPartName(partName)); |
| } |
| AggrStats aggrStats = null; |
| |
| try { |
| aggrStats = getMS().get_aggr_stats_for(catName, dbName, tblName, |
| lowerCasePartNames, lowerCaseColNames, request.getEngine(), request.getValidWriteIdList()); |
| return aggrStats; |
| } finally { |
| endFunction("get_aggr_stats_for", aggrStats == null, null, request.getTblName()); |
| } |
| |
| } |
| |
| @Override |
| public boolean set_aggr_stats_for(SetPartitionsStatsRequest request) throws TException { |
| boolean ret = true; |
| List<ColumnStatistics> csNews = request.getColStats(); |
| if (csNews == null || csNews.isEmpty()) { |
| return ret; |
| } |
| // figure out if it is table level or partition level |
| ColumnStatistics firstColStats = csNews.get(0); |
| ColumnStatisticsDesc statsDesc = firstColStats.getStatsDesc(); |
| String catName = statsDesc.isSetCatName() ? statsDesc.getCatName() : getDefaultCatalog(conf); |
| String dbName = statsDesc.getDbName(); |
| String tableName = statsDesc.getTableName(); |
| List<String> colNames = new ArrayList<>(); |
| for (ColumnStatisticsObj obj : firstColStats.getStatsObj()) { |
| colNames.add(obj.getColName()); |
| } |
| if (statsDesc.isIsTblLevel()) { |
| // there should be only one ColumnStatistics |
| if (request.getColStatsSize() != 1) { |
| throw new MetaException( |
| "Expecting only 1 ColumnStatistics for table's column stats, but find " |
| + request.getColStatsSize()); |
| } |
| if (request.isSetNeedMerge() && request.isNeedMerge()) { |
| return updateTableColumnStatsWithMerge(catName, dbName, tableName, colNames, request); |
| } else { |
| // This is the overwrite case, we do not care about the accuracy. |
| return updateTableColumnStatsInternal(firstColStats, |
| request.getValidWriteIdList(), request.getWriteId()); |
| } |
| } else { |
| // partition level column stats merging |
| // note that we may have two or more duplicate partition names. |
| // see autoColumnStats_2.q under TestMiniLlapLocalCliDriver |
| Map<String, ColumnStatistics> newStatsMap = new HashMap<>(); |
| for (ColumnStatistics csNew : csNews) { |
| String partName = csNew.getStatsDesc().getPartName(); |
| if (newStatsMap.containsKey(partName)) { |
| MetaStoreServerUtils.mergeColStats(csNew, newStatsMap.get(partName)); |
| } |
| newStatsMap.put(partName, csNew); |
| } |
| |
| if (request.isSetNeedMerge() && request.isNeedMerge()) { |
| ret = updatePartColumnStatsWithMerge(catName, dbName, tableName, |
| colNames, newStatsMap, request); |
| } else { // No merge. |
| Table t = getTable(catName, dbName, tableName); |
| MTable mTable = getMS().ensureGetMTable(catName, dbName, tableName); |
| // We don't short-circuit on errors here anymore. That can leave acid stats invalid. |
| if (MetastoreConf.getBoolVar(getConf(), ConfVars.TRY_DIRECT_SQL)) { |
| ret = updatePartitionColStatsInBatch(t, newStatsMap, |
| request.getValidWriteIdList(), request.getWriteId()); |
| } else { |
| for (Map.Entry<String, ColumnStatistics> entry : newStatsMap.entrySet()) { |
| // We don't short-circuit on errors here anymore. That can leave acid stats invalid. |
| ret = updatePartitonColStatsInternal(t, mTable, entry.getValue(), |
| request.getValidWriteIdList(), request.getWriteId()) && ret; |
| } |
| } |
| } |
| } |
| return ret; |
| } |
| |
| private boolean updatePartColumnStatsWithMerge(String catName, String dbName, String tableName, |
| List<String> colNames, Map<String, ColumnStatistics> newStatsMap, SetPartitionsStatsRequest request) |
| throws MetaException, NoSuchObjectException, InvalidObjectException, InvalidInputException { |
| RawStore ms = getMS(); |
| ms.openTransaction(); |
| boolean isCommitted = false, result = false; |
| try { |
| // a single call to get all column stats for all partitions |
| List<String> partitionNames = new ArrayList<>(); |
| partitionNames.addAll(newStatsMap.keySet()); |
| List<ColumnStatistics> csOlds = ms.getPartitionColumnStatistics(catName, dbName, tableName, |
| partitionNames, colNames, request.getEngine(), request.getValidWriteIdList()); |
| if (newStatsMap.values().size() != csOlds.size()) { |
| // some of the partitions miss stats. |
| LOG.debug("Some of the partitions miss stats."); |
| } |
| Map<String, ColumnStatistics> oldStatsMap = new HashMap<>(); |
| for (ColumnStatistics csOld : csOlds) { |
| oldStatsMap.put(csOld.getStatsDesc().getPartName(), csOld); |
| } |
| |
| // another single call to get all the partition objects |
| List<Partition> partitions = ms.getPartitionsByNames(catName, dbName, tableName, partitionNames); |
| Map<String, Partition> mapToPart = new HashMap<>(); |
| for (int index = 0; index < partitionNames.size(); index++) { |
| mapToPart.put(partitionNames.get(index), partitions.get(index)); |
| } |
| |
| Table t = getTable(catName, dbName, tableName); |
| MTable mTable = getMS().ensureGetMTable(catName, dbName, tableName); |
| Map<String, ColumnStatistics> statsMap = new HashMap<>(); |
| boolean useDirectSql = MetastoreConf.getBoolVar(getConf(), ConfVars.TRY_DIRECT_SQL); |
| for (Map.Entry<String, ColumnStatistics> entry : newStatsMap.entrySet()) { |
| ColumnStatistics csNew = entry.getValue(); |
| ColumnStatistics csOld = oldStatsMap.get(entry.getKey()); |
| boolean isInvalidTxnStats = csOld != null |
| && csOld.isSetIsStatsCompliant() && !csOld.isIsStatsCompliant(); |
| Partition part = mapToPart.get(entry.getKey()); |
| if (isInvalidTxnStats) { |
| // No columns can be merged; a shortcut for getMergableCols. |
| csNew.setStatsObj(Lists.newArrayList()); |
| } else { |
| // we first use getParameters() to prune the stats |
| MetaStoreServerUtils.getMergableCols(csNew, part.getParameters()); |
| // we merge those that can be merged |
| if (csOld != null && csOld.getStatsObjSize() != 0 && !csNew.getStatsObj().isEmpty()) { |
| MetaStoreServerUtils.mergeColStats(csNew, csOld); |
| } |
| } |
| |
| if (!csNew.getStatsObj().isEmpty()) { |
| // We don't short-circuit on errors here anymore. That can leave acid stats invalid. |
| if (useDirectSql) { |
| statsMap.put(csNew.getStatsDesc().getPartName(), csNew); |
| } else { |
| result = updatePartitonColStatsInternal(t, mTable, csNew, |
| request.getValidWriteIdList(), request.getWriteId()) && result; |
| } |
| } else if (isInvalidTxnStats) { |
| // For now because the stats state is such as it is, we will invalidate everything. |
| // Overall the sematics here are not clear - we could invalide only some columns, but does |
| // that make any physical sense? Could query affect some columns but not others? |
| part.setWriteId(request.getWriteId()); |
| StatsSetupConst.clearColumnStatsState(part.getParameters()); |
| StatsSetupConst.setBasicStatsState(part.getParameters(), StatsSetupConst.FALSE); |
| ms.alterPartition(catName, dbName, tableName, part.getValues(), part, |
| request.getValidWriteIdList()); |
| result = false; |
| } else { |
| // TODO: why doesn't the original call for non acid tables invalidate the stats? |
| LOG.debug("All the column stats " + csNew.getStatsDesc().getPartName() |
| + " are not accurate to merge."); |
| } |
| } |
| ms.commitTransaction(); |
| isCommitted = true; |
| // updatePartitionColStatsInBatch starts/commit transaction internally. As there is no write or select for update |
| // operations is done in this transaction, it is safe to commit it before calling updatePartitionColStatsInBatch. |
| if (!statsMap.isEmpty()) { |
| updatePartitionColStatsInBatch(t, statsMap, request.getValidWriteIdList(), request.getWriteId()); |
| } |
| } finally { |
| if (!isCommitted) { |
| ms.rollbackTransaction(); |
| } |
| } |
| return result; |
| } |
| |
| |
| private boolean updateTableColumnStatsWithMerge(String catName, String dbName, String tableName, |
| List<String> colNames, SetPartitionsStatsRequest request) throws MetaException, |
| NoSuchObjectException, InvalidObjectException, InvalidInputException { |
| ColumnStatistics firstColStats = request.getColStats().get(0); |
| RawStore ms = getMS(); |
| ms.openTransaction(); |
| boolean isCommitted = false, result = false; |
| try { |
| ColumnStatistics csOld = ms.getTableColumnStatistics(catName, dbName, tableName, colNames, |
| request.getEngine(), request.getValidWriteIdList()); |
| // we first use the valid stats list to prune the stats |
| boolean isInvalidTxnStats = csOld != null |
| && csOld.isSetIsStatsCompliant() && !csOld.isIsStatsCompliant(); |
| if (isInvalidTxnStats) { |
| // No columns can be merged; a shortcut for getMergableCols. |
| firstColStats.setStatsObj(Lists.newArrayList()); |
| } else { |
| Table t = getTable(catName, dbName, tableName); |
| MetaStoreServerUtils.getMergableCols(firstColStats, t.getParameters()); |
| |
| // we merge those that can be merged |
| if (csOld != null && csOld.getStatsObjSize() != 0 && !firstColStats.getStatsObj().isEmpty()) { |
| MetaStoreServerUtils.mergeColStats(firstColStats, csOld); |
| } |
| } |
| |
| if (!firstColStats.getStatsObj().isEmpty()) { |
| result = updateTableColumnStatsInternal(firstColStats, |
| request.getValidWriteIdList(), request.getWriteId()); |
| } else if (isInvalidTxnStats) { |
| // For now because the stats state is such as it is, we will invalidate everything. |
| // Overall the sematics here are not clear - we could invalide only some columns, but does |
| // that make any physical sense? Could query affect some columns but not others? |
| Table t = getTable(catName, dbName, tableName); |
| t.setWriteId(request.getWriteId()); |
| StatsSetupConst.clearColumnStatsState(t.getParameters()); |
| StatsSetupConst.setBasicStatsState(t.getParameters(), StatsSetupConst.FALSE); |
| ms.alterTable(catName, dbName, tableName, t, request.getValidWriteIdList()); |
| } else { |
| // TODO: why doesn't the original call for non acid tables invalidate the stats? |
| LOG.debug("All the column stats are not accurate to merge."); |
| result = true; |
| } |
| |
| ms.commitTransaction(); |
| isCommitted = true; |
| } finally { |
| if (!isCommitted) { |
| ms.rollbackTransaction(); |
| } |
| } |
| return result; |
| } |
| |
| private Table getTable(String catName, String dbName, String tableName) |
| throws MetaException, InvalidObjectException { |
| return getTable(catName, dbName, tableName, null); |
| } |
| |
| private Table getTable(String catName, String dbName, String tableName, |
| String writeIdList) |
| throws MetaException, InvalidObjectException { |
| Table t = getMS().getTable(catName, dbName, tableName, writeIdList); |
| if (t == null) { |
| throw new InvalidObjectException(TableName.getQualified(catName, dbName, tableName) |
| + " table not found"); |
| } |
| return t; |
| } |
| |
| @Override |
| public NotificationEventResponse get_next_notification(NotificationEventRequest rqst) |
| throws TException { |
| authorizeProxyPrivilege(); |
| |
| RawStore ms = getMS(); |
| return ms.getNextNotification(rqst); |
| } |
| |
| @Override |
| public CurrentNotificationEventId get_current_notificationEventId() throws TException { |
| authorizeProxyPrivilege(); |
| |
| RawStore ms = getMS(); |
| return ms.getCurrentNotificationEventId(); |
| } |
| |
| @Override |
| public NotificationEventsCountResponse get_notification_events_count(NotificationEventsCountRequest rqst) |
| throws TException { |
| authorizeProxyPrivilege(); |
| |
| RawStore ms = getMS(); |
| return ms.getNotificationEventsCount(rqst); |
| } |
| |
| private void authorizeProxyPrivilege() throws TException { |
| // Skip the auth in embedded mode or if the auth is disabled |
| if (!HiveMetaStore.isMetaStoreRemote() || |
| !MetastoreConf.getBoolVar(conf, ConfVars.EVENT_DB_NOTIFICATION_API_AUTH) || conf.getBoolean(HIVE_IN_TEST.getVarname(), |
| false)) { |
| return; |
| } |
| String user = null; |
| try { |
| user = SecurityUtils.getUGI().getShortUserName(); |
| } catch (Exception ex) { |
| LOG.error("Cannot obtain username", ex); |
| throw new TException(ex); |
| } |
| if (!MetaStoreServerUtils.checkUserHasHostProxyPrivileges(user, conf, getIPAddress())) { |
| LOG.error("Not authorized to make the get_notification_events_count call. You can try to disable " + |
| ConfVars.EVENT_DB_NOTIFICATION_API_AUTH.toString()); |
| throw new TException("User " + user + " is not allowed to perform this API call"); |
| } |
| } |
| |
| @Override |
| public FireEventResponse fire_listener_event(FireEventRequest rqst) throws TException { |
| switch (rqst.getData().getSetField()) { |
| case INSERT_DATA: |
| case INSERT_DATAS: |
| String catName = |
| rqst.isSetCatName() ? rqst.getCatName() : getDefaultCatalog(conf); |
| String dbName = rqst.getDbName(); |
| String tblName = rqst.getTableName(); |
| boolean isSuccessful = rqst.isSuccessful(); |
| List<InsertEvent> events = new ArrayList<>(); |
| if (rqst.getData().isSetInsertData()) { |
| events.add(new InsertEvent(catName, dbName, tblName, |
| rqst.getPartitionVals(), |
| rqst.getData().getInsertData(), isSuccessful, this)); |
| } else { |
| // this is a bulk fire insert event operation |
| // we use the partition values field from the InsertEventRequestData object |
| // instead of the FireEventRequest object |
| for (InsertEventRequestData insertData : rqst.getData().getInsertDatas()) { |
| if (!insertData.isSetPartitionVal()) { |
| throw new MetaException( |
| "Partition values must be set when firing multiple insert events"); |
| } |
| events.add(new InsertEvent(catName, dbName, tblName, |
| insertData.getPartitionVal(), |
| insertData, isSuccessful, this)); |
| } |
| } |
| FireEventResponse response = new FireEventResponse(); |
| for (InsertEvent event : events) { |
| /* |
| * The transactional listener response will be set already on the event, so there is not need |
| * to pass the response to the non-transactional listener. |
| */ |
| MetaStoreListenerNotifier |
| .notifyEvent(transactionalListeners, EventType.INSERT, event); |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.INSERT, event); |
| if (event.getParameters() != null && event.getParameters() |
| .containsKey( |
| MetaStoreEventListenerConstants.DB_NOTIFICATION_EVENT_ID_KEY_NAME)) { |
| response.addToEventIds(Long.valueOf(event.getParameters() |
| .get(MetaStoreEventListenerConstants.DB_NOTIFICATION_EVENT_ID_KEY_NAME))); |
| } else { |
| String msg = "Insert event id not generated for "; |
| if (event.getPartitionObj() != null) { |
| msg += "partition " + Arrays |
| .toString(event.getPartitionObj().getValues().toArray()) + " of "; |
| } |
| msg += |
| " of table " + event.getTableObj().getDbName() + "." + event.getTableObj() |
| .getTableName(); |
| LOG.warn(msg); |
| } |
| } |
| return response; |
| case REFRESH_EVENT: |
| response = new FireEventResponse(); |
| catName = rqst.isSetCatName() ? rqst.getCatName() : getDefaultCatalog(conf); |
| dbName = rqst.getDbName(); |
| tblName = rqst.getTableName(); |
| List<String> partitionVals = rqst.getPartitionVals(); |
| Map<String, String> tableParams = rqst.getTblParams(); |
| ReloadEvent event = new ReloadEvent(catName, dbName, tblName, partitionVals, rqst.isSuccessful(), |
| rqst.getData().getRefreshEvent(), tableParams, this); |
| MetaStoreListenerNotifier |
| .notifyEvent(transactionalListeners, EventType.RELOAD, event); |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.RELOAD, event); |
| if (event.getParameters() != null && event.getParameters() |
| .containsKey( |
| MetaStoreEventListenerConstants.DB_NOTIFICATION_EVENT_ID_KEY_NAME)) { |
| response.addToEventIds(Long.valueOf(event.getParameters() |
| .get(MetaStoreEventListenerConstants.DB_NOTIFICATION_EVENT_ID_KEY_NAME))); |
| } else { |
| String msg = "Reload event id not generated for "; |
| if (event.getPartitionObj() != null) { |
| msg += "partition " + Arrays |
| .toString(event.getPartitionObj().getValues().toArray()) + " of "; |
| } |
| msg += |
| " of table " + event.getTableObj().getDbName() + "." + event.getTableObj() |
| .getTableName(); |
| LOG.warn(msg); |
| } |
| return response; |
| default: |
| throw new TException("Event type " + rqst.getData().getSetField().toString() |
| + " not currently supported."); |
| } |
| |
| } |
| |
| @Override |
| public GetFileMetadataByExprResult get_file_metadata_by_expr(GetFileMetadataByExprRequest req) |
| throws TException { |
| GetFileMetadataByExprResult result = new GetFileMetadataByExprResult(); |
| RawStore ms = getMS(); |
| if (!ms.isFileMetadataSupported()) { |
| result.setIsSupported(false); |
| result.setMetadata(Collections.emptyMap()); // Set the required field. |
| return result; |
| } |
| result.setIsSupported(true); |
| |
| List<Long> fileIds = req.getFileIds(); |
| boolean needMetadata = !req.isSetDoGetFooters() || req.isDoGetFooters(); |
| FileMetadataExprType type = req.isSetType() ? req.getType() : FileMetadataExprType.ORC_SARG; |
| |
| ByteBuffer[] metadatas = needMetadata ? new ByteBuffer[fileIds.size()] : null; |
| ByteBuffer[] ppdResults = new ByteBuffer[fileIds.size()]; |
| boolean[] eliminated = new boolean[fileIds.size()]; |
| |
| getMS().getFileMetadataByExpr(fileIds, type, req.getExpr(), metadatas, ppdResults, eliminated); |
| for (int i = 0; i < fileIds.size(); ++i) { |
| if (!eliminated[i] && ppdResults[i] == null) |
| { |
| continue; // No metadata => no ppd. |
| } |
| MetadataPpdResult mpr = new MetadataPpdResult(); |
| ByteBuffer ppdResult = eliminated[i] ? null : handleReadOnlyBufferForThrift(ppdResults[i]); |
| mpr.setIncludeBitset(ppdResult); |
| if (needMetadata) { |
| ByteBuffer metadata = eliminated[i] ? null : handleReadOnlyBufferForThrift(metadatas[i]); |
| mpr.setMetadata(metadata); |
| } |
| result.putToMetadata(fileIds.get(i), mpr); |
| } |
| if (!result.isSetMetadata()) { |
| result.setMetadata(Collections.emptyMap()); // Set the required field. |
| } |
| return result; |
| } |
| |
| @Override |
| public GetFileMetadataResult get_file_metadata(GetFileMetadataRequest req) throws TException { |
| GetFileMetadataResult result = new GetFileMetadataResult(); |
| RawStore ms = getMS(); |
| if (!ms.isFileMetadataSupported()) { |
| result.setIsSupported(false); |
| result.setMetadata(Collections.emptyMap()); // Set the required field. |
| return result; |
| } |
| result.setIsSupported(true); |
| List<Long> fileIds = req.getFileIds(); |
| ByteBuffer[] metadatas = ms.getFileMetadata(fileIds); |
| assert metadatas.length == fileIds.size(); |
| for (int i = 0; i < metadatas.length; ++i) { |
| ByteBuffer bb = metadatas[i]; |
| if (bb == null) { |
| continue; |
| } |
| bb = handleReadOnlyBufferForThrift(bb); |
| result.putToMetadata(fileIds.get(i), bb); |
| } |
| if (!result.isSetMetadata()) { |
| result.setMetadata(Collections.emptyMap()); // Set the required field. |
| } |
| return result; |
| } |
| |
| private ByteBuffer handleReadOnlyBufferForThrift(ByteBuffer bb) { |
| if (!bb.isReadOnly()) { |
| return bb; |
| } |
| // Thrift cannot write read-only buffers... oh well. |
| // TODO: actually thrift never writes to the buffer, so we could use reflection to |
| // unset the unnecessary read-only flag if allocation/copy perf becomes a problem. |
| ByteBuffer copy = ByteBuffer.allocate(bb.capacity()); |
| copy.put(bb); |
| copy.flip(); |
| return copy; |
| } |
| |
| @Override |
| public PutFileMetadataResult put_file_metadata(PutFileMetadataRequest req) throws TException { |
| RawStore ms = getMS(); |
| if (ms.isFileMetadataSupported()) { |
| ms.putFileMetadata(req.getFileIds(), req.getMetadata(), req.getType()); |
| } |
| return new PutFileMetadataResult(); |
| } |
| |
| @Override |
| public ClearFileMetadataResult clear_file_metadata(ClearFileMetadataRequest req) |
| throws TException { |
| getMS().putFileMetadata(req.getFileIds(), null, null); |
| return new ClearFileMetadataResult(); |
| } |
| |
| @Override |
| public CacheFileMetadataResult cache_file_metadata( |
| CacheFileMetadataRequest req) throws TException { |
| RawStore ms = getMS(); |
| if (!ms.isFileMetadataSupported()) { |
| return new CacheFileMetadataResult(false); |
| } |
| String dbName = req.getDbName(), tblName = req.getTblName(), |
| partName = req.isSetPartName() ? req.getPartName() : null; |
| boolean isAllPart = req.isSetIsAllParts() && req.isIsAllParts(); |
| ms.openTransaction(); |
| boolean success = false; |
| try { |
| Table tbl = ms.getTable(getDefaultCatalog(conf), dbName, tblName); |
| if (tbl == null) { |
| throw new NoSuchObjectException(dbName + "." + tblName + " not found"); |
| } |
| boolean isPartitioned = tbl.isSetPartitionKeys() && tbl.getPartitionKeysSize() > 0; |
| String tableInputFormat = tbl.isSetSd() ? tbl.getSd().getInputFormat() : null; |
| if (!isPartitioned) { |
| if (partName != null || isAllPart) { |
| throw new MetaException("Table is not partitioned"); |
| } |
| if (!tbl.isSetSd() || !tbl.getSd().isSetLocation()) { |
| throw new MetaException( |
| "Table does not have storage location; this operation is not supported on views"); |
| } |
| FileMetadataExprType type = expressionProxy.getMetadataType(tableInputFormat); |
| if (type == null) { |
| throw new MetaException("The operation is not supported for " + tableInputFormat); |
| } |
| fileMetadataManager.queueCacheMetadata(tbl.getSd().getLocation(), type); |
| success = true; |
| } else { |
| List<String> partNames; |
| if (partName != null) { |
| partNames = Lists.newArrayList(partName); |
| } else if (isAllPart) { |
| partNames = ms.listPartitionNames(getDefaultCatalog(conf), dbName, tblName, (short)-1); |
| } else { |
| throw new MetaException("Table is partitioned"); |
| } |
| int batchSize = MetastoreConf.getIntVar( |
| conf, ConfVars.BATCH_RETRIEVE_OBJECTS_MAX); |
| int index = 0; |
| int successCount = 0, failCount = 0; |
| HashSet<String> failFormats = null; |
| while (index < partNames.size()) { |
| int currentBatchSize = Math.min(batchSize, partNames.size() - index); |
| List<String> nameBatch = partNames.subList(index, index + currentBatchSize); |
| index += currentBatchSize; |
| List<Partition> parts = ms.getPartitionsByNames(getDefaultCatalog(conf), dbName, tblName, nameBatch); |
| for (Partition part : parts) { |
| if (!part.isSetSd() || !part.getSd().isSetLocation()) { |
| throw new MetaException("Partition does not have storage location;" + |
| " this operation is not supported on views"); |
| } |
| String inputFormat = part.getSd().isSetInputFormat() |
| ? part.getSd().getInputFormat() : tableInputFormat; |
| FileMetadataExprType type = expressionProxy.getMetadataType(inputFormat); |
| if (type == null) { |
| ++failCount; |
| if (failFormats == null) { |
| failFormats = new HashSet<>(); |
| } |
| failFormats.add(inputFormat); |
| } else { |
| ++successCount; |
| fileMetadataManager.queueCacheMetadata(part.getSd().getLocation(), type); |
| } |
| } |
| } |
| success = true; // Regardless of the following exception |
| if (failCount > 0) { |
| String errorMsg = "The operation failed for " + failCount + " partitions and " |
| + "succeeded for " + successCount + " partitions; unsupported formats: "; |
| boolean isFirst = true; |
| for (String s : failFormats) { |
| if (!isFirst) { |
| errorMsg += ", "; |
| } |
| isFirst = false; |
| errorMsg += s; |
| } |
| throw new MetaException(errorMsg); |
| } |
| } |
| } finally { |
| if (success) { |
| if (!ms.commitTransaction()) { |
| throw new MetaException("Failed to commit"); |
| } |
| } else { |
| ms.rollbackTransaction(); |
| } |
| } |
| return new CacheFileMetadataResult(true); |
| } |
| |
| @VisibleForTesting |
| void updateMetrics() throws MetaException { |
| if (Metrics.getRegistry() != null) { |
| LOG.info("Begin calculating metadata count metrics."); |
| Metrics.getOrCreateGauge(MetricsConstants.TOTAL_DATABASES).set(getMS().getTableCount()); |
| Metrics.getOrCreateGauge(MetricsConstants.TOTAL_TABLES).set(getMS().getPartitionCount()); |
| Metrics.getOrCreateGauge(MetricsConstants.TOTAL_PARTITIONS).set(getMS().getDatabaseCount()); |
| } |
| } |
| |
| @Override |
| public PrimaryKeysResponse get_primary_keys(PrimaryKeysRequest request) throws TException { |
| request.setCatName(request.isSetCatName() ? request.getCatName() : getDefaultCatalog(conf)); |
| startTableFunction("get_primary_keys", request.getCatName(), request.getDb_name(), request.getTbl_name()); |
| List<SQLPrimaryKey> ret = null; |
| Exception ex = null; |
| try { |
| ret = getMS().getPrimaryKeys(request); |
| } catch (Exception e) { |
| ex = e; |
| throwMetaException(e); |
| } finally { |
| endFunction("get_primary_keys", ret != null, ex, request.getTbl_name()); |
| } |
| return new PrimaryKeysResponse(ret); |
| } |
| |
| @Override |
| public ForeignKeysResponse get_foreign_keys(ForeignKeysRequest request) throws TException { |
| request.setCatName(request.isSetCatName() ? request.getCatName() : getDefaultCatalog(conf)); |
| startFunction("get_foreign_keys", |
| " : parentdb=" + request.getParent_db_name() + " parenttbl=" + request.getParent_tbl_name() + " foreigndb=" |
| + request.getForeign_db_name() + " foreigntbl=" + request.getForeign_tbl_name()); |
| List<SQLForeignKey> ret = null; |
| Exception ex = null; |
| try { |
| ret = getMS().getForeignKeys(request); |
| } catch (Exception e) { |
| ex = e; |
| throwMetaException(e); |
| } finally { |
| endFunction("get_foreign_keys", ret != null, ex, request.getForeign_tbl_name()); |
| } |
| return new ForeignKeysResponse(ret); |
| } |
| |
| @Override |
| public UniqueConstraintsResponse get_unique_constraints(UniqueConstraintsRequest request) throws TException { |
| request.setCatName(request.isSetCatName() ? request.getCatName() : getDefaultCatalog(conf)); |
| startTableFunction("get_unique_constraints", request.getCatName(), request.getDb_name(), request.getTbl_name()); |
| List<SQLUniqueConstraint> ret = null; |
| Exception ex = null; |
| try { |
| ret = getMS().getUniqueConstraints(request); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_unique_constraints", ret != null, ex, request.getTbl_name()); |
| } |
| return new UniqueConstraintsResponse(ret); |
| } |
| |
| @Override |
| public NotNullConstraintsResponse get_not_null_constraints(NotNullConstraintsRequest request) throws TException { |
| request.setCatName(request.isSetCatName() ? request.getCatName() : getDefaultCatalog(conf)); |
| startTableFunction("get_not_null_constraints", request.getCatName(), request.getDb_name(), request.getTbl_name()); |
| List<SQLNotNullConstraint> ret = null; |
| Exception ex = null; |
| try { |
| ret = getMS().getNotNullConstraints(request); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_not_null_constraints", ret != null, ex, request.getTbl_name()); |
| } |
| return new NotNullConstraintsResponse(ret); |
| } |
| |
| @Override |
| public DefaultConstraintsResponse get_default_constraints(DefaultConstraintsRequest request) throws TException { |
| request.setCatName(request.isSetCatName() ? request.getCatName() : getDefaultCatalog(conf)); |
| startTableFunction("get_default_constraints", request.getCatName(), request.getDb_name(), request.getTbl_name()); |
| List<SQLDefaultConstraint> ret = null; |
| Exception ex = null; |
| try { |
| ret = getMS().getDefaultConstraints(request); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_default_constraints", ret != null, ex, request.getTbl_name()); |
| } |
| return new DefaultConstraintsResponse(ret); |
| } |
| |
| @Override |
| public CheckConstraintsResponse get_check_constraints(CheckConstraintsRequest request) throws TException { |
| request.setCatName(request.isSetCatName() ? request.getCatName() : getDefaultCatalog(conf)); |
| startTableFunction("get_check_constraints", request.getCatName(), request.getDb_name(), request.getTbl_name()); |
| List<SQLCheckConstraint> ret = null; |
| Exception ex = null; |
| try { |
| ret = getMS().getCheckConstraints(request); |
| } catch (Exception e) { |
| ex = e; |
| throw newMetaException(e); |
| } finally { |
| endFunction("get_check_constraints", ret != null, ex, request.getTbl_name()); |
| } |
| return new CheckConstraintsResponse(ret); |
| } |
| |
| /** |
| * Api to fetch all table constraints at once. |
| * @param request it consist of catalog name, database name and table name to identify the table in metastore |
| * @return all constraints attached to given table |
| * @throws TException |
| */ |
| @Override |
| public AllTableConstraintsResponse get_all_table_constraints(AllTableConstraintsRequest request) |
| throws TException, MetaException, NoSuchObjectException { |
| request.setCatName(request.isSetCatName() ? request.getCatName() : getDefaultCatalog(conf)); |
| startTableFunction("get_all_table_constraints", request.getCatName(), request.getDbName(), request.getTblName()); |
| SQLAllTableConstraints ret = null; |
| Exception ex = null; |
| try { |
| ret = getMS().getAllTableConstraints(request); |
| } catch (Exception e) { |
| ex = e; |
| throwMetaException(e); |
| } finally { |
| endFunction("get_all_table_constraints", ret != null, ex, request.getTblName()); |
| } |
| return new AllTableConstraintsResponse(ret); |
| } |
| |
| @Override |
| public String get_metastore_db_uuid() throws TException { |
| try { |
| return getMS().getMetastoreDbUuid(); |
| } catch (MetaException e) { |
| LOG.error("Exception thrown while querying metastore db uuid", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMCreateResourcePlanResponse create_resource_plan(WMCreateResourcePlanRequest request) |
| throws AlreadyExistsException, InvalidObjectException, MetaException, TException { |
| int defaultPoolSize = MetastoreConf.getIntVar( |
| conf, MetastoreConf.ConfVars.WM_DEFAULT_POOL_SIZE); |
| WMResourcePlan plan = request.getResourcePlan(); |
| if (defaultPoolSize > 0 && plan.isSetQueryParallelism()) { |
| // If the default pool is not disabled, override the size with the specified parallelism. |
| defaultPoolSize = plan.getQueryParallelism(); |
| } |
| try { |
| getMS().createResourcePlan(plan, request.getCopyFrom(), defaultPoolSize); |
| return new WMCreateResourcePlanResponse(); |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to persist resource plan", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMGetResourcePlanResponse get_resource_plan(WMGetResourcePlanRequest request) |
| throws NoSuchObjectException, MetaException, TException { |
| try { |
| WMFullResourcePlan rp = getMS().getResourcePlan(request.getResourcePlanName(), request.getNs()); |
| WMGetResourcePlanResponse resp = new WMGetResourcePlanResponse(); |
| resp.setResourcePlan(rp); |
| return resp; |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to retrieve resource plan", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMGetAllResourcePlanResponse get_all_resource_plans(WMGetAllResourcePlanRequest request) |
| throws MetaException, TException { |
| try { |
| WMGetAllResourcePlanResponse resp = new WMGetAllResourcePlanResponse(); |
| resp.setResourcePlans(getMS().getAllResourcePlans(request.getNs())); |
| return resp; |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to retrieve resource plans", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMAlterResourcePlanResponse alter_resource_plan(WMAlterResourcePlanRequest request) |
| throws NoSuchObjectException, InvalidOperationException, MetaException, TException { |
| try { |
| if (((request.isIsEnableAndActivate() ? 1 : 0) + (request.isIsReplace() ? 1 : 0) |
| + (request.isIsForceDeactivate() ? 1 : 0)) > 1) { |
| throw new MetaException("Invalid request; multiple flags are set"); |
| } |
| WMAlterResourcePlanResponse response = new WMAlterResourcePlanResponse(); |
| // This method will only return full resource plan when activating one, |
| // to give the caller the result atomically with the activation. |
| WMFullResourcePlan fullPlanAfterAlter = getMS().alterResourcePlan( |
| request.getResourcePlanName(), request.getNs(), request.getResourcePlan(), |
| request.isIsEnableAndActivate(), request.isIsForceDeactivate(), request.isIsReplace()); |
| if (fullPlanAfterAlter != null) { |
| response.setFullResourcePlan(fullPlanAfterAlter); |
| } |
| return response; |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to alter resource plan", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMGetActiveResourcePlanResponse get_active_resource_plan( |
| WMGetActiveResourcePlanRequest request) throws MetaException, TException { |
| try { |
| WMGetActiveResourcePlanResponse response = new WMGetActiveResourcePlanResponse(); |
| response.setResourcePlan(getMS().getActiveResourcePlan(request.getNs())); |
| return response; |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to get active resource plan", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMValidateResourcePlanResponse validate_resource_plan(WMValidateResourcePlanRequest request) |
| throws NoSuchObjectException, MetaException, TException { |
| try { |
| return getMS().validateResourcePlan(request.getResourcePlanName(), request.getNs()); |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to validate resource plan", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMDropResourcePlanResponse drop_resource_plan(WMDropResourcePlanRequest request) |
| throws NoSuchObjectException, InvalidOperationException, MetaException, TException { |
| try { |
| getMS().dropResourcePlan(request.getResourcePlanName(), request.getNs()); |
| return new WMDropResourcePlanResponse(); |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to drop resource plan", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMCreateTriggerResponse create_wm_trigger(WMCreateTriggerRequest request) |
| throws AlreadyExistsException, InvalidObjectException, MetaException, TException { |
| try { |
| getMS().createWMTrigger(request.getTrigger()); |
| return new WMCreateTriggerResponse(); |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to create trigger", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMAlterTriggerResponse alter_wm_trigger(WMAlterTriggerRequest request) |
| throws NoSuchObjectException, InvalidObjectException, MetaException, TException { |
| try { |
| getMS().alterWMTrigger(request.getTrigger()); |
| return new WMAlterTriggerResponse(); |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to alter trigger", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMDropTriggerResponse drop_wm_trigger(WMDropTriggerRequest request) |
| throws NoSuchObjectException, InvalidOperationException, MetaException, TException { |
| try { |
| getMS().dropWMTrigger(request.getResourcePlanName(), request.getTriggerName(), request.getNs()); |
| return new WMDropTriggerResponse(); |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to drop trigger.", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMGetTriggersForResourePlanResponse get_triggers_for_resourceplan( |
| WMGetTriggersForResourePlanRequest request) |
| throws NoSuchObjectException, MetaException, TException { |
| try { |
| List<WMTrigger> triggers = |
| getMS().getTriggersForResourcePlan(request.getResourcePlanName(), request.getNs()); |
| WMGetTriggersForResourePlanResponse response = new WMGetTriggersForResourePlanResponse(); |
| response.setTriggers(triggers); |
| return response; |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to retrieve triggers plans", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMAlterPoolResponse alter_wm_pool(WMAlterPoolRequest request) |
| throws AlreadyExistsException, NoSuchObjectException, InvalidObjectException, MetaException, |
| TException { |
| try { |
| getMS().alterPool(request.getPool(), request.getPoolPath()); |
| return new WMAlterPoolResponse(); |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to alter WMPool", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMCreatePoolResponse create_wm_pool(WMCreatePoolRequest request) |
| throws AlreadyExistsException, NoSuchObjectException, InvalidObjectException, MetaException, |
| TException { |
| try { |
| getMS().createPool(request.getPool()); |
| return new WMCreatePoolResponse(); |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to create WMPool", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMDropPoolResponse drop_wm_pool(WMDropPoolRequest request) |
| throws NoSuchObjectException, InvalidOperationException, MetaException, TException { |
| try { |
| getMS().dropWMPool(request.getResourcePlanName(), request.getPoolPath(), request.getNs()); |
| return new WMDropPoolResponse(); |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to drop WMPool", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMCreateOrUpdateMappingResponse create_or_update_wm_mapping( |
| WMCreateOrUpdateMappingRequest request) throws AlreadyExistsException, |
| NoSuchObjectException, InvalidObjectException, MetaException, TException { |
| try { |
| getMS().createOrUpdateWMMapping(request.getMapping(), request.isUpdate()); |
| return new WMCreateOrUpdateMappingResponse(); |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to create or update WMMapping", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMDropMappingResponse drop_wm_mapping(WMDropMappingRequest request) |
| throws NoSuchObjectException, InvalidOperationException, MetaException, TException { |
| try { |
| getMS().dropWMMapping(request.getMapping()); |
| return new WMDropMappingResponse(); |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to drop WMMapping", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public WMCreateOrDropTriggerToPoolMappingResponse create_or_drop_wm_trigger_to_pool_mapping( |
| WMCreateOrDropTriggerToPoolMappingRequest request) throws AlreadyExistsException, |
| NoSuchObjectException, InvalidObjectException, MetaException, TException { |
| try { |
| if (request.isDrop()) { |
| getMS().dropWMTriggerToPoolMapping(request.getResourcePlanName(), |
| request.getTriggerName(), request.getPoolPath(), request.getNs()); |
| } else { |
| getMS().createWMTriggerToPoolMapping(request.getResourcePlanName(), |
| request.getTriggerName(), request.getPoolPath(), request.getNs()); |
| } |
| return new WMCreateOrDropTriggerToPoolMappingResponse(); |
| } catch (MetaException e) { |
| LOG.error("Exception while trying to create or drop pool mappings", e); |
| throw e; |
| } |
| } |
| |
| @Override |
| public void create_ischema(ISchema schema) throws TException { |
| startFunction("create_ischema", ": " + schema.getName()); |
| boolean success = false; |
| Exception ex = null; |
| RawStore ms = getMS(); |
| try { |
| firePreEvent(new PreCreateISchemaEvent(this, schema)); |
| Map<String, String> transactionalListenersResponses = Collections.emptyMap(); |
| ms.openTransaction(); |
| try { |
| ms.createISchema(schema); |
| |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenersResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.CREATE_ISCHEMA, new CreateISchemaEvent(true, this, schema)); |
| } |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.CREATE_ISCHEMA, |
| new CreateISchemaEvent(success, this, schema), null, |
| transactionalListenersResponses, ms); |
| } |
| } |
| } catch (MetaException|AlreadyExistsException e) { |
| LOG.error("Caught exception creating schema", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("create_ischema", success, ex); |
| } |
| } |
| |
| @Override |
| public void alter_ischema(AlterISchemaRequest rqst) throws TException { |
| startFunction("alter_ischema", ": " + rqst); |
| boolean success = false; |
| Exception ex = null; |
| RawStore ms = getMS(); |
| try { |
| ISchema oldSchema = ms.getISchema(rqst.getName()); |
| if (oldSchema == null) { |
| throw new NoSuchObjectException("Could not find schema " + rqst.getName()); |
| } |
| firePreEvent(new PreAlterISchemaEvent(this, oldSchema, rqst.getNewSchema())); |
| Map<String, String> transactionalListenersResponses = Collections.emptyMap(); |
| ms.openTransaction(); |
| try { |
| ms.alterISchema(rqst.getName(), rqst.getNewSchema()); |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenersResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.ALTER_ISCHEMA, new AlterISchemaEvent(true, this, oldSchema, rqst.getNewSchema())); |
| } |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ALTER_ISCHEMA, |
| new AlterISchemaEvent(success, this, oldSchema, rqst.getNewSchema()), null, |
| transactionalListenersResponses, ms); |
| } |
| } |
| } catch (MetaException|NoSuchObjectException e) { |
| LOG.error("Caught exception altering schema", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("alter_ischema", success, ex); |
| } |
| } |
| |
| @Override |
| public ISchema get_ischema(ISchemaName schemaName) throws TException { |
| startFunction("get_ischema", ": " + schemaName); |
| Exception ex = null; |
| ISchema schema = null; |
| try { |
| schema = getMS().getISchema(schemaName); |
| if (schema == null) { |
| throw new NoSuchObjectException("No schema named " + schemaName + " exists"); |
| } |
| firePreEvent(new PreReadISchemaEvent(this, schema)); |
| return schema; |
| } catch (MetaException e) { |
| LOG.error("Caught exception getting schema", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_ischema", schema != null, ex); |
| } |
| } |
| |
| @Override |
| public void drop_ischema(ISchemaName schemaName) throws TException { |
| startFunction("drop_ischema", ": " + schemaName); |
| Exception ex = null; |
| boolean success = false; |
| RawStore ms = getMS(); |
| try { |
| // look for any valid versions. This will also throw NoSuchObjectException if the schema |
| // itself doesn't exist, which is what we want. |
| SchemaVersion latest = ms.getLatestSchemaVersion(schemaName); |
| if (latest != null) { |
| ex = new InvalidOperationException("Schema " + schemaName + " cannot be dropped, it has" + |
| " at least one valid version"); |
| throw (InvalidObjectException)ex; |
| } |
| ISchema schema = ms.getISchema(schemaName); |
| firePreEvent(new PreDropISchemaEvent(this, schema)); |
| Map<String, String> transactionalListenersResponses = Collections.emptyMap(); |
| ms.openTransaction(); |
| try { |
| ms.dropISchema(schemaName); |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenersResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.DROP_ISCHEMA, new DropISchemaEvent(true, this, schema)); |
| } |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.DROP_ISCHEMA, |
| new DropISchemaEvent(success, this, schema), null, |
| transactionalListenersResponses, ms); |
| } |
| } |
| } catch (MetaException|NoSuchObjectException e) { |
| LOG.error("Caught exception dropping schema", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("drop_ischema", success, ex); |
| } |
| } |
| |
| @Override |
| public void add_schema_version(SchemaVersion schemaVersion) throws TException { |
| startFunction("add_schema_version", ": " + schemaVersion); |
| boolean success = false; |
| Exception ex = null; |
| RawStore ms = getMS(); |
| try { |
| // Make sure the referenced schema exists |
| if (ms.getISchema(schemaVersion.getSchema()) == null) { |
| throw new NoSuchObjectException("No schema named " + schemaVersion.getSchema()); |
| } |
| firePreEvent(new PreAddSchemaVersionEvent(this, schemaVersion)); |
| Map<String, String> transactionalListenersResponses = Collections.emptyMap(); |
| ms.openTransaction(); |
| try { |
| ms.addSchemaVersion(schemaVersion); |
| |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenersResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.ADD_SCHEMA_VERSION, new AddSchemaVersionEvent(true, this, schemaVersion)); |
| } |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ADD_SCHEMA_VERSION, |
| new AddSchemaVersionEvent(success, this, schemaVersion), null, |
| transactionalListenersResponses, ms); |
| } |
| } |
| } catch (MetaException|AlreadyExistsException e) { |
| LOG.error("Caught exception adding schema version", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("add_schema_version", success, ex); |
| } |
| } |
| |
| @Override |
| public SchemaVersion get_schema_version(SchemaVersionDescriptor version) throws TException { |
| startFunction("get_schema_version", ": " + version); |
| Exception ex = null; |
| SchemaVersion schemaVersion = null; |
| try { |
| schemaVersion = getMS().getSchemaVersion(version); |
| if (schemaVersion == null) { |
| throw new NoSuchObjectException("No schema version " + version + "exists"); |
| } |
| firePreEvent(new PreReadhSchemaVersionEvent(this, Collections.singletonList(schemaVersion))); |
| return schemaVersion; |
| } catch (MetaException e) { |
| LOG.error("Caught exception getting schema version", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_schema_version", schemaVersion != null, ex); |
| } |
| } |
| |
| @Override |
| public SchemaVersion get_schema_latest_version(ISchemaName schemaName) throws TException { |
| startFunction("get_latest_schema_version", ": " + schemaName); |
| Exception ex = null; |
| SchemaVersion schemaVersion = null; |
| try { |
| schemaVersion = getMS().getLatestSchemaVersion(schemaName); |
| if (schemaVersion == null) { |
| throw new NoSuchObjectException("No versions of schema " + schemaName + "exist"); |
| } |
| firePreEvent(new PreReadhSchemaVersionEvent(this, Collections.singletonList(schemaVersion))); |
| return schemaVersion; |
| } catch (MetaException e) { |
| LOG.error("Caught exception getting latest schema version", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_latest_schema_version", schemaVersion != null, ex); |
| } |
| } |
| |
| @Override |
| public List<SchemaVersion> get_schema_all_versions(ISchemaName schemaName) throws TException { |
| startFunction("get_all_schema_versions", ": " + schemaName); |
| Exception ex = null; |
| List<SchemaVersion> schemaVersions = null; |
| try { |
| schemaVersions = getMS().getAllSchemaVersion(schemaName); |
| if (schemaVersions == null) { |
| throw new NoSuchObjectException("No versions of schema " + schemaName + "exist"); |
| } |
| firePreEvent(new PreReadhSchemaVersionEvent(this, schemaVersions)); |
| return schemaVersions; |
| } catch (MetaException e) { |
| LOG.error("Caught exception getting all schema versions", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_all_schema_versions", schemaVersions != null, ex); |
| } |
| } |
| |
| @Override |
| public void drop_schema_version(SchemaVersionDescriptor version) throws TException { |
| startFunction("drop_schema_version", ": " + version); |
| Exception ex = null; |
| boolean success = false; |
| RawStore ms = getMS(); |
| try { |
| SchemaVersion schemaVersion = ms.getSchemaVersion(version); |
| if (schemaVersion == null) { |
| throw new NoSuchObjectException("No schema version " + version); |
| } |
| firePreEvent(new PreDropSchemaVersionEvent(this, schemaVersion)); |
| Map<String, String> transactionalListenersResponses = Collections.emptyMap(); |
| ms.openTransaction(); |
| try { |
| ms.dropSchemaVersion(version); |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenersResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.DROP_SCHEMA_VERSION, new DropSchemaVersionEvent(true, this, schemaVersion)); |
| } |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.DROP_SCHEMA_VERSION, |
| new DropSchemaVersionEvent(success, this, schemaVersion), null, |
| transactionalListenersResponses, ms); |
| } |
| } |
| } catch (MetaException|NoSuchObjectException e) { |
| LOG.error("Caught exception dropping schema version", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("drop_schema_version", success, ex); |
| } |
| } |
| |
| @Override |
| public FindSchemasByColsResp get_schemas_by_cols(FindSchemasByColsRqst rqst) throws TException { |
| startFunction("get_schemas_by_cols"); |
| Exception ex = null; |
| List<SchemaVersion> schemaVersions = Collections.emptyList(); |
| try { |
| schemaVersions = getMS().getSchemaVersionsByColumns(rqst.getColName(), |
| rqst.getColNamespace(), rqst.getType()); |
| firePreEvent(new PreReadhSchemaVersionEvent(this, schemaVersions)); |
| final List<SchemaVersionDescriptor> entries = new ArrayList<>(schemaVersions.size()); |
| schemaVersions.forEach(schemaVersion -> entries.add( |
| new SchemaVersionDescriptor(schemaVersion.getSchema(), schemaVersion.getVersion()))); |
| return new FindSchemasByColsResp(entries); |
| } catch (MetaException e) { |
| LOG.error("Caught exception doing schema version query", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_schemas_by_cols", !schemaVersions.isEmpty(), ex); |
| } |
| } |
| |
| @Override |
| public void map_schema_version_to_serde(MapSchemaVersionToSerdeRequest rqst) |
| throws TException { |
| startFunction("map_schema_version_to_serde, :" + rqst); |
| boolean success = false; |
| Exception ex = null; |
| RawStore ms = getMS(); |
| try { |
| SchemaVersion oldSchemaVersion = ms.getSchemaVersion(rqst.getSchemaVersion()); |
| if (oldSchemaVersion == null) { |
| throw new NoSuchObjectException("No schema version " + rqst.getSchemaVersion()); |
| } |
| SerDeInfo serde = ms.getSerDeInfo(rqst.getSerdeName()); |
| if (serde == null) { |
| throw new NoSuchObjectException("No SerDe named " + rqst.getSerdeName()); |
| } |
| SchemaVersion newSchemaVersion = new SchemaVersion(oldSchemaVersion); |
| newSchemaVersion.setSerDe(serde); |
| firePreEvent(new PreAlterSchemaVersionEvent(this, oldSchemaVersion, newSchemaVersion)); |
| Map<String, String> transactionalListenersResponses = Collections.emptyMap(); |
| ms.openTransaction(); |
| try { |
| ms.alterSchemaVersion(rqst.getSchemaVersion(), newSchemaVersion); |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenersResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.ALTER_SCHEMA_VERSION, new AlterSchemaVersionEvent(true, this, |
| oldSchemaVersion, newSchemaVersion)); |
| } |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ALTER_SCHEMA_VERSION, |
| new AlterSchemaVersionEvent(success, this, oldSchemaVersion, newSchemaVersion), null, |
| transactionalListenersResponses, ms); |
| } |
| } |
| } catch (MetaException|NoSuchObjectException e) { |
| LOG.error("Caught exception mapping schema version to serde", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("map_schema_version_to_serde", success, ex); |
| } |
| } |
| |
| @Override |
| public void set_schema_version_state(SetSchemaVersionStateRequest rqst) throws TException { |
| startFunction("set_schema_version_state, :" + rqst); |
| boolean success = false; |
| Exception ex = null; |
| RawStore ms = getMS(); |
| try { |
| SchemaVersion oldSchemaVersion = ms.getSchemaVersion(rqst.getSchemaVersion()); |
| if (oldSchemaVersion == null) { |
| throw new NoSuchObjectException("No schema version " + rqst.getSchemaVersion()); |
| } |
| SchemaVersion newSchemaVersion = new SchemaVersion(oldSchemaVersion); |
| newSchemaVersion.setState(rqst.getState()); |
| firePreEvent(new PreAlterSchemaVersionEvent(this, oldSchemaVersion, newSchemaVersion)); |
| Map<String, String> transactionalListenersResponses = Collections.emptyMap(); |
| ms.openTransaction(); |
| try { |
| ms.alterSchemaVersion(rqst.getSchemaVersion(), newSchemaVersion); |
| if (!transactionalListeners.isEmpty()) { |
| transactionalListenersResponses = |
| MetaStoreListenerNotifier.notifyEvent(transactionalListeners, |
| EventType.ALTER_SCHEMA_VERSION, new AlterSchemaVersionEvent(true, this, |
| oldSchemaVersion, newSchemaVersion)); |
| } |
| success = ms.commitTransaction(); |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| if (!listeners.isEmpty()) { |
| MetaStoreListenerNotifier.notifyEvent(listeners, EventType.ALTER_SCHEMA_VERSION, |
| new AlterSchemaVersionEvent(success, this, oldSchemaVersion, newSchemaVersion), null, |
| transactionalListenersResponses, ms); |
| } |
| } |
| } catch (MetaException|NoSuchObjectException e) { |
| LOG.error("Caught exception changing schema version state", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("set_schema_version_state", success, ex); |
| } |
| } |
| |
| @Override |
| public void add_serde(SerDeInfo serde) throws TException { |
| startFunction("create_serde", ": " + serde.getName()); |
| Exception ex = null; |
| boolean success = false; |
| RawStore ms = getMS(); |
| try { |
| ms.openTransaction(); |
| ms.addSerde(serde); |
| success = ms.commitTransaction(); |
| } catch (MetaException|AlreadyExistsException e) { |
| LOG.error("Caught exception creating serde", e); |
| ex = e; |
| throw e; |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| endFunction("create_serde", success, ex); |
| } |
| } |
| |
| @Override |
| public SerDeInfo get_serde(GetSerdeRequest rqst) throws TException { |
| startFunction("get_serde", ": " + rqst); |
| Exception ex = null; |
| SerDeInfo serde = null; |
| try { |
| serde = getMS().getSerDeInfo(rqst.getSerdeName()); |
| if (serde == null) { |
| throw new NoSuchObjectException("No serde named " + rqst.getSerdeName() + " exists"); |
| } |
| return serde; |
| } catch (MetaException e) { |
| LOG.error("Caught exception getting serde", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_serde", serde != null, ex); |
| } |
| } |
| |
| @Override |
| public LockResponse get_lock_materialization_rebuild(String dbName, String tableName, long txnId) |
| throws TException { |
| return getTxnHandler().lockMaterializationRebuild(dbName, tableName, txnId); |
| } |
| |
| @Override |
| public boolean heartbeat_lock_materialization_rebuild(String dbName, String tableName, long txnId) |
| throws TException { |
| return getTxnHandler().heartbeatLockMaterializationRebuild(dbName, tableName, txnId); |
| } |
| |
| @Override |
| public void add_runtime_stats(RuntimeStat stat) throws TException { |
| startFunction("store_runtime_stats"); |
| Exception ex = null; |
| boolean success = false; |
| RawStore ms = getMS(); |
| try { |
| ms.openTransaction(); |
| ms.addRuntimeStat(stat); |
| success = ms.commitTransaction(); |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| if (!success) { |
| ms.rollbackTransaction(); |
| } |
| endFunction("store_runtime_stats", success, ex); |
| } |
| } |
| |
| @Override |
| public List<RuntimeStat> get_runtime_stats(GetRuntimeStatsRequest rqst) throws TException { |
| startFunction("get_runtime_stats"); |
| Exception ex = null; |
| try { |
| List<RuntimeStat> res = getMS().getRuntimeStats(rqst.getMaxWeight(), rqst.getMaxCreateTime()); |
| return res; |
| } catch (MetaException e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_runtime_stats", ex == null, ex); |
| } |
| } |
| |
| @Override |
| public ScheduledQueryPollResponse scheduled_query_poll(ScheduledQueryPollRequest request) |
| throws MetaException, TException { |
| startFunction("scheduled_query_poll"); |
| Exception ex = null; |
| try { |
| RawStore ms = getMS(); |
| return ms.scheduledQueryPoll(request); |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("scheduled_query_poll", ex == null, ex); |
| } |
| } |
| |
| @Override |
| public void scheduled_query_maintenance(ScheduledQueryMaintenanceRequest request) throws MetaException, TException { |
| startFunction("scheduled_query_poll"); |
| Exception ex = null; |
| try { |
| RawStore ms = getMS(); |
| ms.scheduledQueryMaintenance(request); |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("scheduled_query_poll", ex == null, ex); |
| } |
| } |
| |
| @Override |
| public void scheduled_query_progress(ScheduledQueryProgressInfo info) throws MetaException, TException { |
| startFunction("scheduled_query_poll"); |
| Exception ex = null; |
| try { |
| RawStore ms = getMS(); |
| ms.scheduledQueryProgress(info); |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("scheduled_query_poll", ex == null, ex); |
| } |
| } |
| |
| @Override |
| public ScheduledQuery get_scheduled_query(ScheduledQueryKey scheduleKey) throws TException { |
| startFunction("get_scheduled_query"); |
| Exception ex = null; |
| try { |
| return getMS().getScheduledQuery(scheduleKey); |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_scheduled_query", ex == null, ex); |
| } |
| } |
| |
| @Override |
| public void add_replication_metrics(ReplicationMetricList replicationMetricList) throws MetaException{ |
| startFunction("add_replication_metrics"); |
| Exception ex = null; |
| try { |
| getMS().addReplicationMetrics(replicationMetricList); |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("add_replication_metrics", ex == null, ex); |
| } |
| } |
| |
| @Override |
| public ReplicationMetricList get_replication_metrics(GetReplicationMetricsRequest |
| getReplicationMetricsRequest) throws MetaException{ |
| startFunction("get_replication_metrics"); |
| Exception ex = null; |
| try { |
| return getMS().getReplicationMetrics(getReplicationMetricsRequest); |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_replication_metrics", ex == null, ex); |
| } |
| } |
| |
| @Override |
| public void create_stored_procedure(StoredProcedure proc) throws NoSuchObjectException, MetaException { |
| startFunction("create_stored_procedure"); |
| Exception ex = null; |
| |
| throwUnsupportedExceptionIfRemoteDB(proc.getDbName(), "create_stored_procedure"); |
| try { |
| getMS().createOrUpdateStoredProcedure(proc); |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("create_stored_procedure", ex == null, ex); |
| } |
| } |
| |
| public StoredProcedure get_stored_procedure(StoredProcedureRequest request) throws MetaException, NoSuchObjectException { |
| startFunction("get_stored_procedure"); |
| Exception ex = null; |
| try { |
| StoredProcedure proc = getMS().getStoredProcedure(request.getCatName(), request.getDbName(), request.getProcName()); |
| if (proc == null) { |
| throw new NoSuchObjectException( |
| "HPL/SQL StoredProcedure " + request.getDbName() + "." + request.getProcName() + " does not exist"); |
| } |
| return proc; |
| } catch (Exception e) { |
| if (!(e instanceof NoSuchObjectException)) { |
| LOG.error("Caught exception", e); |
| } |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_stored_procedure", ex == null, ex); |
| } |
| } |
| |
| @Override |
| public void drop_stored_procedure(StoredProcedureRequest request) throws MetaException { |
| startFunction("drop_stored_procedure"); |
| Exception ex = null; |
| try { |
| getMS().dropStoredProcedure(request.getCatName(), request.getDbName(), request.getProcName()); |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("drop_stored_procedure", ex == null, ex); |
| } |
| } |
| |
| @Override |
| public List<String> get_all_stored_procedures(ListStoredProcedureRequest request) throws MetaException { |
| startFunction("get_all_stored_procedures"); |
| Exception ex = null; |
| try { |
| return getMS().getAllStoredProcedures(request); |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_all_stored_procedures", ex == null, ex); |
| } |
| } |
| |
| public Package find_package(GetPackageRequest request) throws MetaException, NoSuchObjectException { |
| startFunction("find_package"); |
| Exception ex = null; |
| try { |
| Package pkg = getMS().findPackage(request); |
| if (pkg == null) { |
| throw new NoSuchObjectException( |
| "HPL/SQL package " + request.getDbName() + "." + request.getPackageName() + " does not exist"); |
| } |
| return pkg; |
| } catch (Exception e) { |
| if (!(e instanceof NoSuchObjectException)) { |
| LOG.error("Caught exception", e); |
| } |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("find_package", ex == null, ex); |
| } |
| } |
| |
| public void add_package(AddPackageRequest request) throws MetaException, NoSuchObjectException { |
| startFunction("add_package"); |
| Exception ex = null; |
| try { |
| getMS().addPackage(request); |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("add_package", ex == null, ex); |
| } |
| } |
| |
| public List<String> get_all_packages(ListPackageRequest request) throws MetaException { |
| startFunction("get_all_packages"); |
| Exception ex = null; |
| try { |
| return getMS().listPackages(request); |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_all_packages", ex == null, ex); |
| } |
| } |
| |
| public void drop_package(DropPackageRequest request) throws MetaException { |
| startFunction("drop_package"); |
| Exception ex = null; |
| try { |
| getMS().dropPackage(request); |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("drop_package", ex == null, ex); |
| } |
| } |
| |
| @Override |
| public List<WriteEventInfo> get_all_write_event_info(GetAllWriteEventInfoRequest request) |
| throws MetaException { |
| startFunction("get_all_write_event_info"); |
| Exception ex = null; |
| try { |
| List<WriteEventInfo> writeEventInfoList = |
| getMS().getAllWriteEventInfo(request.getTxnId(), request.getDbName(), request.getTableName()); |
| return writeEventInfoList == null ? Collections.emptyList() : writeEventInfoList; |
| } catch (Exception e) { |
| LOG.error("Caught exception", e); |
| ex = e; |
| throw e; |
| } finally { |
| endFunction("get_all_write_event_info", ex == null, ex); |
| } |
| } |
| } |