All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.metacat.main.services.impl.TableServiceImpl Maven / Gradle / Ivy

There is a newer version: 1.3.1
Show newest version
/*
 * 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.facebook.presto.Session;
import com.facebook.presto.metadata.QualifiedTableName;
import com.facebook.presto.metadata.TableHandle;
import com.facebook.presto.metadata.TableMetadata;
import com.facebook.presto.spi.ConnectorTableDetailMetadata;
import com.facebook.presto.spi.ConnectorTableMetadata;
import com.facebook.presto.spi.NotFoundException;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.SchemaTableName;
import com.facebook.presto.spi.StorageInfo;
import com.facebook.presto.spi.TableNotFoundException;
import com.facebook.presto.spi.type.TypeManager;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.netflix.metacat.common.NameDateDto;
import com.netflix.metacat.common.QualifiedName;
import com.netflix.metacat.common.dto.StorageDto;
import com.netflix.metacat.common.dto.TableDto;
import com.netflix.metacat.common.exception.MetacatNotSupportedException;
import com.netflix.metacat.common.usermetadata.TagService;
import com.netflix.metacat.common.usermetadata.UserMetadataService;
import com.netflix.metacat.converters.PrestoConverters;
import com.netflix.metacat.main.connector.MetacatConnectorManager;
import com.netflix.metacat.main.presto.metadata.MetadataManager;
import com.netflix.metacat.main.services.DatabaseService;
import com.netflix.metacat.main.services.MViewService;
import com.netflix.metacat.main.services.SessionProvider;
import com.netflix.metacat.main.services.TableService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import static com.facebook.presto.spi.StandardErrorCode.NOT_SUPPORTED;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

public class TableServiceImpl implements TableService {
    private static final Logger log = LoggerFactory.getLogger(TableServiceImpl.class);
    @Inject
    MetacatConnectorManager metacatConnectorManager;
    @Inject
    MetadataManager metadataManager;
    @Inject
    PrestoConverters prestoConverters;
    @Inject
    SessionProvider sessionProvider;
    @Inject
    UserMetadataService userMetadataService;
    @Inject
    DatabaseService databaseService;
    @Inject
    TagService tagService;
    @Inject
    MViewService mViewService;
    private static final String NAME_TAGS = "tags";

    @Override
    public void create(@Nonnull QualifiedName name, @Nonnull TableDto tableDto) {
        Session session = validateAndGetSession(name);
        //
        // Set the owner,if null, with the session user name.
        //
        setOwnerIfNull(tableDto, session.getUser());
        log.info("Creating table {}", name);
        metadataManager.createTable(
                session,
                name.getCatalogName(),
                prestoConverters.fromTableDto(name, tableDto, metadataManager.getTypeManager())
        );

        if (tableDto.getDataMetadata() != null || tableDto.getDefinitionMetadata() != null) {
            log.info("Saving user metadata for table {}", name);
            userMetadataService.saveMetadata(session.getUser(), tableDto, false);
            tag( name, tableDto.getDefinitionMetadata());
        }
    }

    private void setOwnerIfNull(TableDto tableDto, String user) {
        if(!Strings.isNullOrEmpty(user)) {
            StorageDto serde = tableDto.getSerde();
            if (serde == null) {
                serde = new StorageDto();
                tableDto.setSerde(serde);
            }
            if (Strings.isNullOrEmpty(serde.getOwner())) {
                serde.setOwner(user);
            }
        }
    }

    private void tag(QualifiedName name, ObjectNode definitionMetadata) {
        if( definitionMetadata != null && definitionMetadata.get(NAME_TAGS) != null){
            JsonNode tagsNode = definitionMetadata.get(NAME_TAGS);
            Set tags = Sets.newHashSet();
            if( tagsNode.isArray() && tagsNode.size() > 0){
                for(JsonNode tagNode: tagsNode){
                    tags.add( tagNode.textValue());
                }
                log.info("Setting tags {} for table {}", tags, name);
                tagService.setTableTags( name, tags, false);
            }
        }
    }

    @Override
    public TableDto deleteAndReturn(@Nonnull QualifiedName name) {
        Session session = validateAndGetSession(name);
        QualifiedTableName tableName = prestoConverters.getQualifiedTableName(name);

        Optional tableHandle = metadataManager.getTableHandle(session, tableName);
        Optional oTable = get(name, true);
        if (oTable.isPresent()) {
            log.info("Drop table {}", name);
            metadataManager.dropTable(session, tableHandle.get());
        }

        TableDto tableDto = oTable.orElseGet(() -> {
            // If the table doesn't exist construct a blank copy we can use to delete the definitionMetadata and tags
            TableDto t = new TableDto();
            t.setName(name);
            return t;
        });

        // Delete the metadata.  Type doesn't matter since we discard the result
        log.info("Deleting user metadata for table {}", name);
        userMetadataService.deleteMetadatas(Lists.newArrayList(tableDto), false);
        log.info("Deleting tags for table {}", name);
        tagService.delete(name, false);

        return tableDto;
    }

    @Override
    public Optional get(@Nonnull QualifiedName name, boolean includeUserMetadata) {
        return get( name, true, includeUserMetadata, includeUserMetadata);
    }

    @Override
    public Optional get(@Nonnull QualifiedName name, boolean includeInfo, boolean includeDefinitionMetadata, boolean includeDataMetadata) {
        Session session = validateAndGetSession(name);
        TableDto table;
        Optional tableMetadata = Optional.empty();
        if (includeInfo) {
            try {
                tableMetadata = Optional.ofNullable(getTableMetadata(name, session));
            } catch (NotFoundException ignored) {
                return Optional.empty();
            }
            if (!tableMetadata.isPresent()) {
                return Optional.empty();
            }
            String type = metacatConnectorManager.getCatalogConfig(name).getType();
            table = prestoConverters.toTableDto(name, type, tableMetadata.get());
        } else {
            table = new TableDto();
            table.setName(name);
        }

        if (includeDefinitionMetadata) {
            Optional definitionMetadata = userMetadataService.getDefinitionMetadata(name);
            if (definitionMetadata.isPresent()) {
                table.setDefinitionMetadata(definitionMetadata.get());
            }
        }

        if (includeDataMetadata) {
            if (!tableMetadata.isPresent()) {
                tableMetadata = Optional.ofNullable(getTableMetadata(name, session));
            }
            if (tableMetadata.isPresent()) {
                ConnectorTableMetadata connectorTableMetadata = tableMetadata.get().getMetadata();
                if (connectorTableMetadata instanceof ConnectorTableDetailMetadata) {
                    ConnectorTableDetailMetadata detailMetadata = (ConnectorTableDetailMetadata) connectorTableMetadata;
                    StorageInfo storageInfo = detailMetadata.getStorageInfo();
                    if (storageInfo != null) {
                        Optional dataMetadata = userMetadataService.getDataMetadata(storageInfo.getUri());
                        if (dataMetadata.isPresent()) {
                            table.setDataMetadata(dataMetadata.get());
                        }
                    }
                }
            }
        }

        return Optional.of(table);
    }

    @Override
    public Optional getTableHandle(@Nonnull QualifiedName name) {
        Session session = validateAndGetSession(name);

        QualifiedTableName qualifiedTableName = prestoConverters.getQualifiedTableName(name);
        return metadataManager.getTableHandle(session, qualifiedTableName);
    }

    private TableMetadata getTableMetadata(QualifiedName name, Optional tableHandle) {
        if (!tableHandle.isPresent()) {
            return null;
        }
        Session session = validateAndGetSession(name);
        TableMetadata result = metadataManager.getTableMetadata(session, tableHandle.get());
        checkState(name.getDatabaseName().equals(result.getTable().getSchemaName()), "Unexpected database");
        checkState(name.getTableName().equals(result.getTable().getTableName()), "Unexpected table");

        return result;
    }

    private TableMetadata getTableMetadata(QualifiedName name, Session session) {
        QualifiedTableName qualifiedTableName = prestoConverters.getQualifiedTableName(name);
        Optional tableHandle = metadataManager.getTableHandle(session, qualifiedTableName);
        return getTableMetadata(name, tableHandle);
    }

    @Override
    public void rename(@Nonnull QualifiedName oldName, @Nonnull QualifiedName newName, boolean isMView) {
        Session session = validateAndGetSession(oldName);

        QualifiedTableName oldPrestoName = prestoConverters.getQualifiedTableName(oldName);
        QualifiedTableName newPrestoName = prestoConverters.getQualifiedTableName(newName);

        Optional tableHandle = metadataManager.getTableHandle(session, oldPrestoName);
        if (tableHandle.isPresent()) {
            //Ignore if the operation is not supported, so that we can at least go ahead and save the user metadata
            try {
                log.info("Renaming {} {} to {}", isMView?"view":"table", oldName, newName);
                metadataManager.renameTable(session, tableHandle.get(), newPrestoName);

                if( !isMView) {
                    final String prefix = String.format("%s_%s_", oldName.getDatabaseName(),
                            MoreObjects.firstNonNull(oldName.getTableName(), ""));
                    List views = mViewService.list(oldName);
                    if (views != null && !views.isEmpty()) {
                        views.forEach(view -> {
                            QualifiedName newViewName = QualifiedName.ofView(oldName.getCatalogName(), oldName.getDatabaseName(), newName.getTableName(), view.getName().getViewName());
                            mViewService.rename(view.getName(), newViewName);
                        });
                    }
                }
            } catch(PrestoException e){
                if(!NOT_SUPPORTED.toErrorCode().equals(e.getErrorCode())){
                    throw e;
                }
            }
            userMetadataService.renameDefinitionMetadataKey(oldName, newName);
            tagService.rename(oldName, newName.getTableName());
        }
    }

    @Override
    public void update(@Nonnull QualifiedName name, @Nonnull TableDto tableDto) {
        Session session = validateAndGetSession(name);

        //Ignore if the operation is not supported, so that we can at least go ahead and save the user metadata
        try {
            log.info("Updating table {}", name);
            metadataManager.alterTable(session, prestoConverters.fromTableDto(name, tableDto, metadataManager.getTypeManager()));
        } catch(PrestoException e){
            if(!NOT_SUPPORTED.toErrorCode().equals(e.getErrorCode())){
                throw e;
            }
        }

        // Merge in metadata if the user sent any
        if (tableDto.getDataMetadata() != null || tableDto.getDefinitionMetadata() != null) {
            log.info("Saving user metadata for table {}", name);
            userMetadataService.saveMetadata(session.getUser(), tableDto, true);
        }
    }

    @Override
    public void delete(@Nonnull QualifiedName name) {
        deleteAndReturn(name);
    }

    @Override
    public TableDto get(@Nonnull QualifiedName name) {
        Optional dto = get( name, true);
        return dto.orElse(null);
    }

    @Override
    public TableDto copy( @Nonnull QualifiedName sourceName, @Nonnull 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
        Optional oTable = get(sourceName, false);
        if( !oTable.isPresent()){
            throw new TableNotFoundException(new SchemaTableName(sourceName.getDatabaseName(), sourceName.getTableName()));
        }
        // Error out when target table already exists
        Optional oTargetTable = get( targetName, false);
        if( oTargetTable.isPresent()){
            throw new TableNotFoundException(new SchemaTableName(targetName.getDatabaseName(), targetName.getTableName()));
        }
        return copy(oTable.get(), targetName);
    }

    @Override
    public TableDto copy(@Nonnull TableDto tableDto, @Nonnull QualifiedName targetName) {
        if( !databaseService.exists( targetName)){
            databaseService.create( targetName, null);
        }
        TableDto targetTableDto = new TableDto();
        targetTableDto.setName( targetName);
        targetTableDto.setFields(tableDto.getFields());
        targetTableDto.setPartition_keys( tableDto.getPartition_keys());
        StorageDto storageDto = tableDto.getSerde();
        if( storageDto != null) {
            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;
    }

    @Override
    public void saveMetadata(
            @Nonnull
            QualifiedName name, ObjectNode definitionMetadata, ObjectNode dataMetadata) {
        Session session = validateAndGetSession(name);
        Optional tableDtoOptional = get( name, false);
        if( tableDtoOptional.isPresent()){
            TableDto tableDto = tableDtoOptional.get();
            tableDto.setDefinitionMetadata( definitionMetadata);
            tableDto.setDataMetadata( dataMetadata);
            log.info("Saving user metadata for table {}", name);
            userMetadataService.saveMetadata( session.getUser(), tableDto, true);
            tag( name, tableDto.getDefinitionMetadata());
        }
    }

    @Override
    public List getQualifiedNames(String uri, boolean prefixSearch){
        List result = Lists.newArrayList();
        Map catalogNames = metadataManager.getCatalogNames();

        catalogNames.values().stream().forEach(catalogName -> {
            Session session = sessionProvider.getSession(QualifiedName.ofCatalog(catalogName));
            List schemaTableNames = metadataManager.getTableNames( session, uri, prefixSearch);
            List qualifiedNames = schemaTableNames.stream().map(
                    schemaTableName -> QualifiedName.ofTable( catalogName, schemaTableName.getSchemaName(), schemaTableName.getTableName())).collect(Collectors.toList());
            result.addAll(qualifiedNames);
        });
        return result;
    }

    @Override
    public boolean exists(@Nonnull QualifiedName name) {
        return get(name, true, false, false).isPresent();
    }

    private Session validateAndGetSession(QualifiedName name) {
        checkNotNull(name, "name cannot be null");
        checkArgument(name.isTableDefinition(), "Definition {} does not refer to a table", name);

        return sessionProvider.getSession(name);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy