Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix listIndexes bug with double quoted columnName in the index target from indexMetadata #1772

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
public enum ApiIndexType {
// map, set, list
COLLECTION,
// Something on a scalar column, and non analsed text index
// Something on a scalar column, and non analysed text index
REGULAR,
// Not available yet
TEXT_ANALYSED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public ApiIndexDefContainer indexesIncludingUnsupported() {
}

/**
* Factory for creating a {@link ApiTableDef} from a users decription sent in a command {@link
* Factory for creating a {@link ApiTableDef} from a users description sent in a command {@link
* TableDefinitionDesc}.
*
* <p>Use the singleton {@link #FROM_TABLE_DESC_FACTORY} to create an instance.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.datastax.oss.driver.api.core.metadata.schema.IndexKind;
import com.datastax.oss.driver.api.core.metadata.schema.IndexMetadata;
import com.datastax.oss.driver.internal.core.adminrequest.AdminRow;
import com.datastax.oss.driver.internal.core.util.Strings;
Yuqi-Du marked this conversation as resolved.
Show resolved Hide resolved
import io.stargate.sgv2.jsonapi.exception.checked.UnknownCqlIndexFunctionException;
import io.stargate.sgv2.jsonapi.exception.checked.UnsupportedCqlIndexException;
import io.stargate.sgv2.jsonapi.util.defaults.Properties;
Expand Down Expand Up @@ -93,6 +94,10 @@ static boolean indexClassIsSai(String className) {
* {'class_name': 'StorageAttachedIndex', 'target': 'entries(query_timestamp_values)'}
* {'class_name': 'StorageAttachedIndex', 'target': 'comment_vector'}
* {'class_name': 'StorageAttachedIndex', 'similarity_function': 'cosine', 'target': 'my_vector'}
* ------------------------------------------------
* If column is doubleQuoted in the original CQL index
* {'class_name': 'StorageAttachedIndex', 'target': '"Age"'}
* {'class_name': 'StorageAttachedIndex', 'target': 'values("Age")'}
* </pre>
*
* The target can just be the name of the column, or the name of the column in parentheses
Expand All @@ -102,11 +107,14 @@ static boolean indexClassIsSai(String className) {
* <p>The Reg Exp below will match:
*
* <ul>
* <li>"monkeys": group 1 - "monkeys", group 2 - null
* <li>"values(monkeys)": group 1 - "values" group 2 - "monkeys"
* <li>'monkeys': group 1 - 'monkeys', group 2 - null
* <li>'values(monkeys)': group 1 - 'values' group 2 - 'monkeys'
* <li>'"Monkeys": group 1 - '"Monkeys"', group 2 - null
* <li>'values("Monkeys")': group 1 - 'values' group 2 - '"monkeys"'
* </ul>
*/
private static Pattern INDEX_TARGET_PATTERN = Pattern.compile("^(\\w+)?(?:\\((\\w+)\\))?$");
private static Pattern INDEX_TARGET_PATTERN =
Pattern.compile("^(\"?\\w+\"?)?(?:\\((\"?\\w+\"?)\\))?$");
tatu-at-datastax marked this conversation as resolved.
Show resolved Hide resolved

/**
* Parses the target from the IndexMetadata to extract the column name and the function if there
Expand Down Expand Up @@ -144,7 +152,15 @@ static IndexTarget indexTarget(IndexMetadata indexMetadata)
columnName = matcher.group(2);
functionName = matcher.group(1);
}

// At this point, after matcher, we may get a columnName doubleQuoted string from the index
// target
// E.G. 'target': 'values("a_list_column")' -> "a_list_column", 'target':
// 'values("a_regular_column")' -> "a_regular_column"
// 1. We can NOT use fromInternal which will keep the double quote in the identifier
// 2. We can NOT use fromCQL, since it will lowercase the unquoted string, E.G. 'BigApple' ->
// bigapple
// So we should just stripe the doubleQuote if needed
columnName = Strings.unDoubleQuote(columnName);
return functionName == null
? new IndexTarget(CqlIdentifier.fromInternal(columnName), null)
: new IndexTarget(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ public abstract class IndexFactoryFromCql extends FactoryFromCql {
public ApiIndexDef create(ApiColumnDefContainer allColumns, IndexMetadata indexMetadata)
throws UnsupportedCqlIndexException {

// This first check is to see if there is anyway we can support this index, becase we are only
// This first check is to see if there is anyway we can support this index, because we are only
// supporting
// SAI indexes, later on we may find something that we could support but dont like a new type of
// SAI indexes, later on we may find something that we could support but don't like a new type
// of
// sai
if (!isSupported(indexMetadata)) {
return createUnsupported(indexMetadata);
Expand All @@ -33,7 +34,7 @@ public ApiIndexDef create(ApiColumnDefContainer allColumns, IndexMetadata indexM
if (apiColumnDef == null) {
// Cassandra should not let there be an index on a column that is not on the table
throw new IllegalStateException(
"Could not find target column index.name:%s target: "
"Could not find target column index.name:%s target:%s"
.formatted(indexMetadata.getName(), indexTarget.targetColumn()));
}
// will throw if we cannot work it out
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static io.stargate.sgv2.jsonapi.api.v1.util.DataApiCommandSenders.assertNamespaceCommand;
import static io.stargate.sgv2.jsonapi.api.v1.util.DataApiCommandSenders.assertTableCommand;
import static net.javacrumbs.jsonunit.JsonMatchers.jsonEquals;
import static org.hamcrest.Matchers.*;

import io.quarkus.test.common.WithTestResource;
Expand All @@ -28,6 +29,7 @@ public final void createSimpleTable() {
Map.entry("id", Map.of("type", "text")),
Map.entry("age", Map.of("type", "int")),
Map.entry("comment", Map.of("type", "text")),
Map.entry("CapitalLetterColumn", Map.of("type", "text")),
Map.entry("vehicle_id", Map.of("type", "text")),
Map.entry("vehicle_id_1", Map.of("type", "text")),
Map.entry("vehicle_id_2", Map.of("type", "text")),
Expand Down Expand Up @@ -212,16 +214,75 @@ public void createMapIndex() {

@Test
public void createIndexForQuotedColumn() {
var createIndexJson =
"""
{
"name": "physicalAddress_idx_0",
"definition": {
"column": "physicalAddress"
}
}
""";
assertTableCommand(keyspaceName, testTableName)
.postCreateIndex(
"""
{
"name": "physicalAddress_idx",
"definition": {
"column": "physicalAddress"
}
}
""")
.postCreateIndex(createIndexJson)
.wasSuccessful();

var createIndexJsonExpected =
"""
{
"name": "physicalAddress_idx_0",
"definition": {
"column": "physicalAddress",
"options": {
"ascii": false,
"caseSensitive": true,
"normalize": false
}
}
}
""";

assertTableCommand(keyspaceName, testTableName)
.templated()
.listIndexes(true)
.wasSuccessful()
.body("status.indexes", hasItem(jsonEquals(createIndexJsonExpected)));

assertNamespaceCommand(keyspaceName)
.templated()
.dropIndex("physicalAddress_idx_0", false)
.wasSuccessful();
}

@Test
public void createIndexWithOptionForQuotedColumn() {
var createIndexJson =
"""
{
"name": "physicalAddress_idx_1",
"definition": {
"column": "physicalAddress",
"options": {
"ascii": false,
"caseSensitive": true,
"normalize": false
}
}
}
""";
assertTableCommand(keyspaceName, testTableName)
.postCreateIndex(createIndexJson)
.wasSuccessful();

assertTableCommand(keyspaceName, testTableName)
.templated()
.listIndexes(true)
.wasSuccessful()
.body("status.indexes", hasItem(jsonEquals(createIndexJson)));

assertNamespaceCommand(keyspaceName)
.templated()
.dropIndex("physicalAddress_idx_1", false)
.wasSuccessful();
}

Expand Down
Loading
Loading