blob: 0474a24cadee670137a95cb9ddd2822e975a3cc4 [file] [log] [blame]
/*
* 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.ignite.internal.catalog;
import static java.util.stream.Collectors.toList;
import static org.apache.ignite.internal.catalog.CatalogService.SYSTEM_SCHEMA_NAME;
import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
import static org.apache.ignite.internal.util.CompletableFutures.falseCompletedFuture;
import static org.apache.ignite.sql.ColumnType.INT32;
import static org.apache.ignite.sql.ColumnType.STRING;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import org.apache.ignite.internal.catalog.commands.ColumnParams;
import org.apache.ignite.internal.catalog.commands.CreateSystemViewCommand;
import org.apache.ignite.internal.catalog.commands.CreateSystemViewCommandBuilder;
import org.apache.ignite.internal.catalog.descriptors.CatalogObjectDescriptor;
import org.apache.ignite.internal.catalog.descriptors.CatalogObjectDescriptor.Type;
import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
import org.apache.ignite.internal.catalog.descriptors.CatalogSystemViewDescriptor;
import org.apache.ignite.internal.catalog.descriptors.CatalogSystemViewDescriptor.SystemViewType;
import org.apache.ignite.internal.catalog.descriptors.CatalogTableColumnDescriptor;
import org.apache.ignite.internal.catalog.events.CatalogEvent;
import org.apache.ignite.internal.catalog.events.CatalogEventParameters;
import org.apache.ignite.internal.event.EventListener;
import org.apache.ignite.sql.ColumnType;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.mockito.Mockito;
/**
* Tests to verify system view related commands.
*/
public class CatalogSystemViewTest extends BaseCatalogManagerTest {
protected static final String SYS_VIEW_NAME = "test_view";
@ParameterizedTest
@EnumSource(SystemViewType.class)
public void testCreateSystemView(SystemViewType type) {
CreateSystemViewCommand command = CreateSystemViewCommand.builder()
.name(SYS_VIEW_NAME)
.columns(List.of(
ColumnParams.builder().name("col1").type(INT32).build(),
ColumnParams.builder().name("col2").type(STRING).length(1 << 5).build()
))
.type(type)
.build();
assertThat(manager.execute(command), willCompleteSuccessfully());
int catalogVersion = manager.latestCatalogVersion();
CatalogSchemaDescriptor systemSchema = manager.schema(SYSTEM_SCHEMA_NAME, catalogVersion);
assertNotNull(systemSchema, "systemSchema");
CatalogSystemViewDescriptor view1 = systemSchema.systemView(SYS_VIEW_NAME);
assertNotNull(view1, "sys view");
assertEquals(3L, view1.id(), "system view id");
assertEquals(SYS_VIEW_NAME, view1.name());
assertEquals(Type.SYSTEM_VIEW, view1.type(), "type");
assertEquals(type, view1.systemViewType(), "system view type");
List<CatalogTableColumnDescriptor> columns = view1.columns();
assertEquals(2, columns.size(), "columns: " + columns);
CatalogTableColumnDescriptor col1 = columns.get(0);
assertEquals("col1", col1.name());
assertEquals(INT32, col1.type());
CatalogTableColumnDescriptor col2 = columns.get(1);
assertEquals("col2", col2.name());
assertEquals(STRING, col2.type());
}
@ParameterizedTest
@EnumSource(SystemViewType.class)
public void testCreateSystemViewUpdatesDescriptorToken(SystemViewType type) {
CreateSystemViewCommand command = CreateSystemViewCommand.builder()
.name(SYS_VIEW_NAME)
.columns(List.of(
ColumnParams.builder().name("col1").type(INT32).build(),
ColumnParams.builder().name("col2").type(STRING).length(1 << 5).build()
))
.type(type)
.build();
CatalogSchemaDescriptor schema = manager.activeSchema(clock.nowLong());
assertNotNull(schema);
assertEquals(1, schema.updateToken());
assertThat(manager.execute(command), willCompleteSuccessfully());
int catalogVersion = manager.latestCatalogVersion();
CatalogSchemaDescriptor systemSchema = manager.schema(SYSTEM_SCHEMA_NAME, catalogVersion);
assertNotNull(systemSchema, "systemSchema");
schema = manager.activeSchema(clock.nowLong());
assertNotNull(schema);
long schemaCausalityToken = schema.updateToken();
assertEquals(1, schemaCausalityToken);
// Assert that creation of the system view updates token for the descriptor.
assertTrue(systemSchema.updateToken() > schemaCausalityToken);
}
@ParameterizedTest
@EnumSource(SystemViewModification.class)
public void testCreateSystemViewReplacesExistingViewWhenViewChanges(SystemViewModification systemViewModification) {
CreateSystemViewCommandBuilder viewBuilder = SystemViewModification.newSystemView();
CreateSystemViewCommand initialCommand = viewBuilder.build();
assertThat(manager.execute(initialCommand), willCompleteSuccessfully());
CatalogSchemaDescriptor systemSchema = manager.schema(SYSTEM_SCHEMA_NAME, manager.latestCatalogVersion());
assertNotNull(systemSchema, "systemSchema");
List<CatalogSystemViewDescriptor> initialViews = Arrays.stream(systemSchema.systemViews())
.sorted(Comparator.comparing(CatalogObjectDescriptor::name)).collect(toList());
systemViewModification.apply(viewBuilder);
assertThat(manager.execute(viewBuilder.build()), willCompleteSuccessfully());
CatalogSchemaDescriptor mostRecentSchema = manager.schema(SYSTEM_SCHEMA_NAME, manager.latestCatalogVersion());
assertNotNull(mostRecentSchema, "systemSchema");
// Retrieve the most actual system views
List<CatalogSystemViewDescriptor> views = Arrays.stream(mostRecentSchema.systemViews())
.sorted(Comparator.comparing(CatalogObjectDescriptor::name)).collect(toList());
assertEquals(1, views.size());
// System view should have been updated.
CatalogSystemViewDescriptor view1 = views.get(0);
assertNotEquals(view1.id(), initialViews.get(0).id(), "view id didn't change");
}
/**
* System view modifications.
*/
public enum SystemViewModification {
CHANGE_TYPE,
ADD_COLUMN,
REMOVE_COLUMN,
MODIFY_COLUMN;
private static final List<ColumnParams> COLUMNS = List.of(
ColumnParams.builder().name("col1").type(INT32).build(),
ColumnParams.builder().name("col2").type(STRING).length(1 << 5).build()
);
static CreateSystemViewCommandBuilder newSystemView() {
return CreateSystemViewCommand.builder()
.name(SYS_VIEW_NAME)
.columns(COLUMNS)
.type(SystemViewType.NODE);
}
void apply(CreateSystemViewCommandBuilder builder) {
switch (this) {
case CHANGE_TYPE: {
builder.type(SystemViewType.CLUSTER);
break;
}
case ADD_COLUMN: {
ColumnParams column = ColumnParams.builder()
.name("col-x")
.type(ColumnType.BYTE_ARRAY)
.length(1 << 5)
.build();
List<ColumnParams> columns = new ArrayList<>(COLUMNS);
columns.add(column);
builder.columns(columns);
break;
}
case REMOVE_COLUMN: {
List<ColumnParams> columns = new ArrayList<>(COLUMNS);
columns.remove(0);
builder.columns(columns);
break;
}
case MODIFY_COLUMN: {
ColumnParams column = ColumnParams.builder()
.name(COLUMNS.get(0).name())
.type(ColumnType.BYTE_ARRAY)
.length(1 << 5)
.build();
List<ColumnParams> columns = new ArrayList<>(COLUMNS);
columns.set(0, column);
builder.columns(columns);
break;
}
default:
throw new IllegalArgumentException("Unexpected modification type: " + this);
}
}
}
@ParameterizedTest
@EnumSource(SystemViewType.class)
public void testCreateSystemViewDoesNotReplaceExistingViewWithTheSameNameIfItsStructureIsTheSame(SystemViewType type) {
EventListener<CatalogEventParameters> eventListener = mock(EventListener.class);
when(eventListener.notify(any())).thenReturn(falseCompletedFuture());
manager.listen(CatalogEvent.SYSTEM_VIEW_CREATE, eventListener);
CreateSystemViewCommand command = CreateSystemViewCommand.builder()
.name(SYS_VIEW_NAME)
.columns(List.of(
ColumnParams.builder().name("col1").type(INT32).build(),
ColumnParams.builder().name("col2").type(STRING).length(1 << 5).build()
))
.type(type)
.build();
assertThat(manager.execute(command), willCompleteSuccessfully());
verify(eventListener, times(1)).notify(any());
Mockito.reset(eventListener);
// Create view
int catalogVersion = manager.latestCatalogVersion();
CatalogSchemaDescriptor systemSchema1 = manager.schema(SYSTEM_SCHEMA_NAME, catalogVersion);
assertNotNull(systemSchema1, "systemSchema");
CatalogSystemViewDescriptor view1 = systemSchema1.systemView(SYS_VIEW_NAME);
// Use the same command to create an identical view.
assertThat(manager.execute(command), willCompleteSuccessfully());
CatalogSchemaDescriptor systemSchema2 = manager.schema(SYSTEM_SCHEMA_NAME, catalogVersion);
assertNotNull(systemSchema2, "systemSchema");
// view1 should be the same.
CatalogSystemViewDescriptor view2 = systemSchema2.systemView(SYS_VIEW_NAME);
assertSame(view1, view2, "system view was replaced");
// Event listener should not have been called.
verify(eventListener, never()).notify(any());
}
}