IGNITE-5533: Proper index cleanup in H2 database during DROP TALBE. This closes ...
authorAlexander Paschenko <alexander.a.paschenko@gmail.com>
Thu, 6 Jul 2017 10:00:33 +0000 (13:00 +0300)
committerdevozerov <vozerov@gridgain.com>
Thu, 6 Jul 2017 10:00:33 +0000 (13:00 +0300)
modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2Table.java
modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/index/H2DynamicTableSelfTest.java

index d656cc3..76d0258 100644 (file)
@@ -40,7 +40,9 @@ import org.apache.ignite.internal.util.offheap.unsafe.GridUnsafeMemory;
 import org.apache.ignite.internal.util.typedef.F;
 import org.apache.ignite.lang.IgniteBiTuple;
 import org.h2.command.ddl.CreateTableData;
+import org.h2.engine.DbObject;
 import org.h2.engine.Session;
+import org.h2.engine.SysProperties;
 import org.h2.index.Index;
 import org.h2.index.IndexType;
 import org.h2.index.SpatialIndex;
@@ -48,6 +50,7 @@ import org.h2.message.DbException;
 import org.h2.result.Row;
 import org.h2.result.SearchRow;
 import org.h2.result.SortOrder;
+import org.h2.schema.SchemaObject;
 import org.h2.table.IndexColumn;
 import org.h2.table.TableBase;
 import org.h2.table.TableType;
@@ -76,6 +79,9 @@ public class GridH2Table extends TableBase {
     /** */
     private final int pkIndexPos;
 
+    /** Total number of system indexes. */
+    private final int sysIdxsCnt;
+
     /** */
     private final Map<String, GridH2IndexBase> tmpIdxs = new HashMap<>();
 
@@ -184,6 +190,8 @@ public class GridH2Table extends TableBase {
 
         pkIndexPos = hasHashIndex ? 2 : 1;
 
+        sysIdxsCnt = idxs.size();
+
         final int segments = desc != null ? desc.context().config().getQueryParallelism() :
             // Get index segments count from PK index. Null desc can be passed from tests.
             index(pkIndexPos).segmentsCount();
@@ -449,6 +457,45 @@ public class GridH2Table extends TableBase {
         // No-op.
     }
 
+    /** {@inheritDoc} */
+    @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+    @Override public void removeChildrenAndResources(Session ses) {
+        lock(true);
+
+        try {
+            super.removeChildrenAndResources(ses);
+
+            // Clear all user indexes registered in schema.
+            while (idxs.size() > sysIdxsCnt) {
+                Index idx = idxs.get(sysIdxsCnt);
+
+                if (idx.getName() != null && idx.getSchema().findIndex(ses, idx.getName()) == idx) {
+                    // This call implicitly removes both idx and its proxy, if any, from idxs.
+                    database.removeSchemaObject(ses, idx);
+
+                    // We have to call destroy here if we are who has removed this index from the table.
+                    if (idx instanceof GridH2IndexBase)
+                        ((GridH2IndexBase)idx).destroy();
+                }
+            }
+
+            if (SysProperties.CHECK) {
+                for (SchemaObject obj : database.getAllSchemaObjects(DbObject.INDEX)) {
+                    Index idx = (Index) obj;
+                    if (idx.getTable() == this)
+                        DbException.throwInternalError("index not dropped: " + idx.getName());
+                }
+            }
+
+            database.removeMeta(ses, getId());
+            invalidate();
+
+        }
+        finally {
+            unlock(true);
+        }
+    }
+
     /**
      * Destroy the table.
      */
@@ -791,7 +838,7 @@ public class GridH2Table extends TableBase {
             Index cloneIdx = createDuplicateIndexIfNeeded(idx);
 
             ArrayList<Index> newIdxs = new ArrayList<>(
-                    idxs.size() + ((cloneIdx == null) ? 1 : 2));
+                idxs.size() + ((cloneIdx == null) ? 1 : 2));
 
             newIdxs.addAll(idxs);
 
index 5975b4e..b0e69f1 100644 (file)
@@ -694,6 +694,59 @@ public class H2DynamicTableSelfTest extends AbstractSchemaSelfTest {
     }
 
     /**
+     * Tests behavior on sequential create and drop of a table and its index.
+     */
+    public void testTableAndIndexRecreate() {
+        execute("drop table if exists \"PUBLIC\".t");
+
+        // First let's check behavior without index name set
+        execute("create table \"PUBLIC\".t (a int primary key, b varchar(30))");
+
+        fillRecreatedTable();
+
+        execute("create index on \"PUBLIC\".t (b desc)");
+        execute("drop table \"PUBLIC\".t");
+
+        assertNull(client().cache("t"));
+
+        execute("create table \"PUBLIC\".t (a int primary key, b varchar(30))");
+
+        fillRecreatedTable();
+
+        execute("create index on \"PUBLIC\".t (b desc)");
+        execute("drop table \"PUBLIC\".t");
+
+        assertNull(client().cache("t"));
+
+        // And now let's do the same for the named index
+        execute("create table \"PUBLIC\".t (a int primary key, b varchar(30))");
+
+        fillRecreatedTable();
+
+        execute("create index namedIdx on \"PUBLIC\".t (b desc)");
+        execute("drop table \"PUBLIC\".t");
+
+        assertNull(client().cache("t"));
+
+        execute("create table \"PUBLIC\".t (a int primary key, b varchar(30))");
+
+        fillRecreatedTable();
+
+        execute("create index namedIdx on \"PUBLIC\".t (b desc)");
+        execute("drop table \"PUBLIC\".t");
+    }
+
+    /**
+     * Fill re-created table with data.
+     */
+    private void fillRecreatedTable() {
+        for (int j = 1; j < 10; j++) {
+            String s = Integer.toString(j);
+            execute("insert into \"PUBLIC\".t (a,b) values (" + s + ", '" + s + "')");
+        }
+    }
+
+    /**
      * Check that dynamic cache created with {@code CREATE TABLE} is correctly configured affinity wise.
      * @param cacheName Cache name to check.
      * @param affKeyFieldName Expected affinity key field name.