Skip to content

Commit

Permalink
IGNITE-19912 Duplicated index creation using SQL and already existing…
Browse files Browse the repository at this point in the history
… QueryFields index leads to node start-up failure
  • Loading branch information
zstan committed Jul 5, 2023
1 parent ab168e7 commit 8cbfe89
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.apache.ignite.internal.processors.query.h2.H2Utils;
import org.apache.ignite.internal.processors.query.h2.QueryTable;
import org.apache.ignite.internal.processors.query.h2.database.H2TreeIndex;
import org.apache.ignite.internal.processors.query.schema.SchemaOperationException;
import org.apache.ignite.internal.processors.query.stat.ObjectStatistics;
import org.apache.ignite.internal.processors.query.stat.StatisticsKey;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
Expand All @@ -76,6 +77,7 @@

import static org.apache.ignite.cache.CacheMode.PARTITIONED;
import static org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion.NONE;
import static org.apache.ignite.internal.processors.query.schema.SchemaOperationException.CODE_INDEX_EXISTS;

/**
* H2 Table implementation.
Expand Down Expand Up @@ -751,22 +753,16 @@ public boolean rebuildFromHashInProgress() {
}

/**
* Checks index presence, return {@link Index} if index with same name or same fields and search direction already
* exist or {@code null} othervise.
* Checks that equivalent fields collection index already present.
*
* @param curIdx Index to check.
* @return Index if equal or subset index exist.
* @throws IgniteCheckedException If failed.
*/
private @Nullable Index checkIndexPresence(Index curIdx) throws IgniteCheckedException {
private void checkEquivalentFieldsIndexIsPresent(Index curIdx) {
IndexColumn[] curColumns = curIdx.getIndexColumns();

Index registredIdx = null;

for (Index idx : idxs) {
if (F.eq(curIdx.getName(), idx.getName()))
throw new IgniteCheckedException("Index already exists: " + idx.getName());

if (!(curIdx instanceof H2TreeIndex) || !(idx instanceof H2TreeIndex))
continue;

Expand All @@ -789,11 +785,17 @@ public boolean rebuildFromHashInProgress() {
}
}

if (registredIdx != null)
return registredIdx;
}
if (registredIdx != null) {
String idxCols = Stream.of(registredIdx.getIndexColumns())
.map(k -> k.columnName).collect(Collectors.joining(", "));

return null;
U.warn(log, "Index with the given set or subset of columns already exists " +
"(consider dropping either new or existing index) [cacheName=" + cacheInfo.name() + ", " +
"schemaName=" + getSchema().getName() + ", tableName=" + getName() +
", newIndexName=" + curIdx.getName() + ", existingIndexName=" + registredIdx.getName() +
", existingIndexColumns=[" + idxCols + "]]");
}
}
}

/**
Expand All @@ -811,19 +813,13 @@ public void proposeUserIndex(Index idx) throws IgniteCheckedException {
try {
ensureNotDestroyed();

Index idxExist = checkIndexPresence(idx);

if (idxExist != null) {
String idxCols = Stream.of(idxExist.getIndexColumns())
.map(k -> k.columnName).collect(Collectors.joining(", "));

U.warn(log, "Index with the given set or subset of columns already exists " +
"(consider dropping either new or existing index) [cacheName=" + cacheInfo.name() + ", " +
"schemaName=" + getSchema().getName() + ", tableName=" + getName() +
", newIndexName=" + idx.getName() + ", existingIndexName=" + idxExist.getName() +
", existingIndexColumns=[" + idxCols + "]]");
for (Index idx0 : idxs) {
if (F.eq(idx.getName(), idx0.getName()))
throw new SchemaOperationException(CODE_INDEX_EXISTS, idx.getName());
}

checkEquivalentFieldsIndexIsPresent(idx);

Index oldTmpIdx = tmpIdxs.put(idx.getName(), (H2IndexCostedBase)idx);

assert oldTmpIdx == null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* 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.processors.cache;

import java.io.Serializable;
import javax.cache.CacheException;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.cache.query.SqlFieldsQuery;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
import org.apache.ignite.cluster.ClusterState;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
import org.apache.ignite.testframework.GridTestUtils;
import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
import org.junit.Test;
import static java.lang.String.format;

/** Duplicate index tests. */
public class DuplicateIndexCreationTest extends GridCommonAbstractTest {
/** {@inheritDoc} */
@Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
cfg.setDataStorageConfiguration(
new DataStorageConfiguration()
.setDefaultDataRegionConfiguration(new DataRegionConfiguration()
.setPersistenceEnabled(true)
.setMaxSize(256 * 1024L * 1024L)));
cfg.setCacheConfiguration(
new CacheConfiguration<>()
.setName(DEFAULT_CACHE_NAME)
.setSqlSchema("PUBLIC")
.setIndexedTypes(Integer.class, Person.class));
return cfg;
}

/** {@inheritDoc} */
@Override protected void beforeTest() throws Exception {
super.beforeTest();
stopAllGrids();
cleanPersistenceDir();
}

/** {@inheritDoc} */
@Override protected void afterTest() throws Exception {
super.afterTest();
stopAllGrids();
cleanPersistenceDir();
}

/** Repedeatly create index with the same name, rerun cluster. */
@Test
public void testIndexCreation() throws Exception {
IgniteEx node = startGrid(0);
node.cluster().state(ClusterState.ACTIVE);

IgniteCache<Object, Object> cache = node.cache(DEFAULT_CACHE_NAME);
String sqlCreateIndexTemplate = "CREATE INDEX %s ON PUBLIC.PERSON (NAME)";

SqlFieldsQuery queryCreateIndex = new SqlFieldsQuery(format(sqlCreateIndexTemplate, ""));
SqlFieldsQuery queryCreateIndexIfNotExist = new SqlFieldsQuery(format(sqlCreateIndexTemplate, "IF NOT EXISTS"));

cache.query(queryCreateIndex).getAll();

GridTestUtils.assertThrows(log, () -> cache.query(queryCreateIndex).getAll(), CacheException.class, null);

stopGrid(0);
startGrid(0);

stopGrid(0);
cleanPersistenceDir();

node = startGrid(0);
node.cluster().state(ClusterState.ACTIVE);
IgniteCache<Object, Object> cache1 = node.cache(DEFAULT_CACHE_NAME);
cache1.query(queryCreateIndexIfNotExist).getAll();

stopGrid(0);
startGrid(0);
}

/**
* Person class.
*/
private static class Person implements Serializable {
/** Indexed name. */
@QuerySqlField(index = true)
public String name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.apache.ignite.internal.processors.cache.CacheOffheapBatchIndexingMultiTypeTest;
import org.apache.ignite.internal.processors.cache.CacheQueryBuildValueTest;
import org.apache.ignite.internal.processors.cache.DdlTransactionSelfTest;
import org.apache.ignite.internal.processors.cache.DuplicateIndexCreationTest;
import org.apache.ignite.internal.processors.cache.GridCacheCrossCacheQuerySelfTest;
import org.apache.ignite.internal.processors.cache.GridCacheLazyQueryPartitionsReleaseTest;
import org.apache.ignite.internal.processors.cache.GridCacheQueryIndexDisabledSelfTest;
Expand Down Expand Up @@ -199,6 +200,7 @@
SqlResultSetMetaSelfTest.class,

BasicIndexTest.class,
DuplicateIndexCreationTest.class,
ErroneousQueryEntityConfigurationTest.class,
ArrayIndexTest.class,
BasicIndexMultinodeTest.class,
Expand Down

0 comments on commit 8cbfe89

Please sign in to comment.