feat(opengauss): support A-compatibility packages
This commit is contained in:
1 parent
760cad6d1b
commit
deb47df2eb
28 files changed
+1706
-139
No files matched your search
@@ -294,7 +294,11 @@ const settingsStore = useSettingsStore();
|
||||
|
||||
function sqlStatementParameterOptions() {
|
||||
const toggles = resolveSqlVariableSyntaxToggles(settingsStore.editorSettings.sqlVariableSyntaxOverrides, props.databaseType, settingsStore.editorSettings.sqlVariableSubstitutionEnabled);
|
||||
return { databaseType: props.databaseType, enabledSyntaxes: enabledSqlParameterSyntaxes(toggles) };
|
||||
return {
|
||||
databaseType: props.databaseType,
|
||||
compatibilityMode: props.databaseType === "opengauss" ? connectionStore.databaseCompatibilityMode(props.connectionId, props.database) : undefined,
|
||||
enabledSyntaxes: enabledSqlParameterSyntaxes(toggles),
|
||||
};
|
||||
}
|
||||
const { isDark, themePalette, activeCustomUiColors } = useTheme();
|
||||
const { t } = useI18n();
|
||||
@@ -3473,7 +3477,7 @@ async function refreshSemanticDiagnostics(options: { preserveOutsideRanges?: boo
|
||||
if (props.databaseType !== "sqlserver") {
|
||||
executableStatementRangeCache = executableStatementRangeCacheForDoc(executableStatementRangeCache, currentView.state.doc, props.databaseType, sqlStatementParameterOptions());
|
||||
}
|
||||
const diagnosticRanges = sqlSemanticDiagnosticRangesForViewport(sql, visibleRanges, props.databaseType, props.databaseType === "sqlserver" ? undefined : executableStatementRangeCache?.ranges);
|
||||
const diagnosticRanges = sqlSemanticDiagnosticRangesForViewport(sql, visibleRanges, props.databaseType, props.databaseType === "sqlserver" ? undefined : executableStatementRangeCache?.ranges, sqlStatementParameterOptions());
|
||||
if (diagnosticRanges.length === 0) {
|
||||
if (!options.preserveOutsideRanges) setSemanticDiagnostics([]);
|
||||
return;
|
||||
@@ -5334,9 +5338,31 @@ function shouldLoadCompletionObjects(completionContext: ReturnType<typeof getSql
|
||||
return routineContext && !isReferencedTableQualifier(completionContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Databases whose qualified routine completion treats the first qualifier as a
|
||||
* package (Oracle) or as a package-or-schema in A compatibility mode
|
||||
* (openGauss). The backend re-validates: non-package parents fall through to
|
||||
* the ordinary routine search, so routing is safe even when the compatibility
|
||||
* map is not yet warm. openGauss keeps the package-aware routing while the
|
||||
* mode is unknown (cold start, so A-mode completion works immediately after
|
||||
* connect) and once the mode is known to be A; a known B/PG mode skips the
|
||||
* extra package-style queries entirely.
|
||||
*/
|
||||
function usesPackageAwareRoutineCompletion(): boolean {
|
||||
if (props.databaseType === "oracle") return true;
|
||||
if (props.databaseType !== "opengauss") return false;
|
||||
const mode = connectionStore.databaseCompatibilityMode(props.connectionId, props.database)?.trim().toUpperCase();
|
||||
return mode === undefined || mode === "A";
|
||||
}
|
||||
|
||||
function oracleRoutineCompletionTargets(completionContext: ReturnType<typeof getSqlCompletionContext>): RoutineCompletionTarget[] {
|
||||
const parts = (completionContext.qualifierParts?.length ? completionContext.qualifierParts : completionContext.qualifier?.split("."))?.filter(Boolean) ?? [];
|
||||
if (parts.length === 0) return [{ schema: props.schema, globalSearch: true }];
|
||||
if (parts.length === 0) {
|
||||
// Oracle resolves unqualified routines across all schemas (owner semantics).
|
||||
// openGauss keeps the PG search_path scope for the unqualified case.
|
||||
if (props.databaseType === "oracle") return [{ schema: props.schema, globalSearch: true }];
|
||||
return [{ schema: props.schema }];
|
||||
}
|
||||
if (parts.length === 1) {
|
||||
return [{ schema: props.schema, parentName: parts[0] }, { schema: parts[0] }];
|
||||
}
|
||||
@@ -5353,14 +5379,14 @@ function routineCompletionTargetForContext(completionContext: ReturnType<typeof
|
||||
}
|
||||
|
||||
function routineCompletionScopeForContext(completionContext: ReturnType<typeof getSqlCompletionContext>, scope: CompletionMetadataScope): CompletionMetadataScope {
|
||||
if (props.databaseType === "oracle") return scope;
|
||||
if (usesPackageAwareRoutineCompletion()) return scope;
|
||||
const target = routineCompletionTargetForContext(completionContext, scope);
|
||||
return { database: target.database, schema: target.schema };
|
||||
}
|
||||
|
||||
function lookupLocalCompletionObjectsForContext(completionContext: ReturnType<typeof getSqlCompletionContext>, scope: CompletionMetadataScope): SqlCompletionObject[] {
|
||||
if (!props.connectionId || props.database == null) return [];
|
||||
if (props.databaseType === "oracle") {
|
||||
if (usesPackageAwareRoutineCompletion()) {
|
||||
return connectionStore.lookupLocalCompletionObjects(props.connectionId, scope.database, completionContext.prefix, MAX_COMPLETION_TABLES);
|
||||
}
|
||||
const target = routineCompletionTargetForContext(completionContext, scope);
|
||||
@@ -5370,7 +5396,7 @@ function lookupLocalCompletionObjectsForContext(completionContext: ReturnType<ty
|
||||
async function listCompletionObjectsForContext(completionContext: ReturnType<typeof getSqlCompletionContext>, scope: CompletionMetadataScope): Promise<SqlCompletionObject[]> {
|
||||
if (!props.connectionId || props.database == null) return [];
|
||||
const objectKinds = completionObjectKindsForContext(completionContext);
|
||||
if (props.databaseType !== "oracle") {
|
||||
if (!usesPackageAwareRoutineCompletion()) {
|
||||
const target = routineCompletionTargetForContext(completionContext, scope);
|
||||
return connectionStore.listCompletionObjects(props.connectionId, target.database, target.mask, MAX_COMPLETION_TABLES, target.schema, undefined, false, scope.schema, objectKinds);
|
||||
}
|
||||
@@ -7134,6 +7160,23 @@ watch([() => props.databaseType, () => props.dialect, () => props.syntaxDialect,
|
||||
});
|
||||
});
|
||||
|
||||
// openGauss compatibility mode is loaded asynchronously from the backend into a
|
||||
// dedicated store map (not the sidebar tree). A restored tab may open before the
|
||||
// map is warm; when the mode arrives, re-derive statement boundaries and
|
||||
// diagnostics so package DDL is parsed with the correct PL/SQL rules.
|
||||
watch(
|
||||
() => (props.databaseType === "opengauss" ? connectionStore.databaseCompatibilityMode(props.connectionId, props.database) : undefined),
|
||||
(now, before) => {
|
||||
if (now === before) return;
|
||||
executableStatementRangeCache = null;
|
||||
if (props.databaseType !== "opengauss") return;
|
||||
if (!view.value) return;
|
||||
refreshCompletionCache();
|
||||
setSemanticDiagnostics([]);
|
||||
scheduleSemanticDiagnostics(0);
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.forceWordWrap,
|
||||
() => {
|
||||
|
||||
@@ -176,6 +176,7 @@ import type { QueryTab, TableInfoTab, TreeNode, VectorCollectionMeta } from "@/t
|
||||
import type { SqlObjectNavigationTarget } from "@/lib/sql/sqlNavigation";
|
||||
import { sqlFormatDialectForDbType, type SqlFormatDialect } from "@/lib/sql/sqlFormatter";
|
||||
import { productionContextForDatabase } from "@/lib/database/productionSafety";
|
||||
import { sqlStatementParameterOptionsForCompatibility } from "@/lib/sql/sqlStatementRanges";
|
||||
import { connectionIsEffectivelyReadOnly } from "@/lib/database/readOnlyWriteAccess";
|
||||
|
||||
type DataGridHandle = DataGridColumnLayoutHandle & {
|
||||
@@ -342,6 +343,9 @@ const activeEffectiveDatabaseType = computed(() => effectiveDatabaseTypeForConne
|
||||
const canOpenTableImport = computed(() => supportsTableImport(activeEffectiveDatabaseType.value));
|
||||
const activeVectorConnection = computed(() => connectionStore.getConfig(props.activeTab.connectionId) ?? props.activeConnection);
|
||||
const activeDataTabExecutionDatabase = computed(() => dataTabExecutionDatabase(props.activeConnection, props.activeTab.database, activeDataTabTableMeta.value?.catalog));
|
||||
const activeSqlStatementParameterOptions = computed(() =>
|
||||
sqlStatementParameterOptionsForCompatibility(activeEffectiveDatabaseType.value, activeEffectiveDatabaseType.value === "opengauss" ? connectionStore.databaseCompatibilityMode(activeResultConnectionId.value, activeResultDatabase.value) : undefined),
|
||||
);
|
||||
const activeProductionContext = computed(() => productionContextForDatabase(props.activeConnection, props.activeTab.database));
|
||||
const productionWatermarkText = computed(() => (locale.value.startsWith("zh") ? "生产环境" : "PROD"));
|
||||
const productionSessionDetail = computed(() => {
|
||||
@@ -474,6 +478,7 @@ const activeStatementExecutionMarkers = computed(() =>
|
||||
props.activeTab.resultBaseSql || props.activeTab.lastExecutedSql || props.activeTab.sql,
|
||||
props.activeTab.resultEditorFingerprint ?? "",
|
||||
props.activeTab.batchSqlExecution,
|
||||
activeSqlStatementParameterOptions.value,
|
||||
),
|
||||
);
|
||||
const activeElasticsearchJsonResponse = computed(() => elasticsearchJsonResponseForResult(activeEffectiveDatabaseType.value, activeResultSql.value, props.activeTab.result));
|
||||
@@ -1146,7 +1151,7 @@ function toggleResultAutoSave() {
|
||||
function selectResultItem(item: (typeof visibleResultItems.value)[number]) {
|
||||
queryStore.setActiveResultIndex(props.activeTab.id, item.index);
|
||||
emit("update:activeOutputView", props.activeTab.id, "result");
|
||||
const range = resultSourceRange(props.activeTab.sql, item.result, item.index, activeEffectiveDatabaseType.value) ?? null;
|
||||
const range = resultSourceRange(props.activeTab.sql, item.result, item.index, activeEffectiveDatabaseType.value, activeSqlStatementParameterOptions.value) ?? null;
|
||||
if (queryEditorRef.value) {
|
||||
nextTick(() => queryEditorRef.value?.previewStatementRange(range));
|
||||
} else {
|
||||
@@ -1158,7 +1163,7 @@ function executionSummaryItemRange(item: ExecutionSummaryItem) {
|
||||
if (typeof item.sourceFrom === "number" && typeof item.sourceTo === "number" && item.sql && props.activeTab.sql.slice(item.sourceFrom, item.sourceTo) === item.sql) {
|
||||
return { from: item.sourceFrom, to: item.sourceTo };
|
||||
}
|
||||
return item.result ? resultSourceRange(props.activeTab.sql, item.result, item.statementIndex, activeEffectiveDatabaseType.value) : undefined;
|
||||
return item.result ? resultSourceRange(props.activeTab.sql, item.result, item.statementIndex, activeEffectiveDatabaseType.value, activeSqlStatementParameterOptions.value) : undefined;
|
||||
}
|
||||
|
||||
function previewExecutionSummaryItem(item: ExecutionSummaryItem) {
|
||||
|
||||
@@ -903,7 +903,8 @@ async function toggle(requestId = beginNavigationRequest()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.type === "package" && node.connectionId && supportsPackageMemberExpansion(currentDatabaseType())) {
|
||||
const packageDatabaseType = currentDatabaseType();
|
||||
if (node.type === "package" && node.connectionId && supportsPackageMemberExpansion(packageDatabaseType, packageDatabaseType === "opengauss" ? connectionStore.databaseCompatibilityMode(node.connectionId, node.database) : undefined)) {
|
||||
await connectionStore.loadPackageMembers(node);
|
||||
emitNodeToggled(node, wasExpanded);
|
||||
return;
|
||||
|
||||
Reference in new issue
Block a user