![JAR search and dependency download from the Maven repository](/logo.png)
com.netflix.metacat.main.services.impl.TableServiceImpl Maven / Gradle / Ivy
/*
* Copyright 2016 Netflix, Inc.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.metacat.main.services.impl;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.netflix.metacat.common.MetacatRequestContext;
import com.netflix.metacat.common.QualifiedName;
import com.netflix.metacat.common.dto.DatabaseDto;
import com.netflix.metacat.common.dto.StorageDto;
import com.netflix.metacat.common.dto.TableDto;
import com.netflix.metacat.common.exception.MetacatBadRequestException;
import com.netflix.metacat.common.exception.MetacatNotSupportedException;
import com.netflix.metacat.common.json.MetacatJson;
import com.netflix.metacat.common.server.connectors.exception.NotFoundException;
import com.netflix.metacat.common.server.connectors.exception.TableMigrationInProgressException;
import com.netflix.metacat.common.server.connectors.exception.TableNotFoundException;
import com.netflix.metacat.common.server.connectors.model.TableInfo;
import com.netflix.metacat.common.server.converter.ConverterUtil;
import com.netflix.metacat.common.server.events.MetacatCreateTablePostEvent;
import com.netflix.metacat.common.server.events.MetacatCreateTablePreEvent;
import com.netflix.metacat.common.server.events.MetacatDeleteTablePostEvent;
import com.netflix.metacat.common.server.events.MetacatDeleteTablePreEvent;
import com.netflix.metacat.common.server.events.MetacatEventBus;
import com.netflix.metacat.common.server.events.MetacatRenameTablePostEvent;
import com.netflix.metacat.common.server.events.MetacatRenameTablePreEvent;
import com.netflix.metacat.common.server.events.MetacatUpdateIcebergTablePostEvent;
import com.netflix.metacat.common.server.events.MetacatUpdateTablePostEvent;
import com.netflix.metacat.common.server.events.MetacatUpdateTablePreEvent;
import com.netflix.metacat.common.server.monitoring.Metrics;
import com.netflix.metacat.common.server.properties.Config;
import com.netflix.metacat.common.server.spi.MetacatCatalogConfig;
import com.netflix.metacat.common.server.usermetadata.AuthorizationService;
import com.netflix.metacat.common.server.usermetadata.GetMetadataInterceptorParameters;
import com.netflix.metacat.common.server.usermetadata.MetacatOperation;
import com.netflix.metacat.common.server.usermetadata.TagService;
import com.netflix.metacat.common.server.usermetadata.UserMetadataService;
import com.netflix.metacat.common.server.util.MetacatContextManager;
import com.netflix.metacat.common.server.util.MetacatUtils;
import com.netflix.metacat.main.manager.ConnectorManager;
import com.netflix.metacat.main.services.DatabaseService;
import com.netflix.metacat.main.services.GetTableNamesServiceParameters;
import com.netflix.metacat.main.services.GetTableServiceParameters;
import com.netflix.metacat.main.services.OwnerValidationService;
import com.netflix.metacat.main.services.TableService;
import com.netflix.spectator.api.Registry;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* Table service implementation.
*/
@Slf4j
@RequiredArgsConstructor
public class TableServiceImpl implements TableService {
private final ConnectorManager connectorManager;
private final ConnectorTableServiceProxy connectorTableServiceProxy;
private final DatabaseService databaseService;
private final TagService tagService;
private final UserMetadataService userMetadataService;
private final MetacatJson metacatJson;
private final MetacatEventBus eventBus;
private final Registry registry;
private final Config config;
private final ConverterUtil converterUtil;
private final AuthorizationService authorizationService;
private final OwnerValidationService ownerValidationService;
/**
* {@inheritDoc}
*/
@Override
public TableDto create(final QualifiedName name, final TableDto tableDto) {
final MetacatRequestContext metacatRequestContext = MetacatContextManager.getContext();
validate(name);
this.authorizationService.checkPermission(metacatRequestContext.getUserName(),
tableDto.getName(), MetacatOperation.CREATE);
setDefaultAttributes(tableDto);
ownerValidationService.enforceOwnerValidation("createTable", name, tableDto);
log.info("Creating table {}", name);
eventBus.post(new MetacatCreateTablePreEvent(name, metacatRequestContext, this, tableDto));
connectorTableServiceProxy.create(name, converterUtil.fromTableDto(tableDto));
if (tableDto.getDataMetadata() != null || tableDto.getDefinitionMetadata() != null) {
log.info("Saving user metadata for table {}", name);
final long start = registry.clock().wallTime();
userMetadataService.saveMetadata(metacatRequestContext.getUserName(), tableDto, true);
final long duration = registry.clock().wallTime() - start;
log.info("Time taken to save user metadata for table {} is {} ms", name, duration);
registry.timer(registry.createId(Metrics.TimerSaveTableMetadata.getMetricName()).withTags(name.parts()))
.record(duration, TimeUnit.MILLISECONDS);
tag(name, tableDto.getDefinitionMetadata());
}
TableDto dto = tableDto;
try {
dto = get(name, GetTableServiceParameters.builder()
.disableOnReadMetadataIntercetor(false)
.includeInfo(true)
.includeDataMetadata(true)
.includeDefinitionMetadata(true)
.build()).orElse(tableDto);
} catch (Exception e) {
handleExceptionOnCreate(name, "getTable", e);
}
try {
eventBus.post(new MetacatCreateTablePostEvent(name, metacatRequestContext, this, dto));
} catch (Exception e) {
handleExceptionOnCreate(name, "postEvent", e);
}
return dto;
}
private void setDefaultAttributes(final TableDto tableDto) {
setDefaultSerdeIfNull(tableDto);
setDefaultDefinitionMetadataIfNull(tableDto);
setOwnerIfNull(tableDto);
setOwnerGroupIfAvailable(tableDto);
}
private void setDefaultDefinitionMetadataIfNull(final TableDto tableDto) {
ObjectNode definitionMetadata = tableDto.getDefinitionMetadata();
if (definitionMetadata == null) {
definitionMetadata = metacatJson.emptyObjectNode();
tableDto.setDefinitionMetadata(definitionMetadata);
}
}
private void setDefaultSerdeIfNull(final TableDto tableDto) {
StorageDto serde = tableDto.getSerde();
if (serde == null) {
serde = new StorageDto();
tableDto.setSerde(serde);
}
}
/**
* Sets the owner of the table. The order of priority of selecting the owner is:
*
* 1. Explicitly set in the table dto
* 2. Username from the request headers
* 3. Owner set in the serde
*
*
* @param tableDto the table DTO
*/
private void setOwnerIfNull(final TableDto tableDto) {
final List potentialOwners = ownerValidationService.extractPotentialOwners(tableDto);
final String validOwner = potentialOwners.stream()
.filter(this::isOwnerValid)
.findFirst()
.orElse(null);
if (validOwner != null) {
updateTableOwner(tableDto, validOwner);
} else {
potentialOwners.stream()
.filter(Objects::nonNull)
.findFirst()
.ifPresent(nonNullOwner -> updateTableOwner(tableDto, nonNullOwner));
}
}
private void setOwnerGroupIfAvailable(final TableDto tableDto) {
final List potentialOwnerGroups = ownerValidationService.extractPotentialOwnerGroups(tableDto);
potentialOwnerGroups.stream()
.filter(this::isOwnerGroupValid)
.findFirst()
.ifPresent(validOwnerGroup -> updateTableOwnerGroup(tableDto, validOwnerGroup));
}
void updateTableOwner(final TableDto tableDto, final String userId) {
final ObjectNode ownerNode = tableDto.getDefinitionMetadata().with("owner");
ownerNode.put("userId", userId);
}
void updateTableOwnerGroup(final TableDto tableDto, final String groupName) {
final ObjectNode ownerNode = tableDto.getDefinitionMetadata().with("owner");
ownerNode.put("google_group", groupName);
}
private boolean isOwnerValid(@Nullable final String userId) {
return ownerValidationService.isUserValid(userId);
}
private boolean isOwnerGroupValid(@Nullable final String groupName) {
return ownerValidationService.isGroupValid(groupName);
}
@SuppressFBWarnings
private void tag(final QualifiedName name, final ObjectNode definitionMetadata) {
final Set tags = MetacatUtils.getTableTags(definitionMetadata);
if (!tags.isEmpty()) {
log.info("Setting tags {} for table {}", tags, name);
final Set result = tagService.setTags(name, tags, false);
}
}
/**
* {@inheritDoc}
*/
@Override
public TableDto deleteAndReturn(final QualifiedName name, final boolean isMView) {
final MetacatRequestContext metacatRequestContext = MetacatContextManager.getContext();
validate(name);
this.authorizationService.checkPermission(metacatRequestContext.getUserName(),
name, MetacatOperation.DELETE);
eventBus.post(new MetacatDeleteTablePreEvent(name, metacatRequestContext, this));
TableDto tableDto = new TableDto();
tableDto.setName(name);
try {
final Optional oTable = get(name,
GetTableServiceParameters.builder()
.includeInfo(true)
.disableOnReadMetadataIntercetor(false)
.includeDefinitionMetadata(true)
.includeDataMetadata(true)
.build());
tableDto = oTable.orElse(tableDto);
} catch (Exception e) {
handleException(name, true, "deleteAndReturn_get", e);
}
// Fail if the table is tagged not to be deleted.
if (hasTags(tableDto, config.getNoTableDeleteOnTags())) {
if (MetacatUtils.hasDoNotModifyForIcebergMigrationTag(tableDto, config.getNoTableDeleteOnTags())) {
throw new TableMigrationInProgressException(
MetacatUtils.getIcebergMigrationExceptionMsg("Delete", name.toString()));
} else {
throw new IllegalArgumentException(
String.format("Table %s cannot be deleted because it is tagged with %s.", name,
config.getNoTableDeleteOnTags()));
}
}
// Try to delete the table even if get above fails
try {
connectorTableServiceProxy.delete(name);
// If this is a common view, the storage_table if present
// should also be deleted.
if (MetacatUtils.isCommonView(tableDto.getMetadata())
&& config.deleteCommonViewStorageTable()) {
final Optional storageTableName = MetacatUtils
.getCommonViewStorageTable(tableDto.getMetadata());
if (storageTableName.isPresent()) {
final QualifiedName qualifiedStorageTableName = QualifiedName.ofTable(name.getCatalogName(),
name.getDatabaseName(), storageTableName.get());
deleteCommonViewStorageTable(name, qualifiedStorageTableName);
}
}
} catch (NotFoundException ignored) {
log.debug("NotFoundException ignored for table {}", name);
}
if (canDeleteMetadata(name)) {
// Delete the metadata. Type doesn't matter since we discard the result
log.info("Deleting user metadata for table {}", name);
userMetadataService.deleteMetadata(metacatRequestContext.getUserName(), Lists.newArrayList(tableDto));
log.info("Deleting tags for table {}", name);
tagService.delete(name, false);
} else {
if (config.canSoftDeleteDataMetadata() && tableDto.isDataExternal()) {
userMetadataService.softDeleteDataMetadata(metacatRequestContext.getUserName(),
Lists.newArrayList(tableDto.getDataUri()));
}
}
eventBus.post(new MetacatDeleteTablePostEvent(name, metacatRequestContext, this, tableDto, isMView));
return tableDto;
}
private boolean hasTags(@Nullable final TableDto tableDto, final Set hasTags) {
if (!hasTags.isEmpty() && tableDto != null) {
final Set tags = MetacatUtils.getTableTags(tableDto.getDefinitionMetadata());
if (!tags.isEmpty()) {
for (String t: hasTags) {
if (tags.contains(t)) {
return true;
}
}
}
}
return false;
}
/**
* Returns true
* 1. If the system is configured to delete deifnition metadata.
* 2. If the system is configured not to but the tableName is configured to either explicitly or if the
* table's database/catalog is configure to.
*
* @param tableName table name
* @return whether or not to delete definition metadata
*/
private boolean canDeleteMetadata(final QualifiedName tableName) {
return config.canDeleteTableDefinitionMetadata() || isEnabledForTableDefinitionMetadataDelete(tableName);
}
/**
* Returns true if tableName is enabled for deifnition metadata delete either explicitly or if the
* table's database/catalog is configure to.
*
* @param tableName table name
* @return whether or not to delete definition metadata
*/
private boolean isEnabledForTableDefinitionMetadataDelete(final QualifiedName tableName) {
final Set enableDeleteForQualifiedNames = config.getNamesEnabledForDefinitionMetadataDelete();
return enableDeleteForQualifiedNames.contains(tableName)
|| enableDeleteForQualifiedNames.contains(
QualifiedName.ofDatabase(tableName.getCatalogName(), tableName.getDatabaseName()))
|| enableDeleteForQualifiedNames.contains(QualifiedName.ofCatalog(tableName.getCatalogName()));
}
/**
* {@inheritDoc}
*/
@Override
public Optional get(final QualifiedName name, final GetTableServiceParameters getTableServiceParameters) {
validate(name);
TableDto tableInternal = null;
final TableDto table;
final MetacatCatalogConfig catalogConfig = connectorManager.getCatalogConfig(name);
if (getTableServiceParameters.isIncludeInfo()
|| (getTableServiceParameters.isIncludeDefinitionMetadata() && catalogConfig.isInterceptorEnabled()
&& !getTableServiceParameters.isDisableOnReadMetadataIntercetor())) {
try {
final boolean useCache = getTableServiceParameters.isUseCache() && config.isCacheEnabled()
&& catalogConfig.isCacheEnabled();
tableInternal = converterUtil.toTableDto(
getFromTableServiceProxy(name, getTableServiceParameters, useCache));
} catch (NotFoundException ignored) {
return Optional.empty();
}
table = tableInternal;
} else {
table = new TableDto();
table.setName(name);
}
if (getTableServiceParameters.isIncludeDefinitionMetadata()) {
final Optional definitionMetadata =
(getTableServiceParameters.isDisableOnReadMetadataIntercetor())
? userMetadataService.getDefinitionMetadata(name)
: userMetadataService.getDefinitionMetadataWithInterceptor(name,
GetMetadataInterceptorParameters.builder().hasMetadata(tableInternal).build());
definitionMetadata.ifPresent(table::setDefinitionMetadata);
}
if (getTableServiceParameters.isIncludeDataMetadata() && catalogConfig.isHasDataExternal()) {
TableDto dto = table;
if (tableInternal == null && !getTableServiceParameters.isIncludeInfo()) {
try {
final boolean useCache = getTableServiceParameters.isUseCache() && config.isCacheEnabled();
dto = converterUtil.toTableDto(
getFromTableServiceProxy(name, getTableServiceParameters, useCache));
} catch (NotFoundException ignored) {
}
}
if (dto != null && dto.getSerde() != null) {
final Optional dataMetadata =
userMetadataService.getDataMetadata(dto.getSerde().getUri());
dataMetadata.ifPresent(table::setDataMetadata);
}
}
return Optional.of(table);
}
/**
* {@inheritDoc}
*/
@Override
public void rename(
final QualifiedName oldName,
final QualifiedName newName,
final boolean isMView
) {
validate(oldName);
final MetacatRequestContext metacatRequestContext = MetacatContextManager.getContext();
this.authorizationService.checkPermission(metacatRequestContext.getUserName(),
oldName, MetacatOperation.RENAME);
final TableDto oldTable = get(oldName, GetTableServiceParameters.builder()
.includeInfo(true)
.disableOnReadMetadataIntercetor(false)
.includeDefinitionMetadata(true)
.includeDataMetadata(true)
.build()).orElseThrow(() -> new TableNotFoundException(oldName));
// Fail if the table is tagged not to be renamed.
if (hasTags(oldTable, config.getNoTableRenameOnTags())) {
if (MetacatUtils.hasDoNotModifyForIcebergMigrationTag(oldTable, config.getNoTableRenameOnTags())) {
throw new TableMigrationInProgressException(
MetacatUtils.getIcebergMigrationExceptionMsg("Rename", oldName.toString()));
} else {
throw new IllegalArgumentException(
String.format("Table %s cannot be renamed because it is tagged with %s.", oldName,
config.getNoTableRenameOnTags()));
}
}
if (oldTable != null) {
//Ignore if the operation is not supported, so that we can at least go ahead and save the user metadata
eventBus.post(new MetacatRenameTablePreEvent(oldName, metacatRequestContext, this, newName));
connectorTableServiceProxy.rename(oldName, newName, isMView);
userMetadataService.renameDefinitionMetadataKey(oldName, newName);
tagService.renameTableTags(oldName, newName.getTableName());
final TableDto dto = get(newName, GetTableServiceParameters.builder()
.includeInfo(true)
.disableOnReadMetadataIntercetor(false)
.includeDefinitionMetadata(true)
.includeDataMetadata(true)
.build()).orElseThrow(() -> new IllegalStateException("should exist"));
eventBus.post(
new MetacatRenameTablePostEvent(oldName, metacatRequestContext, this, oldTable, dto, isMView));
}
}
/**
* {@inheritDoc}
*/
@Override
public void update(final QualifiedName name, final TableDto tableDto) {
updateAndReturn(name, tableDto);
}
/**
* {@inheritDoc}
*/
@Override
public TableDto updateAndReturn(final QualifiedName name, final TableDto tableDto) {
validate(name);
final MetacatRequestContext metacatRequestContext = MetacatContextManager.getContext();
final TableDto oldTable = get(name, GetTableServiceParameters.builder()
.disableOnReadMetadataIntercetor(false)
.includeInfo(true)
.includeDataMetadata(true)
.includeDefinitionMetadata(true)
.build()).orElseThrow(() -> new TableNotFoundException(name));
eventBus.post(new MetacatUpdateTablePreEvent(name, metacatRequestContext, this, oldTable, tableDto));
if (MetacatUtils.hasDoNotModifyForIcebergMigrationTag(oldTable, config.getNoTableUpdateOnTags())) {
throw new TableMigrationInProgressException(
MetacatUtils.getIcebergMigrationExceptionMsg("Updates", name.toString()));
}
//
// Check if the table schema info is provided. If provided, we should continue calling the update on the table
// schema. Uri may exist in the serde when updating data metadata for a table.
//
boolean ignoreErrorsAfterUpdate = false;
if (isTableInfoProvided(tableDto, oldTable)) {
ignoreErrorsAfterUpdate = connectorTableServiceProxy.update(name, converterUtil.fromTableDto(tableDto));
}
// we do ownership validation and enforcement only if table owner is set in the dto
// because if it is null, we do not update the owner in the existing metadata record
if (tableDto.getTableOwner().isPresent()) {
// only if the owner is different from the previous, we run the enforcement
// for backwards compatibility
if (!tableDto.getTableOwner().get().equals(oldTable.getTableOwner().orElse(null))) {
ownerValidationService.enforceOwnerValidation("updateTable", name, tableDto);
}
}
try {
// Merge in metadata if the user sent any
if (tableDto.getDataMetadata() != null || tableDto.getDefinitionMetadata() != null) {
log.info("Saving user metadata for table {}", name);
final long start = registry.clock().wallTime();
userMetadataService.saveMetadata(metacatRequestContext.getUserName(), tableDto, true);
final long duration = registry.clock().wallTime() - start;
log.info("Time taken to save user metadata for table {} is {} ms", name, duration);
registry.timer(registry.createId(Metrics.TimerSaveTableMetadata.getMetricName()).withTags(name.parts()))
.record(duration, TimeUnit.MILLISECONDS);
}
} catch (Exception e) {
handleException(name, ignoreErrorsAfterUpdate, "saveMetadata", e);
}
// ignoreErrorsAfterUpdate is currently set only for iceberg tables
if (config.isUpdateIcebergTableAsyncPostEventEnabled() && ignoreErrorsAfterUpdate) {
eventBus.post(new MetacatUpdateIcebergTablePostEvent(name,
metacatRequestContext, this, oldTable, tableDto));
return tableDto;
} else {
TableDto updatedDto = tableDto;
try {
updatedDto = get(name,
GetTableServiceParameters.builder()
.disableOnReadMetadataIntercetor(false)
.includeInfo(true)
.includeDataMetadata(true)
.includeDefinitionMetadata(true)
.build()).orElse(tableDto);
} catch (Exception e) {
handleException(name, ignoreErrorsAfterUpdate, "getTable", e);
}
try {
eventBus.post(new MetacatUpdateTablePostEvent(name, metacatRequestContext, this, oldTable,
updatedDto, updatedDto != tableDto));
} catch (Exception e) {
handleException(name, ignoreErrorsAfterUpdate, "postEvent", e);
}
return updatedDto;
}
}
/**
* Throws exception if the provided ignoreErrorsAfterUpdate
is false. If true, it will swallow the
* exception and log it.
*
*/
private void handleException(final QualifiedName name,
final boolean ignoreErrorsAfterUpdate,
final String request,
final Exception ex) {
if (ignoreErrorsAfterUpdate) {
log.warn("Failed {} for table {}. Error: {}", request, name, ex.getMessage());
registry.counter(registry.createId(
Metrics.CounterTableUpdateIgnoredException.getMetricName()).withTags(name.parts())
.withTag("request", request)).increment();
} else {
throw Throwables.propagate(ex);
}
}
/**
* Swallow the exception and log it.
*
*/
private void handleExceptionOnCreate(final QualifiedName name,
final String request,
final Exception ex) {
log.warn("Failed {} for create table {}. Error: {}", request, name, ex.getMessage());
registry.counter(registry.createId(
Metrics.CounterTableCreateIgnoredException.getMetricName()).withTags(name.parts())
.withTag("request", request)).increment();
}
@VisibleForTesting
private boolean isTableInfoProvided(final TableDto tableDto, final TableDto oldTableDto) {
boolean result = false;
if ((tableDto.getFields() != null && !tableDto.getFields().isEmpty())
|| isSerdeInfoProvided(tableDto, oldTableDto)
|| (tableDto.getMetadata() != null && !tableDto.getMetadata().isEmpty())
|| tableDto.getAudit() != null) {
result = true;
}
return result;
}
private boolean isSerdeInfoProvided(final TableDto tableDto, final TableDto oldTableDto) {
boolean result = false;
final StorageDto serde = tableDto.getSerde();
if (serde == null) {
result = false;
} else {
final StorageDto oldSerde = oldTableDto.getSerde();
final String oldUri = oldSerde != null ? oldSerde.getUri() : null;
if (serde.getInputFormat() != null
|| serde.getOutputFormat() != null
|| serde.getOwner() != null
|| serde.getParameters() != null
|| serde.getSerdeInfoParameters() != null
|| serde.getSerializationLib() != null
|| (serde.getUri() != null && !Objects.equals(serde.getUri(), oldUri))) {
result = true;
}
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public void delete(final QualifiedName name) {
deleteAndReturn(name, false);
}
/**
* {@inheritDoc}
*/
@Override
public TableDto get(final QualifiedName name) {
//this is used for different purpose, need to change the ineral calls
final Optional dto = get(name, GetTableServiceParameters.builder()
.includeInfo(true)
.includeDefinitionMetadata(true)
.includeDataMetadata(true)
.disableOnReadMetadataIntercetor(false)
.build());
return dto.orElse(null);
}
/**
* {@inheritDoc}
*/
@Override
public TableDto copy(final QualifiedName sourceName, final QualifiedName targetName) {
// Source should be same
if (!sourceName.getCatalogName().equals(targetName.getCatalogName())) {
throw new MetacatNotSupportedException("Cannot copy a table from a different source");
}
// Error out when source table does not exists
final Optional oTable = get(sourceName,
GetTableServiceParameters.builder()
.includeInfo(true)
.disableOnReadMetadataIntercetor(true)
.includeDataMetadata(false)
.includeDefinitionMetadata(false)
.build());
if (!oTable.isPresent()) {
throw new TableNotFoundException(sourceName);
}
// Error out when target table already exists
final Optional oTargetTable = get(targetName,
GetTableServiceParameters.builder()
.disableOnReadMetadataIntercetor(true)
.includeInfo(true)
.includeDataMetadata(false)
.includeDefinitionMetadata(false)
.build());
if (oTargetTable.isPresent()) {
throw new TableNotFoundException(targetName);
}
return copy(oTable.get(), targetName);
}
/**
* {@inheritDoc}
*/
@Override
public TableDto copy(final TableDto tableDto, final QualifiedName targetName) {
final QualifiedName databaseName =
QualifiedName.ofDatabase(targetName.getCatalogName(), targetName.getDatabaseName());
if (!databaseService.exists(databaseName)) {
final DatabaseDto databaseDto = new DatabaseDto();
databaseDto.setName(databaseName);
databaseService.create(databaseName, databaseDto);
}
final TableDto targetTableDto = new TableDto();
targetTableDto.setName(targetName);
targetTableDto.setFields(tableDto.getFields());
targetTableDto.setPartition_keys(tableDto.getPartition_keys());
final StorageDto storageDto = tableDto.getSerde();
if (storageDto != null) {
final StorageDto targetStorageDto = new StorageDto();
targetStorageDto.setInputFormat(storageDto.getInputFormat());
targetStorageDto.setOwner(storageDto.getOwner());
targetStorageDto.setOutputFormat(storageDto.getOutputFormat());
targetStorageDto.setParameters(storageDto.getParameters());
targetStorageDto.setUri(storageDto.getUri());
targetStorageDto.setSerializationLib(storageDto.getSerializationLib());
targetTableDto.setSerde(targetStorageDto);
}
create(targetName, targetTableDto);
return targetTableDto;
}
/**
* {@inheritDoc}
*/
@Override
public void saveMetadata(final QualifiedName name, final ObjectNode definitionMetadata,
final ObjectNode dataMetadata) {
validate(name);
final Optional tableDtoOptional = get(name, GetTableServiceParameters.builder().includeInfo(true)
.disableOnReadMetadataIntercetor(true)
.includeDefinitionMetadata(false)
.includeDataMetadata(false)
.build());
if (tableDtoOptional.isPresent()) {
final MetacatRequestContext metacatRequestContext = MetacatContextManager.getContext();
final TableDto tableDto = tableDtoOptional.get();
tableDto.setDefinitionMetadata(definitionMetadata); //override the previous one
tableDto.setDataMetadata(dataMetadata);
log.info("Saving user metadata for table {}", name);
userMetadataService.saveMetadata(metacatRequestContext.getUserName(), tableDto, true);
tag(name, tableDto.getDefinitionMetadata());
}
}
/**
* {@inheritDoc}
*/
@Override
public List getQualifiedNames(final String uri, final boolean prefixSearch) {
return connectorTableServiceProxy.getQualifiedNames(uri, prefixSearch);
}
/**
* {@inheritDoc}
*/
@Override
public Map> getQualifiedNames(final List uris, final boolean prefixSearch) {
return connectorTableServiceProxy.getQualifiedNames(uris, prefixSearch);
}
@Override
public List getQualifiedNames(final QualifiedName name,
final GetTableNamesServiceParameters parameters) {
if (Strings.isNullOrEmpty(parameters.getFilter())) {
throw new MetacatBadRequestException("Filter expression cannot be empty");
}
return connectorTableServiceProxy.getQualifiedNames(name, parameters);
}
private TableInfo getFromTableServiceProxy(final QualifiedName name,
final GetTableServiceParameters getTableServiceParameters,
final boolean useCache) {
return getTableServiceParameters.isIncludeMetadataLocationOnly()
? connectorTableServiceProxy.getWithMetadataLocationOnly(name, getTableServiceParameters, useCache)
: (getTableServiceParameters.isIncludeMetadataFromConnector()
? connectorTableServiceProxy.getWithInfoDetails(name, getTableServiceParameters, useCache)
: connectorTableServiceProxy.get(name, getTableServiceParameters, useCache));
}
private void deleteCommonViewStorageTable(final QualifiedName viewName,
final QualifiedName storageTableName) {
try {
log.warn("Deleting storage table: {} belonging to common view: {}",
storageTableName, viewName);
deleteAndReturn(storageTableName, false);
} catch (Exception e) {
// For now only register failures to drop
handleException(storageTableName, true, "deleteCommonViewStorageTable", e);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean exists(final QualifiedName name) {
return connectorTableServiceProxy.exists(name);
}
private void validate(final QualifiedName name) {
Preconditions.checkNotNull(name, "name cannot be null");
Preconditions.checkArgument(name.isTableDefinition(), "Definition {} does not refer to a table", name);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy