From 88a3b32d427de8621afbbb1ff54ee9ce557a43ca Mon Sep 17 00:00:00 2001
From: Dessan Hemrayev <43703214+dessanhemrayev@users.noreply.github.com>
Date: Thu, 9 Oct 2025 11:25:54 +0300
Subject: [PATCH] fixes #265: Ensure cross-database compatibility for unread
message count query
In the "ofpresence" table, the "offlineDate" field has the data type "VARCHAR/CHAR" depending on the
database, and in the query we are trying to compare this field with the "sentDate" field, which has
the data type "BIGINT/INTEGER". This results in the following error:
```
ERROR: operator does not exist: bigint > character varying
LINE 3: JOIN ofPresence p ON (a.sentDate > p.offlineDate)
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
```
This change replaces implicit comparison with explicit, database-specific CAST expressions.
The correct CAST is selected at runtime based on the underlying database (MySQL, PostgreSQL,
SQL Server, Oracle, Sybase), ensuring reliable and portable behavior across all Openfire-supported DBs.
---
changelog.html | 1 +
.../rest/controller/MsgArchiveController.java | 30 +++++++++++++++----
2 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/changelog.html b/changelog.html
index 8a463878d..4c3de09d6 100644
--- a/changelog.html
+++ b/changelog.html
@@ -47,6 +47,7 @@
1.12.1 (to be determined)
- Now requires Openfire 5.1.0 or later
+ - [#265] - Ensure cross-database compatibility for unread message count query
- [#261] - Remove the deprecated 'userservice' endpoint
- [#259] - Prevent system property requests from affecting properties other than the one requested
- [#256] - Record configuration changes in audit log
diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MsgArchiveController.java b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MsgArchiveController.java
index f87ad819c..004a3abc0 100644
--- a/src/java/org/jivesoftware/openfire/plugin/rest/controller/MsgArchiveController.java
+++ b/src/java/org/jivesoftware/openfire/plugin/rest/controller/MsgArchiveController.java
@@ -22,6 +22,7 @@
import java.sql.SQLException;
import org.jivesoftware.database.DbConnectionManager;
+import org.jivesoftware.database.DbConnectionManager.DatabaseType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmpp.packet.JID;
@@ -36,12 +37,29 @@ public class MsgArchiveController {
/** The Constant INSTANCE. */
public static final MsgArchiveController INSTANCE = new MsgArchiveController();
+ /**
+ * Builds the SQL query for counting unread messages based on the underlying database type.
+ *
+ * @param databaseType the database connection used to detect the database product name
+ * @return the database-specific SQL query string for counting unread messages
+ */
+ private String buildUserMessageCountQuery(DatabaseType databaseType) {
+ String castExpr;
+ switch (databaseType) {
+ case mysql:
+ castExpr = "CAST(p.offlineDate AS SIGNED)"; break;
+ case oracle:
+ castExpr = "CAST(p.offlineDate AS NUMBER)"; break;
+ default:
+ // PostgreSQL, SQL Server, Sybase — all understand BIGINT in CAST
+ castExpr = "CAST(p.offlineDate AS BIGINT)";
+ break;
+ }
- /** The Constant USER_MESSAGE_COUNT. */
- private static final String USER_MESSAGE_COUNT = "select COUNT(1) from ofMessageArchive a " +
- "join ofPresence p on (a.sentDate > p.offlineDate) " +
+ return "SELECT COUNT(1) FROM ofMessageArchive a " +
+ "JOIN ofPresence p ON (a.sentDate > " + castExpr + ") " +
"WHERE a.toJID = ? AND p.username = ?";
-
+ }
/**
* Gets the single instance of MsgArchiveController.
*
@@ -70,7 +88,9 @@ public int getUnReadMessagesCount(JID jid) {
ResultSet rs = null;
try {
con = DbConnectionManager.getConnection();
- pstmt = con.prepareStatement(USER_MESSAGE_COUNT);
+ DatabaseType databaseType = DbConnectionManager.getDatabaseType();
+ String userMessageCountQuery = buildUserMessageCountQuery(databaseType);
+ pstmt = con.prepareStatement(userMessageCountQuery);
pstmt.setString(1, jid.toBareJID());
pstmt.setString(2, jid.getNode());
rs = pstmt.executeQuery();