fix(jdbc): support Caché data grid pagination

Closes #3423
This commit is contained in:
t8y2 committed 2026-08-13 20:50:07 +08:00
1 parent 65cd4cfe28
commit fc6c5d4aa6
14 files changed
+160 -1

No files matched your search

+3
View File
@@ -900,6 +900,7 @@ export async function executeQuery(
catalog?: string;
fetchSize?: number;
pageSize?: number;
rowOffset?: number;
resultSessionId?: string;
clientSessionId?: string;
timeoutSecs?: number;
@@ -927,6 +928,7 @@ export async function executeMulti(
catalog?: string;
fetchSize?: number;
pageSize?: number;
rowOffset?: number;
maxResultBytes?: number;
resultKeyColumns?: string[];
tableDataPreview?: boolean;
@@ -970,6 +972,7 @@ export async function executeMultiWithProgress(
catalog?: string;
fetchSize?: number;
pageSize?: number;
rowOffset?: number;
maxResultBytes?: number;
resultKeyColumns?: string[];
tableDataPreview?: boolean;
+3
View File
@@ -1115,6 +1115,7 @@ export async function executeQuery(
catalog?: string;
fetchSize?: number;
pageSize?: number;
rowOffset?: number;
resultSessionId?: string;
clientSessionId?: string;
timeoutSecs?: number;
@@ -1146,6 +1147,7 @@ export async function executeMulti(
catalog?: string;
fetchSize?: number;
pageSize?: number;
rowOffset?: number;
maxResultBytes?: number;
resultKeyColumns?: string[];
tableDataPreview?: boolean;
@@ -1193,6 +1195,7 @@ export async function executeMultiWithProgress(
catalog?: string;
fetchSize?: number;
pageSize?: number;
rowOffset?: number;
maxResultBytes?: number;
resultKeyColumns?: string[];
tableDataPreview?: boolean;
@@ -19,6 +19,7 @@ export interface BuildTableSelectSqlOptions {
orderBy?: string;
limit?: number;
offset?: number;
useDriverRowOffset?: boolean;
whereInput?: string;
includeRowId?: boolean;
catalog?: string;
@@ -128,6 +128,44 @@ describe("queryStore table data refresh", () => {
expect(store.tabs.find((tab) => tab.id === archiveTabId)?.result).toBeUndefined();
});
it("uses JDBC ResultSet offset pagination for Caché data tabs", async () => {
mocks.getConnectionConfig.mockReturnValue({
id: "cache-1",
name: "Caché 2016",
db_type: "jdbc",
database: "USER",
connection_string: "jdbc:Cache://localhost:1972/USER",
query_timeout_secs: 30,
});
mocks.buildTableSelectSql.mockResolvedValue('SELECT * FROM "SS"."SS_User" ORDER BY "ID" ASC');
const { useQueryStore } = await import("@/stores/queryStore");
const store = useQueryStore();
const tabId = store.createTab("cache-1", "USER", "SS_User", "data", "SS");
store.setTableMeta(tabId, {
schema: "SS",
tableName: "SS_User",
tableType: "TABLE",
columns: [{ name: "ID", data_type: "%Library.Integer", is_nullable: false, column_default: null, is_primary_key: true, extra: null }],
primaryKeys: ["ID"],
});
const tab = store.tabs.find((candidate) => candidate.id === tabId)!;
tab.orderByInput = '"ID" ASC';
tab.resultPageLimit = 100;
tab.resultPageOffset = 100;
await store.refreshDataTab(tabId);
expect(mocks.buildTableSelectSql).toHaveBeenCalledWith(
expect.objectContaining({
databaseType: "iris",
limit: 100,
offset: 100,
useDriverRowOffset: true,
}),
);
expect(mocks.executeMulti).toHaveBeenCalledWith("cache-1", "USER", 'SELECT * FROM "SS"."SS_User" ORDER BY "ID" ASC', undefined, expect.any(String), expect.objectContaining({ maxRows: 100, fetchSize: 100, rowOffset: 100 }));
});
it("refreshes one targeted tab while preserving its query context", async () => {
const { useQueryStore } = await import("@/stores/queryStore");
const store = useQueryStore();
+4
View File
@@ -2766,6 +2766,7 @@ export const useQueryStore = defineStore("query", () => {
const orderBy = tab.orderByInput?.trim() || sortOrder;
const limit = tab.resultPageLimit ?? tableOpenPageLimit(settingsStore.editorSettings.tableOpenPageSize);
const offset = tab.resultPageOffset ?? 0;
const useDriverRowOffset = conn?.db_type === "jdbc" && effectiveDbType === "iris";
const refreshPreparationId = uuid();
// Reserve the tab synchronously before SQL construction yields so repeated
@@ -2789,6 +2790,7 @@ export const useQueryStore = defineStore("query", () => {
orderBy,
limit,
offset,
...(useDriverRowOffset ? { useDriverRowOffset: true } : {}),
});
if (!sql.trim()) throw new Error("Failed to build table refresh SQL");
const current = tabs.value.find((candidate) => candidate.id === id);
@@ -4577,6 +4579,7 @@ export const useQueryStore = defineStore("query", () => {
// connection-local state and avoid MySQL pool resets on every refresh.
const dataTabMeta = tab.mode === "data" ? tableMetaForDataTab(tab) : undefined;
const useTableDataPreview = canUseTableDataLargeValuePreview(effectiveDbType, dataTabMeta?.columns ?? [], dataTabMeta?.primaryKeys ?? []);
const useJdbcDriverRowOffset = tab.mode === "data" && conn?.db_type === "jdbc" && effectiveDbType === "iris";
const executionOptions = {
...(typeof pageLimit === "number"
? useAgentResultSession
@@ -4589,6 +4592,7 @@ export const useQueryStore = defineStore("query", () => {
}
: { maxRows: pageLimit, fetchSize: pageLimit }
: { maxRows: agentProtocolQueryResultMaxRows(queryResultMaxRows) }),
...(useJdbcDriverRowOffset && typeof pageOffset === "number" && pageOffset > 0 ? { rowOffset: pageOffset } : {}),
...(executionClientSessionId ? { clientSessionId: executionClientSessionId } : {}),
...(tab.mode === "data" && (effectiveDbType === "mysql" || effectiveDbType === "postgres")
? {