feat(opengauss): support A-compatibility packages

This commit is contained in:
amwps290 authored and GitHub committed 2026-09-14 16:13:44 +08:00
1 parent 760cad6d1b
commit deb47df2eb
28 files changed
+1706 -139

No files matched your search

+156 -19
View File
@@ -461,6 +461,11 @@ export const useConnectionStore = defineStore("connection", () => {
const etcdAccessCapabilityGenerations = new Map<string, number>();
const etcdAccessCapabilityLoads = new Map<string, Promise<EtcdAccessCapabilities>>();
const identifierQuotes = ref<Record<string, string>>({});
/** Per-database compatibility mode (key `${connectionId}\u0000${database}`),
* decoupled from sidebar tree-node lifecycle so editor parsing and sidebar
* capabilities stay correct before/independent of database-tree rendering. */
const databaseCompatibilityModes = ref<Record<string, string>>({});
const databaseCompatibilityRefreshes = new Map<string, Promise<void>>();
const lastConnectionHealthCheckAt = ref<Record<string, number>>({});
const agentDrivers = ref<AgentDriverInstallState[]>([]);
let agentDriversRefreshPromise: Promise<void> | null = null;
@@ -788,13 +793,130 @@ export const useConnectionStore = defineStore("connection", () => {
return identifierQuotes.value[connectionId];
}
function databaseCompatibilityKey(connectionId: string, database: string): string {
return `${connectionId}\u0000${database.trim().toLowerCase()}`;
}
function databaseCompatibilityModeFromTree(connectionId: string, database: string): string | undefined {
const wanted = database.trim().toLowerCase();
const visit = (nodes: readonly TreeNode[]): string | undefined => {
for (const node of nodes) {
if (node.connectionId === connectionId && node.type === "database" && node.database?.trim().toLowerCase() === wanted) {
return node.compatibilityMode?.trim() || undefined;
}
const nested = node.children ? visit(node.children) : undefined;
if (nested) return nested;
}
return undefined;
};
return visit(treeNodes.value);
}
/** Compatibility mode for one database. Live metadata wins over persisted tree data. */
function databaseCompatibilityMode(connectionId: string | undefined, database: string | undefined): string | undefined {
if (!connectionId || database == null) return undefined;
return databaseCompatibilityModes.value[databaseCompatibilityKey(connectionId, database)] ?? databaseCompatibilityModeFromTree(connectionId, database);
}
function setDatabaseCompatibilityModesFromDatabases(connectionId: string, databases: readonly { name: string; compatibility_mode?: string | null }[]) {
if (!connectionId) return;
const prefix = `${connectionId}\u0000`;
const next = { ...databaseCompatibilityModes.value };
// Prune entries for databases that no longer exist on this connection.
let changed = false;
for (const key of Object.keys(next)) {
if (key.startsWith(prefix) && !databases.some((database) => databaseCompatibilityKey(connectionId, database.name.trim()) === key)) {
delete next[key];
changed = true;
}
}
for (const database of databases) {
const name = database.name.trim();
if (!name) continue;
const mode = database.compatibility_mode?.trim() || undefined;
const key = databaseCompatibilityKey(connectionId, name);
if (mode !== undefined && next[key] !== mode) {
next[key] = mode;
changed = true;
} else if (mode === undefined && key in next) {
delete next[key];
changed = true;
}
}
if (changed) databaseCompatibilityModes.value = next;
}
/** Ensure the compatibility map is warm for an openGauss connection even when
* the database tree has not loaded (restored tab, filtered node, …). Runs in
* the background (void caller) and is timeout-bounded so it never blocks or
* hangs the connect flow. The connection-state revision guards against stale
* refreshes overwriting a newer connection's map after a disconnect/reconnect. */
async function refreshConnectionDatabaseModesOnce(connectionId: string, config: ConnectionConfig) {
try {
if (config.db_type !== "opengauss") return;
const revision = connectionStateRevision(connectionId);
const databases = await withMetadataLoadTimeout(connectionId, api.listDatabases(connectionId), "compatibility modes").catch(() => undefined);
if (!databases || !isCurrentConnectionStateRevision(connectionId, revision)) return;
const before = databaseCompatibilityModes.value;
setDatabaseCompatibilityModesFromDatabases(connectionId, databases);
if (databaseCompatibilityModes.value === before) return;
// The mode map changed; invalidate completion caches for every affected
// database. The completion cache key does not include the compatibility
// mode, so a result cached while the mode was unknown/wrong (e.g. package
// members surfaced as top-level routines) would otherwise be served forever.
for (const database of databases) {
invalidateCompletionCache(connectionId, database.name.trim());
}
// Refresh any expanded openGauss database subtree so PACKAGE groups appear
// without waiting for the next manual expand.
for (const database of databases) {
if (!database.compatibility_mode?.trim()) continue;
if (database.compatibility_mode.trim().toUpperCase() !== "A") continue;
const node = findNode(treeNodes.value, `${connectionId}:${database.name.trim()}`);
if (!node || node.type !== "database" || !node.isExpanded) continue;
void refreshTreeNode(node).catch(() => undefined);
}
} catch {
// Compatibility metadata is optional and must never create an unhandled
// rejection in the background connection lifecycle.
}
}
function refreshConnectionDatabaseModes(connectionId: string, config: ConnectionConfig): Promise<void> {
const existing = databaseCompatibilityRefreshes.get(connectionId);
if (existing) return existing;
const refresh = refreshConnectionDatabaseModesOnce(connectionId, config).finally(() => {
if (databaseCompatibilityRefreshes.get(connectionId) === refresh) databaseCompatibilityRefreshes.delete(connectionId);
});
databaseCompatibilityRefreshes.set(connectionId, refresh);
return refresh;
}
async function ensureDatabaseCompatibilityMode(connectionId: string, database: string | undefined): Promise<string | undefined> {
const config = getConfig(connectionId);
if (config?.db_type !== "opengauss" || database == null) return undefined;
const current = databaseCompatibilityMode(connectionId, database);
if (current !== undefined) return current;
await refreshConnectionDatabaseModes(connectionId, { ...config, id: connectionId });
return databaseCompatibilityMode(connectionId, database);
}
function clearConnectionIdentifierQuote(connectionId: string) {
clearConnectionDatabaseModes(connectionId);
if (!(connectionId in identifierQuotes.value)) return;
const next = { ...identifierQuotes.value };
delete next[connectionId];
identifierQuotes.value = next;
}
function clearConnectionDatabaseModes(connectionId: string) {
const prefix = `${connectionId}\u0000`;
const entries = Object.entries(databaseCompatibilityModes.value).filter(([key]) => !key.startsWith(prefix));
if (entries.length !== Object.keys(databaseCompatibilityModes.value).length) {
databaseCompatibilityModes.value = Object.fromEntries(entries);
}
}
async function refreshConnectionIdentifierQuote(connectionId: string, config: ConnectionConfig) {
clearConnectionIdentifierQuote(connectionId);
if (!connectionShouldLoadIdentifierQuote(config)) return;
@@ -1902,23 +2024,26 @@ export const useConnectionStore = defineStore("connection", () => {
return config?.db_type === "informix" ? `${version}-informix-owner-v2` : version;
}
function supportedSidebarObjectTypes(config?: ConnectionConfig): DatabaseObjectTreeKind[] {
function supportedSidebarObjectTypes(config?: ConnectionConfig, database?: string): DatabaseObjectTreeKind[] {
const dbType = effectiveDatabaseTypeForConnection(config);
return sidebarObjectKindsForDatabase(dbType);
return sidebarObjectKindsForDatabase(dbType, databaseCompatibilityMode(config?.id, database));
}
function sidebarObjectTypesForScope(config: ConnectionConfig | undefined, schema?: string): DatabaseObjectTreeKind[] {
function sidebarObjectTypesForScope(config: ConnectionConfig | undefined, database: string | undefined, schema?: string): DatabaseObjectTreeKind[] {
if (config?.db_type === "xugu" && isXuguPublicSynonymScope(schema)) {
return ["SYNONYM"];
}
if (config?.db_type === "xugu" && isXuguSchedulerJobScope(schema)) {
return ["JOB"];
}
return supportedSidebarObjectTypes(config);
return supportedSidebarObjectTypes(config, database);
}
function objectTreeCacheVersion(config: ConnectionConfig | undefined, schema: string | undefined, baseVersion: string): string {
const scopedVersion = config?.db_type === "xugu" && isXuguPublicSynonymScope(schema) ? `${baseVersion}-public-synonyms` : config?.db_type === "xugu" && isXuguSchedulerJobScope(schema) ? `${baseVersion}-scheduler-jobs` : baseVersion;
function objectTreeCacheVersion(config: ConnectionConfig | undefined, database: string | undefined, schema: string | undefined, baseVersion: string): string {
let scopedVersion = config?.db_type === "xugu" && isXuguPublicSynonymScope(schema) ? `${baseVersion}-public-synonyms` : config?.db_type === "xugu" && isXuguSchedulerJobScope(schema) ? `${baseVersion}-scheduler-jobs` : baseVersion;
if (config?.db_type === "opengauss" && databaseCompatibilityMode(config.id, database)?.trim().toUpperCase() === "A") {
scopedVersion = `${scopedVersion}-a-packages-v1`;
}
return ownerAwareMetadataCacheVersion(config, scopedVersion);
}
@@ -1955,7 +2080,7 @@ export const useConnectionStore = defineStore("connection", () => {
const objectTreeProfileCacheKey = driverProfileObjectTreeProfileForConnection(config)?.cacheKey;
// objects-v8: object-group listing SQL gained a pg_type branch for
// PostgreSQL-family user-defined types; older cached lists miss TYPE nodes.
const baseCacheVersion = objectTreeCacheVersion(config, node.schema, config?.db_type === "oracle" ? "objects-v7" : "objects-v8");
const baseCacheVersion = objectTreeCacheVersion(config, node.database, node.schema, config?.db_type === "oracle" ? "objects-v7" : "objects-v8");
const cacheVersion = objectTreeProfileCacheKey ? `${baseCacheVersion}:${objectTreeProfileCacheKey}` : baseCacheVersion;
return schemaCacheKey(node.connectionId || "", node.database || "", node.schema || "", node.type, cacheVersion);
}
@@ -2194,7 +2319,7 @@ export const useConnectionStore = defineStore("connection", () => {
});
const refreshedGroup = grouped.find((group) => group.type === options.node.type);
const children = refreshedGroup?.children ?? [];
return supportsPackageMemberExpansion(databaseType) ? markPackageNodesExpandable(children) : children;
return supportsPackageMemberExpansion(databaseType, databaseCompatibilityMode(options.node.connectionId, options.node.database)) ? markPackageNodesExpandable(children) : children;
}
function completionTableDetail(comment: string | null | undefined): string | undefined {
@@ -2504,7 +2629,7 @@ export const useConnectionStore = defineStore("connection", () => {
objects: supplementalObjects,
databaseType,
});
if (supportsPackageMemberExpansion(databaseType)) {
if (supportsPackageMemberExpansion(databaseType, databaseCompatibilityMode(options.connectionId, options.database))) {
supplementalChildren = markPackageNodesExpandable(supplementalChildren);
}
if (supplementalChildren.length === 0) return;
@@ -2983,7 +3108,8 @@ export const useConnectionStore = defineStore("connection", () => {
if (connectionUsesVisibleSchemaFilter(config)) {
return schemaCacheKey(connectionId, config.database || "", config.db_type === "oracle" ? "schemas-v2" : "schemas", config.show_system_schemas === true ? "show-system" : "hide-system");
}
return schemaCacheKey(connectionId, "databases-v2");
// v3 includes per-database compatibilityMode used by openGauss A-mode capabilities.
return schemaCacheKey(connectionId, "databases-v3");
}
async function hydrateTreeNodeFromCache(node: TreeNode | null, cacheKey: string | null): Promise<boolean> {
@@ -3969,6 +4095,9 @@ export const useConnectionStore = defineStore("connection", () => {
if (config.db_type !== "plugin") {
void refreshConnectedDatabaseInfo(id, { ...config, id });
await refreshConnectionIdentifierQuote(id, { ...config, id });
// Compatibility modes warm asynchronously; the QueryEditor watcher and the
// backend's own A-mode/EXISTS validation make a cold map safe.
void refreshConnectionDatabaseModes(id, { ...config, id });
}
if (id !== config.id) markSuccessfulLocalConnectionAttempt(config.id, localAttempt);
markSuccessfulLocalConnectionAttempt(id, localAttempt);
@@ -4228,6 +4357,7 @@ export const useConnectionStore = defineStore("connection", () => {
if (config.db_type !== "plugin") {
void refreshConnectedDatabaseInfo(connectionId, config);
await refreshConnectionIdentifierQuote(connectionId, config);
void refreshConnectionDatabaseModes(connectionId, config);
}
markSuccessfulLocalConnectionAttempt(connectionId, localAttempt);
markConnectionHealthChecked(connectionId);
@@ -4479,6 +4609,7 @@ export const useConnectionStore = defineStore("connection", () => {
}
}
const [databases, schemas] = await Promise.all([withMetadataLoadTimeout(connectionId, api.listDatabases(connectionId), "databases"), withMetadataLoadTimeout(connectionId, api.listSchemas(connectionId, "main"), "schemas")]);
setDatabaseCompatibilityModesFromDatabases(connectionId, databases);
const databaseNames = databases.map((database) => database.name);
const visibleNames = filterDatabaseNamesForConnection(databaseNames, config);
const visibleNameSet = new Set(visibleNames);
@@ -4561,7 +4692,8 @@ export const useConnectionStore = defineStore("connection", () => {
setChildren(targetNode, children);
await savePersistedConnectionTreeChildren(cacheKey, targetNode.children || children);
} else {
const cacheKey = schemaCacheKey(connectionId, "databases-v2");
// v3 persists per-database compatibilityMode for openGauss A-mode features.
const cacheKey = schemaCacheKey(connectionId, "databases-v3");
if (!options?.force) {
const cached = await loadPersistedTreeChildren(node, cacheKey, load);
if (cached.hit) {
@@ -4571,6 +4703,7 @@ export const useConnectionStore = defineStore("connection", () => {
}
}
const databases = await withMetadataLoadTimeout(connectionId, api.listDatabases(connectionId), "databases");
setDatabaseCompatibilityModesFromDatabases(connectionId, databases);
const visibleNames = filterDatabaseNamesForConnection(
databases.map((database) => database.name),
config,
@@ -5537,7 +5670,7 @@ export const useConnectionStore = defineStore("connection", () => {
const { database, catalog } = node;
const configForScope = getConfig(connectionId);
const simpleObjectDisplayForScope = useSettingsStore().editorSettings.sidebarObjectDisplay === "simple";
const objectTypesForScope = simpleObjectDisplayForScope ? supportedSidebarObjectTypes(configForScope) : undefined;
const objectTypesForScope = simpleObjectDisplayForScope ? supportedSidebarObjectTypes(configForScope, database) : undefined;
const searchFilterForScope = activeTreeLoadSearchFilter(options);
const pageSizeForScope = sidebarObjectGroupPageSize();
return runTreeMetadataLoad(
@@ -5619,7 +5752,7 @@ export const useConnectionStore = defineStore("connection", () => {
async function loadTables(connectionId: string, database: string, schema?: string, options?: LoadTreeOptions) {
const configForScope = getConfig(connectionId);
const simpleObjectDisplayForScope = useSettingsStore().editorSettings.sidebarObjectDisplay === "simple";
const objectTypesForScope = simpleObjectDisplayForScope ? sidebarObjectTypesForScope(configForScope, schema) : undefined;
const objectTypesForScope = simpleObjectDisplayForScope ? sidebarObjectTypesForScope(configForScope, database, schema) : undefined;
const searchFilter = activeTreeLoadSearchFilter(options);
const querySchemaForScope = connectionObjectTreeQuerySchema(configForScope, database, schema);
const effectiveSchemaForScope = connectionObjectTreeNodeSchema(configForScope, database, schema);
@@ -5635,7 +5768,7 @@ export const useConnectionStore = defineStore("connection", () => {
// collapses that to the database node id, so the loaded tables were attached to the
// database node instead of the schema node, leaving `(default)` permanently empty.
const nodeId = schema != null ? `${connectionId}:${database}:${schema}` : `${connectionId}:${database}`;
const cacheKey = schemaCacheKey(connectionId, database, schema || "", objectTreeCacheVersion(configForScope, schema, "objects-simple-v8"));
const cacheKey = schemaCacheKey(connectionId, database, schema || "", objectTreeCacheVersion(configForScope, database, schema, "objects-simple-v8"));
if (await hydrateTreeNodeFromCache(findNode(treeNodes.value, nodeId), cacheKey)) {
void loadTables(connectionId, database, schema, { ...options, force: true }).catch(() => undefined);
return;
@@ -5675,7 +5808,7 @@ export const useConnectionStore = defineStore("connection", () => {
const objectTreeProfile = driverProfileObjectTreeProfileForConnection(config);
const isPublicSynonymScope = config?.db_type === "xugu" && isXuguPublicSynonymScope(schema);
const isSchedulerJobScope = config?.db_type === "xugu" && isXuguSchedulerJobScope(schema);
const baseCacheVersion = objectTreeCacheVersion(config, schema, simpleObjectDisplay ? "objects-simple-v8" : "objects-grouped-v8");
const baseCacheVersion = objectTreeCacheVersion(config, database, schema, simpleObjectDisplay ? "objects-simple-v8" : "objects-grouped-v8");
const cacheVersion = !simpleObjectDisplay && objectTreeProfile?.cacheKey ? `${baseCacheVersion}:${objectTreeProfile.cacheKey}` : baseCacheVersion;
const cacheKey = schemaCacheKey(connectionId, database, schema || "", cacheVersion);
const querySchema = connectionObjectTreeQuerySchema(config, database, schema);
@@ -5695,7 +5828,7 @@ export const useConnectionStore = defineStore("connection", () => {
}
}
const nonTableObjectTypes = simpleObjectDisplay ? sidebarObjectTypesForScope(config, schema).filter((objectType) => objectType !== "TABLE") : [];
const nonTableObjectTypes = simpleObjectDisplay ? sidebarObjectTypesForScope(config, database, schema).filter((objectType) => objectType !== "TABLE") : [];
let children: TreeNode[];
let nextObjectCount: number | undefined;
if (simpleObjectDisplay && !isPublicSynonymScope && !isSchedulerJobScope) {
@@ -5727,7 +5860,7 @@ export const useConnectionStore = defineStore("connection", () => {
connectionId,
database,
schema: effectiveSchema,
objectTypes: sidebarObjectTypesForScope(config, schema),
objectTypes: sidebarObjectTypesForScope(config, database, schema),
groupOverrides: objectTreeProfile?.groupOverrides,
});
if (!schema && isPostgresLikeForExtensions(config?.db_type)) {
@@ -7148,7 +7281,7 @@ export const useConnectionStore = defineStore("connection", () => {
async function loadPackageMembers(node: TreeNode, options?: LoadTreeOptions): Promise<void> {
if (node.type !== "package" || !node.connectionId || !node.database) return;
const databaseType = effectiveDatabaseTypeForConnection(getConfig(node.connectionId));
if (!supportsPackageMemberExpansion(databaseType)) return;
if (!supportsPackageMemberExpansion(databaseType, databaseCompatibilityMode(node.connectionId, node.database))) return;
const connectionId = node.connectionId;
const database = node.database;
const schema = node.schema;
@@ -7278,7 +7411,7 @@ export const useConnectionStore = defineStore("connection", () => {
}
const ORACLE_SYSTEM_COMPLETION_SCHEMAS = new Set(["SYS", "SYSTEM", "SYSMAN", "DBSNMP", "OUTLN", "XDB", "MDSYS", "CTXSYS", "WMSYS"]);
const FILTERED_ROUTINE_COMPLETION_DATABASES = new Set<DatabaseType>(["mysql", "postgres", "sqlserver", "oracle"]);
const FILTERED_ROUTINE_COMPLETION_DATABASES = new Set<DatabaseType>(["mysql", "postgres", "sqlserver", "oracle", "opengauss"]);
function completionPreferredSchema(connectionId: string, preferredSchema?: string): string | undefined {
return preferredSchema?.trim() || getConfig(connectionId)?.username?.trim() || undefined;
@@ -7730,6 +7863,7 @@ export const useConnectionStore = defineStore("connection", () => {
await ensureConnected(connectionId);
const config = getConfig(connectionId);
const databases = await api.listDatabases(connectionId);
setDatabaseCompatibilityModesFromDatabases(connectionId, databases);
completionDatabasesCache.value[connectionId] = filterDatabaseNamesForConnection(
databases.map((database) => database.name),
config,
@@ -9062,6 +9196,9 @@ export const useConnectionStore = defineStore("connection", () => {
ensureEtcdAccessCapabilities,
canWriteEtcdKey,
connectionIdentifierQuote,
databaseCompatibilityMode,
ensureDatabaseCompatibilityMode,
databaseCompatibilityModes,
isTreeNodePinned,
orderByPinnedTreeNodes,
toggleTreeNodePin,
+33 -13
View File
@@ -58,7 +58,8 @@ import { appendLargeValueCells, canUseTableDataLargeValuePreview, remapLargeValu
import { simpleDataGridOrderByReferencesMissingColumn, sortDataGridRowIndexes, type DataGridSortDirection } from "@/lib/dataGrid/dataGridSort";
import { normalizeResultPageSize } from "@/lib/dataGrid/paginationPageSize";
import { agentProtocolQueryResultMaxRows, capQueryResultTotal, effectiveQueryResultMaxRows, limitQueryPagination, queryResultLimitReached } from "@/lib/dataGrid/queryResultRowLimit";
import { elasticsearchRestRequestRanges, executableStatementRanges, splitSqlStatementRanges } from "@/lib/sql/sqlStatementRanges";
import { elasticsearchRestRequestRanges, executableStatementRanges, splitSqlStatementRanges, sqlStatementParameterOptionsForCompatibility } from "@/lib/sql/sqlStatementRanges";
import type { SqlParameterOptions } from "@/lib/sql/sqlParameters";
import { replaceSqlServerLeadingUseQuery, sqlServerLeadingUseScript, sqlServerUseDatabaseFromStatement } from "@/lib/sql/sqlCompletionLookupTarget";
import { classifySqlRisk } from "@/lib/sql/sqlRisk";
import { externalSqlFileDisplayTitles, normalizeExternalSqlPath } from "@/lib/sql/sqlFileOpen";
@@ -316,8 +317,8 @@ function preservedResultIndex(results: QueryResult[], currentIndex: number | und
return currentIndex;
}
function annotateQueryResultSources(results: QueryResult[], sql: string, database: string | undefined, databaseType?: DatabaseType, sourceOffset?: number): { results: QueryResult[]; sqlServerUseDatabase?: string } {
const statements = splitSqlStatementRanges(sql, databaseType);
function annotateQueryResultSources(results: QueryResult[], sql: string, database: string | undefined, databaseType?: DatabaseType, sourceOffset?: number, parameterOptions?: SqlParameterOptions): { results: QueryResult[]; sqlServerUseDatabase?: string } {
const statements = splitSqlStatementRanges(sql, databaseType, parameterOptions);
let statementIndex = 0;
let sourceDatabase = database;
let sqlServerUseDatabase: string | undefined;
@@ -356,8 +357,8 @@ function clearLiveBatchSqlExecution(tab: QueryTab, executionId: string) {
if (liveBatchSqlExecutions.get(tab)?.executionId === executionId) liveBatchSqlExecutions.delete(tab);
}
function createBatchSqlExecution(executionId: string, editorSql: string, submittedSql: string, databaseType: DatabaseType | undefined, sourceOffset: number | undefined, executionTarget: MultiDbExecutionTarget): BatchSqlExecution | undefined {
const statements = databaseType === "mongodb" ? splitMongoCommandRanges(submittedSql).map(({ from, to, text }) => ({ from, to, sql: text })) : splitSqlStatementRanges(submittedSql, databaseType);
function createBatchSqlExecution(executionId: string, editorSql: string, submittedSql: string, databaseType: DatabaseType | undefined, sourceOffset: number | undefined, executionTarget: MultiDbExecutionTarget, parameterOptions?: SqlParameterOptions): BatchSqlExecution | undefined {
const statements = databaseType === "mongodb" ? splitMongoCommandRanges(submittedSql).map(({ from, to, text }) => ({ from, to, sql: text })) : splitSqlStatementRanges(submittedSql, databaseType, parameterOptions);
if (statements.length === 0) return undefined;
if (statements.length > 1 && databaseType && NON_STREAMING_BATCH_DATABASE_TYPES.has(databaseType)) return undefined;
const offset = sourceOffset ?? 0;
@@ -5873,19 +5874,31 @@ export const useQueryStore = defineStore("query", () => {
const executionCatalog = resumedExecutionTarget ? resumedExecutionTarget.catalog : targetContext ? (targetContext.scope === "catalog" ? targetContext.catalog : undefined) : (executionTarget?.catalog ?? (tab.mode === "data" ? tab.tableMeta?.catalog : tab.catalog));
const contextDatabase = databaseTargetContext?.database;
const targetDatabase = resumedExecutionTarget ? resumedExecutionTarget.database : targetContext?.scope === "connection" ? "" : (contextDatabase ?? executionTarget?.database ?? tab.database);
if (effectiveDbType === "opengauss") {
await connStore.ensureDatabaseCompatibilityMode(executionConnectionId, targetDatabase || conn?.database);
}
const targetSchema = resumedExecutionTarget ? resumedExecutionTarget.schema : targetContext?.scope === "connection" ? undefined : (databaseTargetContext?.schema ?? executionTarget?.schema ?? tab.schema);
const executionDatabase = dataTabExecutionDatabase(conn, targetDatabase, executionCatalog);
const sqlStatementParameterOptions = sqlStatementParameterOptionsForCompatibility(effectiveDbType, effectiveDbType === "opengauss" ? connStore.databaseCompatibilityMode(executionConnectionId, targetDatabase || conn?.database) : undefined);
const useAgentCursor = usesAgentCursorForQuery(conn?.db_type, conn?.driver_profile);
const queryTimeoutSecs = queryTimeoutSecsForConnection(conn, settingsStore.editorSettings.globalQueryTimeoutSecs);
if (!batchResume) {
const statementExecution =
tab.mode === "query"
? createBatchSqlExecution(executionId, tab.sql, sql, effectiveDbType, options?.sourceOffset, {
connectionId: executionConnectionId,
catalog: executionCatalog,
database: targetDatabase,
schema: targetSchema,
})
? createBatchSqlExecution(
executionId,
tab.sql,
sql,
effectiveDbType,
options?.sourceOffset,
{
connectionId: executionConnectionId,
catalog: executionCatalog,
database: targetDatabase,
schema: targetSchema,
},
sqlStatementParameterOptions,
)
: undefined;
tab.batchSqlExecution = statementExecution && (tab.autoCommit !== false || statementExecution.total === 1) ? statementExecution : undefined;
if (tab.batchSqlExecution) liveBatchSqlExecutions.set(tab, tab.batchSqlExecution);
@@ -6536,7 +6549,7 @@ export const useQueryStore = defineStore("query", () => {
// could not rewrite the SQL for these engines, so a plain execution
// would return the first page again (#8993).
const isOffsetJumpPage = typeof pageOffset === "number" && pageOffset > 0 && !options?.pagination?.sessionId;
const frontendTimeoutSecs = frontendQueryTimeoutSecsForSql(sqlToExecute, effectiveDbType, queryTimeoutSecs);
const frontendTimeoutSecs = frontendQueryTimeoutSecsForSql(sqlToExecute, effectiveDbType, queryTimeoutSecs, sqlStatementParameterOptions);
const sourceLabelDatabase = targetDatabase || conn?.database;
const executionClientSessionId = options?.pagination?.clientSessionId ?? (tab.mode === "query" || tab.mode === "data" ? tabClientSessionId(tab) : undefined);
const currentBeforeDispatch = findExecutionTab(id);
@@ -6695,7 +6708,14 @@ export const useQueryStore = defineStore("query", () => {
} else {
executionPromise = executeWithoutManualTransaction();
}
const annotatedResults = annotateQueryResultSources(markQueryResultsRowsRaw(await withFrontendQueryTimeout(executionPromise, frontendTimeoutSecs, t("editor.queryTimeoutError", { seconds: frontendTimeoutSecs }))), queryBaseSql, sourceLabelDatabase, effectiveDbType, options?.sourceOffset);
const annotatedResults = annotateQueryResultSources(
markQueryResultsRowsRaw(await withFrontendQueryTimeout(executionPromise, frontendTimeoutSecs, t("editor.queryTimeoutError", { seconds: frontendTimeoutSecs }))),
queryBaseSql,
sourceLabelDatabase,
effectiveDbType,
options?.sourceOffset,
sqlStatementParameterOptions,
);
const results = offsetBatchQueryResultIndexes(annotatedResults.results, batchResume?.startStatementIndex ?? 0);
reconcileBatchSqlResults(tab, executionId, results);
// Oracle-only sticky state aggregation. Only the initial manual execution