369 lines
21 KiB
TypeScript
369 lines
21 KiB
TypeScript
import type { ConnectionConfig, DatabaseType } from "@/types/database";
|
|
import { isSchemaAware, spannerObjectTreeSchema, usesDatabaseObjectTreeMode, usesTreeSchemaMode } from "@/lib/database/databaseFeatureSupport";
|
|
import type { CodeMirrorSqlDialectName } from "@/lib/editor/codemirrorSqlDialect";
|
|
|
|
type JdbcDialectConnection = Partial<Pick<ConnectionConfig, "db_type" | "driver_profile" | "driver_label" | "connection_string" | "url_params" | "jdbc_driver_class" | "jdbc_driver_paths" | "database_info" | "external_config" | "username">>;
|
|
|
|
export type GaussdbIdentifierQuoteStyle = "auto" | "double" | "backtick";
|
|
export type GaussdbConnectionMode = "native" | "m-jdbc";
|
|
export type GaussdbTargetServerType = "master" | "slave" | "any";
|
|
export type GaussdbCountQueryDop = 1 | 2 | 4 | 8 | 16;
|
|
|
|
const GAUSSDB_IDENTIFIER_QUOTE_STYLE_KEY = "gaussdbIdentifierQuoteStyle";
|
|
const GAUSSDB_TARGET_SERVER_TYPE_KEY = "gaussdbTargetServerType";
|
|
const GAUSSDB_COUNT_QUERY_DOP_KEY = "gaussdbCountQueryDop";
|
|
export const GAUSSDB_M_JDBC_DRIVER_PROFILE = "gaussdb-m";
|
|
export const GAUSSDB_M_JDBC_DRIVER_CLASS = "com.huawei.gaussdb.jdbc.Driver";
|
|
|
|
const DATABASE_AS_EXECUTION_SCHEMA_TYPES = new Set<DatabaseType>(["hive", "kyuubi", "impala", "argo", "spark"]);
|
|
const CONNECTION_ROOT_SCHEMA_TYPES = new Set<DatabaseType>(["oracle", "dameng", "oceanbase-oracle"]);
|
|
|
|
const JDBC_DIALECT_MATCHERS: Array<{ type: DatabaseType; patterns: RegExp[] }> = [
|
|
{ type: "databend", patterns: [/jdbc:databend:/i, /com\.databend\.jdbc\.DatabendDriver/i, /databend-jdbc/i] },
|
|
{ type: "starrocks", patterns: [/starrocks/i] },
|
|
// Phoenix remains a generic JDBC connection, but exposes queryable schemas.
|
|
// Returning `jdbc` keeps its generic SQL behavior while enabling the schema tree.
|
|
{ type: "jdbc", patterns: [/phoenix/i] },
|
|
{ type: "doris", patterns: [/doris/i] },
|
|
{ type: "goldendb", patterns: [/jdbc:goldendb:/i, /goldendb/i] },
|
|
// GBase 8a only: 8s (`gbasedbt`/`jdbc:gbasedbt-sqli`) is Informix-based and must stay on jdbc.
|
|
{ type: "gbase", patterns: [/jdbc:gbase:/i, /cn\.gbase\./i, /gbase(?!dbt)(?!8s).*jdbc/i] },
|
|
{ type: "mysql", patterns: [/kyuubi/i] },
|
|
{ type: "hive", patterns: [/inceptor/i, /\bapache\s+hive\b/i, /org\.apache\.hive\.jdbc\.HiveDriver/i, /hive-jdbc/i] },
|
|
{ type: "mysql", patterns: [/jdbc:mysql:/i, /mysql/i, /mariadb/i, /hive2/i] },
|
|
{ type: "gaussdb", patterns: [/jdbc:gaussdb:/i, /com\.huawei\.gaussdb/i, /gaussdb/i] },
|
|
{ type: "dameng", patterns: [/jdbc:dm:/i, /dm\.jdbc\.driver/i, /dameng/i] },
|
|
{ type: "opengauss", patterns: [/jdbc:opengauss:/i, /org\.opengauss/i, /opengauss/i] },
|
|
{ type: "postgres", patterns: [/jdbc:postgresql:/i, /postgres/i] },
|
|
{ type: "sqlserver", patterns: [/jdbc:sqlserver:/i, /sqlserver/i, /mssql/i] },
|
|
{ type: "oracle", patterns: [/jdbc:oracle:/i, /oracle/i] },
|
|
{ type: "clickhouse", patterns: [/jdbc:clickhouse:/i, /clickhouse/i] },
|
|
{ type: "tdengine", patterns: [/jdbc:taos(?:-rs|-ws)?:/i, /taosdata/i, /tdengine/i] },
|
|
{ type: "h2", patterns: [/jdbc:h2:/i, /\bh2\b/i] },
|
|
{ type: "sqlite", patterns: [/jdbc:sqlite:/i, /sqlite/i] },
|
|
{ type: "db2", patterns: [/jdbc:db2:/i, /\bdb2\b/i] },
|
|
{ type: "informix", patterns: [/jdbc:informix/i, /informix/i] },
|
|
// CacheDB.jar (legacy Caché driver) carries none of the intersystems URL or
|
|
// class-name markers, so match the jar file name / driver label directly.
|
|
{ type: "iris", patterns: [/jdbc:(?:iris|cache):/i, /com\.intersystems\.jdbc\.(?:IRIS|Cache)Driver/i, /intersystems-jdbc/i, /\bcache(?:db)?\b/i] },
|
|
];
|
|
|
|
// ASE uses Transact-SQL, but treating it as SQL Server globally would also
|
|
// enable SQL Server metadata, pagination, and identifier rules. Keep this
|
|
// narrower matcher exclusively for editor syntax parsing.
|
|
const JDBC_ASE_PROFILE_PATTERNS = [/(?:^|[\s_-])ase(?:$|[\s_-])/i, /\bsap[\s_-]+ase\b/i, /\badaptive server enterprise\b/i];
|
|
|
|
export function inferJdbcDialect(connection?: JdbcDialectConnection): DatabaseType | undefined {
|
|
if (!connection || connection.db_type !== "jdbc") return undefined;
|
|
if (isGbase8sProfile(connection.driver_profile)) return undefined;
|
|
const haystack = [connection.driver_profile, connection.driver_label, connection.connection_string, connection.jdbc_driver_class, ...(connection.jdbc_driver_paths ?? []), connection.database_info?.productName, connection.database_info?.serverComment, connection.database_info?.driverName]
|
|
.filter(Boolean)
|
|
.join("\n");
|
|
if (!haystack) return undefined;
|
|
return JDBC_DIALECT_MATCHERS.find((matcher) => matcher.patterns.some((pattern) => pattern.test(haystack)))?.type;
|
|
}
|
|
|
|
export function jdbcDriverProfileUsesSchemaQualification(driverProfile?: string): boolean {
|
|
return inferJdbcDialect({ db_type: "jdbc", driver_profile: driverProfile }) === "jdbc";
|
|
}
|
|
|
|
export function effectiveDatabaseTypeForConnection(connection?: JdbcDialectConnection): DatabaseType | undefined {
|
|
if (!connection) return undefined;
|
|
if (connection.db_type === "gbase" && isGbase8sProfile(connection.driver_profile)) return "informix";
|
|
if (connection.db_type === "gbase") return "mysql";
|
|
// MySQL-protocol connections to Doris/StarRocks (db_type=mysql with a
|
|
// starrocks/doris driver_profile) must use the Doris/StarRocks SQL dialect so
|
|
// that multi-catalog 3-part names (`catalog.database.table`) are emitted.
|
|
// mysql and starrocks share the backtick-quoting + LIMIT dialect, so this
|
|
// only widens the catalog-aware SQL generation path without other side effects.
|
|
if (connection.db_type === "mysql") {
|
|
const profile = connection.driver_profile?.toLowerCase();
|
|
if (profile === "starrocks") return "starrocks";
|
|
if (profile === "doris" || profile === "selectdb") return "doris";
|
|
}
|
|
if (connection.db_type !== "jdbc") return connection.db_type;
|
|
return inferJdbcDialect(connection) ?? "jdbc";
|
|
}
|
|
|
|
/**
|
|
* Database type the data-transfer pipeline treats a connection as. Doris-family
|
|
* engines (Doris/SelectDB/StarRocks) are saved as `db_type=mysql` with a
|
|
* doris/starrocks `driver_profile`, and the transfer backend routes them through
|
|
* the MySQL object family (catalog routing is driver_profile-aware); the
|
|
* standalone `doris`/`starrocks` manifest entries are not transfer-capable.
|
|
* Resolve those connections back to their raw db_type so they stay selectable in
|
|
* the transfer dialog and keep the MySQL object kinds — the effective type alone
|
|
* would drop them from the connection list and disable every non-table kind.
|
|
*/
|
|
export function transferDatabaseTypeForConnection(connection?: JdbcDialectConnection): DatabaseType | undefined {
|
|
const effective = effectiveDatabaseTypeForConnection(connection);
|
|
if (effective === "doris" || effective === "starrocks") return connection?.db_type;
|
|
return effective;
|
|
}
|
|
|
|
export function connectionUsesConnectionRootSchemaMode(connection?: JdbcDialectConnection): boolean {
|
|
const type = effectiveDatabaseTypeForConnection(connection);
|
|
return !!type && CONNECTION_ROOT_SCHEMA_TYPES.has(type);
|
|
}
|
|
|
|
export function connectionShouldLoadIdentifierQuote(connection: JdbcDialectConnection | undefined): boolean {
|
|
if (!connection) return false;
|
|
if (connection.db_type === "gbase" && isGbase8sProfile(connection.driver_profile)) return true;
|
|
if (connection.db_type === "kingbase") return true;
|
|
// Cloud Spanner is dual-dialect: the agent reports a backtick for GoogleSQL and
|
|
// a double quote for PostgreSQL-dialect databases. The backend counts Spanner
|
|
// unconditionally in `uses_connection_identifier_quote`, so the UI must fetch
|
|
// the reported quote or every locally built statement would use the GoogleSQL
|
|
// default against a PostgreSQL-dialect database.
|
|
if (connection.db_type === "spanner") return true;
|
|
if (gaussdbIdentifierQuoteStyle(connection) !== "auto") return false;
|
|
if (connection.db_type === "gaussdb") return true;
|
|
if (connection.db_type !== "jdbc") return false;
|
|
const dialect = inferJdbcDialect(connection);
|
|
return dialect === "jdbc" || ["gaussdb", "opengauss", "postgres"].includes(dialect ?? "");
|
|
}
|
|
|
|
export function supportsGaussdbIdentifierQuoteStyle(connection: JdbcDialectConnection | undefined): boolean {
|
|
return effectiveDatabaseTypeForConnection(connection) === "gaussdb";
|
|
}
|
|
|
|
export function gaussdbIdentifierQuoteStyle(connection: JdbcDialectConnection | undefined): GaussdbIdentifierQuoteStyle {
|
|
const external = externalConfigRecord(connection?.external_config);
|
|
const style = external[GAUSSDB_IDENTIFIER_QUOTE_STYLE_KEY];
|
|
return style === "double" || style === "backtick" ? style : "auto";
|
|
}
|
|
|
|
export function gaussdbIdentifierQuoteOverride(connection: JdbcDialectConnection | undefined): string | undefined {
|
|
const style = gaussdbIdentifierQuoteStyle(connection);
|
|
if (style === "double") return '"';
|
|
if (style === "backtick") return "`";
|
|
return undefined;
|
|
}
|
|
|
|
export function gaussdbConnectionMode(connection: JdbcDialectConnection | undefined): GaussdbConnectionMode {
|
|
return connection?.db_type === "gaussdb" && connection.driver_profile?.toLowerCase() === GAUSSDB_M_JDBC_DRIVER_PROFILE ? "m-jdbc" : "native";
|
|
}
|
|
|
|
export function setGaussdbConnectionMode(connection: JdbcDialectConnection, mode: GaussdbConnectionMode) {
|
|
if (connection.db_type !== "gaussdb") return;
|
|
connection.driver_profile = mode === "m-jdbc" ? GAUSSDB_M_JDBC_DRIVER_PROFILE : "gaussdb";
|
|
connection.driver_label = "GaussDB";
|
|
connection.jdbc_driver_class = mode === "m-jdbc" ? GAUSSDB_M_JDBC_DRIVER_CLASS : undefined;
|
|
connection.connection_string = undefined;
|
|
}
|
|
|
|
export function setGaussdbIdentifierQuoteStyle(
|
|
connection: Pick<ConnectionConfig, "db_type"> & Partial<Pick<ConnectionConfig, "driver_profile" | "driver_label" | "connection_string" | "jdbc_driver_class" | "jdbc_driver_paths" | "database_info" | "external_config">>,
|
|
style: GaussdbIdentifierQuoteStyle,
|
|
) {
|
|
const external = externalConfigRecord(connection.external_config);
|
|
if (style === "auto") {
|
|
delete external[GAUSSDB_IDENTIFIER_QUOTE_STYLE_KEY];
|
|
} else {
|
|
external[GAUSSDB_IDENTIFIER_QUOTE_STYLE_KEY] = style;
|
|
}
|
|
connection.external_config = Object.keys(external).length > 0 ? external : undefined;
|
|
}
|
|
|
|
export function gaussdbTargetServerType(connection: JdbcDialectConnection | undefined): GaussdbTargetServerType {
|
|
const external = externalConfigRecord(connection?.external_config);
|
|
return normalizeGaussdbTargetServerType(external[GAUSSDB_TARGET_SERVER_TYPE_KEY]) ?? gaussdbTargetServerTypeFromUrl(connection) ?? "any";
|
|
}
|
|
|
|
export function setGaussdbTargetServerType(connection: Pick<ConnectionConfig, "db_type"> & Partial<Pick<ConnectionConfig, "driver_profile" | "driver_label" | "connection_string" | "jdbc_driver_class" | "jdbc_driver_paths" | "database_info" | "external_config">>, value: GaussdbTargetServerType) {
|
|
const external = externalConfigRecord(connection.external_config);
|
|
external[GAUSSDB_TARGET_SERVER_TYPE_KEY] = value;
|
|
connection.external_config = Object.keys(external).length > 0 ? external : undefined;
|
|
}
|
|
|
|
function normalizeGaussdbTargetServerType(value: unknown): GaussdbTargetServerType | undefined {
|
|
if (typeof value !== "string") return undefined;
|
|
const normalized = value.trim().toLowerCase();
|
|
return normalized === "master" || normalized === "slave" || normalized === "any" ? normalized : undefined;
|
|
}
|
|
|
|
function gaussdbTargetServerTypeFromUrl(connection: JdbcDialectConnection | undefined): GaussdbTargetServerType | undefined {
|
|
const values = [connection?.url_params, connection?.connection_string?.split("?", 2)[1]?.split("#", 1)[0]];
|
|
for (const value of values) {
|
|
const params = new URLSearchParams((value || "").trim().replace(/^\?/, "").replace(/;/g, "&"));
|
|
for (const [key, paramValue] of params) {
|
|
if (key.toLowerCase() === "targetservertype") {
|
|
return normalizeGaussdbTargetServerType(paramValue);
|
|
}
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
export function sqlSnippetDatabaseTypeForConnection(connection?: JdbcDialectConnection): DatabaseType | undefined {
|
|
// ASE uses T-SQL snippets, but mapping it globally to SQL Server would also
|
|
// enable incompatible SQL Server metadata and pagination behavior.
|
|
if (isJdbcAseProfile(connection)) return "sqlserver";
|
|
return effectiveDatabaseTypeForConnection(connection);
|
|
}
|
|
|
|
export function tableStructureDatabaseTypeForConnection(connection?: JdbcDialectConnection): DatabaseType | undefined {
|
|
if (!connection) return undefined;
|
|
if (connection.db_type === "gbase" && !isGbase8sProfile(connection.driver_profile)) return "gbase";
|
|
return effectiveDatabaseTypeForConnection(connection);
|
|
}
|
|
|
|
export function connectionUsesDatabaseObjectTreeMode(connection?: JdbcDialectConnection): boolean {
|
|
if (!connection) return false;
|
|
if (connection.db_type !== "jdbc") return usesDatabaseObjectTreeMode(effectiveDatabaseTypeForConnection(connection));
|
|
if (connectionUsesConnectionRootSchemaMode(connection)) return false;
|
|
const dialect = inferJdbcDialect(connection);
|
|
if (!dialect) return true;
|
|
if (dialect === "hive" || dialect === "trino") return false;
|
|
// TDengine exposes databases as the top-level namespace, without schemas.
|
|
if (dialect === "tdengine") return false;
|
|
if (dialect === "databend") return true;
|
|
return !usesTreeSchemaMode(dialect);
|
|
}
|
|
|
|
export function connectionShouldDiscoverJdbcSchemas(connection?: JdbcDialectConnection): boolean {
|
|
// GBase 8s exposes owner schemas only when the current database can use them
|
|
// in DML; non-ANSI databases fall back to the flat table tree.
|
|
if (connection?.db_type === "gbase" && isGbase8sProfile(connection.driver_profile)) return true;
|
|
return connection?.db_type === "jdbc" && !inferJdbcDialect(connection);
|
|
}
|
|
|
|
export function connectionUsesSchemaExecutionContext(connection?: JdbcDialectConnection): boolean {
|
|
return connection?.db_type === "jdbc" && inferJdbcDialect(connection) === "databend";
|
|
}
|
|
|
|
export function connectionQueryExecutionSchema(connection: JdbcDialectConnection | undefined, database: string | undefined, schema: string | undefined, dataMode: boolean): string | undefined {
|
|
if (connectionUsesSchemaExecutionContext(connection)) return schema || database || undefined;
|
|
if (dataMode || connectionUsesDatabaseObjectTreeMode(connection)) return undefined;
|
|
if (schema) return schema;
|
|
const type = effectiveDatabaseTypeForConnection(connection);
|
|
// Hive and Spark display their SQL namespace in the database selector, but
|
|
// their agents switch it with USE through the schema execution parameter.
|
|
return type && DATABASE_AS_EXECUTION_SCHEMA_TYPES.has(type) ? database || undefined : undefined;
|
|
}
|
|
|
|
/**
|
|
* Whether the database name is a wrong guess for an unknown schema. Engines that keep tables under a
|
|
* dedicated schema level in the tree (PostgreSQL and relatives, SQL Server, DB2, Trino, generic JDBC)
|
|
* address objects as database.schema.table, so the database name matches no schema there. Oracle,
|
|
* Dameng and the Hive family are schema-aware without that level — their database *is* the schema —
|
|
* and must keep the `schema || database` fallback.
|
|
*/
|
|
function databaseNameIsNotASchema(type: DatabaseType | undefined): boolean {
|
|
return isSchemaAware(type) && usesTreeSchemaMode(type);
|
|
}
|
|
|
|
export function connectionObjectTreeQuerySchema(connection: JdbcDialectConnection | undefined, database: string, schema?: string): string {
|
|
if (connection?.db_type === "jdbc" && inferJdbcDialect(connection) === "databend") return schema || database;
|
|
if (connectionUsesDatabaseObjectTreeMode(connection)) return "";
|
|
const type = effectiveDatabaseTypeForConnection(connection);
|
|
if (type === "informix") return schema || "";
|
|
if (type === "spanner") return spannerObjectTreeSchema(schema);
|
|
// A query tab that never picked a schema (toolbar "new query", a reopened .sql
|
|
// file) would otherwise send the database name and match no objects at all, so
|
|
// sidebar locate silently did nothing (issue #7648). The blank schema is the
|
|
// established "resolve it on the backend" value used by the completion paths.
|
|
if (!schema && databaseNameIsNotASchema(type)) return "";
|
|
return schema || database;
|
|
}
|
|
|
|
/**
|
|
* Schema sent with object-list (listObjects) requests. Dameng's object SQL
|
|
* filters on a fixed `WHERE o.OWNER = ?`, so a blank schema matches nothing and
|
|
* the object tab renders empty when the schema could not be resolved (#8301).
|
|
* Mirror the completion path (connectionStore.listCompletionColumns) and fall
|
|
* back to the connection username — uppercased, the Oracle-family storage
|
|
* convention for unquoted Dameng users. Other Oracle-family types keep the
|
|
* blank schema so the backend resolves the current schema itself.
|
|
*/
|
|
export function objectListSchemaForConnection(connection: JdbcDialectConnection | undefined, schema?: string): string {
|
|
if (schema) return schema;
|
|
if (effectiveDatabaseTypeForConnection(connection) !== "dameng") return "";
|
|
return connection?.username?.trim().toUpperCase() || "";
|
|
}
|
|
|
|
/**
|
|
* Metadata schema for query paths that treat the database name as the schema when a
|
|
* connection exposes no schema level (MySQL, HBase, and the other flat engines).
|
|
* Cloud Spanner is the exception: its database is a resource path, so the blank
|
|
* GoogleSQL schema has to be sent instead of the path. Behavior for every other
|
|
* database type is exactly `schema || database`.
|
|
*/
|
|
export function connectionDatabaseMetadataSchema(connection: JdbcDialectConnection | undefined, database: string, schema?: string): string {
|
|
if (schema) return schema;
|
|
return effectiveDatabaseTypeForConnection(connection) === "spanner" ? "" : database;
|
|
}
|
|
|
|
export function metadataSchemaForConnection(connection: JdbcDialectConnection | undefined, database: string, schema?: string): string {
|
|
const type = effectiveDatabaseTypeForConnection(connection);
|
|
if (type === "sqlserver") return schema || "dbo";
|
|
return connectionObjectTreeQuerySchema(connection, database, schema);
|
|
}
|
|
|
|
export function connectionObjectTreeNodeSchema(connection: JdbcDialectConnection | undefined, database: string, schema?: string): string | undefined {
|
|
if (connection?.db_type === "jdbc" && inferJdbcDialect(connection) === "databend") return schema || database;
|
|
if (connectionUsesDatabaseObjectTreeMode(connection)) return undefined;
|
|
const type = effectiveDatabaseTypeForConnection(connection);
|
|
if (type === "informix") return schema || undefined;
|
|
if (type === "sqlite") return schema || database;
|
|
if (type === "spanner") return spannerObjectTreeSchema(schema);
|
|
if (!type) return schema;
|
|
if (!schema && databaseNameIsNotASchema(type)) return undefined;
|
|
return isSchemaAware(type) ? schema || database : undefined;
|
|
}
|
|
|
|
/** GBase 8s reports the table owner as a schema, but does not accept it in table DML/DDL names. */
|
|
export function connectionTableSqlSchema(connection: JdbcDialectConnection | undefined, schema?: string): string | undefined {
|
|
if (connection?.db_type === "gbase" && isGbase8sProfile(connection.driver_profile)) return undefined;
|
|
return schema;
|
|
}
|
|
|
|
/** Maps a database type to the corresponding CodeMirror SQL dialect name used by QueryEditor and DdlViewDialog. */
|
|
export function codeMirrorSqlDialect(dbType: DatabaseType | undefined): "mysql" | "postgres" | "sqlserver" {
|
|
if (dbType === "postgres" || dbType === "gaussdb" || dbType === "kwdb" || dbType === "opengauss") return "postgres";
|
|
if (dbType === "sqlserver") return "sqlserver";
|
|
return "mysql";
|
|
}
|
|
|
|
export function codeMirrorSqlDialectForConnection(connection?: JdbcDialectConnection): CodeMirrorSqlDialectName {
|
|
if (isJdbcAseProfile(connection)) return "sqlserver";
|
|
const databaseType = effectiveDatabaseTypeForConnection(connection);
|
|
if (databaseType === "clickhouse") return "clickhouse";
|
|
return codeMirrorSqlDialect(databaseType);
|
|
}
|
|
|
|
export function gaussdbCountQueryDop(connection: JdbcDialectConnection | undefined): GaussdbCountQueryDop {
|
|
const external = externalConfigRecord(connection?.external_config);
|
|
const value = external[GAUSSDB_COUNT_QUERY_DOP_KEY];
|
|
return value === 2 || value === 4 || value === 8 || value === 16 ? value : 1;
|
|
}
|
|
|
|
export function setGaussdbCountQueryDop(connection: Pick<ConnectionConfig, "db_type"> & Partial<Pick<ConnectionConfig, "external_config">>, value: GaussdbCountQueryDop) {
|
|
const external = externalConfigRecord(connection.external_config);
|
|
if (value === 1) {
|
|
delete external[GAUSSDB_COUNT_QUERY_DOP_KEY];
|
|
} else {
|
|
external[GAUSSDB_COUNT_QUERY_DOP_KEY] = value;
|
|
}
|
|
connection.external_config = Object.keys(external).length > 0 ? external : undefined;
|
|
}
|
|
|
|
export function gaussdbCountQueryDopHint(connection: JdbcDialectConnection | undefined): string | undefined {
|
|
const dop = gaussdbCountQueryDop(connection);
|
|
return dop > 1 ? `/*+ set(query_dop ${dop}) */` : undefined;
|
|
}
|
|
|
|
function isJdbcAseProfile(connection?: JdbcDialectConnection): boolean {
|
|
if (connection?.db_type !== "jdbc") return false;
|
|
const explicitIdentity = [connection.driver_profile, connection.driver_label, connection.database_info?.productName].filter(Boolean).join("\n");
|
|
return JDBC_ASE_PROFILE_PATTERNS.some((pattern) => pattern.test(explicitIdentity));
|
|
}
|
|
|
|
function isGbase8sProfile(driverProfile?: string): boolean {
|
|
return driverProfile === "gbase8s";
|
|
}
|
|
|
|
function externalConfigRecord(value: unknown): Record<string, unknown> {
|
|
return value && typeof value === "object" && !Array.isArray(value) ? { ...(value as Record<string, unknown>) } : {};
|
|
}
|