This is an automated email from the ASF dual-hosted git repository.

ppa pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new ff52ee946b IGNITE-24025 Introduced catalog command to drop schema 
(#4972)
ff52ee946b is described below

commit ff52ee946b374241e638ab93f3e301cdea4a375e
Author: Pavel Pereslegin <[email protected]>
AuthorDate: Mon Dec 30 13:13:49 2024 +0300

    IGNITE-24025 Introduced catalog command to drop schema (#4972)
---
 .../ignite/internal/catalog/CatalogService.java    |  12 ++
 .../internal/catalog/commands/CatalogUtils.java    |   8 +-
 .../catalog/commands/DropSchemaCommand.java        | 116 +++++++++++++++++++
 .../catalog/commands/DropSchemaCommandBuilder.java |  34 ++++++
 .../descriptors/CatalogSchemaDescriptor.java       |   5 +
 .../internal/catalog/storage/DropSchemaEntry.java  |  91 +++++++++++++++
 .../CatalogEntrySerializerProvider.java            |   2 +
 .../serialization/MarshallableEntryType.java       |   3 +-
 .../ignite/internal/catalog/CatalogIndexTest.java  |   8 +-
 .../ignite/internal/catalog/CatalogSchemaTest.java | 125 +++++++++++++++++++--
 .../catalog/CatalogSchemaValidationTest.java       |  98 ++++++++++++++++
 .../storage/CatalogEntrySerializationTest.java     |   4 +
 .../internal/catalog/BaseCatalogManagerTest.java   |  23 +++-
 .../internal/sql/engine/ItCreateTableDdlTest.java  |  11 +-
 14 files changed, 521 insertions(+), 19 deletions(-)

diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogService.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogService.java
index ecdb478974..4e91a28f04 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogService.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/CatalogService.java
@@ -48,6 +48,18 @@ public interface CatalogService extends 
EventProducer<CatalogEvent, CatalogEvent
     /** System schema name. */
     String SYSTEM_SCHEMA_NAME = "SYSTEM";
 
+    /**
+     * Information schema - a system schema defined by SQL standard.
+     * The schema provides system-views, which describe Catalog objects, and 
can be read by a user.
+     */
+    String INFORMATION_SCHEMA = "INFORMATION_SCHEMA";
+
+    /**
+     * Definition schema - a system schema defined by SQL standard.
+     * The schema provides tables/sources for Catalog object’s metadata and 
can’t be accessed by a user directly.
+     */
+    String DEFINITION_SCHEMA = "DEFINITION_SCHEMA";
+
     /** Default storage profile. */
     String DEFAULT_STORAGE_PROFILE = "default";
 
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
index 3db8bc06ab..b822113c56 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CatalogUtils.java
@@ -18,6 +18,8 @@
 package org.apache.ignite.internal.catalog.commands;
 
 import static java.util.stream.Collectors.toList;
+import static 
org.apache.ignite.internal.catalog.CatalogService.DEFINITION_SCHEMA;
+import static 
org.apache.ignite.internal.catalog.CatalogService.INFORMATION_SCHEMA;
 import static 
org.apache.ignite.internal.catalog.CatalogService.SYSTEM_SCHEMA_NAME;
 import static 
org.apache.ignite.internal.catalog.commands.DefaultValue.Type.FUNCTION_CALL;
 import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
@@ -145,7 +147,11 @@ public class CatalogUtils {
         FUNCTIONAL_DEFAULT_FUNCTIONS.put("RAND_UUID", ColumnType.UUID);
     }
 
-    public static final List<String> SYSTEM_SCHEMAS = 
List.of(SYSTEM_SCHEMA_NAME);
+    public static final Set<String> SYSTEM_SCHEMAS = Set.of(
+            SYSTEM_SCHEMA_NAME,
+            DEFINITION_SCHEMA,
+            INFORMATION_SCHEMA
+    );
 
     /** System schema names. */
     public static boolean isSystemSchema(String schemaName) {
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropSchemaCommand.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropSchemaCommand.java
new file mode 100644
index 0000000000..d166d5db79
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropSchemaCommand.java
@@ -0,0 +1,116 @@
+/*
+ * 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.commands;
+
+import static 
org.apache.ignite.internal.catalog.CatalogParamsValidationUtils.validateIdentifier;
+import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.schemaOrThrow;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.internal.catalog.Catalog;
+import org.apache.ignite.internal.catalog.CatalogCommand;
+import org.apache.ignite.internal.catalog.CatalogValidationException;
+import org.apache.ignite.internal.catalog.descriptors.CatalogIndexDescriptor;
+import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
+import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
+import org.apache.ignite.internal.catalog.storage.DropIndexEntry;
+import org.apache.ignite.internal.catalog.storage.DropSchemaEntry;
+import org.apache.ignite.internal.catalog.storage.DropTableEntry;
+import org.apache.ignite.internal.catalog.storage.UpdateEntry;
+
+/**
+ * A command that drops a schema with specified name.
+ */
+public class DropSchemaCommand implements CatalogCommand {
+    /** Returns builder to create a command to drop schema with specified 
name. */
+    public static DropSchemaCommandBuilder builder() {
+        return new Builder();
+    }
+
+    private final String schemaName;
+
+    private final boolean cascade;
+
+    /**
+     * Constructor.
+     *
+     * @param schemaName Name of the schema.
+     * @param cascade Flag indicating forced deletion of a non-empty schema.
+     * @throws CatalogValidationException if any of restrictions above is 
violated.
+     */
+    private DropSchemaCommand(String schemaName, boolean cascade) throws 
CatalogValidationException {
+        validateIdentifier(schemaName, "Name of the schema");
+
+        this.schemaName = schemaName;
+        this.cascade = cascade;
+    }
+
+    @Override
+    public List<UpdateEntry> get(Catalog catalog) {
+        if (CatalogUtils.isSystemSchema(schemaName)) {
+            throw new CatalogValidationException("System schema can't be 
dropped [name={}].", schemaName);
+        }
+
+        CatalogSchemaDescriptor schema = schemaOrThrow(catalog, schemaName);
+
+        if (!cascade && !schema.isEmpty()) {
+            throw new CatalogValidationException("Schema '{}' is not empty. 
Use CASCADE to drop it anyway.", schemaName);
+        }
+
+        List<UpdateEntry> updateEntries = new ArrayList<>();
+
+        for (CatalogIndexDescriptor idx : schema.indexes()) {
+            updateEntries.add(new DropIndexEntry(idx.id()));
+        }
+
+        for (CatalogTableDescriptor tbl : schema.tables()) {
+            updateEntries.add(new DropTableEntry(tbl.id()));
+        }
+
+        updateEntries.add(new DropSchemaEntry(schema.id()));
+
+        return updateEntries;
+    }
+
+    /**
+     * Implementation of {@link DropSchemaCommandBuilder}.
+     */
+    private static class Builder implements DropSchemaCommandBuilder {
+        private String schemaName;
+        private boolean cascade;
+
+        @Override
+        public DropSchemaCommandBuilder name(String schemaName) {
+            this.schemaName = schemaName;
+
+            return this;
+        }
+
+        @Override
+        public DropSchemaCommandBuilder cascade(boolean cascade) {
+            this.cascade = cascade;
+
+            return this;
+        }
+
+        @Override
+        public CatalogCommand build() {
+            return new DropSchemaCommand(schemaName, cascade);
+        }
+    }
+}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropSchemaCommandBuilder.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropSchemaCommandBuilder.java
new file mode 100644
index 0000000000..fa5d5d7ad4
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/DropSchemaCommandBuilder.java
@@ -0,0 +1,34 @@
+/*
+ * 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.commands;
+
+import org.apache.ignite.internal.catalog.CatalogCommand;
+
+/**
+ * Builder of a command that drop specified schema.
+ */
+public interface DropSchemaCommandBuilder {
+    /** Sets schema name. Should not be null or blank. */
+    DropSchemaCommandBuilder name(String schemaName);
+
+    /** Sets flag indicating forced deletion of a non-empty schema. */
+    DropSchemaCommandBuilder cascade(boolean cascade);
+
+    /** Returns a command with specified parameters. */
+    CatalogCommand build();
+}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSchemaDescriptor.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSchemaDescriptor.java
index ec3567bcdd..b5ce13a894 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSchemaDescriptor.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/descriptors/CatalogSchemaDescriptor.java
@@ -100,6 +100,11 @@ public class CatalogSchemaDescriptor extends 
CatalogObjectDescriptor {
         return systemViewsMap.get(name);
     }
 
+    /** Returns {@code true} if the schema doesn't contain any objects, 
otherwise {@code false}. */
+    public boolean isEmpty() {
+        return tables.length == 0 && indexes.length == 0 && systemViews.length 
== 0;
+    }
+
     private void rebuildMaps() {
         tablesMap = 
Arrays.stream(tables).collect(toUnmodifiableMap(CatalogObjectDescriptor::name, 
Function.identity()));
         indexesMap = Arrays.stream(indexes)
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropSchemaEntry.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropSchemaEntry.java
new file mode 100644
index 0000000000..ad8241c8b5
--- /dev/null
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/DropSchemaEntry.java
@@ -0,0 +1,91 @@
+/*
+ * 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.storage;
+
+import static java.util.stream.Collectors.toList;
+import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.defaultZoneIdOpt;
+
+import java.io.IOException;
+import org.apache.ignite.internal.catalog.Catalog;
+import 
org.apache.ignite.internal.catalog.storage.serialization.CatalogObjectSerializer;
+import 
org.apache.ignite.internal.catalog.storage.serialization.MarshallableEntryType;
+import org.apache.ignite.internal.tostring.S;
+import org.apache.ignite.internal.util.io.IgniteDataInput;
+import org.apache.ignite.internal.util.io.IgniteDataOutput;
+
+/**
+ * Describes deletion of a schema.
+ */
+public class DropSchemaEntry implements UpdateEntry {
+    public static final CatalogObjectSerializer<DropSchemaEntry> SERIALIZER = 
new DropSchemaSerializer();
+
+    private final int schemaId;
+
+    /**
+     * Constructs the object.
+     *
+     * @param schemaId An id of a schema to drop.
+     */
+    public DropSchemaEntry(int schemaId) {
+        this.schemaId = schemaId;
+    }
+
+    /** Returns an id of a schema to drop. */
+    public int schemaId() {
+        return schemaId;
+    }
+
+    @Override
+    public int typeId() {
+        return MarshallableEntryType.DROP_SCHEMA.id();
+    }
+
+    @Override
+    public Catalog applyUpdate(Catalog catalog, long causalityToken) {
+        return new Catalog(
+                catalog.version(),
+                catalog.time(),
+                catalog.objectIdGenState(),
+                catalog.zones(),
+                catalog.schemas().stream().filter(s -> s.id() != 
schemaId).collect(toList()),
+                defaultZoneIdOpt(catalog)
+        );
+    }
+
+    @Override
+    public String toString() {
+        return S.toString(this);
+    }
+
+    /**
+     * Serializer for {@link DropSchemaEntry}.
+     */
+    private static class DropSchemaSerializer implements 
CatalogObjectSerializer<DropSchemaEntry> {
+        @Override
+        public DropSchemaEntry readFrom(IgniteDataInput input) throws 
IOException {
+            int schemaId = input.readVarIntAsInt();
+
+            return new DropSchemaEntry(schemaId);
+        }
+
+        @Override
+        public void writeTo(DropSchemaEntry entry, IgniteDataOutput output) 
throws IOException {
+            output.writeVarInt(entry.schemaId());
+        }
+    }
+}
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogEntrySerializerProvider.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogEntrySerializerProvider.java
index 86167adc33..5b14f5b93f 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogEntrySerializerProvider.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/CatalogEntrySerializerProvider.java
@@ -23,6 +23,7 @@ import 
org.apache.ignite.internal.catalog.storage.AlterColumnEntry;
 import org.apache.ignite.internal.catalog.storage.AlterZoneEntry;
 import org.apache.ignite.internal.catalog.storage.DropColumnsEntry;
 import org.apache.ignite.internal.catalog.storage.DropIndexEntry;
+import org.apache.ignite.internal.catalog.storage.DropSchemaEntry;
 import org.apache.ignite.internal.catalog.storage.DropTableEntry;
 import org.apache.ignite.internal.catalog.storage.DropZoneEntry;
 import org.apache.ignite.internal.catalog.storage.MakeIndexAvailableEntry;
@@ -81,6 +82,7 @@ public interface CatalogEntrySerializerProvider {
             serializers[MarshallableEntryType.RENAME_INDEX.id()] = 
RenameIndexEntry.SERIALIZER;
             serializers[MarshallableEntryType.SET_DEFAULT_ZONE.id()] = 
SetDefaultZoneEntry.SERIALIZER;
             serializers[MarshallableEntryType.NEW_SCHEMA.id()] = 
NewSchemaEntry.SERIALIZER;
+            serializers[MarshallableEntryType.DROP_SCHEMA.id()] = 
DropSchemaEntry.SERIALIZER;
             //noinspection ThisEscapedInObjectConstruction
             serializers[MarshallableEntryType.VERSIONED_UPDATE.id()] = new 
VersionedUpdateSerializer(this);
 
diff --git 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/MarshallableEntryType.java
 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/MarshallableEntryType.java
index 8cdb7230d0..13ddeaddcf 100644
--- 
a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/MarshallableEntryType.java
+++ 
b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/storage/serialization/MarshallableEntryType.java
@@ -41,7 +41,8 @@ public enum MarshallableEntryType {
     VERSIONED_UPDATE(17),
     RENAME_INDEX(18),
     SET_DEFAULT_ZONE(19),
-    NEW_SCHEMA(20);
+    NEW_SCHEMA(20),
+    DROP_SCHEMA(21);
 
     /** Type ID. */
     private final int id;
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogIndexTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogIndexTest.java
index 27ce986cca..b9f112108b 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogIndexTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogIndexTest.java
@@ -848,10 +848,10 @@ public class CatalogIndexTest extends 
BaseCatalogManagerTest {
     }
 
     private void createSomeSortedIndex(String tableName, String indexName) {
-        assertThat(
-                manager.execute(createSortedIndexCommand(tableName, indexName, 
false, List.of("key1"), List.of(ASC_NULLS_LAST))),
-                willCompleteSuccessfully()
-        );
+        CatalogCommand newSortedIndexCommand = createSortedIndexCommand(
+                SqlCommon.DEFAULT_SCHEMA_NAME, tableName, indexName, false, 
List.of("key1"), List.of(ASC_NULLS_LAST));
+
+        assertThat(manager.execute(newSortedIndexCommand), 
willCompleteSuccessfully());
     }
 
     private void renameIndex(String indexName, String newIndexName) {
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogSchemaTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogSchemaTest.java
index f468fd9f1f..3a00f619c7 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogSchemaTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogSchemaTest.java
@@ -17,33 +17,144 @@
 
 package org.apache.ignite.internal.catalog;
 
+import static org.apache.ignite.internal.catalog.CatalogTestUtils.columnParams;
+import static 
org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation.ASC_NULLS_LAST;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureExceptionMatcher.willThrowFast;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
+import static org.apache.ignite.sql.ColumnType.INT32;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
+import java.util.List;
 import org.apache.ignite.internal.catalog.commands.CreateSchemaCommand;
+import org.apache.ignite.internal.catalog.commands.DropSchemaCommand;
+import org.apache.ignite.internal.catalog.descriptors.CatalogSchemaDescriptor;
 import org.apache.ignite.internal.sql.SqlCommon;
 import org.junit.jupiter.api.Test;
 
 /** Tests for schema related commands. */
 public class CatalogSchemaTest extends BaseCatalogManagerTest {
+    private static final String TEST_SCHEMA = "S1";
 
     @Test
     public void testCreateSchema() {
-        String schemaName = "S1";
+        
assertThat(manager.execute(CreateSchemaCommand.builder().name(TEST_SCHEMA).build()),
 willCompleteSuccessfully());
 
-        
assertThat(manager.execute(CreateSchemaCommand.builder().name(schemaName).build()),
 willCompleteSuccessfully());
+        Catalog latestCatalog = latestCatalog();
 
-        Catalog latestCatalog = 
manager.catalog(manager.activeCatalogVersion(clock.nowLong()));
-
-        assertNotNull(latestCatalog);
-        assertNotNull(latestCatalog.schema(schemaName));
+        assertNotNull(latestCatalog.schema(TEST_SCHEMA));
         assertNotNull(latestCatalog.schema(SqlCommon.DEFAULT_SCHEMA_NAME));
 
         assertThat(
-                
manager.execute(CreateSchemaCommand.builder().name(schemaName).build()),
+                
manager.execute(CreateSchemaCommand.builder().name(TEST_SCHEMA).build()),
                 willThrowFast(CatalogValidationException.class, "Schema with 
name 'S1' already exists")
         );
     }
+
+    @Test
+    public void testDropEmpty() {
+        int initialSchemasCount = latestCatalog().schemas().size();
+
+        
assertThat(manager.execute(CreateSchemaCommand.builder().name(TEST_SCHEMA).build()),
 willCompleteSuccessfully());
+
+        assertThat(latestCatalog().schemas(), hasSize(initialSchemasCount + 
1));
+
+        CatalogCommand cmd = 
DropSchemaCommand.builder().name(TEST_SCHEMA).build();
+
+        assertThat(manager.execute(cmd), willCompleteSuccessfully());
+        assertThat(latestCatalog().schema(TEST_SCHEMA), nullValue());
+        assertThat(latestCatalog().schemas(), hasSize(initialSchemasCount));
+
+        assertThat(
+                
manager.execute(DropSchemaCommand.builder().name(TEST_SCHEMA).build()),
+                willThrowFast(CatalogValidationException.class, "Schema with 
name 'S1' not found")
+        );
+    }
+
+    @Test
+    public void testDropDefaultSchemaIsAllowed() {
+        CatalogCommand cmd = 
DropSchemaCommand.builder().name(SqlCommon.DEFAULT_SCHEMA_NAME).build();
+
+        assertThat(manager.execute(cmd), willCompleteSuccessfully());
+        assertThat(latestCatalog().schema(SqlCommon.DEFAULT_SCHEMA_NAME), 
nullValue());
+
+        assertThat(
+                manager.execute(simpleTable("test")),
+                willThrowFast(CatalogValidationException.class, "Schema with 
name 'PUBLIC' not found")
+        );
+    }
+
+    @Test
+    public void testDropNonEmpty() {
+        CatalogCommand newSchemaCmd = 
CreateSchemaCommand.builder().name(TEST_SCHEMA).build();
+        CatalogCommand newTableCmd = newTableCommand("T1");
+        CatalogCommand idxCmd = newIndexCommand("T1", "I1");
+
+        assertThat(
+                manager.execute(List.of(newSchemaCmd, newTableCmd, idxCmd)),
+                willCompleteSuccessfully()
+        );
+
+        // RESTRICT
+        {
+            assertThat(
+                    
manager.execute(DropSchemaCommand.builder().name(TEST_SCHEMA).build()),
+                    willThrowFast(CatalogValidationException.class, "Schema 
'S1' is not empty. Use CASCADE to drop it anyway.")
+            );
+
+            CatalogSchemaDescriptor schemaDescriptor = 
latestCatalog().schema(TEST_SCHEMA);
+
+            assertThat(schemaDescriptor, is(notNullValue()));
+            assertThat(schemaDescriptor.tables().length, is(1));
+        }
+
+        // CASCADE
+        {
+            assertThat(latestCatalog().tables(), hasSize(1));
+            assertThat(latestCatalog().indexes(), hasSize(2));
+
+            CatalogCommand dropCmd = 
DropSchemaCommand.builder().name(TEST_SCHEMA).cascade(true).build();
+
+            assertThat(manager.execute(dropCmd), willCompleteSuccessfully());
+
+            assertThat(latestCatalog().schema(TEST_SCHEMA), nullValue());
+            assertThat(latestCatalog().tables(), hasSize(0));
+            assertThat(latestCatalog().indexes(), hasSize(0));
+        }
+    }
+
+    private Catalog latestCatalog() {
+        Catalog latestCatalog = 
manager.catalog(manager.activeCatalogVersion(clock.nowLong()));
+
+        assertThat(latestCatalog, is(notNullValue()));
+
+        return latestCatalog;
+    }
+
+    @SuppressWarnings("SameParameterValue")
+    private static CatalogCommand newTableCommand(String tableName) {
+        return createTableCommand(
+                TEST_SCHEMA,
+                tableName,
+                List.of(columnParams("key1", INT32), columnParams("key2", 
INT32), columnParams("val", INT32, true)),
+                List.of("key1", "key2"),
+                List.of("key2")
+        );
+    }
+
+    @SuppressWarnings("SameParameterValue")
+    private static CatalogCommand newIndexCommand(String tableName, String 
indexName) {
+        return createSortedIndexCommand(
+                TEST_SCHEMA,
+                tableName,
+                indexName,
+                false,
+                List.of("key1"),
+                List.of(ASC_NULLS_LAST)
+        );
+    }
 }
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogSchemaValidationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogSchemaValidationTest.java
new file mode 100644
index 0000000000..134495cfe3
--- /dev/null
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/CatalogSchemaValidationTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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 org.apache.ignite.internal.lang.IgniteStringFormatter.format;
+import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureExceptionMatcher.willThrowFast;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import org.apache.ignite.internal.catalog.commands.CreateSchemaCommand;
+import org.apache.ignite.internal.catalog.commands.DropSchemaCommand;
+import org.apache.ignite.internal.sql.SqlCommon;
+import org.apache.ignite.internal.testframework.IgniteTestUtils;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+/** Validation tests for schema related commands. */
+public class CatalogSchemaValidationTest extends BaseCatalogManagerTest {
+    @Test
+    public void testCreateSchemaWithExistingName() {
+        assertThat(
+                
manager.execute(CreateSchemaCommand.builder().name(SqlCommon.DEFAULT_SCHEMA_NAME).build()),
+                willThrowFast(CatalogValidationException.class, "Schema with 
name 'PUBLIC' already exists")
+        );
+    }
+
+    @Test
+    public void testDropNonExistingSchema() {
+        assertThat(
+                
manager.execute(DropSchemaCommand.builder().name("NON_EXISTING").build()),
+                willThrowFast(CatalogValidationException.class, "Schema with 
name 'NON_EXISTING' not found")
+        );
+    }
+
+    @Test
+    @SuppressWarnings("ThrowableNotThrown")
+    public void testCreateSchemaWithNullOrEmptyNameIsRejected() {
+        //noinspection DataFlowIssue
+        IgniteTestUtils.assertThrows(
+                CatalogValidationException.class,
+                () -> 
manager.execute(CreateSchemaCommand.builder().name(null).build()),
+                "Name of the schema can't be null or blank"
+        );
+
+        IgniteTestUtils.assertThrows(
+                CatalogValidationException.class,
+                () -> 
manager.execute(CreateSchemaCommand.builder().name("").build()),
+                "Name of the schema can't be null or blank"
+        );
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {
+            CatalogService.SYSTEM_SCHEMA_NAME,
+            CatalogService.INFORMATION_SCHEMA,
+            CatalogService.DEFINITION_SCHEMA
+    })
+    public void testDropSystemSchemaIsForbidden(String schemaName) {
+        CatalogCommand dropCmd = 
DropSchemaCommand.builder().name(schemaName).build();
+
+        assertThat(
+                manager.execute(dropCmd),
+                willThrowFast(CatalogValidationException.class, format("System 
schema can't be dropped [name={}]", schemaName))
+        );
+    }
+
+    @Test
+    @SuppressWarnings("ThrowableNotThrown")
+    public void testDropSchemaWithNullOrEmptyNameIsRejected() {
+        //noinspection DataFlowIssue
+        IgniteTestUtils.assertThrows(
+                CatalogValidationException.class,
+                () -> 
manager.execute(DropSchemaCommand.builder().name(null).build()),
+                "Name of the schema can't be null or blank"
+        );
+
+        IgniteTestUtils.assertThrows(
+                CatalogValidationException.class,
+                () -> 
manager.execute(DropSchemaCommand.builder().name("").build()),
+                "Name of the schema can't be null or blank"
+        );
+    }
+}
diff --git 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
index 0a54dfed3e..1254fd2ae6 100644
--- 
a/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
+++ 
b/modules/catalog/src/test/java/org/apache/ignite/internal/catalog/storage/CatalogEntrySerializationTest.java
@@ -173,6 +173,10 @@ public class CatalogEntrySerializationTest extends 
BaseIgniteAbstractTest {
                         0, "S", new CatalogTableDescriptor[0], new 
CatalogIndexDescriptor[0], new CatalogSystemViewDescriptor[0], 0)));
                 break;
 
+            case DROP_SCHEMA:
+                checkSerialization(new DropSchemaEntry(1));
+                break;
+
             default:
                 throw new UnsupportedOperationException("Test not implemented 
" + type);
         }
diff --git 
a/modules/catalog/src/testFixtures/java/org/apache/ignite/internal/catalog/BaseCatalogManagerTest.java
 
b/modules/catalog/src/testFixtures/java/org/apache/ignite/internal/catalog/BaseCatalogManagerTest.java
index 75958af48c..deb75422d0 100644
--- 
a/modules/catalog/src/testFixtures/java/org/apache/ignite/internal/catalog/BaseCatalogManagerTest.java
+++ 
b/modules/catalog/src/testFixtures/java/org/apache/ignite/internal/catalog/BaseCatalogManagerTest.java
@@ -193,10 +193,11 @@ public abstract class BaseCatalogManagerTest extends 
BaseIgniteAbstractTest {
             @Nullable List<String> indexColumns,
             @Nullable List<CatalogColumnCollation> columnsCollations
     ) {
-        return createSortedIndexCommand(TABLE_NAME, indexName, unique, 
indexColumns, columnsCollations);
+        return createSortedIndexCommand(SqlCommon.DEFAULT_SCHEMA_NAME, 
TABLE_NAME, indexName, unique, indexColumns, columnsCollations);
     }
 
     protected static CatalogCommand createSortedIndexCommand(
+            String schemaName,
             String tableName,
             String indexName,
             boolean unique,
@@ -204,7 +205,7 @@ public abstract class BaseCatalogManagerTest extends 
BaseIgniteAbstractTest {
             @Nullable List<CatalogColumnCollation> columnsCollations
     ) {
         return CreateSortedIndexCommand.builder()
-                .schemaName(SqlCommon.DEFAULT_SCHEMA_NAME)
+                .schemaName(schemaName)
                 .tableName(tableName)
                 .indexName(indexName)
                 .unique(unique)
@@ -227,11 +228,23 @@ public abstract class BaseCatalogManagerTest extends 
BaseIgniteAbstractTest {
             List<String> primaryKeys,
             @Nullable List<String> colocationColumns
     ) {
-        return createTableCommandBuilder(tableName, columns, primaryKeys, 
colocationColumns)
+        return createTableCommand(SqlCommon.DEFAULT_SCHEMA_NAME, tableName, 
columns, primaryKeys, colocationColumns);
+    }
+
+    protected static CatalogCommand createTableCommand(
+            String schemaName,
+            String tableName,
+            List<ColumnParams> columns,
+            List<String> primaryKeys,
+            @Nullable List<String> colocationColumns
+    ) {
+        return createTableCommandBuilder(schemaName, tableName, columns, 
primaryKeys, colocationColumns)
                 .build();
     }
 
-    protected static CreateTableCommandBuilder 
createTableCommandBuilder(String tableName,
+    protected static CreateTableCommandBuilder createTableCommandBuilder(
+            String schemaName,
+            String tableName,
             List<ColumnParams> columns,
             List<String> primaryKeys, @Nullable List<String> 
colocationColumns) {
 
@@ -240,7 +253,7 @@ public abstract class BaseCatalogManagerTest extends 
BaseIgniteAbstractTest {
                 .build();
 
         return CreateTableCommand.builder()
-                .schemaName(SqlCommon.DEFAULT_SCHEMA_NAME)
+                .schemaName(schemaName)
                 .tableName(tableName)
                 .columns(columns)
                 .primaryKey(primaryKey)
diff --git 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
index 3f50938557..990db0efeb 100644
--- 
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
+++ 
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java
@@ -20,6 +20,8 @@ package org.apache.ignite.internal.sql.engine;
 import static org.apache.ignite.internal.TestWrappers.unwrapIgniteImpl;
 import static org.apache.ignite.internal.TestWrappers.unwrapTableViewInternal;
 import static 
org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
+import static 
org.apache.ignite.internal.catalog.CatalogService.DEFINITION_SCHEMA;
+import static 
org.apache.ignite.internal.catalog.CatalogService.INFORMATION_SCHEMA;
 import static 
org.apache.ignite.internal.catalog.commands.CatalogUtils.SYSTEM_SCHEMAS;
 import static org.apache.ignite.internal.lang.IgniteStringFormatter.format;
 import static 
org.apache.ignite.internal.sql.engine.util.SqlTestUtils.assertThrowsSqlException;
@@ -41,11 +43,13 @@ import org.apache.ignite.Ignite;
 import org.apache.ignite.internal.app.IgniteImpl;
 import org.apache.ignite.internal.catalog.Catalog;
 import org.apache.ignite.internal.catalog.CatalogManager;
+import org.apache.ignite.internal.catalog.commands.CreateSchemaCommand;
 import org.apache.ignite.internal.catalog.descriptors.CatalogTableDescriptor;
 import org.apache.ignite.internal.catalog.descriptors.CatalogZoneDescriptor;
 import org.apache.ignite.internal.schema.Column;
 import org.apache.ignite.internal.sql.BaseSqlIntegrationTest;
 import org.apache.ignite.internal.table.partition.HashPartition;
+import org.apache.ignite.internal.testframework.IgniteTestUtils;
 import org.apache.ignite.internal.testframework.WithSystemProperty;
 import org.apache.ignite.sql.SqlException;
 import org.apache.ignite.table.Table;
@@ -328,8 +332,13 @@ public class ItCreateTableDdlTest extends 
BaseSqlIntegrationTest {
 
     @ParameterizedTest
     @MethodSource("reservedSchemaNames")
-    @SuppressWarnings("ThrowableNotThrown")
     public void testItIsNotPossibleToCreateTablesInSystemSchema(String schema) 
{
+        if (DEFINITION_SCHEMA.equals(schema) || 
INFORMATION_SCHEMA.equals(schema)) {
+            IgniteImpl igniteImpl = unwrapIgniteImpl(CLUSTER.aliveNode());
+
+            
IgniteTestUtils.await(igniteImpl.catalogManager().execute(CreateSchemaCommand.builder().name(schema).build()));
+        }
+
         assertThrowsSqlException(
                 STMT_VALIDATION_ERR,
                 "Operations with system schemas are not allowed",


Reply via email to