feat(sqlserver): add sql server 2000 legacy support
This commit is contained in:
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);
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user