kfaraz commented on code in PR #18515:
URL: https://github.com/apache/druid/pull/18515#discussion_r2377761570


##########
processing/src/main/java/org/apache/druid/metadata/MetadataStorageTablesConfig.java:
##########
@@ -101,6 +106,7 @@ public MetadataStorageTablesConfig(
     this.auditTable = makeTableName(auditTable, "audit");
     this.supervisorTable = makeTableName(supervisorTable, "supervisors");
     this.segmentSchemasTable = makeTableName(segmentSchemasTable, 
"segmentSchemas");
+    this.useUniqueIndexNames = Configs.valueOrDefault(useUniqueIndexNames, 
Boolean.FALSE);

Review Comment:
   ```suggestion
       this.useUniqueIndexNames = Configs.valueOrDefault(useUniqueIndexNames, 
false);
   ```



##########
server/src/test/java/org/apache/druid/metadata/SQLMetadataConnectorTest.java:
##########
@@ -305,6 +307,170 @@ public void testIsTransientException()
     );
   }
 
+  @ParameterizedTest
+  @ValueSource(booleans = {true, false})
+  public void test_tableIndices_areNotReplaced_ifExist(boolean 
useUniqueIndexName)
+  {
+    tablesConfig = new MetadataStorageTablesConfig(
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        useUniqueIndexName
+    );
+    connector = new TestDerbyConnector(new MetadataStorageConnectorConfig(), 
tablesConfig);
+
+    final String segmentsTable = tablesConfig.getSegmentsTable();
+    final List<String> legacyIndexTemplates = Arrays.asList(
+        "idx_%1$s_used",
+        "idx_%1$s_datasource_used_end_start",
+        "idx_%1$s_datasource_upgraded_from_segment_id"
+    );
+    final List<List<String>> indexColumns = List.of(
+        List.of("used"),
+        List.of(
+            "dataSource",
+            "used",
+            StringUtils.format("%send%S", connector.getQuoteString(), 
connector.getQuoteString()),

Review Comment:
   Nit: Why capitalize the second `%S`?



##########
server/src/main/java/org/apache/druid/metadata/SQLMetadataConnector.java:
##########
@@ -1106,43 +1168,51 @@ public ResultSet getIndexInfo(DatabaseMetaData 
databaseMetaData, String tableNam
   }
 
   /**
-   * create index on the table with retry if not already exist, to be called 
after createTable
+   * Create index on the table with retry if not already exist, to be called 
after createTable
+   * Format of index name is either specified via legacy {@param 
legacyIndexNameFormat} or {@code generateSHABasedIndexIdentifier}.
    *
    * @param tableName       Name of the table to create index on
-   * @param indexName       case-insensitive string index name, it helps to 
check the existing index on table
-   * @param indexCols       List of columns to be indexed on
+   * @param legacyIndexNameFormat   Template to create index ID (nullable for 
forwards compatibility with SHA-based indices)
+   * @param indexCols       List of un-escaped columns to be indexed on 
(case-sensitive).
    * @param createdIndexSet
    */
   public void createIndex(
       final String tableName,
-      final String indexName,
+      final String legacyIndexNameFormat,
       final List<String> indexCols,
       final Set<String> createdIndexSet
   )
   {
+    final String shaIndexName = 
StringUtils.toUpperCase(generateUniqueIndexName(tableName, indexCols));
+    final String legacyIndexName = legacyIndexNameFormat != null

Review Comment:
   Since we use the `legacyIndexName` to ensure that the legacy index doesn't 
already exist,
   I think the `legacyIndexNameFormat` should never be null.



##########
server/src/main/java/org/apache/druid/metadata/SQLMetadataConnector.java:
##########
@@ -1206,4 +1276,27 @@ public static boolean isStatementException(Throwable e)
     return e instanceof StatementException ||
            (e instanceof CallbackFailedException && e.getCause() instanceof 
StatementException);
   }
+
+  /**
+   * Creates a unique index name of the format {@code idx_<tableName>_<SHA of 
table name + column list>}
+   * with an SHA length of {@value MAX_INDEX_HASH_LENGTH}.
+   *
+   * @param tableName the table name
+   * @param columns   the set of columns to create the index on 
(case-insensitive)
+   * @return unique index identifier
+   */
+  protected String generateUniqueIndexName(String tableName, List<String> 
columns)
+  {
+    final String prefix = "idx_" + tableName + "_";
+    final String tableAndColumnDigest = DigestUtils.sha1Hex(
+        StringUtils.format(
+            "%s_%s",
+            tableName,
+            columns.stream()
+                   .map(StringUtils::toLowerCase)
+                   .collect(Collectors.joining("_")

Review Comment:
   This may cause duplicates since `_` can occur in a column name.
   Better use a character that cannot occur in a column name like comma or 
something.



##########
server/src/test/java/org/apache/druid/metadata/SQLMetadataConnectorTest.java:
##########
@@ -305,6 +307,170 @@ public void testIsTransientException()
     );
   }
 
+  @ParameterizedTest
+  @ValueSource(booleans = {true, false})
+  public void test_tableIndices_areNotReplaced_ifExist(boolean 
useUniqueIndexName)
+  {
+    tablesConfig = new MetadataStorageTablesConfig(
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        useUniqueIndexName
+    );
+    connector = new TestDerbyConnector(new MetadataStorageConnectorConfig(), 
tablesConfig);
+
+    final String segmentsTable = tablesConfig.getSegmentsTable();
+    final List<String> legacyIndexTemplates = Arrays.asList(
+        "idx_%1$s_used",
+        "idx_%1$s_datasource_used_end_start",
+        "idx_%1$s_datasource_upgraded_from_segment_id"
+    );
+    final List<List<String>> indexColumns = List.of(
+        List.of("used"),
+        List.of(
+            "dataSource",
+            "used",
+            StringUtils.format("%send%S", connector.getQuoteString(), 
connector.getQuoteString()),
+            "start"
+        ),
+        List.of("dataSource", "upgraded_from_segment_id")
+    );
+    final List<String> legacyIndexNames = legacyIndexTemplates.stream()
+                                                              .map(template -> 
StringUtils.toUpperCase(StringUtils.format(
+                                                                  template,
+                                                                  segmentsTable
+                                                              )))
+                                                              
.collect(Collectors.toList());
+    final List<String> uniqueIndexNames = indexColumns.stream()
+                                                      .map(cols -> 
connector.generateUniqueIndexName(
+                                                          segmentsTable,
+                                                          cols
+                                                      ))
+                                                      
.collect(Collectors.toList());
+
+    connector.createSegmentTable(segmentsTable);
+    connector.alterSegmentTable();
+
+    // drop legacy/unique indices and create the other type
+    connector.getDBI().withHandle(handle -> {
+      for (int i = 0; i < legacyIndexNames.size(); i++) {
+        handle.execute(StringUtils.toUpperCase(StringUtils.format(
+            "DROP INDEX %s",
+            useUniqueIndexName ? uniqueIndexNames.get(i) : 
legacyIndexNames.get(i)
+        )));
+        handle.execute(StringUtils.format(
+            "CREATE INDEX %s ON %s(%s)",
+            useUniqueIndexName ? legacyIndexNames.get(i) : 
uniqueIndexNames.get(i),
+            segmentsTable,
+            String.join(",", indexColumns.get(i))
+        ));
+      }
+      return null;
+    });
+
+    // run again to index name checks are run and duplicate indices are not 
created
+    connector.createSegmentTable(segmentsTable);
+    connector.alterSegmentTable();
+    assertIndicesPresentOnTable(segmentsTable, useUniqueIndexName ? 
legacyIndexNames : uniqueIndexNames);
+
+    dropTable(segmentsTable);
+    connector.tearDown();
+  }
+
+  @ParameterizedTest
+  @ValueSource(booleans = {true, false})
+  public void test_tableIndices_areReplaced_IfNotExist(boolean 
useUniqueIndexName)
+  {
+    tablesConfig = new MetadataStorageTablesConfig(
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        useUniqueIndexName
+    );
+    connector = new TestDerbyConnector(new MetadataStorageConnectorConfig(), 
tablesConfig);
+
+    final String segmentsTable = tablesConfig.getSegmentsTable();
+    final List<String> legacyIndexTemplates = Arrays.asList(
+        "idx_%1$s_used",
+        "idx_%1$s_datasource_used_end_start",
+        "idx_%1$s_datasource_upgraded_from_segment_id"
+    );
+    final List<List<String>> indexColumns = List.of(
+        List.of("used"),
+        List.of(
+            "dataSource",
+            "used",
+            StringUtils.format("%send%S", connector.getQuoteString(), 
connector.getQuoteString()),
+            "start"
+        ),
+        List.of("dataSource", "upgraded_from_segment_id")
+    );

Review Comment:
   Since this is a test setup, we shouldn't have to go through the whole flow 
of generating index names.\
   
   Instead, we should just do something like:
   
   ```java
   connector.createSegmentsTable(...);
   
   verifyIndicesPresentOnTable(
      "IDX_ABC",
      "IDX_DEF",
      "IDX_XYZ
   )
   ```
   
   This would make the tests much more readable.
   It is okay even if we have to change the test when the logic in the 
`SQLMetadataConnector.generateIndexName()` (in fact, it is desirable).



##########
server/src/test/java/org/apache/druid/metadata/SQLMetadataConnectorTest.java:
##########
@@ -305,6 +307,170 @@ public void testIsTransientException()
     );
   }
 
+  @ParameterizedTest
+  @ValueSource(booleans = {true, false})
+  public void test_tableIndices_areNotReplaced_ifExist(boolean 
useUniqueIndexName)
+  {
+    tablesConfig = new MetadataStorageTablesConfig(
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        useUniqueIndexName
+    );
+    connector = new TestDerbyConnector(new MetadataStorageConnectorConfig(), 
tablesConfig);
+
+    final String segmentsTable = tablesConfig.getSegmentsTable();
+    final List<String> legacyIndexTemplates = Arrays.asList(
+        "idx_%1$s_used",
+        "idx_%1$s_datasource_used_end_start",
+        "idx_%1$s_datasource_upgraded_from_segment_id"
+    );
+    final List<List<String>> indexColumns = List.of(
+        List.of("used"),
+        List.of(
+            "dataSource",
+            "used",
+            StringUtils.format("%send%S", connector.getQuoteString(), 
connector.getQuoteString()),
+            "start"
+        ),
+        List.of("dataSource", "upgraded_from_segment_id")
+    );
+    final List<String> legacyIndexNames = legacyIndexTemplates.stream()
+                                                              .map(template -> 
StringUtils.toUpperCase(StringUtils.format(
+                                                                  template,
+                                                                  segmentsTable
+                                                              )))
+                                                              
.collect(Collectors.toList());
+    final List<String> uniqueIndexNames = indexColumns.stream()
+                                                      .map(cols -> 
connector.generateUniqueIndexName(
+                                                          segmentsTable,
+                                                          cols
+                                                      ))
+                                                      
.collect(Collectors.toList());
+
+    connector.createSegmentTable(segmentsTable);
+    connector.alterSegmentTable();
+
+    // drop legacy/unique indices and create the other type
+    connector.getDBI().withHandle(handle -> {
+      for (int i = 0; i < legacyIndexNames.size(); i++) {
+        handle.execute(StringUtils.toUpperCase(StringUtils.format(
+            "DROP INDEX %s",

Review Comment:
   We should avoid these formatters and instead just drop some of the indexes
   (preferably not all so that we can verify the difference in behaviour)
   
   e.g. `DROP INDEX IDX_ABC`.



##########
processing/src/main/java/org/apache/druid/metadata/MetadataStorageTablesConfig.java:
##########
@@ -72,6 +73,9 @@ public static MetadataStorageTablesConfig fromBase(String 
base)
   @JsonProperty("segmentSchemas")
   private final String segmentSchemasTable;
 
+  @JsonProperty("useUniqueIndexNames")
+  private final Boolean useUniqueIndexNames;

Review Comment:
   Since this will always be non-null
   ```suggestion
     private final boolean useUniqueIndexNames;
   ```



##########
server/src/main/java/org/apache/druid/metadata/SQLMetadataConnector.java:
##########
@@ -1106,43 +1168,51 @@ public ResultSet getIndexInfo(DatabaseMetaData 
databaseMetaData, String tableNam
   }
 
   /**
-   * create index on the table with retry if not already exist, to be called 
after createTable
+   * Create index on the table with retry if not already exist, to be called 
after createTable
+   * Format of index name is either specified via legacy {@param 
legacyIndexNameFormat} or {@code generateSHABasedIndexIdentifier}.
    *
    * @param tableName       Name of the table to create index on
-   * @param indexName       case-insensitive string index name, it helps to 
check the existing index on table
-   * @param indexCols       List of columns to be indexed on
+   * @param legacyIndexNameFormat   Template to create index ID (nullable for 
forwards compatibility with SHA-based indices)

Review Comment:
   Side note (non blocking):
   there can be another use for this nullability (assuming we don't need it for 
forward compat).
   
   ```java
   String legacyIndexName = legacyIndexNameFormat == null
     ? upperCase(StringUtils.format("IDX_%s_%s", tableName, columns.join(`_`)))
     : upperCase(StringUtils.format("IDX_%s", tableName);
   ```
   
   This will help simplify arguments to `createIndex` method. What do you think?



##########
server/src/main/java/org/apache/druid/metadata/SQLMetadataConnector.java:
##########
@@ -1206,4 +1276,27 @@ public static boolean isStatementException(Throwable e)
     return e instanceof StatementException ||
            (e instanceof CallbackFailedException && e.getCause() instanceof 
StatementException);
   }
+
+  /**
+   * Creates a unique index name of the format {@code idx_<tableName>_<SHA of 
table name + column list>}
+   * with an SHA length of {@value MAX_INDEX_HASH_LENGTH}.
+   *
+   * @param tableName the table name
+   * @param columns   the set of columns to create the index on 
(case-insensitive)
+   * @return unique index identifier
+   */
+  protected String generateUniqueIndexName(String tableName, List<String> 
columns)
+  {
+    final String prefix = "idx_" + tableName + "_";
+    final String tableAndColumnDigest = DigestUtils.sha1Hex(
+        StringUtils.format(
+            "%s_%s",
+            tableName,

Review Comment:
   I recall I had advised adding the table name in the generation of the digest.
   But now since we are already prefixing the table name, we may omit this.
   



##########
processing/src/main/java/org/apache/druid/metadata/MetadataStorageTablesConfig.java:
##########
@@ -174,4 +180,9 @@ public String getSegmentSchemasTable()
   {
     return segmentSchemasTable;
   }
+
+  public boolean getUseUniqueIndexNames()

Review Comment:
   ```suggestion
     public boolean isUseUniqueIndexNames()
   ```



##########
server/src/main/java/org/apache/druid/metadata/SQLMetadataConnector.java:
##########
@@ -1106,43 +1168,51 @@ public ResultSet getIndexInfo(DatabaseMetaData 
databaseMetaData, String tableNam
   }
 
   /**
-   * create index on the table with retry if not already exist, to be called 
after createTable
+   * Create index on the table with retry if not already exist, to be called 
after createTable
+   * Format of index name is either specified via legacy {@param 
legacyIndexNameFormat} or {@code generateSHABasedIndexIdentifier}.
    *
    * @param tableName       Name of the table to create index on
-   * @param indexName       case-insensitive string index name, it helps to 
check the existing index on table
-   * @param indexCols       List of columns to be indexed on
+   * @param legacyIndexNameFormat   Template to create index ID (nullable for 
forwards compatibility with SHA-based indices)

Review Comment:
   How does making this nullable help with forwards compat?



##########
server/src/main/java/org/apache/druid/metadata/SQLMetadataConnector.java:
##########
@@ -1206,4 +1276,27 @@ public static boolean isStatementException(Throwable e)
     return e instanceof StatementException ||
            (e instanceof CallbackFailedException && e.getCause() instanceof 
StatementException);
   }
+
+  /**
+   * Creates a unique index name of the format {@code idx_<tableName>_<SHA of 
table name + column list>}
+   * with an SHA length of {@value MAX_INDEX_HASH_LENGTH}.
+   *
+   * @param tableName the table name
+   * @param columns   the set of columns to create the index on 
(case-insensitive)
+   * @return unique index identifier
+   */
+  protected String generateUniqueIndexName(String tableName, List<String> 
columns)
+  {
+    final String prefix = "idx_" + tableName + "_";

Review Comment:
   Nit: doesn't really affect functionality but probably helps with visualizing 
the final index names.
   
   ```suggestion
       final String prefix = "IDX_" + tableName + "_";
   ```



##########
server/src/main/java/org/apache/druid/metadata/SQLMetadataConnector.java:
##########
@@ -1106,43 +1168,51 @@ public ResultSet getIndexInfo(DatabaseMetaData 
databaseMetaData, String tableNam
   }
 
   /**
-   * create index on the table with retry if not already exist, to be called 
after createTable
+   * Create index on the table with retry if not already exist, to be called 
after createTable
+   * Format of index name is either specified via legacy {@param 
legacyIndexNameFormat} or {@code generateSHABasedIndexIdentifier}.
    *
    * @param tableName       Name of the table to create index on
-   * @param indexName       case-insensitive string index name, it helps to 
check the existing index on table
-   * @param indexCols       List of columns to be indexed on
+   * @param legacyIndexNameFormat   Template to create index ID (nullable for 
forwards compatibility with SHA-based indices)
+   * @param indexCols       List of un-escaped columns to be indexed on 
(case-sensitive).

Review Comment:
   ```suggestion
      * @param indexCols       List of un-escaped column names to be indexed on 
(case-sensitive).
   ```



##########
server/src/main/java/org/apache/druid/metadata/SQLMetadataConnector.java:
##########
@@ -1038,18 +1089,29 @@ public void createSegmentSchemaTable(final String 
tableName)
                 + "  UNIQUE (fingerprint) \n"
                 + ")",
                 tableName, getSerialType(), getPayloadType()
-            ),
-            StringUtils.format("CREATE INDEX idx_%1$s_fingerprint ON 
%1$s(fingerprint)", tableName),
-            StringUtils.format("CREATE INDEX idx_%1$s_used ON %1$s(used, 
used_status_last_updated)", tableName)
+            )
         )
     );
+    final Set<String> createdIndexSet = getIndexOnTable(tableName);
+    createIndex(
+        tableName,
+        "idx_%s_fingerprint",

Review Comment:
   Since we are touching this anyway, should we capitalize the index name 
formats?
   I feel like it helps with visualizing the actual index name.
   
   ```suggestion
           "IDX_%s_FINGERPRINT",
   ```



##########
server/src/main/java/org/apache/druid/metadata/SQLMetadataConnector.java:
##########
@@ -371,15 +378,28 @@ public void createSegmentTable(final String tableName)
             StringUtils.format(
                 createStatementBuilder.toString(),
                 tableName, getPayloadType(), getQuoteString(), getCollation()
-            ),
-            StringUtils.format("CREATE INDEX idx_%1$s_used ON %1$s(used)", 
tableName),
-            StringUtils.format(
-                "CREATE INDEX idx_%1$s_datasource_used_end_start ON 
%1$s(dataSource, used, %2$send%2$s, start)",
-                tableName,
-                getQuoteString()
             )
         )
     );
+
+    final Set<String> createdIndexSet = getIndexOnTable(tableName);
+    createIndex(
+        tableName,
+        "idx_%1$s_used",
+        ImmutableList.of("used"),
+        createdIndexSet
+    );
+    createIndex(
+        tableName,
+        "idx_%1$s_datasource_used_end_start",
+        ImmutableList.of(
+            "dataSource",
+            "used",
+            StringUtils.format("%send%S", getQuoteString(), getQuoteString()),

Review Comment:
   I see. Thanks for checking.



##########
server/src/main/java/org/apache/druid/metadata/SQLMetadataConnector.java:
##########
@@ -371,15 +378,28 @@ public void createSegmentTable(final String tableName)
             StringUtils.format(
                 createStatementBuilder.toString(),
                 tableName, getPayloadType(), getQuoteString(), getCollation()
-            ),
-            StringUtils.format("CREATE INDEX idx_%1$s_used ON %1$s(used)", 
tableName),
-            StringUtils.format(
-                "CREATE INDEX idx_%1$s_datasource_used_end_start ON 
%1$s(dataSource, used, %2$send%2$s, start)",
-                tableName,
-                getQuoteString()
             )
         )
     );
+
+    final Set<String> createdIndexSet = getIndexOnTable(tableName);
+    createIndex(
+        tableName,
+        "idx_%s_used",
+        List.of("used"),
+        createdIndexSet
+    );
+    createIndex(
+        tableName,
+        "idx_%s_datasource_used_end_start",
+        List.of(
+            "dataSource",
+            "used",
+            StringUtils.format("%send%S", getQuoteString(), getQuoteString()),

Review Comment:
   ```suggestion
               StringUtils.format("%1$send%1$s", getQuoteString()),
   ```



##########
server/src/test/java/org/apache/druid/metadata/SQLMetadataConnectorTest.java:
##########
@@ -305,6 +307,170 @@ public void testIsTransientException()
     );
   }
 
+  @ParameterizedTest
+  @ValueSource(booleans = {true, false})
+  public void test_tableIndices_areNotReplaced_ifExist(boolean 
useUniqueIndexName)
+  {
+    tablesConfig = new MetadataStorageTablesConfig(
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        useUniqueIndexName
+    );
+    connector = new TestDerbyConnector(new MetadataStorageConnectorConfig(), 
tablesConfig);
+
+    final String segmentsTable = tablesConfig.getSegmentsTable();
+    final List<String> legacyIndexTemplates = Arrays.asList(
+        "idx_%1$s_used",
+        "idx_%1$s_datasource_used_end_start",
+        "idx_%1$s_datasource_upgraded_from_segment_id"
+    );
+    final List<List<String>> indexColumns = List.of(
+        List.of("used"),
+        List.of(
+            "dataSource",
+            "used",
+            StringUtils.format("%send%S", connector.getQuoteString(), 
connector.getQuoteString()),

Review Comment:
   Suggestion (doesn't need to be in this PR):
   
   To simplify this, we should probably add a method to `SQLMetadataConnector`,
   
   ```java
   public String quote(String column)
   {
      return StringUtils.format("%1$s%2$s%1$s", getQuoteString(), column);
   }
   ```
   



##########
server/src/test/java/org/apache/druid/metadata/SQLMetadataConnectorTest.java:
##########
@@ -305,6 +307,170 @@ public void testIsTransientException()
     );
   }
 
+  @ParameterizedTest
+  @ValueSource(booleans = {true, false})
+  public void test_tableIndices_areNotReplaced_ifExist(boolean 
useUniqueIndexName)
+  {
+    tablesConfig = new MetadataStorageTablesConfig(
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        useUniqueIndexName
+    );
+    connector = new TestDerbyConnector(new MetadataStorageConnectorConfig(), 
tablesConfig);
+
+    final String segmentsTable = tablesConfig.getSegmentsTable();
+    final List<String> legacyIndexTemplates = Arrays.asList(
+        "idx_%1$s_used",
+        "idx_%1$s_datasource_used_end_start",
+        "idx_%1$s_datasource_upgraded_from_segment_id"
+    );
+    final List<List<String>> indexColumns = List.of(
+        List.of("used"),
+        List.of(
+            "dataSource",
+            "used",
+            StringUtils.format("%send%S", connector.getQuoteString(), 
connector.getQuoteString()),
+            "start"
+        ),
+        List.of("dataSource", "upgraded_from_segment_id")
+    );
+    final List<String> legacyIndexNames = legacyIndexTemplates.stream()
+                                                              .map(template -> 
StringUtils.toUpperCase(StringUtils.format(
+                                                                  template,
+                                                                  segmentsTable
+                                                              )))
+                                                              
.collect(Collectors.toList());
+    final List<String> uniqueIndexNames = indexColumns.stream()
+                                                      .map(cols -> 
connector.generateUniqueIndexName(
+                                                          segmentsTable,
+                                                          cols
+                                                      ))
+                                                      
.collect(Collectors.toList());
+
+    connector.createSegmentTable(segmentsTable);
+    connector.alterSegmentTable();
+
+    // drop legacy/unique indices and create the other type
+    connector.getDBI().withHandle(handle -> {
+      for (int i = 0; i < legacyIndexNames.size(); i++) {
+        handle.execute(StringUtils.toUpperCase(StringUtils.format(
+            "DROP INDEX %s",
+            useUniqueIndexName ? uniqueIndexNames.get(i) : 
legacyIndexNames.get(i)
+        )));
+        handle.execute(StringUtils.format(
+            "CREATE INDEX %s ON %s(%s)",
+            useUniqueIndexName ? legacyIndexNames.get(i) : 
uniqueIndexNames.get(i),
+            segmentsTable,
+            String.join(",", indexColumns.get(i))
+        ));
+      }
+      return null;
+    });
+
+    // run again to index name checks are run and duplicate indices are not 
created
+    connector.createSegmentTable(segmentsTable);
+    connector.alterSegmentTable();
+    assertIndicesPresentOnTable(segmentsTable, useUniqueIndexName ? 
legacyIndexNames : uniqueIndexNames);
+
+    dropTable(segmentsTable);
+    connector.tearDown();
+  }
+
+  @ParameterizedTest
+  @ValueSource(booleans = {true, false})
+  public void test_tableIndices_areReplaced_IfNotExist(boolean 
useUniqueIndexName)
+  {
+    tablesConfig = new MetadataStorageTablesConfig(
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        null,
+        useUniqueIndexName
+    );
+    connector = new TestDerbyConnector(new MetadataStorageConnectorConfig(), 
tablesConfig);
+
+    final String segmentsTable = tablesConfig.getSegmentsTable();
+    final List<String> legacyIndexTemplates = Arrays.asList(
+        "idx_%1$s_used",
+        "idx_%1$s_datasource_used_end_start",
+        "idx_%1$s_datasource_upgraded_from_segment_id"
+    );
+    final List<List<String>> indexColumns = List.of(
+        List.of("used"),
+        List.of(
+            "dataSource",
+            "used",
+            StringUtils.format("%send%S", connector.getQuoteString(), 
connector.getQuoteString()),
+            "start"
+        ),
+        List.of("dataSource", "upgraded_from_segment_id")
+    );

Review Comment:
   Please simplify the other test too.



##########
server/src/test/java/org/apache/druid/metadata/SQLMetadataConnectorTest.java:
##########
@@ -305,6 +307,170 @@ public void testIsTransientException()
     );
   }
 
+  @ParameterizedTest
+  @ValueSource(booleans = {true, false})

Review Comment:
   These should be separate test cases.



##########
processing/src/main/java/org/apache/druid/metadata/MetadataStorageTablesConfig.java:
##########
@@ -72,6 +73,9 @@ public static MetadataStorageTablesConfig fromBase(String 
base)
   @JsonProperty("segmentSchemas")
   private final String segmentSchemasTable;
 
+  @JsonProperty("useUniqueIndexNames")
+  private final Boolean useUniqueIndexNames;

Review Comment:
   Setting this flag to false would imply that index names may not be unique 
anymore which is not exactly right.
   
   Maybe use some other name like `useShortIndexNames` or `useShaIndexNames` or 
something?
   



##########
server/src/main/java/org/apache/druid/metadata/SQLMetadataConnector.java:
##########
@@ -371,15 +378,28 @@ public void createSegmentTable(final String tableName)
             StringUtils.format(
                 createStatementBuilder.toString(),
                 tableName, getPayloadType(), getQuoteString(), getCollation()
-            ),
-            StringUtils.format("CREATE INDEX idx_%1$s_used ON %1$s(used)", 
tableName),
-            StringUtils.format(
-                "CREATE INDEX idx_%1$s_datasource_used_end_start ON 
%1$s(dataSource, used, %2$send%2$s, start)",
-                tableName,
-                getQuoteString()
             )
         )
     );
+
+    final Set<String> createdIndexSet = getIndexOnTable(tableName);
+    createIndex(
+        tableName,
+        "idx_%s_used",
+        List.of("used"),
+        createdIndexSet
+    );
+    createIndex(
+        tableName,
+        "idx_%s_datasource_used_end_start",
+        List.of(
+            "dataSource",
+            "used",
+            StringUtils.format("%send%S", getQuoteString(), getQuoteString()),

Review Comment:
   Why capitalize the second `%S`?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to