Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1143,6 +1143,33 @@ public static String getIdentifierQuoteString() {
return identifierQuoteString;
}

/**
* Returns the keyword or keyword phrase used by the active database to limit result sets.
*
* Depending on the database, this can be {@link ResultSetLimitKeyword#TOP},
* {@link ResultSetLimitKeyword#LIMIT}, or {@link ResultSetLimitKeyword#FETCH_FIRST}.
*
* @return the result-set limit keyword used by the current database.
* @see <a href="https://igniterealtime.atlassian.net/browse/OF-3277">OF-3277</a>
*/
public static ResultSetLimitKeyword getResultSetLimitKeyword()
{
return databaseType.getResultSetLimitKeyword();
}

/**
* Indicates if the result-set limit keyword is used as a prefix before the selected columns.
*
* This is true for databases such as SQL Server that use {@code SELECT TOP (...) ...}.
*
* @return {@code true} if the keyword is prefix-style, otherwise {@code false}.
* @see <a href="https://igniterealtime.atlassian.net/browse/OF-3277">OF-3277</a>
*/
public static boolean isResultSetLimitKeywordPrefix()
{
return databaseType.isResultSetLimitKeywordPrefix();
}
Comment thread
guusdk marked this conversation as resolved.

public static String getTestSQL(String driver) {
if (driver == null) {
return "select 1";
Expand Down Expand Up @@ -1196,5 +1223,58 @@ public String escapeIdentifier(final String keyword) {
return keyword;
}
}

/**
* Returns the keyword or keyword phrase that should be used to limit result sets for this database type.
*
* @return the result-set limit keyword, or a sensible default when the database type is unknown.
* @see <a href="https://igniterealtime.atlassian.net/browse/OF-3277">OF-3277</a>
*/
public ResultSetLimitKeyword getResultSetLimitKeyword()
{
return switch (this) {
case sqlserver -> ResultSetLimitKeyword.TOP;
case oracle, db2 -> ResultSetLimitKeyword.FETCH_FIRST;
default -> ResultSetLimitKeyword.LIMIT;
};
}

/**
* Indicates if the result-set limit keyword is used before the column list in a SELECT statement.
*
* @return {@code true} when the keyword is prefix-style (for example {@code TOP}), otherwise {@code false}.
* @see <a href="https://igniterealtime.atlassian.net/browse/OF-3277">OF-3277</a>
*/
public boolean isResultSetLimitKeywordPrefix()
{
return getResultSetLimitKeyword().isPrefix();
}
}

/**
* Identifies how a database limits result sets.
*
* @see <a href="https://igniterealtime.atlassian.net/browse/OF-3277">OF-3277</a>
*/
public enum ResultSetLimitKeyword
{
TOP(true),
FETCH_FIRST(false),
LIMIT(false);

Comment thread
guusdk marked this conversation as resolved.
private final boolean prefix;

ResultSetLimitKeyword(final boolean prefix) {
this.prefix = prefix;
}

/**
* Indicates if the keyword is used before the selected columns in a {@code SELECT} statement.
*
* @return {@code true} for prefix-style keywords such as {@code TOP}, otherwise {@code false}.
*/
public boolean isPrefix() {
return prefix;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1631,21 +1631,26 @@ else if (maxPublished != -1)
{
con = DbConnectionManager.getConnection();
// Get published items of the specified node
if (DbConnectionManager.getDatabaseType().equals(DbConnectionManager.DatabaseType.sqlserver)) {
pstmt = con.prepareStatement(LOAD_LAST_ITEMS_TOP);
} else if (DbConnectionManager.getDatabaseType().equals(DbConnectionManager.DatabaseType.oracle)) {
pstmt = con.prepareStatement(LOAD_LAST_ITEMS_FETCHFIRST);
} else {
pstmt = con.prepareStatement(LOAD_LAST_ITEMS_LIMIT);
switch (DbConnectionManager.getDatabaseType().getResultSetLimitKeyword()) {
case TOP:
pstmt = con.prepareStatement(LOAD_LAST_ITEMS_TOP);
break;
case FETCH_FIRST:
pstmt = con.prepareStatement(LOAD_LAST_ITEMS_FETCHFIRST);
break;
case LIMIT: // Intended fall-through
Comment thread
guusdk marked this conversation as resolved.
default:
pstmt = con.prepareStatement(LOAD_LAST_ITEMS_LIMIT);
break;
}
pstmt.setMaxRows(max);
int paramIndex = 0;
if (DbConnectionManager.getDatabaseType().equals(DbConnectionManager.DatabaseType.sqlserver)){
if (DbConnectionManager.getDatabaseType().isResultSetLimitKeywordPrefix()) {
pstmt.setLong(++paramIndex, max);
}
pstmt.setString(++paramIndex, node.getUniqueIdentifier().getServiceIdentifier().getServiceId());
pstmt.setString(++paramIndex, encodeNodeID(node.getNodeID()));
if (!DbConnectionManager.getDatabaseType().equals(DbConnectionManager.DatabaseType.sqlserver)){
if (!DbConnectionManager.getDatabaseType().isResultSetLimitKeywordPrefix()) {
pstmt.setLong(++paramIndex, max);
}
Comment thread
guusdk marked this conversation as resolved.
rs = pstmt.executeQuery();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (C) 2026 Ignite Realtime Foundation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.database;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class DbConnectionManagerTest {

/**
* Verifies that SQL Server uses TOP as a prefix-style row limiting keyword.
*/
@Test
public void sqlServerUsesTopAsAPrefixKeyword() {
assertEquals(DbConnectionManager.ResultSetLimitKeyword.TOP, DbConnectionManager.DatabaseType.sqlserver.getResultSetLimitKeyword(), "SQL Server should map to the TOP limit keyword.");
assertTrue(DbConnectionManager.DatabaseType.sqlserver.isResultSetLimitKeywordPrefix(), "TOP should be marked as a prefix-style keyword.");
}

/**
* Verifies that Oracle uses FETCH FIRST as a suffix-style row limiting keyword.
*/
@Test
public void oracleUsesFetchFirstAsASuffixKeyword() {
assertEquals(DbConnectionManager.ResultSetLimitKeyword.FETCH_FIRST, DbConnectionManager.DatabaseType.oracle.getResultSetLimitKeyword(), "Oracle should map to the FETCH_FIRST limit keyword.");
assertFalse(DbConnectionManager.DatabaseType.oracle.isResultSetLimitKeywordPrefix(), "FETCH_FIRST should be marked as a suffix-style keyword.");
}

/**
* Verifies that DB2 uses FETCH FIRST as a suffix-style row limiting keyword.
*/
@Test
public void db2UsesFetchFirstAsASuffixKeyword() {
assertEquals(DbConnectionManager.ResultSetLimitKeyword.FETCH_FIRST, DbConnectionManager.DatabaseType.db2.getResultSetLimitKeyword(), "DB2 should map to the FETCH_FIRST limit keyword.");
assertFalse(DbConnectionManager.DatabaseType.db2.isResultSetLimitKeywordPrefix(), "DB2 should use a suffix-style limit keyword.");
}

/**
* Verifies that common ANSI-style databases map to LIMIT.
*/
@Test
public void commonAnsiStyleDatabasesUseLimit() {
assertEquals(DbConnectionManager.ResultSetLimitKeyword.LIMIT, DbConnectionManager.DatabaseType.mysql.getResultSetLimitKeyword(), "MySQL should map to the LIMIT keyword.");
assertEquals(DbConnectionManager.ResultSetLimitKeyword.LIMIT, DbConnectionManager.DatabaseType.postgresql.getResultSetLimitKeyword(), "PostgreSQL should map to the LIMIT keyword.");
assertEquals(DbConnectionManager.ResultSetLimitKeyword.LIMIT, DbConnectionManager.DatabaseType.hsqldb.getResultSetLimitKeyword(), "HSQLDB should map to the LIMIT keyword.");
assertEquals(DbConnectionManager.ResultSetLimitKeyword.LIMIT, DbConnectionManager.DatabaseType.unknown.getResultSetLimitKeyword(), "Unknown database types should default to LIMIT.");
}

/**
* Verifies prefix-position metadata for all supported row limiting keywords.
*/
@Test
public void enumKnowsWhetherItIsPrefixStyle() {
assertTrue(DbConnectionManager.ResultSetLimitKeyword.TOP.isPrefix(), "TOP should be treated as a prefix-style keyword.");
assertFalse(DbConnectionManager.ResultSetLimitKeyword.FETCH_FIRST.isPrefix(), "FETCH_FIRST should be treated as a suffix-style keyword.");
assertFalse(DbConnectionManager.ResultSetLimitKeyword.LIMIT.isPrefix(), "LIMIT should be treated as a suffix-style keyword.");
}
}


Loading