fix(sqlserver): preserve local temp table count sessions
This commit is contained in:
6 files changed
+158
-8
No files matched your search
@@ -1807,6 +1807,7 @@ defineExpose({
|
||||
:page-offset="activeTab.resultPageOffset"
|
||||
:page-limit="activeTab.resultPageLimit"
|
||||
:count-sql="activeTab.resultCountSql"
|
||||
:count-total-rows="activeTab.resultCountSql ? () => queryStore.countTabResultRows(activeTab.id) : undefined"
|
||||
:total-row-count="activeTab.resultTotalRowCount"
|
||||
:total-row-count-is-exact="activeTab.resultTotalRowCount !== undefined || activeTab.result.total_is_exact !== false"
|
||||
:total-row-count-loading="activeTab.resultTotalRowCountLoading"
|
||||
|
||||
@@ -30,6 +30,7 @@ describe("ContentArea query result grid", () => {
|
||||
expect(queryGridBlock).toContain("<DataGrid");
|
||||
expect(queryGridBlock).toContain(':connection-id="activeResultConnectionId"');
|
||||
expect(queryGridBlock).toContain(':count-sql="activeTab.resultCountSql"');
|
||||
expect(queryGridBlock).toContain(':count-total-rows="activeTab.resultCountSql ? () => queryStore.countTabResultRows(activeTab.id) : undefined"');
|
||||
expect(queryGridBlock).toContain(':total-row-count="activeTab.resultTotalRowCount"');
|
||||
expect(queryGridBlock).toContain(':total-row-count-loading="activeTab.resultTotalRowCountLoading"');
|
||||
});
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { tokenizeSqlSemantic, unquoteSqlSemanticIdentifier } from "@/lib/sql/semantic/tokens";
|
||||
import type { DatabaseType } from "@/types/database";
|
||||
|
||||
export function sqlServerCountUsesLocalTempTable(databaseType: DatabaseType | undefined, sql: string): boolean {
|
||||
if (databaseType !== "sqlserver" || !sql) return false;
|
||||
|
||||
return tokenizeSqlSemantic(sql, "sqlserver").some((token) => {
|
||||
if (token.kind !== "word" && token.kind !== "quoted_identifier") return false;
|
||||
const identifier = token.kind === "quoted_identifier" ? unquoteSqlSemanticIdentifier(token) : token.text;
|
||||
return identifier.startsWith("#") && !identifier.startsWith("##");
|
||||
});
|
||||
}
|
||||
@@ -48,6 +48,7 @@ import { buildTableSelectSql, quoteTableDataIdentifier } from "@/lib/table/table
|
||||
import { connectionObjectTreeNodeSchema, connectionQueryExecutionSchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, gaussdbCountQueryDopHint, metadataSchemaForConnection } from "@/lib/database/jdbcDialect";
|
||||
import { frontendQueryTimeoutDelayMs, frontendQueryTimeoutSecsForSql, queryTimeoutSecsForConnection } from "@/lib/sql/queryTimeout";
|
||||
import { queryResultNameFromPreamble, queryResultSourceLabel } from "@/lib/sql/queryResultSource";
|
||||
import { sqlServerCountUsesLocalTempTable } from "@/lib/query/queryResultCountSession";
|
||||
import { beginDataGridNativeSelectionBlock, finishDataGridNativeSelectionBlock } from "@/lib/dataGrid/dataGridNativeSelection";
|
||||
import { appendLargeValueCells, canUseTableDataLargeValuePreview, remapLargeValueCells, tableDataLargeValuePreviewOptions, TABLE_DATA_RESULT_MAX_BYTES } from "@/lib/dataGrid/dataGridLargeValues";
|
||||
import { simpleDataGridOrderByReferencesMissingColumn, sortDataGridRowIndexes, type DataGridSortDirection } from "@/lib/dataGrid/dataGridSort";
|
||||
@@ -4497,6 +4498,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
traceId: string;
|
||||
elapsed: () => string;
|
||||
timeoutSecs: number;
|
||||
txnSessionId?: string;
|
||||
}) {
|
||||
const resultRowCount = options.result.rows.length;
|
||||
if (resultRowCount <= 0) {
|
||||
@@ -4519,9 +4521,9 @@ export const useQueryStore = defineStore("query", () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const clientSessionId = tabClientSessionId({ id: options.tabId }, "count");
|
||||
const countExecutionId = `${options.executionId}:count`;
|
||||
void (async () => {
|
||||
let isolatedClientSessionId: string | undefined;
|
||||
try {
|
||||
const countTarget = options.countSql ? { sql: options.countSql, schema: options.schema } : await options.countSqlTarget?.();
|
||||
if (!countTarget?.sql) {
|
||||
@@ -4529,12 +4531,23 @@ export const useQueryStore = defineStore("query", () => {
|
||||
return;
|
||||
}
|
||||
queryExecutionLog("info", "count:start", { traceId: options.traceId, elapsed: options.elapsed() });
|
||||
const countResult = await api.executeQuery(options.connectionId, options.database, countTarget.sql, countTarget.schema, countExecutionId, {
|
||||
clientSessionId,
|
||||
catalog: options.catalog,
|
||||
timeoutSecs: options.timeoutSecs,
|
||||
});
|
||||
const total = Number(countResult.rows?.[0]?.[0] ?? 0);
|
||||
let countResult: QueryResult | undefined;
|
||||
if (options.txnSessionId) {
|
||||
const txnResults = await api.executeInManualTransaction(options.txnSessionId, countTarget.sql, options.database, countTarget.schema, 1);
|
||||
countResult = txnResults.find((result) => result.columns.length > 0) ?? txnResults[txnResults.length - 1];
|
||||
} else {
|
||||
const connection = useConnectionStore().getConfig(options.connectionId);
|
||||
const databaseType = effectiveDatabaseTypeForConnection(connection);
|
||||
const usesQuerySession = sqlServerCountUsesLocalTempTable(databaseType, countTarget.sql);
|
||||
const clientSessionId = usesQuerySession ? tabClientSessionId({ id: options.tabId }) : tabClientSessionId({ id: options.tabId }, "count");
|
||||
if (!usesQuerySession) isolatedClientSessionId = clientSessionId;
|
||||
countResult = await api.executeQuery(options.connectionId, options.database, countTarget.sql, countTarget.schema, countExecutionId, {
|
||||
clientSessionId,
|
||||
catalog: options.catalog,
|
||||
timeoutSecs: options.timeoutSecs,
|
||||
});
|
||||
}
|
||||
const total = Number(countResult?.rows?.[0]?.[0] ?? 0);
|
||||
if (!Number.isFinite(total) || total < 0) {
|
||||
setQueryTotalRowCountIfCurrent(options.tabId, options.executionId, options.result, undefined);
|
||||
return;
|
||||
@@ -4555,11 +4568,48 @@ export const useQueryStore = defineStore("query", () => {
|
||||
error,
|
||||
});
|
||||
} finally {
|
||||
void closeClientSessionId(options.connectionId, options.database, clientSessionId, options.catalog, { tabId: options.tabId });
|
||||
if (isolatedClientSessionId) {
|
||||
void closeClientSessionId(options.connectionId, options.database, isolatedClientSessionId, options.catalog, { tabId: options.tabId });
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
async function countTabResultRows(id: string): Promise<number | undefined> {
|
||||
const tab = tabs.value.find((candidate) => candidate.id === id);
|
||||
if (!tab?.resultCountSql) return undefined;
|
||||
|
||||
const target = queryResultExecutionLocation(tab);
|
||||
const connection = useConnectionStore().getConfig(target.connectionId);
|
||||
const databaseType = effectiveDatabaseTypeForConnection(connection);
|
||||
let countResult: QueryResult | undefined;
|
||||
let isolatedClientSessionId: string | undefined;
|
||||
|
||||
try {
|
||||
if (tab.autoCommit === false && tab.txnSessionId) {
|
||||
const txnResults = await api.executeInManualTransaction(tab.txnSessionId, tab.resultCountSql, target.database, target.schema, 1);
|
||||
countResult = txnResults.find((result) => result.columns.length > 0) ?? txnResults[txnResults.length - 1];
|
||||
} else {
|
||||
const usesQuerySession = sqlServerCountUsesLocalTempTable(databaseType, tab.resultCountSql);
|
||||
const clientSessionId = usesQuerySession ? tabClientSessionId(tab) : tabClientSessionId(tab, "count");
|
||||
if (!usesQuerySession) isolatedClientSessionId = clientSessionId;
|
||||
countResult = await api.executeQuery(target.connectionId, target.database, tab.resultCountSql, target.schema, undefined, {
|
||||
clientSessionId,
|
||||
catalog: target.catalog,
|
||||
maxRows: 1,
|
||||
timeoutSecs: queryTimeoutSecsForConnection(connection, useSettingsStore().editorSettings.globalQueryTimeoutSecs),
|
||||
});
|
||||
}
|
||||
|
||||
const total = Number(countResult?.rows?.[0]?.[0]);
|
||||
return Number.isFinite(total) && total >= 0 ? total : undefined;
|
||||
} finally {
|
||||
if (isolatedClientSessionId) {
|
||||
void closeClientSessionId(target.connectionId, target.database, isolatedClientSessionId, target.catalog, { tabId: tab.id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function executeTabSql(
|
||||
id: string,
|
||||
sql: string,
|
||||
@@ -5647,6 +5697,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
traceId,
|
||||
elapsed,
|
||||
timeoutSecs: queryTimeoutSecs,
|
||||
txnSessionId: current.autoCommit === false ? current.txnSessionId : undefined,
|
||||
});
|
||||
}
|
||||
queryExecutionLog("info", "result:assigned", {
|
||||
@@ -6969,6 +7020,7 @@ export const useQueryStore = defineStore("query", () => {
|
||||
exportResultArchive,
|
||||
importResultArchive,
|
||||
fetchTabResultForExport,
|
||||
countTabResultRows,
|
||||
buildQueryResultExportRequest,
|
||||
exportQuerySqlDirect,
|
||||
getResourceLifecycleDiagnostics: () => resourceLifecycleDiagnostics(tabs.value),
|
||||
|
||||
Reference in new issue
Block a user