This is an automated email from the ASF dual-hosted git repository.

chibenwa pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 3fd59f7afed71a4902b8f295d41d4ec817d51649
Author: Benoit TELLIER <[email protected]>
AuthorDate: Sat Sep 5 20:12:49 2026 +0200

    JAMES-4225 Remove flagUser collumn
---
 .../cassandra/mail/CassandraMessageIdDAO.java      | 11 ------
 .../mail/CassandraMessageIdToImapUidDAO.java       | 13 +------
 .../modules/CassandraMessageDataDefinition.java    |  2 -
 .../apache/james/mailbox/cassandra/table/Flag.java |  5 +--
 .../cassandra/mail/CassandraMessageIdDAOTest.java  |  8 ++--
 .../mail/CassandraMessageIdToImapUidDAOTest.java   |  8 ++--
 upgrade-instructions.md                            | 43 +++++++++++++---------
 7 files changed, 38 insertions(+), 52 deletions(-)

diff --git 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
index 3b7dc735f4..0d5401cc46 100644
--- 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
+++ 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
@@ -42,7 +42,6 @@ import static 
org.apache.james.mailbox.cassandra.table.Flag.DRAFT;
 import static org.apache.james.mailbox.cassandra.table.Flag.FLAGGED;
 import static org.apache.james.mailbox.cassandra.table.Flag.RECENT;
 import static org.apache.james.mailbox.cassandra.table.Flag.SEEN;
-import static org.apache.james.mailbox.cassandra.table.Flag.USER;
 import static org.apache.james.mailbox.cassandra.table.Flag.USER_FLAGS;
 import static 
org.apache.james.mailbox.cassandra.table.MessageIdToImapUid.MOD_SEQ;
 import static org.apache.james.util.ReactorUtils.publishIfPresent;
@@ -181,7 +180,6 @@ public class CassandraMessageIdDAO {
                 setColumn(FLAGGED, bindMarker(FLAGGED)),
                 setColumn(RECENT, bindMarker(RECENT)),
                 setColumn(SEEN, bindMarker(SEEN)),
-                setColumn(USER, bindMarker(USER)),
                 setColumn(INTERNAL_DATE, bindMarker(INTERNAL_DATE)),
                 setColumn(SAVE_DATE, bindMarker(SAVE_DATE)),
                 setColumn(BODY_START_OCTET, bindMarker(BODY_START_OCTET)),
@@ -213,7 +211,6 @@ public class CassandraMessageIdDAO {
                 setColumn(FLAGGED, bindMarker(FLAGGED)),
                 setColumn(RECENT, bindMarker(RECENT)),
                 setColumn(SEEN, bindMarker(SEEN)),
-                setColumn(USER, bindMarker(USER)),
                 append(USER_FLAGS, bindMarker(ADDED_USERS_FLAGS)),
                 remove(USER_FLAGS, bindMarker(REMOVED_USERS_FLAGS)))
             .where(column(MAILBOX_ID).isEqualTo(bindMarker(MAILBOX_ID)),
@@ -310,7 +307,6 @@ public class CassandraMessageIdDAO {
                     RECENT,
                     SEEN,
                     FLAGGED,
-                    USER,
                     USER_FLAGS,
                     MOD_SEQ)
                 .where(column(MAILBOX_ID).isEqualTo(bindMarker(MAILBOX_ID)),
@@ -372,7 +368,6 @@ public class CassandraMessageIdDAO {
             .setBoolean(FLAGGED, flags.contains(Flag.FLAGGED))
             .setBoolean(RECENT, flags.contains(Flag.RECENT))
             .setBoolean(SEEN, flags.contains(Flag.SEEN))
-            .setBoolean(USER, flags.contains(Flag.USER))
             .setInstant(INTERNAL_DATE, 
metadata.getInternalDate().get().toInstant())
             .setInstant(SAVE_DATE, 
metadata.getSaveDate().map(Date::toInstant).orElse(null))
             .setInt(BODY_START_OCTET, 
Math.toIntExact(metadata.getBodyStartOctet().get()))
@@ -433,11 +428,6 @@ public class CassandraMessageIdDAO {
         } else {
             statementBuilder.unset(SEEN);
         }
-        if (updatedFlags.isChanged(Flag.USER)) {
-            statementBuilder.setBoolean(USER, 
updatedFlags.isModifiedToSet(Flag.USER));
-        } else {
-            statementBuilder.unset(USER);
-        }
         Sets.SetView<String> removedFlags = Sets.difference(
             ImmutableSet.copyOf(updatedFlags.getOldFlags().getUserFlags()),
             ImmutableSet.copyOf(updatedFlags.getNewFlags().getUserFlags()));
@@ -671,7 +661,6 @@ public class CassandraMessageIdDAO {
             .setBoolean(FLAGGED, flags.contains(Flag.FLAGGED))
             .setBoolean(RECENT, flags.contains(Flag.RECENT))
             .setBoolean(SEEN, flags.contains(Flag.SEEN))
-            .setBoolean(USER, flags.contains(Flag.USER))
             .setInstant(INTERNAL_DATE, null)
             .setInt(BODY_START_OCTET, 0)
             .setLong(FULL_CONTENT_OCTETS, 0)
diff --git 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
index bfb7b7339e..a57973e133 100644
--- 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
+++ 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
@@ -40,7 +40,6 @@ import static 
org.apache.james.mailbox.cassandra.table.Flag.DRAFT;
 import static org.apache.james.mailbox.cassandra.table.Flag.FLAGGED;
 import static org.apache.james.mailbox.cassandra.table.Flag.RECENT;
 import static org.apache.james.mailbox.cassandra.table.Flag.SEEN;
-import static org.apache.james.mailbox.cassandra.table.Flag.USER;
 import static org.apache.james.mailbox.cassandra.table.Flag.USER_FLAGS;
 import static 
org.apache.james.mailbox.cassandra.table.MessageIdToImapUid.MOD_SEQ;
 import static 
org.apache.james.mailbox.cassandra.table.MessageIdToImapUid.TABLE_NAME;
@@ -149,7 +148,6 @@ public class CassandraMessageIdToImapUidDAO {
             .value(FLAGGED, bindMarker(FLAGGED))
             .value(RECENT, bindMarker(RECENT))
             .value(SEEN, bindMarker(SEEN))
-            .value(USER, bindMarker(USER))
             .value(USER_FLAGS, bindMarker(USER_FLAGS))
             .value(INTERNAL_DATE, bindMarker(INTERNAL_DATE))
             .value(SAVE_DATE, bindMarker(SAVE_DATE))
@@ -168,7 +166,6 @@ public class CassandraMessageIdToImapUidDAO {
                     setColumn(FLAGGED, bindMarker(FLAGGED)),
                     setColumn(RECENT, bindMarker(RECENT)),
                     setColumn(SEEN, bindMarker(SEEN)),
-                    setColumn(USER, bindMarker(USER)),
                     setColumn(INTERNAL_DATE, bindMarker(INTERNAL_DATE)),
                     setColumn(SAVE_DATE, bindMarker(SAVE_DATE)),
                     setColumn(BODY_START_OCTET, bindMarker(BODY_START_OCTET)),
@@ -202,8 +199,7 @@ public class CassandraMessageIdToImapUidDAO {
                 setColumn(DRAFT, bindMarker(DRAFT)),
                 setColumn(FLAGGED, bindMarker(FLAGGED)),
                 setColumn(RECENT, bindMarker(RECENT)),
-                setColumn(SEEN, bindMarker(SEEN)),
-                setColumn(USER, bindMarker(USER)))
+                setColumn(SEEN, bindMarker(SEEN)))
             .append(USER_FLAGS, bindMarker(ADDED_USERS_FLAGS))
             .remove(USER_FLAGS, bindMarker(REMOVED_USERS_FLAGS))
             .where(column(MESSAGE_ID).isEqualTo(bindMarker(MESSAGE_ID)),
@@ -267,7 +263,6 @@ public class CassandraMessageIdToImapUidDAO {
             .setBoolean(FLAGGED, flags.contains(Flag.FLAGGED))
             .setBoolean(RECENT, flags.contains(Flag.RECENT))
             .setBoolean(SEEN, flags.contains(Flag.SEEN))
-            .setBoolean(USER, flags.contains(Flag.USER))
             .setInstant(INTERNAL_DATE, 
metadata.getInternalDate().get().toInstant())
             .setInstant(SAVE_DATE, 
metadata.getSaveDate().map(Date::toInstant).orElse(null))
             .setInt(BODY_START_OCTET, 
Math.toIntExact(metadata.getBodyStartOctet().get()))
@@ -334,11 +329,6 @@ public class CassandraMessageIdToImapUidDAO {
         } else {
             statementBuilder.unset(SEEN);
         }
-        if (updatedFlags.isChanged(Flag.USER)) {
-            statementBuilder.setBoolean(USER, 
updatedFlags.isModifiedToSet(Flag.USER));
-        } else {
-            statementBuilder.unset(USER);
-        }
         Sets.SetView<String> removedFlags = Sets.difference(
             ImmutableSet.copyOf(updatedFlags.getOldFlags().getUserFlags()),
             ImmutableSet.copyOf(updatedFlags.getNewFlags().getUserFlags()));
@@ -460,7 +450,6 @@ public class CassandraMessageIdToImapUidDAO {
             .setBoolean(FLAGGED, flags.contains(Flag.FLAGGED))
             .setBoolean(RECENT, flags.contains(Flag.RECENT))
             .setBoolean(SEEN, flags.contains(Flag.SEEN))
-            .setBoolean(USER, flags.contains(Flag.USER))
             .setInstant(INTERNAL_DATE, null)
             .setInt(BODY_START_OCTET, 0)
             .setLong(FULL_CONTENT_OCTETS, 0)
diff --git 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMessageDataDefinition.java
 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMessageDataDefinition.java
index 10a6bfaaf4..92169be355 100644
--- 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMessageDataDefinition.java
+++ 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMessageDataDefinition.java
@@ -61,7 +61,6 @@ public interface CassandraMessageDataDefinition {
             .withColumn(Flag.FLAGGED, BOOLEAN)
             .withColumn(Flag.RECENT, BOOLEAN)
             .withColumn(Flag.SEEN, BOOLEAN)
-            .withColumn(Flag.USER, BOOLEAN)
             .withColumn(Flag.USER_FLAGS, setOf(TEXT))
             .withColumn(CassandraMessageV3Table.INTERNAL_DATE, TIMESTAMP)
             .withColumn(CassandraMessageIdTable.SAVE_DATE, TIMESTAMP)
@@ -86,7 +85,6 @@ public interface CassandraMessageDataDefinition {
             .withColumn(Flag.FLAGGED, BOOLEAN)
             .withColumn(Flag.RECENT, BOOLEAN)
             .withColumn(Flag.SEEN, BOOLEAN)
-            .withColumn(Flag.USER, BOOLEAN)
             .withColumn(Flag.USER_FLAGS, setOf(TEXT))
             .withColumn(CassandraMessageV3Table.INTERNAL_DATE, TIMESTAMP)
             .withColumn(CassandraMessageIdTable.SAVE_DATE, TIMESTAMP)
diff --git 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/Flag.java
 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/Flag.java
index fdf367b5ac..ac6a5ac011 100644
--- 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/Flag.java
+++ 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/Flag.java
@@ -30,7 +30,6 @@ public interface Flag {
     CqlIdentifier RECENT = CqlIdentifier.fromCql("flagRecent");
     CqlIdentifier SEEN = CqlIdentifier.fromCql("flagSeen");
     CqlIdentifier FLAGGED = CqlIdentifier.fromCql("flagFlagged");
-    CqlIdentifier USER = CqlIdentifier.fromCql("flagUser");
     CqlIdentifier USER_FLAGS = CqlIdentifier.fromCql("userFlags");
 
     CqlIdentifier[] ALL_LOWERCASE = {
@@ -39,8 +38,7 @@ public interface Flag {
         DRAFT,
         RECENT,
         SEEN,
-        FLAGGED,
-        USER
+        FLAGGED
     };
 
     ImmutableMap<CqlIdentifier, Flags.Flag> JAVAX_MAIL_FLAG = 
ImmutableMap.<CqlIdentifier, Flags.Flag>builder()
@@ -50,6 +48,5 @@ public interface Flag {
         .put(RECENT, Flags.Flag.RECENT)
         .put(SEEN, Flags.Flag.SEEN)
         .put(FLAGGED, Flags.Flag.FLAGGED)
-        .put(USER, Flags.Flag.USER)
         .build();
 }
\ No newline at end of file
diff --git 
a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAOTest.java
 
b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAOTest.java
index 3bb94fd39f..6aa9370f41 100644
--- 
a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAOTest.java
+++ 
b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAOTest.java
@@ -554,7 +554,7 @@ class CassandraMessageIdDAOTest {
     }
 
     @Test
-    void updateShouldUpdateUserFlag() {
+    void updateShouldNotPersistUserFlag() {
         CassandraMessageId messageId = messageIdFactory.generate();
         CassandraId mailboxId = CassandraId.timeBased();
         MessageUid messageUid = MessageUid.of(1);
@@ -574,9 +574,11 @@ class CassandraMessageIdDAOTest {
             .build())
             .block();
 
+        // \* is IMAP PERMANENTFLAGS syntax, a mailbox capability rather than 
a flag of a message: no
+        // protocol lets a client set it on one, so it is not stored.
         ComposedMessageIdWithMetaData expectedComposedMessageId = 
ComposedMessageIdWithMetaData.builder()
                 .composedMessageId(composedMessageId)
-                .flags(new Flags(Flags.Flag.USER))
+                .flags(new Flags())
                 .modSeq(ModSeq.of(2))
                 .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
@@ -585,7 +587,7 @@ class CassandraMessageIdDAOTest {
             .messageId(messageId)
             .modSeq(expectedComposedMessageId.getModSeq())
             .oldFlags(new Flags())
-            .newFlags(expectedComposedMessageId.getFlags())
+            .newFlags(new Flags(Flags.Flag.USER))
             .build();
 
         
testee.updateMetadata(expectedComposedMessageId.getComposedMessageId(), 
updatedFlags)
diff --git 
a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAOTest.java
 
b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAOTest.java
index c2cb662d7a..b420bc8c16 100644
--- 
a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAOTest.java
+++ 
b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAOTest.java
@@ -604,7 +604,7 @@ class CassandraMessageIdToImapUidDAOTest {
     }
 
     @Test
-    void updateShouldUpdateUserFlag() {
+    void updateShouldNotPersistUserFlag() {
         CassandraMessageId messageId = 
CassandraMessageId.Factory.of(Uuids.timeBased());
         CassandraId mailboxId = CassandraId.timeBased();
         MessageUid messageUid = MessageUid.of(1);
@@ -624,9 +624,11 @@ class CassandraMessageIdToImapUidDAOTest {
             .build())
             .block();
 
+        // \* is IMAP PERMANENTFLAGS syntax, a mailbox capability rather than 
a flag of a message: no
+        // protocol lets a client set it on one, so it is not stored.
         ComposedMessageIdWithMetaData expectedComposedMessageId = 
ComposedMessageIdWithMetaData.builder()
                 .composedMessageId(composedMessageId)
-                .flags(new Flags(Flags.Flag.USER))
+                .flags(new Flags())
                 .modSeq(ModSeq.of(2))
                 .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
@@ -635,7 +637,7 @@ class CassandraMessageIdToImapUidDAOTest {
             .messageId(messageId)
             .modSeq(ModSeq.of(2))
             .oldFlags(new Flags())
-            .newFlags(expectedComposedMessageId.getFlags())
+            .newFlags(new Flags(Flags.Flag.USER))
             .build();
 
         
testee.updateMetadata(expectedComposedMessageId.getComposedMessageId(), 
updatedFlags, ModSeq.of(1)).block();
diff --git a/upgrade-instructions.md b/upgrade-instructions.md
index 8f63beb31a..4e84aba178 100644
--- a/upgrade-instructions.md
+++ b/upgrade-instructions.md
@@ -23,9 +23,34 @@ Change list:
  - [JAMES-4210 POP3 USER/PASS requires TLS by 
default](#james-4210-pop3-userpass-requires-tls-by-default)
  - [JAMES-4210 ManageSieve SASL 
adoption](#james-4210-managesieve-sasl-adoption)
  - [JAMES-4225 Blob ids default to 128 bits of 
entropy](#james-4225-blob-ids-default-to-128-bits-of-entropy)
- - [Dropping the bodyOctets column of the Cassandra messagev3 
table](#dropping-the-bodyoctets-column-of-the-cassandra-messagev3-table)
+ - [Dropping unneeded Cassandra schema 
columns](#dropping-unneeded-cassandra-schema-columns)
  - [Cassandra schema version 16: mandatory message denormalization 
migration](#cassandra-schema-version-16-mandatory-message-denormalization-migration)
 
+### Dropping unneeded Cassandra schema columns
+
+Date: 05/09/2026
+
+Concerned products: James products using Cassandra as mailbox storage
+
+Three columns held no information and are no longer written:
+
+- `messagev3.bodyOctets`, never read back, the body size being derived from 
`fullContentOctets` and
+`bodyStartOctet`.
+- `messageIdTable.flagUser` and `imapUidTable.flagUser`, persisting the 
JavaMail `Flags.Flag.USER`. That
+flag is IMAP `PERMANENTFLAGS` syntax, `\*`, a property of a mailbox rather 
than of a message, that no
+protocol lets a client set on one: the column only ever held `false`.
+
+New messages stop paying for them right away. Reclaiming the space taken by 
existing rows requires
+dropping the columns manually, once the new version runs on every node:
+
+```sql
+ALTER TABLE james_keyspace.messagev3 DROP bodyOctets;
+ALTER TABLE james_keyspace.messageIdTable DROP flagUser;
+ALTER TABLE james_keyspace.imapUidTable DROP flagUser;
+```
+
+Disk space comes back progressively, as SSTables get compacted.
+
 ### Cassandra schema version 16: mandatory message denormalization migration
 
 Date: 05/09/2026
@@ -62,22 +87,6 @@ against its shorter counterpart until it is rewritten. To 
keep the previous beha
 james.blobid.entropy=256
 ```
 
-### Dropping the bodyOctets column of the Cassandra messagev3 table
-
-Date: 05/09/2026
-
-Concerned products: James products using Cassandra as mailbox storage
-
-The `messagev3.bodyOctets` column was written upon every message save but 
never read back: the body size
-is derived from `fullContentOctets` and `bodyStartOctet`. James no longer 
writes it.
-
-New messages stop paying for the column right away. Reclaiming the space taken 
by existing rows requires
-dropping the column manually, after upgrading all james servers:
-
-```sql
-ALTER TABLE james_keyspace.messagev3 DROP bodyOctets;
-```
-
 ### JAMES-4210 POP3 USER/PASS requires TLS by default
 
 Date: 16/07/2026


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

Reply via email to