Skip to content

Commit

Permalink
Use MaterializedViewPropertyManager in CreateMaterializedViewTask
Browse files Browse the repository at this point in the history
  • Loading branch information
raunaqmorarka authored and sopel39 committed Apr 20, 2021
1 parent 1cb9b19 commit 3504fcc
Show file tree
Hide file tree
Showing 4 changed files with 325 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public ListenableFuture<?> execute(
.orElseThrow(() -> new TrinoException(NOT_FOUND, "Catalog does not exist: " + name.getCatalogName()));

Map<String, Expression> sqlProperties = mapFromProperties(statement.getProperties());
Map<String, Object> properties = metadata.getTablePropertyManager().getProperties(
Map<String, Object> properties = metadata.getMaterializedViewPropertyManager().getProperties(
catalogName,
name.getCatalogName(),
sqlProperties,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@

/*
* 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 io.trino.execution;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.trino.Session;
import io.trino.connector.CatalogName;
import io.trino.cost.StatsCalculator;
import io.trino.execution.warnings.WarningCollector;
import io.trino.metadata.AbstractMockMetadata;
import io.trino.metadata.Catalog;
import io.trino.metadata.CatalogManager;
import io.trino.metadata.MaterializedViewPropertyManager;
import io.trino.metadata.MetadataManager;
import io.trino.metadata.QualifiedObjectName;
import io.trino.metadata.TableHandle;
import io.trino.metadata.TableMetadata;
import io.trino.metadata.TablePropertyManager;
import io.trino.metadata.TableSchema;
import io.trino.security.AccessControl;
import io.trino.security.AllowAllAccessControl;
import io.trino.spi.TrinoException;
import io.trino.spi.connector.ColumnHandle;
import io.trino.spi.connector.ColumnMetadata;
import io.trino.spi.connector.ConnectorMaterializedViewDefinition;
import io.trino.spi.connector.ConnectorTableMetadata;
import io.trino.spi.connector.ConnectorViewDefinition;
import io.trino.spi.connector.SchemaTableName;
import io.trino.spi.connector.TestingColumnHandle;
import io.trino.spi.resourcegroups.ResourceGroupId;
import io.trino.sql.parser.SqlParser;
import io.trino.sql.planner.TestingConnectorTransactionHandle;
import io.trino.sql.planner.TypeAnalyzer;
import io.trino.sql.tree.AllColumns;
import io.trino.sql.tree.CreateMaterializedView;
import io.trino.sql.tree.Identifier;
import io.trino.sql.tree.Property;
import io.trino.sql.tree.QualifiedName;
import io.trino.sql.tree.StringLiteral;
import io.trino.testing.TestingGroupProvider;
import io.trino.testing.TestingMetadata.TestingTableHandle;
import io.trino.transaction.TransactionManager;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
import static io.airlift.concurrent.MoreFutures.getFutureValue;
import static io.trino.cost.StatsCalculatorModule.createNewStatsCalculator;
import static io.trino.metadata.MetadataManager.createTestMetadataManager;
import static io.trino.spi.StandardErrorCode.ALREADY_EXISTS;
import static io.trino.spi.StandardErrorCode.INVALID_MATERIALIZED_VIEW_PROPERTY;
import static io.trino.spi.session.PropertyMetadata.stringProperty;
import static io.trino.spi.type.BigintType.BIGINT;
import static io.trino.spi.type.SmallintType.SMALLINT;
import static io.trino.sql.QueryUtil.selectList;
import static io.trino.sql.QueryUtil.simpleQuery;
import static io.trino.sql.QueryUtil.table;
import static io.trino.testing.TestingSession.createBogusTestingCatalog;
import static io.trino.testing.TestingSession.testSessionBuilder;
import static io.trino.testing.assertions.TrinoExceptionAssert.assertTrinoExceptionThrownBy;
import static io.trino.transaction.InMemoryTransactionManager.createTestTransactionManager;
import static java.util.Objects.requireNonNull;
import static org.testng.Assert.assertEquals;

@Test(singleThreaded = true)
public class TestCreateMaterializedViewTask
{
private static final String CATALOG_NAME = "catalog";
private static final ConnectorTableMetadata MOCK_TABLE = new ConnectorTableMetadata(
new SchemaTableName("schema", "mock_table"),
List.of(new ColumnMetadata("a", SMALLINT), new ColumnMetadata("b", BIGINT)),
ImmutableMap.of("baz", "property_value"));

private Session testSession;
private MockMetadata metadata;
private TransactionManager transactionManager;
private SqlParser parser;
private StatsCalculator statsCalculator;
private QueryStateMachine queryStateMachine;

@BeforeMethod
public void setUp()
{
CatalogManager catalogManager = new CatalogManager();
transactionManager = createTestTransactionManager(catalogManager);
TablePropertyManager tablePropertyManager = new TablePropertyManager();
MaterializedViewPropertyManager materializedViewPropertyManager = new MaterializedViewPropertyManager();
Catalog testCatalog = createBogusTestingCatalog(CATALOG_NAME);
catalogManager.registerCatalog(testCatalog);
tablePropertyManager.addProperties(
testCatalog.getConnectorCatalogName(),
ImmutableList.of(stringProperty("baz", "test property", null, false)));
materializedViewPropertyManager.addProperties(
testCatalog.getConnectorCatalogName(),
ImmutableList.of(stringProperty("foo", "test materialized view property", null, false)));
testSession = testSessionBuilder()
.setTransactionId(transactionManager.beginTransaction(false))
.build();
metadata = new MockMetadata(
tablePropertyManager,
materializedViewPropertyManager,
testCatalog.getConnectorCatalogName());
parser = new SqlParser();
statsCalculator = createNewStatsCalculator(metadata, new TypeAnalyzer(parser, metadata));
queryStateMachine = stateMachine(transactionManager, createTestMetadataManager(), new AllowAllAccessControl());
}

@Test
public void testCreateMaterializedViewIfNotExists()
{
CreateMaterializedView statement = new CreateMaterializedView(
Optional.empty(),
QualifiedName.of("test_mv"),
simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("catalog", "schema", "mock_table"))),
false,
true,
ImmutableList.of(),
Optional.empty());

getFutureValue(new CreateMaterializedViewTask(parser, new TestingGroupProvider(), statsCalculator)
.execute(statement, transactionManager, metadata, new AllowAllAccessControl(), queryStateMachine, ImmutableList.of(), WarningCollector.NOOP));
assertEquals(metadata.getCreateMaterializedViewCallCount(), 1);
}

@Test
public void testCreateMaterializedViewWithExistingView()
{
CreateMaterializedView statement = new CreateMaterializedView(
Optional.empty(),
QualifiedName.of("test_mv"),
simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("catalog", "schema", "mock_table"))),
false,
false,
ImmutableList.of(),
Optional.empty());

assertTrinoExceptionThrownBy(() -> getFutureValue(new CreateMaterializedViewTask(parser, new TestingGroupProvider(), statsCalculator)
.execute(statement, transactionManager, metadata, new AllowAllAccessControl(), queryStateMachine, ImmutableList.of(), WarningCollector.NOOP)))
.hasErrorCode(ALREADY_EXISTS)
.hasMessage("Materialized view already exists");

assertEquals(metadata.getCreateMaterializedViewCallCount(), 1);
}

@Test
public void testCreateMaterializedViewWithInvalidProperty()
{
CreateMaterializedView statement = new CreateMaterializedView(
Optional.empty(),
QualifiedName.of("test_mv"),
simpleQuery(selectList(new AllColumns()), table(QualifiedName.of("catalog", "schema", "mock_table"))),
false,
true,
ImmutableList.of(new Property(new Identifier("baz"), new StringLiteral("abc"))),
Optional.empty());

assertTrinoExceptionThrownBy(() -> getFutureValue(new CreateMaterializedViewTask(parser, new TestingGroupProvider(), statsCalculator)
.execute(statement, transactionManager, metadata, new AllowAllAccessControl(), queryStateMachine, ImmutableList.of(), WarningCollector.NOOP)))
.hasErrorCode(INVALID_MATERIALIZED_VIEW_PROPERTY)
.hasMessage("Catalog 'catalog' does not support materialized view property 'baz'");

assertEquals(metadata.getCreateMaterializedViewCallCount(), 0);
}

private QueryStateMachine stateMachine(TransactionManager transactionManager, MetadataManager metadata, AccessControl accessControl)
{
return QueryStateMachine.begin(
"test",
Optional.empty(),
testSession,
URI.create("fake://uri"),
new ResourceGroupId("test"),
false,
transactionManager,
accessControl,
directExecutor(),
metadata,
WarningCollector.NOOP,
Optional.empty());
}

private static class MockMetadata
extends AbstractMockMetadata
{
private final TablePropertyManager tablePropertyManager;
private final MaterializedViewPropertyManager materializedViewPropertyManager;
private final CatalogName catalogHandle;
private final Map<SchemaTableName, ConnectorMaterializedViewDefinition> materializedViews = new ConcurrentHashMap<>();

public MockMetadata(
TablePropertyManager tablePropertyManager,
MaterializedViewPropertyManager materializedViewPropertyManager,
CatalogName catalogHandle)
{
this.tablePropertyManager = requireNonNull(tablePropertyManager, "tablePropertyManager is null");
this.materializedViewPropertyManager = requireNonNull(materializedViewPropertyManager, "materializedViewPropertyManager is null");
this.catalogHandle = requireNonNull(catalogHandle, "catalogHandle is null");
}

@Override
public void createMaterializedView(Session session, QualifiedObjectName viewName, ConnectorMaterializedViewDefinition definition, boolean replace, boolean ignoreExisting)
{
materializedViews.put(viewName.asSchemaTableName(), definition);
if (!ignoreExisting) {
throw new TrinoException(ALREADY_EXISTS, "Materialized view already exists");
}
}

@Override
public TablePropertyManager getTablePropertyManager()
{
return tablePropertyManager;
}

@Override
public MaterializedViewPropertyManager getMaterializedViewPropertyManager()
{
return materializedViewPropertyManager;
}

@Override
public Optional<CatalogName> getCatalogHandle(Session session, String catalogName)
{
if (catalogHandle.getCatalogName().equals(catalogName)) {
return Optional.of(catalogHandle);
}
return Optional.empty();
}

@Override
public TableSchema getTableSchema(Session session, TableHandle tableHandle)
{
return new TableSchema(tableHandle.getCatalogName(), MOCK_TABLE.getTableSchema());
}

@Override
public Optional<TableHandle> getTableHandle(Session session, QualifiedObjectName tableName)
{
if (tableName.asSchemaTableName().equals(MOCK_TABLE.getTable())) {
return Optional.of(
new TableHandle(
new CatalogName(CATALOG_NAME),
new TestingTableHandle(tableName.asSchemaTableName()),
TestingConnectorTransactionHandle.INSTANCE,
Optional.empty()));
}
return Optional.empty();
}

@Override
public Map<String, ColumnHandle> getColumnHandles(Session session, TableHandle tableHandle)
{
return MOCK_TABLE.getColumns().stream()
.collect(toImmutableMap(
ColumnMetadata::getName,
column -> new TestingColumnHandle(column.getName())));
}

@Override
public TableMetadata getTableMetadata(Session session, TableHandle tableHandle)
{
if ((tableHandle.getConnectorHandle() instanceof TestingTableHandle)) {
if (((TestingTableHandle) tableHandle.getConnectorHandle()).getTableName().equals(MOCK_TABLE.getTable())) {
return new TableMetadata(new CatalogName("catalog"), MOCK_TABLE);
}
}

return super.getTableMetadata(session, tableHandle);
}

@Override
public Optional<ConnectorMaterializedViewDefinition> getMaterializedView(Session session, QualifiedObjectName viewName)
{
return Optional.ofNullable(materializedViews.get(viewName.asSchemaTableName()));
}

@Override
public Optional<ConnectorViewDefinition> getView(Session session, QualifiedObjectName viewName)
{
return Optional.empty();
}

public int getCreateMaterializedViewCallCount()
{
return materializedViews.size();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ public List<PropertyMetadata<?>> getTableProperties()
return tableProperties;
}

@Override
public List<PropertyMetadata<?>> getMaterializedViewProperties()
{
return tableProperties;
}

@Override
public ConnectorAccessControl getAccessControl()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import static io.trino.testing.assertions.Assert.assertEquals;
import static io.trino.testing.assertions.Assert.assertFalse;
import static io.trino.testing.assertions.Assert.assertTrue;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class TestIcebergMaterializedViews
extends AbstractTestQueryFramework
Expand All @@ -52,6 +53,15 @@ public void setUp()
assertQuery("SELECT count(*) FROM base_table2", "VALUES 3");
}

@Test
public void testCreateWithInvalidPropertyFails()
{
assertThatThrownBy(() -> computeActual("CREATE MATERIALIZED VIEW materialized_view_with_property " +
"WITH (invalid_property = ARRAY['_date']) AS " +
"SELECT _bigint, _date FROM base_table1"))
.hasMessage("Catalog 'iceberg' does not support materialized view property 'invalid_property'");
}

@Test(enabled = false) // TODO https://github.com/trinodb/trino/issues/5892
public void testCreateRefreshSelect()
{
Expand Down

0 comments on commit 3504fcc

Please sign in to comment.