From 429a25b92b7036e6387c58edc26099a7cb68978e Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Thu, 01 Oct 2026 10:19:15 +0000
Subject: [PATCH] [#1134] Give a VLV row of backendstat show-index-status its Confidential cell, so that its record count lands under Record Count (#1139)

---
 opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java |   87 +++++++++++++++++++++
 opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/BackendStat.java                  |  104 ++++++++++++++-----------
 2 files changed, 145 insertions(+), 46 deletions(-)

diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/BackendStat.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/BackendStat.java
index 4ee981c..200c1b1 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/BackendStat.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/pluggable/BackendStat.java
@@ -314,6 +314,7 @@
    */
   public static int main(String[] args, OutputStream outStream, OutputStream errStream)
   {
+    JDKLogging.disableLogging();
     BackendStat app = new BackendStat(outStream, errStream);
     return app.run(args);
   }
@@ -328,7 +329,6 @@
   {
     this.out = NullOutputStream.wrapOrNullStream(out);
     this.err = NullOutputStream.wrapOrNullStream(err);
-    JDKLogging.disableLogging();
 
     LocalizableMessage toolDescription = INFO_DESCRIPTION_BACKEND_TOOL.get();
     this.parser = new SubCommandArgumentParser(getClass().getName(), toolDescription, false);
@@ -1022,58 +1022,16 @@
 
     try
     {
-      // Create a table of their properties.
-      TableBuilder builder = new TableBuilder();
-      int count = 0;
-
-      builder.appendHeading(INFO_LABEL_BACKEND_DEBUG_INDEX_NAME.get());
-      builder.appendHeading(INFO_LABEL_BACKEND_TOOL_RAW_DB_NAME.get());
-      builder.appendHeading(INFO_LABEL_BACKEND_DEBUG_INDEX_STATUS.get());
-      builder.appendHeading(INFO_LABEL_BACKEND_DEBUG_INDEX_CONFIDENTIAL.get());
-      builder.appendHeading(INFO_LABEL_BACKEND_DEBUG_RECORD_COUNT.get());
-      builder.appendHeading(INFO_LABEL_BACKEND_TOOL_INDEX_UNDEFINED_RECORD_COUNT.get());
-      builder.appendHeading(LocalizableMessage.raw("95%"));
-      builder.appendHeading(LocalizableMessage.raw("90%"));
-      builder.appendHeading(LocalizableMessage.raw("85%"));
-
       EntryContainer ec = rc.getEntryContainer(base);
       if (ec == null)
       {
         return printEntryContainerError(backend, base);
       }
 
+      // Create a table of their properties.
+      TableBuilder builder = new TableBuilder();
       Map<Index, StringBuilder> undefinedKeys = new HashMap<>();
-      for (AttributeIndex attrIndex : ec.getAttributeIndexes())
-      {
-        for (AttributeIndex.MatchingRuleIndex index : attrIndex.getNameToIndexes().values())
-        {
-          builder.startRow();
-          builder.appendCell(index.getName().getIndexId());
-          builder.appendCell(index.getName());
-          builder.appendCell(index.isTrusted());
-          builder.appendCell(index.isEncrypted());
-          if (index.isTrusted())
-          {
-            appendIndexStats(builder, ec, index, undefinedKeys);
-          }
-          else
-          {
-            appendStatsNoData(builder, 5);
-          }
-          count++;
-        }
-      }
-
-      for (VLVIndex vlvIndex : ec.getVLVIndexes())
-      {
-        builder.startRow();
-        builder.appendCell(vlvIndex.getName().getIndexId());
-        builder.appendCell(vlvIndex.getName());
-        builder.appendCell(vlvIndex.isTrusted());
-        builder.appendCell(getTreeRecordCount(ec, vlvIndex));
-        appendStatsNoData(builder, 4);
-        count++;
-      }
+      int count = appendIndexStatusTable(builder, ec, undefinedKeys);
 
       builder.print(new TextTablePrinter(out));
       out.print(INFO_LABEL_BACKEND_TOOL_TOTAL.get(count).toString());
@@ -1091,6 +1049,60 @@
     }
   }
 
+  /**
+   * Fills the table {@code show-index-status} prints for the indexes of an entry container, a row
+   * per index, and returns the number of its rows. The keys of an index that are over its entry
+   * limit are collected into {@code undefinedKeys}.
+   */
+  int appendIndexStatusTable(TableBuilder builder, EntryContainer ec, Map<Index, StringBuilder> undefinedKeys)
+  {
+    builder.appendHeading(INFO_LABEL_BACKEND_DEBUG_INDEX_NAME.get());
+    builder.appendHeading(INFO_LABEL_BACKEND_TOOL_RAW_DB_NAME.get());
+    builder.appendHeading(INFO_LABEL_BACKEND_DEBUG_INDEX_STATUS.get());
+    builder.appendHeading(INFO_LABEL_BACKEND_DEBUG_INDEX_CONFIDENTIAL.get());
+    builder.appendHeading(INFO_LABEL_BACKEND_DEBUG_RECORD_COUNT.get());
+    builder.appendHeading(INFO_LABEL_BACKEND_TOOL_INDEX_UNDEFINED_RECORD_COUNT.get());
+    builder.appendHeading(LocalizableMessage.raw("95%"));
+    builder.appendHeading(LocalizableMessage.raw("90%"));
+    builder.appendHeading(LocalizableMessage.raw("85%"));
+
+    int count = 0;
+    for (AttributeIndex attrIndex : ec.getAttributeIndexes())
+    {
+      for (AttributeIndex.MatchingRuleIndex index : attrIndex.getNameToIndexes().values())
+      {
+        builder.startRow();
+        builder.appendCell(index.getName().getIndexId());
+        builder.appendCell(index.getName());
+        builder.appendCell(index.isTrusted());
+        builder.appendCell(index.isEncrypted());
+        if (index.isTrusted())
+        {
+          appendIndexStats(builder, ec, index, undefinedKeys);
+        }
+        else
+        {
+          appendStatsNoData(builder, 5);
+        }
+        count++;
+      }
+    }
+
+    for (VLVIndex vlvIndex : ec.getVLVIndexes())
+    {
+      builder.startRow();
+      builder.appendCell(vlvIndex.getName().getIndexId());
+      builder.appendCell(vlvIndex.getName());
+      builder.appendCell(vlvIndex.isTrusted());
+      // A VLV index has no confidentiality setting, but its row still needs the cell of that column
+      appendStatsNoData(builder, 1);
+      builder.appendCell(getTreeRecordCount(ec, vlvIndex));
+      appendStatsNoData(builder, 4);
+      count++;
+    }
+    return count;
+  }
+
   private void appendStatsNoData(TableBuilder builder, int columns)
   {
     while (columns > 0)
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java
index 475a64e..f902196 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/backends/pluggable/PluggableBackendImplTestCase.java
@@ -18,6 +18,8 @@
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.forgerock.opendj.ldap.ModificationType.*;
+import static org.opends.messages.ToolMessages.INFO_LABEL_BACKEND_DEBUG_INDEX_CONFIDENTIAL;
+import static org.opends.messages.ToolMessages.INFO_LABEL_BACKEND_DEBUG_RECORD_COUNT;
 import static org.mockito.Mockito.*;
 import static org.opends.server.protocols.internal.InternalClientConnection.getRootConnection;
 import static org.opends.server.protocols.internal.Requests.newSearchRequest;
@@ -39,6 +41,9 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import com.google.common.io.Resources;
+import com.forgerock.opendj.cli.TableBuilder;
+import com.forgerock.opendj.cli.TablePrinter;
+import com.forgerock.opendj.cli.TableSerializer;
 import org.forgerock.opendj.ldap.*;
 import org.forgerock.opendj.ldap.schema.AttributeType;
 import org.forgerock.opendj.ldap.schema.CoreSchema;
@@ -1178,6 +1183,88 @@
     assertThat(backend.verifyBackend(config)).isEqualTo(0);
   }
 
+  /**
+   * Every row {@code backendstat show-index-status} prints has a cell under each of its headings.
+   * A VLV index, which has no confidentiality, shows {@code -} under Confidential, so that its
+   * record count lands under Record Count; the key counts that follow are {@code -} as well.
+   */
+  @Test
+  public void testShowIndexStatusPutsEachCellOfAVlvRowUnderItsHeading() throws Exception
+  {
+    final EntryContainer ec = backend.getRootContainer().getEntryContainer(testBaseDN);
+    final VLVIndex vlvIndex = ec.getVLVIndexes().iterator().next();
+    final long recordCount = backend.getRootContainer().getStorage().read(new ReadOperation<Long>()
+    {
+      @Override
+      public Long run(ReadableTransaction txn) throws Exception
+      {
+        return vlvIndex.getRecordCount(txn);
+      }
+    });
+
+    final ByteArrayOutputStream err = new ByteArrayOutputStream();
+    final TableBuilder builder = new TableBuilder();
+    final int count =
+        new BackendStat(null, err).appendIndexStatusTable(builder, ec, new HashMap<Index, StringBuilder>());
+    final CapturedTable table = new CapturedTable();
+    builder.print(table);
+
+    assertThat(err.toString()).as("errors").isEmpty();
+    assertThat(table.rows).hasSize(count);
+    for (List<String> row : table.rows)
+    {
+      assertThat(row).as("the cells of " + row.get(0) + " under " + table.headings).doesNotContain("");
+    }
+
+    List<String> vlvRow = null;
+    for (List<String> row : table.rows)
+    {
+      if (row.get(0).equals(vlvIndex.getName().getIndexId()))
+      {
+        vlvRow = row;
+      }
+    }
+    assertThat(vlvRow).as("the row of " + vlvIndex.getName()).isNotNull();
+    final int confidential = table.headings.indexOf(INFO_LABEL_BACKEND_DEBUG_INDEX_CONFIDENTIAL.get().toString());
+    final int recordCountColumn = table.headings.indexOf(INFO_LABEL_BACKEND_DEBUG_RECORD_COUNT.get().toString());
+    assertThat(vlvRow.get(confidential)).as(table.headings.get(confidential)).isEqualTo("-");
+    assertThat(vlvRow.get(recordCountColumn)).as(table.headings.get(recordCountColumn))
+        .isEqualTo(String.valueOf(recordCount));
+    assertThat(vlvRow.subList(recordCountColumn + 1, vlvRow.size())).containsExactly("-", "-", "-", "-");
+  }
+
+  /** The headings and the cells of a table, as a table printer is handed them. */
+  private static final class CapturedTable extends TablePrinter
+  {
+    private final List<String> headings = new ArrayList<>();
+    private final List<List<String>> rows = new ArrayList<>();
+
+    @Override
+    protected TableSerializer getSerializer()
+    {
+      return new TableSerializer()
+      {
+        @Override
+        public void addHeading(String s)
+        {
+          headings.add(s);
+        }
+
+        @Override
+        public void startRow()
+        {
+          rows.add(new ArrayList<String>());
+        }
+
+        @Override
+        public void addCell(String s)
+        {
+          rows.get(rows.size() - 1).add(s);
+        }
+      };
+    }
+  }
+
   @Test
   public void testRebuildDegradedIndex() throws Exception
   {

--
Gitblit v1.10.0