feat(sqlserver): add sql server 2000 legacy support

This commit is contained in:
zipg authored and GitHub committed 2026-09-01 00:33:06 +08:00
1 parent 69a032ed63
commit afe3b3f3c2
20 files changed
+682 -24

No files matched your search

@@ -1461,6 +1461,7 @@ async function loadServerFilterValues(columnIndex: number, searchValue: string)
const columnInfo = tableMeta.columns.find((column) => column.name === columnName);
const sql = await buildDataGridColumnDistinctValuesSql({
databaseType: resolvedDatabaseType.value,
driverProfile: props.connectionId ? connectionStore.getConfig(props.connectionId)?.driver_profile : undefined,
identifierQuote: connectionStore.connectionIdentifierQuote?.(props.connectionId),
catalog: tableMeta.catalog,
database: tableMeta.database,
@@ -198,6 +198,7 @@ async function searchTable(task: SearchTableTask, databaseType: DatabaseType, cu
const columns = await api.getColumns(props.prefillConnectionId, props.prefillDatabase, schema, task.table.name);
const query = await buildDatabaseSearchSql({
databaseType,
driverProfile: connection.value?.driver_profile,
schema: task.schema,
tableName: task.table.name,
columns,
@@ -446,8 +446,13 @@ export function useDataGridActions(activeTab: ComputedRef<QueryTab | undefined>)
tab.whereInput = whereInput ?? "";
const sql = await buildTableSql(tab, { limit, offset, whereInput, orderBy: orderBy ?? tab.orderByInput });
queryStore.updateSql(tab.id, sql);
const expectedNextOffset = appendResult ? tab.result?.rows.length : (tab.resultPageOffset ?? 0) + (tab.resultPageLimit ?? limit);
const continuesResultSession = offset === expectedNextOffset && limit === tab.resultPageLimit;
const connection = useConnectionStore().getConfig(tab.connectionId);
const isSqlServerLegacy = connection?.db_type === "sqlserver" && connection.driver_profile?.trim().toLowerCase() === "sqlserver-legacy";
const sessionId = isSqlServerLegacy && tab.result?.has_more && tab.result.session_id && continuesResultSession ? tab.result.session_id : undefined;
await queryStore.executeTabSql(tab.id, sql, {
pagination: { offset, limit },
pagination: { offset, limit, sessionId, clientSessionId: sessionId ? tab.resultClientSessionId : undefined },
...appendOptions,
preserveResultDuringExecution: true,
preserveTotalRowCountDuringExecution: true,
@@ -12,6 +12,9 @@ describe("databaseDriverManifest", () => {
expect(usesAgentCursorForQuery("mysql")).toBe(false);
expect(usesAgentCursorForQuery("jdbc")).toBe(true);
expect(usesAgentCursorForQuery("prestosql")).toBe(true);
expect(usesAgentCursorForQuery("sqlserver")).toBe(false);
expect(usesAgentCursorForQuery("sqlserver", "sqlserver-legacy")).toBe(true);
expect(usesAgentCursorForQuery("sqlserver", " SQLSERVER-LEGACY ")).toBe(true);
});
it("exposes connection defaults from the shared manifest", () => {
@@ -89,6 +89,7 @@ export interface DataGridColumnValuesFilterConditionOptions {
export interface DataGridColumnDistinctValuesSqlOptions {
databaseType?: DatabaseType;
driverProfile?: string;
identifierQuote?: string;
catalog?: string;
database?: string;
@@ -153,7 +153,8 @@ export function manifestDatabaseTypes(): DatabaseType[] {
return DATABASE_DRIVER_ENTRIES.map((entry) => entry.dbType);
}
export function usesAgentCursorForQuery(dbType?: DatabaseType): boolean {
export function usesAgentCursorForQuery(dbType?: DatabaseType, driverProfile?: string): boolean {
if (dbType === "sqlserver" && driverProfile?.trim().toLowerCase() === "sqlserver-legacy") return true;
const runtimeMode = databaseRuntimeMode(dbType);
return runtimeMode === "agent" || runtimeMode === "external";
}
@@ -3,6 +3,7 @@ import * as api from "@/lib/backend/api.ts";
export interface DatabaseSearchSqlOptions {
databaseType?: DatabaseType;
driverProfile?: string;
schema?: string;
tableName: string;
columns: ColumnInfo[];
@@ -76,4 +76,35 @@ describe("queryStore query result export", () => {
expect(request?.identifierQuote).toBe("[");
expect(mocks.connectionIdentifierQuote).toHaveBeenCalledWith("kingbase-1");
});
it("uses the Agent cursor for SQL Server legacy result export", async () => {
mocks.getConfig.mockReturnValue({
id: "sqlserver-2000",
name: "SQL Server 2000",
db_type: "sqlserver",
driver_profile: "sqlserver-legacy",
database: "master",
query_timeout_secs: 30,
});
const { useQueryStore } = await import("@/stores/queryStore");
const store = useQueryStore();
const tabId = store.createTab("sqlserver-2000", "master", "Query");
const tab = store.tabs.find((item) => item.id === tabId)!;
tab.sql = "SELECT * FROM dbo.Users";
tab.lastExecutedSql = tab.sql;
tab.result = {
columns: ["id"],
rows: [[1]],
affected_rows: 0,
execution_time_ms: 1,
};
const request = await store.buildQueryResultExportRequest(tabId, {
exportId: "export-legacy",
filePath: "users.csv",
format: "csv",
});
expect(request?.useAgentCursor).toBe(true);
});
});
+35 -12
View File
@@ -4714,7 +4714,7 @@ export const useQueryStore = defineStore("query", () => {
const targetDatabase = resumedExecutionTarget ? resumedExecutionTarget.database : targetContext?.scope === "connection" ? "" : (contextDatabase ?? executionTarget?.database ?? tab.database);
const targetSchema = resumedExecutionTarget ? resumedExecutionTarget.schema : targetContext?.scope === "connection" ? undefined : (databaseTargetContext?.schema ?? executionTarget?.schema ?? tab.schema);
const executionDatabase = dataTabExecutionDatabase(conn, targetDatabase, executionCatalog);
const useAgentCursor = usesAgentCursorForQuery(conn?.db_type);
const useAgentCursor = usesAgentCursorForQuery(conn?.db_type, conn?.driver_profile);
const queryTimeoutSecs = queryTimeoutSecsForConnection(conn, settingsStore.editorSettings.globalQueryTimeoutSecs);
if (!batchResume) {
const statementExecution =
@@ -5322,6 +5322,7 @@ export const useQueryStore = defineStore("query", () => {
const pagination = limitQueryPagination(requestedPagination, queryResultMaxRows);
pageLimit = pagination.limit;
pageOffset = pagination.offset;
useAgentResultSession = conn?.db_type === "sqlserver" && conn?.driver_profile?.trim().toLowerCase() === "sqlserver-legacy";
}
const executionSchema = connectionQueryExecutionSchema(conn, targetDatabase, targetSchema, tab.mode === "data");
@@ -6495,6 +6496,8 @@ export const useQueryStore = defineStore("query", () => {
let offset = 0;
const clientSessionId = tabClientSessionId(tab, "export");
const exportExecutionId = uuid();
const useAgentCursor = conn?.db_type === "sqlserver" && conn?.driver_profile?.trim().toLowerCase() === "sqlserver-legacy";
let sessionId: string | undefined;
try {
while (true) {
@@ -6514,23 +6517,43 @@ export const useQueryStore = defineStore("query", () => {
limit: pageLimit,
offset,
});
const results = await api.executeMulti(tab.connectionId, executionDatabase, sql, undefined, exportExecutionId, {
maxRows: pageLimit,
fetchSize: pageLimit,
clientSessionId,
catalog: tableMeta.catalog,
timeoutSecs: queryTimeoutSecs,
});
const results = await api.executeMulti(
tab.connectionId,
executionDatabase,
sql,
undefined,
exportExecutionId,
useAgentCursor
? {
maxRows: 2_147_483_647,
fetchSize: pageLimit,
pageSize: pageLimit,
resultSessionId: sessionId,
clientSessionId,
catalog: tableMeta.catalog,
timeoutSecs: queryTimeoutSecs,
}
: {
maxRows: pageLimit,
fetchSize: pageLimit,
clientSessionId,
catalog: tableMeta.catalog,
timeoutSecs: queryTimeoutSecs,
},
);
const result = results[0];
if (!result) break;
if (columns.length === 0) columns = result.columns;
rows.push(...result.rows);
executionTimeMs += result.execution_time_ms ?? 0;
onProgress?.({ rowsExported: rows.length, totalRows });
if (result.rows.length < pageLimit) break;
sessionId = result.session_id ?? undefined;
const shouldFetchNextPage = useAgentCursor ? result.has_more === true : result.rows.length >= pageLimit;
if (!shouldFetchNextPage) break;
offset += result.rows.length;
}
} finally {
if (sessionId) void api.closeQuerySession(tab.connectionId, executionDatabase, sessionId, clientSessionId, tableMeta.catalog);
void closeClientSessionId(tab.connectionId, executionDatabase, clientSessionId, tableMeta.catalog, { tabId: tab.id });
}
@@ -6558,7 +6581,7 @@ export const useQueryStore = defineStore("query", () => {
// main 引入全局查询超时:queryTimeoutSecsForConnection 现需传入全局默认值;
// settingsStore 取 defineStore 顶层声明的实例(本函数无局部覆盖)。
const queryTimeoutSecs = queryTimeoutSecsForConnection(conn, settingsStore.editorSettings.globalQueryTimeoutSecs);
const useAgentCursor = usesAgentCursorForQuery(conn?.db_type);
const useAgentCursor = usesAgentCursorForQuery(conn?.db_type, conn?.driver_profile);
const queryBaseSql = queryResultBaseSql(tab);
const exportSettings = useSettingsStore().editorSettings;
const exportRowLimit = exportSettings.exportRowLimitEnabled ? exportSettings.exportRowLimit : Number.POSITIVE_INFINITY;
@@ -6699,7 +6722,7 @@ export const useQueryStore = defineStore("query", () => {
const effectiveDbType = effectiveDatabaseTypeForConnection(conn);
if (!effectiveDbType) return undefined;
if (effectiveDbType === "mongodb") return undefined;
const useAgentCursor = usesAgentCursorForQuery(conn?.db_type);
const useAgentCursor = usesAgentCursorForQuery(conn?.db_type, conn?.driver_profile);
const queryBaseSql = queryResultBaseSql(tab);
const resultStatementIndex = tab.result.statement_index;
const batchSql = tab.resultBaseSql ?? tab.lastExecutedSql ?? tab.sql;
@@ -6757,7 +6780,7 @@ export const useQueryStore = defineStore("query", () => {
sql,
queryBaseSql: sql,
databaseType: effectiveDbType,
useAgentCursor: usesAgentCursorForQuery(conn?.db_type),
useAgentCursor: usesAgentCursorForQuery(conn?.db_type, conn?.driver_profile),
filePath,
format,
pageSize: settings.exportBatchSize,