diff --git a/cmd/optional-driver-agent/provider_trino.go b/cmd/optional-driver-agent/provider_trino.go
new file mode 100644
index 0000000..77bfade
--- /dev/null
+++ b/cmd/optional-driver-agent/provider_trino.go
@@ -0,0 +1,12 @@
+//go:build gonavi_trino_driver
+
+package main
+
+import "GoNavi-Wails/internal/db"
+
+func init() {
+ agentDriverType = "trino"
+ agentDatabaseFactory = func() db.Database {
+ return &db.TrinoDB{}
+ }
+}
diff --git a/frontend/src/components/QueryEditor.tsx b/frontend/src/components/QueryEditor.tsx
index ab3a346..f9704a3 100644
--- a/frontend/src/components/QueryEditor.tsx
+++ b/frontend/src/components/QueryEditor.tsx
@@ -24,7 +24,7 @@ import {
import { extractQueryResultTableRef, type QueryResultTableRef } from '../utils/queryResultTable';
import { quoteIdentPart, quoteQualifiedIdent } from '../utils/sql';
import { formatSqlExecutionError } from '../utils/sqlErrorSemantics';
-import { shouldUseSqlEditorManagedTransaction } from '../utils/sqlEditorTransaction';
+import { shouldUseSqlEditorManagedTransactionForType } from '../utils/sqlEditorTransaction';
import { findSqlStatementRanges, resolveCurrentSqlStatementRange, resolveExecutableSql } from '../utils/sqlStatementSelection';
import { isMacLikePlatform } from '../utils/appearance';
import { splitSidebarQualifiedName } from '../utils/sidebarLocate';
@@ -3205,13 +3205,13 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
setActiveResultKey('');
return;
}
- const useManagedTransaction = shouldUseSqlEditorManagedTransaction(sourceStatements);
+ const useManagedTransaction = shouldUseSqlEditorManagedTransactionForType(connCaps.type, sourceStatements);
if (useManagedTransaction && pendingSqlTransactionRef.current) {
message.warning(translate('query_editor.transaction.message.pending_managed_transaction'));
return;
}
const managedTransactionStatementCount = sourceStatements
- .filter((statement) => shouldUseSqlEditorManagedTransaction([statement]))
+ .filter((statement) => shouldUseSqlEditorManagedTransactionForType(connCaps.type, [statement]))
.length || sourceStatements.length;
const forceReadOnlyResult = connCaps.forceReadOnlyQueryResult;
diff --git a/frontend/src/components/connectionModal/ConnectionModalStep2.tsx b/frontend/src/components/connectionModal/ConnectionModalStep2.tsx
index 2e4f27f..92b2fb4 100644
--- a/frontend/src/components/connectionModal/ConnectionModalStep2.tsx
+++ b/frontend/src/components/connectionModal/ConnectionModalStep2.tsx
@@ -1191,7 +1191,8 @@ const renderStep2 = () => {
dbType === "highgo" ||
dbType === "vastbase" ||
dbType === "opengauss" ||
- dbType === "gaussdb") &&
+ dbType === "gaussdb" ||
+ dbType === "trino") &&
renderConfigSectionCard({
sectionKey: "service",
icon: ,
diff --git a/frontend/src/components/connectionModal/connectionModalUri.test.ts b/frontend/src/components/connectionModal/connectionModalUri.test.ts
new file mode 100644
index 0000000..ef67e85
--- /dev/null
+++ b/frontend/src/components/connectionModal/connectionModalUri.test.ts
@@ -0,0 +1,50 @@
+import { describe, expect, it } from 'vitest';
+
+import {
+ buildUriFromValues,
+ getConnectionParamsPlaceholder,
+ getUriPlaceholder,
+ parseTrinoUriToValues,
+ parseUriToValues,
+} from './connectionModalUri';
+
+describe('connectionModalUri trino support', () => {
+ it('parses catalog and schema from a Trino URI into the database field', () => {
+ expect(parseTrinoUriToValues('https://alice@127.0.0.1:8443?catalog=hive&schema=default&source=GoNavi&query_timeout=30s'))
+ .toMatchObject({
+ host: '127.0.0.1',
+ port: 8443,
+ user: 'alice',
+ database: 'hive.default',
+ useSSL: true,
+ sslMode: 'required',
+ connectionParams: 'source=GoNavi&query_timeout=30s',
+ });
+ });
+
+ it('routes generic URI parsing through the Trino parser', () => {
+ expect(parseUriToValues('http://alice@127.0.0.1:8080?catalog=iceberg&schema=ods', 'trino'))
+ .toMatchObject({
+ host: '127.0.0.1',
+ port: 8080,
+ user: 'alice',
+ database: 'iceberg.ods',
+ });
+ });
+
+ it('builds a Trino URI with catalog and schema in query parameters', () => {
+ expect(buildUriFromValues({
+ type: 'trino',
+ host: '127.0.0.1',
+ port: 8080,
+ user: 'alice',
+ database: 'hive.default',
+ connectionParams: 'query_timeout=45s',
+ })).toBe('http://alice@127.0.0.1:8080?query_timeout=45s&catalog=hive&schema=default&source=GoNavi');
+ });
+
+ it('keeps dedicated Trino placeholders concise', () => {
+ expect(getUriPlaceholder('trino')).toBe('http://user@127.0.0.1:8080?catalog=hive&schema=default&source=GoNavi');
+ expect(getConnectionParamsPlaceholder('trino', 'mysql')).toBe('session_properties=query_max_execution_time:30m&query_timeout=30s');
+ });
+});
diff --git a/frontend/src/components/connectionModal/connectionModalUri.ts b/frontend/src/components/connectionModal/connectionModalUri.ts
index 48c84de..86640d6 100644
--- a/frontend/src/components/connectionModal/connectionModalUri.ts
+++ b/frontend/src/components/connectionModal/connectionModalUri.ts
@@ -342,9 +342,9 @@ const parseSingleHostUri = (
};
};
-export const parseClickHouseHTTPUriToValues = (
- uriText: string,
- fallbackPort?: number,
+export const parseClickHouseHTTPUriToValues = (
+ uriText: string,
+ fallbackPort?: number,
): Record | null => {
const trimmed = String(uriText || "").trim();
const lower = trimmed.toLowerCase();
@@ -379,8 +379,71 @@ export const parseClickHouseHTTPUriToValues = (
sslMode: isHttps ? (skipVerify ? "skip-verify" : "required") : "disable",
...extractSSLPathValuesFromParams(parsed.params, "clickhouse"),
connectionParams: serializeConnectionParams(parsed.params),
- };
-};
+ };
+};
+
+const splitTrinoNamespace = (
+ raw: unknown,
+): { catalog: string; schema: string } => {
+ const text = String(raw || "").trim();
+ if (!text) {
+ return { catalog: "", schema: "" };
+ }
+ const [catalog, schema = ""] = text.split(".", 2);
+ return {
+ catalog: String(catalog || "").trim(),
+ schema: String(schema || "").trim(),
+ };
+};
+
+const joinTrinoNamespace = (catalog: string, schema: string) => {
+ const safeCatalog = String(catalog || "").trim();
+ const safeSchema = String(schema || "").trim();
+ if (!safeCatalog) return safeSchema;
+ if (!safeSchema) return safeCatalog;
+ return `${safeCatalog}.${safeSchema}`;
+};
+
+export const parseTrinoUriToValues = (
+ uriText: string,
+): Record | null => {
+ const trimmed = String(uriText || "").trim();
+ const parsed = parseSingleHostUri(
+ trimmed,
+ ["trino", "http", "https"],
+ getDefaultPortByType("trino"),
+ );
+ if (!parsed) {
+ return null;
+ }
+ const params = new URLSearchParams(parsed.params);
+ const catalog = String(params.get("catalog") || "").trim();
+ const schema = String(params.get("schema") || "").trim();
+ params.delete("catalog");
+ params.delete("schema");
+
+ const skipVerify = normalizeUriBool(
+ params.get("skip_verify") || params.get("skipVerify"),
+ );
+ params.delete("skip_verify");
+ params.delete("skipVerify");
+
+ const namespace =
+ joinTrinoNamespace(catalog, schema) || String(parsed.database || "").trim();
+ return {
+ host: parsed.host,
+ port: parsed.port,
+ user: parsed.username,
+ password: parsed.password,
+ database: namespace,
+ useSSL: trimmed.toLowerCase().startsWith("https://"),
+ sslMode: trimmed.toLowerCase().startsWith("https://")
+ ? (skipVerify ? "skip-verify" : "required")
+ : "disable",
+ ...extractSSLPathValuesFromParams(params, "trino"),
+ connectionParams: serializeConnectionParams(params),
+ };
+};
const firstConnectionParamValue = (
params: URLSearchParams,
@@ -827,8 +890,8 @@ export const parseUriToValues = (
};
}
- if (type === "rabbitmq") {
- const defaultPort = getDefaultPortByType(type);
+ if (type === "rabbitmq") {
+ const defaultPort = getDefaultPortByType(type);
const parsed = parseSingleHostUri(
trimmedUri,
["rabbitmq", "http", "https"],
@@ -864,11 +927,15 @@ export const parseUriToValues = (
Number.isFinite(timeoutValue) && timeoutValue > 0
? Math.min(MAX_TIMEOUT_SECONDS, Math.trunc(timeoutValue))
: undefined,
- };
- }
-
- if (type === "clickhouse") {
- const httpValues = parseClickHouseHTTPUriToValues(trimmedUri);
+ };
+ }
+
+ if (type === "trino") {
+ return parseTrinoUriToValues(trimmedUri);
+ }
+
+ if (type === "clickhouse") {
+ const httpValues = parseClickHouseHTTPUriToValues(trimmedUri);
if (httpValues) {
return httpValues;
}
@@ -1070,10 +1137,13 @@ export const getUriPlaceholder = (dbType: string) => {
if (dbType === "mongodb") {
return "mongodb+srv://user:pass@cluster0.example.com/db_name?authSource=admin&authMechanism=SCRAM-SHA-256";
}
- if (dbType === "clickhouse") {
- return "clickhouse://default:pass@127.0.0.1:9000/default";
- }
- if (dbType === "chroma") {
+ if (dbType === "clickhouse") {
+ return "clickhouse://default:pass@127.0.0.1:9000/default";
+ }
+ if (dbType === "trino") {
+ return "http://user@127.0.0.1:8080?catalog=hive&schema=default&source=GoNavi";
+ }
+ if (dbType === "chroma") {
return "http://127.0.0.1:8000/default_database?tenant=default_tenant";
}
if (dbType === "qdrant") {
@@ -1140,10 +1210,12 @@ export const getConnectionParamsPlaceholder = (
return "app name=GoNavi&packet size=32767";
case "iris":
return "timeout=30";
- case "clickhouse":
- return "max_execution_time=60&compress=lz4";
- case "mongodb":
- return "retryWrites=true&readPreference=secondaryPreferred";
+ case "clickhouse":
+ return "max_execution_time=60&compress=lz4";
+ case "trino":
+ return "session_properties=query_max_execution_time:30m&query_timeout=30s";
+ case "mongodb":
+ return "retryWrites=true&readPreference=secondaryPreferred";
case "chroma":
return "tenant=default_tenant&apiKey=...";
case "qdrant":
@@ -1167,9 +1239,9 @@ export const getConnectionParamsPlaceholder = (
}
};
-export const buildUriFromValues = (values: any) => {
- const type = String(values.type || "")
- .trim()
+export const buildUriFromValues = (values: any) => {
+ const type = String(values.type || "")
+ .trim()
.toLowerCase();
const defaultPort = getDefaultPortByType(type);
const host = String(values.host || "localhost").trim();
@@ -1178,11 +1250,49 @@ export const buildUriFromValues = (values: any) => {
const password = String(values.password || "");
const database = String(values.database || "").trim();
const timeout = Number(values.timeout || 30);
- const encodedAuth = user
- ? `${encodeURIComponent(user)}${password ? `:${encodeURIComponent(password)}` : ""}@`
- : "";
-
- if (isMySQLCompatibleType(type)) {
+ const encodedAuth = user
+ ? `${encodeURIComponent(user)}${password ? `:${encodeURIComponent(password)}` : ""}@`
+ : "";
+
+ if (type === "trino") {
+ const params = new URLSearchParams();
+ mergeConnectionParams(params, values.connectionParams);
+
+ const { catalog, schema } = splitTrinoNamespace(values.database);
+ if (catalog) {
+ params.set("catalog", catalog);
+ } else {
+ params.delete("catalog");
+ }
+ if (schema) {
+ params.set("schema", schema);
+ } else {
+ params.delete("schema");
+ }
+ if (!String(params.get("source") || "").trim()) {
+ params.set("source", "GoNavi");
+ }
+
+ if (values.useSSL) {
+ const mode = String(values.sslMode || "required")
+ .trim()
+ .toLowerCase();
+ if (mode === "skip-verify" || mode === "preferred") {
+ params.set("skip_verify", "true");
+ } else {
+ params.delete("skip_verify");
+ }
+ appendSSLPathParamsForUri(params, type, values);
+ } else {
+ params.delete("skip_verify");
+ }
+
+ const query = params.toString();
+ const scheme = values.useSSL ? "https" : "http";
+ return `${scheme}://${encodedAuth}${toAddress(host, port, defaultPort)}${query ? `?${query}` : ""}`;
+ }
+
+ if (isMySQLCompatibleType(type)) {
const selectedOceanBaseProtocol =
type === "oceanbase"
? normalizeOceanBaseProtocolValue(values.oceanBaseProtocol)
diff --git a/frontend/src/store.ts b/frontend/src/store.ts
index 6c9d0c3..a1ec64f 100644
--- a/frontend/src/store.ts
+++ b/frontend/src/store.ts
@@ -315,6 +315,7 @@ const SUPPORTED_CONNECTION_TYPES = new Set([
"starrocks",
"sphinx",
"clickhouse",
+ "trino",
"postgres",
"redis",
"tdengine",
@@ -346,6 +347,7 @@ const SSL_SUPPORTED_CONNECTION_TYPES = new Set([
"sphinx",
"dameng",
"clickhouse",
+ "trino",
"postgres",
"sqlserver",
"oracle",
@@ -382,6 +384,8 @@ const getDefaultPortByType = (type: string): number => {
return 9306;
case "clickhouse":
return 9000;
+ case "trino":
+ return 8080;
case "postgres":
case "vastbase":
case "opengauss":
diff --git a/frontend/src/utils/connectionModalPresentation.test.ts b/frontend/src/utils/connectionModalPresentation.test.ts
index e43bf0f..38b280f 100644
--- a/frontend/src/utils/connectionModalPresentation.test.ts
+++ b/frontend/src/utils/connectionModalPresentation.test.ts
@@ -111,6 +111,7 @@ describe('connectionModalPresentation', () => {
'starrocks',
'sphinx',
'clickhouse',
+ 'trino',
'postgres',
'sqlserver',
'sqlite',
@@ -231,6 +232,14 @@ describe('connectionModalPresentation', () => {
'credentials',
'databaseScope',
]);
+ expect(resolveConnectionConfigLayout('trino').sections).toEqual([
+ 'identity',
+ 'uri',
+ 'target',
+ 'service',
+ 'credentials',
+ 'databaseScope',
+ ]);
expect(resolveConnectionConfigLayout('gaussdb').sections).toEqual([
'identity',
'uri',
diff --git a/frontend/src/utils/connectionModalPresentation.ts b/frontend/src/utils/connectionModalPresentation.ts
index 48be115..65b8c14 100644
--- a/frontend/src/utils/connectionModalPresentation.ts
+++ b/frontend/src/utils/connectionModalPresentation.ts
@@ -300,6 +300,19 @@ export const resolveConnectionConfigLayout = (
],
};
}
+ if (type === 'trino') {
+ return {
+ kind: 'generic-sql',
+ sections: [
+ 'identity',
+ 'uri',
+ 'target',
+ 'service',
+ 'credentials',
+ 'databaseScope',
+ ],
+ };
+ }
if (postgresCompatibleTypes.has(type)) {
return {
kind: 'postgres-compatible',
diff --git a/frontend/src/utils/connectionTypeCapabilities.test.ts b/frontend/src/utils/connectionTypeCapabilities.test.ts
index e60f39b..1e1d62f 100644
--- a/frontend/src/utils/connectionTypeCapabilities.test.ts
+++ b/frontend/src/utils/connectionTypeCapabilities.test.ts
@@ -16,6 +16,7 @@ describe('connectionTypeCapabilities', () => {
expect(singleHostUriSchemesByType.postgres).toEqual(['postgresql', 'postgres']);
expect(singleHostUriSchemesByType.opengauss).toContain('jdbc:opengauss');
expect(singleHostUriSchemesByType.gaussdb).toEqual(['gaussdb', 'postgresql', 'postgres']);
+ expect(singleHostUriSchemesByType.trino).toEqual(['trino', 'http', 'https']);
expect(singleHostUriSchemesByType.dameng).toEqual(['dameng', 'dm']);
expect(singleHostUriSchemesByType.elasticsearch).toEqual(['http', 'https']);
expect(singleHostUriSchemesByType.chroma).toEqual(['http', 'https', 'chroma']);
@@ -28,6 +29,7 @@ describe('connectionTypeCapabilities', () => {
expect(supportsSSLForType('redis')).toBe(true);
expect(supportsSSLForType('MongoDB')).toBe(true);
expect(supportsSSLForType('elasticsearch')).toBe(true);
+ expect(supportsSSLForType('trino')).toBe(true);
expect(supportsSSLForType('gaussdb')).toBe(true);
expect(supportsSSLForType('greatdb')).toBe(true);
expect(supportsSSLForType('chroma')).toBe(true);
@@ -45,7 +47,9 @@ describe('connectionTypeCapabilities', () => {
expect(supportsSSLCAPathForType('gaussdb')).toBe(true);
expect(supportsSSLClientCertificateForType('gaussdb')).toBe(true);
expect(supportsSSLCAPathForType('sqlserver')).toBe(true);
+ expect(supportsSSLCAPathForType('trino')).toBe(true);
expect(supportsSSLClientCertificateForType('sqlserver')).toBe(false);
+ expect(supportsSSLClientCertificateForType('trino')).toBe(true);
expect(supportsSSLCAPathForType('redis')).toBe(true);
expect(supportsSSLClientCertificateForType('redis')).toBe(true);
expect(supportsSSLCAPathForType('chroma')).toBe(true);
@@ -80,6 +84,7 @@ describe('connectionTypeCapabilities', () => {
expect(supportsConnectionParamsForType('gdb')).toBe(true);
expect(supportsConnectionParamsForType('postgres')).toBe(true);
expect(supportsConnectionParamsForType('gaussdb')).toBe(true);
+ expect(supportsConnectionParamsForType('trino')).toBe(true);
expect(supportsConnectionParamsForType('oracle')).toBe(true);
expect(supportsConnectionParamsForType('mongodb')).toBe(true);
expect(supportsConnectionParamsForType('dameng')).toBe(true);
diff --git a/frontend/src/utils/connectionTypeCapabilities.ts b/frontend/src/utils/connectionTypeCapabilities.ts
index d001cd4..a88cf87 100644
--- a/frontend/src/utils/connectionTypeCapabilities.ts
+++ b/frontend/src/utils/connectionTypeCapabilities.ts
@@ -3,6 +3,7 @@ export const singleHostUriSchemesByType: Record = {
opengauss: ["opengauss", "jdbc:opengauss", "postgresql", "postgres"],
gaussdb: ["gaussdb", "postgresql", "postgres"],
clickhouse: ["clickhouse"],
+ trino: ["trino", "http", "https"],
oracle: ["oracle"],
sqlserver: ["sqlserver"],
iris: ["iris", "intersystems"],
@@ -55,6 +56,7 @@ const sslSupportedTypes = new Set([
"sphinx",
"dameng",
"clickhouse",
+ "trino",
"postgres",
"sqlserver",
"oracle",
@@ -86,6 +88,7 @@ const sslCAPathSupportedTypes = new Set([
"starrocks",
"sphinx",
"clickhouse",
+ "trino",
"postgres",
"sqlserver",
"kingbase",
@@ -113,6 +116,7 @@ const sslClientCertificateSupportedTypes = new Set([
"sphinx",
"dameng",
"clickhouse",
+ "trino",
"postgres",
"kingbase",
"highgo",
@@ -167,6 +171,7 @@ export const supportsConnectionParamsForType = (type: string) =>
type === "sqlserver" ||
type === "iris" ||
type === "clickhouse" ||
+ type === "trino" ||
type === "mongodb" ||
type === "dameng" ||
type === "tdengine" ||
diff --git a/frontend/src/utils/connectionTypeCatalog.test.ts b/frontend/src/utils/connectionTypeCatalog.test.ts
index 3a85f9c..a0a5157 100644
--- a/frontend/src/utils/connectionTypeCatalog.test.ts
+++ b/frontend/src/utils/connectionTypeCatalog.test.ts
@@ -24,6 +24,7 @@ describe('connectionTypeCatalog', () => {
expect(keys).toContain('oceanbase');
expect(keys).toContain('gaussdb');
expect(keys).toContain('goldendb');
+ expect(keys).toContain('trino');
expect(keys).toContain('mongodb');
expect(keys).toContain('redis');
expect(keys).toContain('elasticsearch');
@@ -46,6 +47,7 @@ describe('connectionTypeCatalog', () => {
expect(getConnectionTypeDefaultPort('redis')).toBe(6379);
expect(getConnectionTypeDefaultPort('oracle')).toBe(1521);
expect(getConnectionTypeDefaultPort('mongodb')).toBe(27017);
+ expect(getConnectionTypeDefaultPort('trino')).toBe(8080);
expect(getConnectionTypeDefaultPort('elasticsearch')).toBe(9200);
expect(getConnectionTypeDefaultPort('chroma')).toBe(8000);
expect(getConnectionTypeDefaultPort('qdrant')).toBe(6333);
@@ -66,6 +68,7 @@ describe('connectionTypeCatalog', () => {
expect(getConnectionTypeHint('kafka')).toContain('Consumer Group');
expect(getConnectionTypeHint('oceanbase')).toBe('MySQL / Oracle 租户');
expect(getConnectionTypeHint('goldendb')).toBe('MySQL 兼容 / 分布式事务');
+ expect(getConnectionTypeHint('trino')).toBe('HTTP / HTTPS / catalog.schema');
expect(getConnectionTypeHint('duckdb')).toBe('本地文件连接');
expect(getConnectionTypeHint('mysql')).toBe('标准连接配置');
});
diff --git a/frontend/src/utils/connectionTypeCatalog.ts b/frontend/src/utils/connectionTypeCatalog.ts
index 696f34b..40e9f53 100644
--- a/frontend/src/utils/connectionTypeCatalog.ts
+++ b/frontend/src/utils/connectionTypeCatalog.ts
@@ -18,6 +18,7 @@ export const CONNECTION_TYPE_GROUPS: ConnectionTypeCatalogGroup[] = [
{ key: 'starrocks', name: 'StarRocks' },
{ key: 'sphinx', name: 'Sphinx' },
{ key: 'clickhouse', name: 'ClickHouse' },
+ { key: 'trino', name: 'Trino' },
{ key: 'postgres', name: 'PostgreSQL' },
{ key: 'sqlserver', name: 'SQL Server' },
{ key: 'iris', name: 'InterSystems IRIS' },
@@ -97,6 +98,8 @@ export const getConnectionTypeDefaultPort = (type: string): number => {
return 9306;
case 'clickhouse':
return 9000;
+ case 'trino':
+ return 8080;
case 'postgres':
case 'opengauss':
case 'gaussdb':
@@ -180,6 +183,8 @@ export const getConnectionTypeHint = (type: string): string => {
case 'sqlite':
case 'duckdb':
return '本地文件连接';
+ case 'trino':
+ return 'HTTP / HTTPS / catalog.schema';
default:
return '标准连接配置';
}
diff --git a/frontend/src/utils/dataSourceCapabilities.test.ts b/frontend/src/utils/dataSourceCapabilities.test.ts
index a82ebf3..a2674a2 100644
--- a/frontend/src/utils/dataSourceCapabilities.test.ts
+++ b/frontend/src/utils/dataSourceCapabilities.test.ts
@@ -58,6 +58,19 @@ describe('dataSourceCapabilities', () => {
});
});
+ it('treats Trino as an editable SQL datasource without database-level DDL shortcuts', () => {
+ expect(getDataSourceCapabilities({ type: 'trino' })).toMatchObject({
+ type: 'trino',
+ supportsQueryEditor: true,
+ supportsSqlQueryExport: true,
+ supportsCopyInsert: true,
+ supportsCreateDatabase: false,
+ supportsRenameDatabase: false,
+ supportsDropDatabase: false,
+ forceReadOnlyQueryResult: false,
+ });
+ });
+
it('keeps InterSystems IRIS as an editable SQL datasource capability', () => {
expect(getDataSourceCapabilities({ type: 'iris' })).toMatchObject({
type: 'iris',
diff --git a/frontend/src/utils/dataSourceCapabilities.ts b/frontend/src/utils/dataSourceCapabilities.ts
index 7a64616..737e93b 100644
--- a/frontend/src/utils/dataSourceCapabilities.ts
+++ b/frontend/src/utils/dataSourceCapabilities.ts
@@ -111,6 +111,7 @@ const SQL_QUERY_EXPORT_TYPES = new Set([
'dameng',
'tdengine',
'clickhouse',
+ 'trino',
]);
const COPY_INSERT_TYPES = new Set([
@@ -135,6 +136,7 @@ const COPY_INSERT_TYPES = new Set([
'dameng',
'tdengine',
'clickhouse',
+ 'trino',
]);
const QUERY_EDITOR_DISABLED_TYPES = new Set(['redis']);
diff --git a/frontend/src/utils/sqlEditorTransaction.test.ts b/frontend/src/utils/sqlEditorTransaction.test.ts
index efd76e5..a0cdb73 100644
--- a/frontend/src/utils/sqlEditorTransaction.test.ts
+++ b/frontend/src/utils/sqlEditorTransaction.test.ts
@@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest';
import {
resolveSqlEditorOperationKeyword,
shouldUseSqlEditorManagedTransaction,
+ shouldUseSqlEditorManagedTransactionForType,
} from './sqlEditorTransaction';
describe('sqlEditorTransaction', () => {
@@ -44,4 +45,10 @@ describe('sqlEditorTransaction', () => {
'DELETE FROM users WHERE id = 1',
])).toBe(false);
});
+
+ it('keeps Trino DML on the plain multi-statement execution path', () => {
+ expect(shouldUseSqlEditorManagedTransactionForType('trino', [
+ 'UPDATE hive.default.orders SET status = \'done\'',
+ ])).toBe(false);
+ });
});
diff --git a/frontend/src/utils/sqlEditorTransaction.ts b/frontend/src/utils/sqlEditorTransaction.ts
index 6cc505f..69ef545 100644
--- a/frontend/src/utils/sqlEditorTransaction.ts
+++ b/frontend/src/utils/sqlEditorTransaction.ts
@@ -249,7 +249,13 @@ const isSqlEditorTransactionControlStatement = (statement: string): boolean => {
return keyword === 'start' && /\btransaction\b/i.test(statement);
};
-export const shouldUseSqlEditorManagedTransaction = (statements: string[]): boolean => {
+export const shouldUseSqlEditorManagedTransactionForType = (
+ type: string,
+ statements: string[],
+): boolean => {
+ if (String(type || '').trim().toLowerCase() === 'trino') {
+ return false;
+ }
let hasManagedWrite = false;
for (const statement of statements) {
const trimmed = String(statement || '').trim();
@@ -265,3 +271,6 @@ export const shouldUseSqlEditorManagedTransaction = (statements: string[]): bool
}
return hasManagedWrite;
};
+
+export const shouldUseSqlEditorManagedTransaction = (statements: string[]): boolean =>
+ shouldUseSqlEditorManagedTransactionForType('', statements);
diff --git a/go.mod b/go.mod
index b49cb10..ee928f7 100644
--- a/go.mod
+++ b/go.mod
@@ -24,6 +24,7 @@ require (
github.com/segmentio/kafka-go v0.4.51
github.com/sijms/go-ora/v2 v2.9.0
github.com/taosdata/driver-go/v3 v3.7.8
+ github.com/trinodb/trino-go-client v0.333.0
github.com/wailsapp/wails/v2 v2.11.0
github.com/xuri/excelize/v2 v2.10.0
go.mongodb.org/mongo-driver v1.17.9
@@ -44,10 +45,18 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/mock v1.5.0 // indirect
github.com/google/jsonschema-go v0.4.3 // indirect
+ github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
+ github.com/jcmturner/aescts/v2 v2.0.0 // indirect
+ github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
+ github.com/jcmturner/gofork v1.7.6 // indirect
+ github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
+ github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect
+ github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
+ github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/segmentio/encoding v0.5.4 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/tidwall/gjson v1.14.2 // indirect
diff --git a/go.sum b/go.sum
index b9ecdff..215ae28 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,6 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
+dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
gitea.com/kingbase/gokb v0.0.0-20201021123113-29bd62a876c3 h1:QjslQNaH5Nuap5i4nijS0OYV6GMk5kqrAmgU90zBKd4=
@@ -19,6 +21,8 @@ github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZb
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww=
+github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
+github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
@@ -30,6 +34,12 @@ github.com/ClickHouse/clickhouse-go/v2 v2.43.0 h1:fUR05TrF1GyvLDa/mAQjkx7KbgwdLR
github.com/ClickHouse/clickhouse-go/v2 v2.43.0/go.mod h1:o6jf7JM/zveWC/PP277BLxjHy5KjnGX/jfljhM4s34g=
github.com/HuaweiCloudDeveloper/gaussdb-go v1.0.0-rc1 h1:OIZ83SgbK0ImF/vKFSfNoCAQWQySx5sYhj0RgsWbMbA=
github.com/HuaweiCloudDeveloper/gaussdb-go v1.0.0-rc1/go.mod h1:Xf3AtRet+/ygewEMfzt0FbIFydPkv47Hx5Xz3w07+yo=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
+github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
+github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
+github.com/ahmetb/dlog v0.0.0-20170105205344-4fb5f8204f26 h1:3YVZUqkoev4mL+aCwVOSWV4M7pN+NURHL38Z2zq5JKA=
+github.com/ahmetb/dlog v0.0.0-20170105205344-4fb5f8204f26/go.mod h1:ymXt5bw5uSNu4jveerFxE0vNYxF8ncqbptntMaFMg3k=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/apache/arrow-go/v18 v18.5.1 h1:yaQ6zxMGgf9YCYw4/oaeOU3AULySDlAYDOcnr4LdHdI=
@@ -41,12 +51,52 @@ github.com/apache/rocketmq-client-go/v2 v2.1.2/go.mod h1:6I6vgxHR3hzrvn+6n/4mrhS
github.com/apache/thrift v0.15.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
github.com/apache/thrift v0.22.0 h1:r7mTJdj51TMDe6RtcmNdQxgn9XcyfGDOzegMDRg47uc=
github.com/apache/thrift v0.22.0/go.mod h1:1e7J/O1Ae6ZQMTYdy9xa3w9k+XHWPfRvdPyJeynQ+/g=
+github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=
+github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=
+github.com/aws/aws-sdk-go-v2 v1.39.0 h1:xm5WV/2L4emMRmMjHFykqiA4M/ra0DJVSWUkDyBjbg4=
+github.com/aws/aws-sdk-go-v2 v1.39.0/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E=
+github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1/go.mod h1:ddqbooRZYNoJ2dsTwOty16rM+/Aqmk/GOXrK8cg7V00=
+github.com/aws/aws-sdk-go-v2/config v1.31.8 h1:kQjtOLlTU4m4A64TsRcqwNChhGCwaPBt+zCQt/oWsHU=
+github.com/aws/aws-sdk-go-v2/config v1.31.8/go.mod h1:QPpc7IgljrKwH0+E6/KolCgr4WPLerURiU592AYzfSY=
+github.com/aws/aws-sdk-go-v2/credentials v1.18.12 h1:zmc9e1q90wMn8wQbjryy8IwA6Q4XlaL9Bx2zIqdNNbk=
+github.com/aws/aws-sdk-go-v2/credentials v1.18.12/go.mod h1:3VzdRDR5u3sSJRI4kYcOSIBbeYsgtVk7dG5R/U6qLWY=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 h1:Is2tPmieqGS2edBnmOJIbdvOA6Op+rRpaYR60iBAwXM=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7/go.mod h1:F1i5V5421EGci570yABvpIXgRIBPb5JM+lSkHF6Dq5w=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 h1:UCxq0X9O3xrlENdKf1r9eRJoKz/b0AfGkpp3a7FPlhg=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7/go.mod h1:rHRoJUNUASj5Z/0eqI4w32vKvC7atoWR0jC+IkmVH8k=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 h1:Y6DTZUn7ZUC4th9FMBbo8LVE+1fyq3ofw+tRwkUd3PY=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7/go.mod h1:x3XE6vMnU9QvHN/Wrx2s44kwzV2o2g5x/siw4ZUJ9g8=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.7 h1:BszAktdUo2xlzmYHjWMq70DqJ7cROM8iBd3f6hrpuMQ=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.7/go.mod h1:XJ1yHki/P7ZPuG4fd3f0Pg/dSGA2cTQBCLw82MH2H48=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.7 h1:zmZ8qvtE9chfhBPuKB2aQFxW5F/rpwXUgmcVCgQzqRw=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.7/go.mod h1:vVYfbpd2l+pKqlSIDIOgouxNsGu5il9uDp0ooWb0jys=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 h1:mLgc5QIgOy26qyh5bvW+nDoAppxgn3J2WV3m9ewq7+8=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7/go.mod h1:wXb/eQnqt8mDQIQTTmcw58B5mYGxzLGZGK8PWNFZ0BA=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.7 h1:u3VbDKUCWarWiU+aIUK4gjTr/wQFXV17y3hgNno9fcA=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.7/go.mod h1:/OuMQwhSyRapYxq6ZNpPer8juGNrB4P5Oz8bZ2cgjQE=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.88.1 h1:+RpGuaQ72qnU83qBKVwxkznewEdAGhIWo/PQCmkhhog=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.88.1/go.mod h1:xajPTguLoeQMAOE44AAP2RQoUhF8ey1g5IFHARv71po=
+github.com/aws/aws-sdk-go-v2/service/sso v1.29.3 h1:7PKX3VYsZ8LUWceVRuv0+PU+E7OtQb1lgmi5vmUE9CM=
+github.com/aws/aws-sdk-go-v2/service/sso v1.29.3/go.mod h1:Ql6jE9kyyWI5JHn+61UT/Y5Z0oyVJGmgmJbZD5g4unY=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 h1:e0XBRn3AptQotkyBFrHAxFB8mDhAIOfsG+7KyJ0dg98=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4/go.mod h1:XclEty74bsGBCr1s0VSaA11hQ4ZidK4viWK7rRfO88I=
+github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 h1:PR00NXRYgY4FWHqOGx3fC3lhVKjsp1GdloDv2ynMSd8=
+github.com/aws/aws-sdk-go-v2/service/sts v1.38.4/go.mod h1:Z+Gd23v97pX9zK97+tX4ppAgqCt3Z2dIXB02CtBncK8=
+github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE=
+github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
@@ -56,6 +106,8 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
+github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -64,6 +116,14 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/docker/cli v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY=
+github.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
+github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
+github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
+github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
+github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/duckdb/duckdb-go-bindings v0.3.3 h1:lXogtCY8hiGLQvTfK55HcgvaA3K2MrwKeZGqhIin35U=
github.com/duckdb/duckdb-go-bindings v0.3.3/go.mod h1:zS7OpBP8zwVlP38OljRZOnqWYlNd4KLcVfMoA1JFzpk=
github.com/duckdb/duckdb-go-bindings/lib/darwin-amd64 v0.3.3 h1:ue8BtIOSt+2Bt2fEfTAvBcQLxzBFhgfCcyzPtqQWTRA=
@@ -94,6 +154,8 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
@@ -161,16 +223,25 @@ github.com/google/jsonschema-go v0.4.3/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
+github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
+github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
+github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
+github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
+github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4=
github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
@@ -188,6 +259,18 @@ github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
+github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
+github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
+github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
+github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
+github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
+github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
+github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
+github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
+github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
+github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
+github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
+github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
@@ -242,6 +325,12 @@ github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpsp
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI=
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=
+github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
+github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
+github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
+github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
+github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
+github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/modelcontextprotocol/go-sdk v1.6.1 h1:0zOSupjKUxPKSocPT1Wtago+mUHU2/uZ4xSOY0FGReU=
github.com/modelcontextprotocol/go-sdk v1.6.1/go.mod h1:kzm3kzFL1/+AziGOE0nUs3gvPoNxMCvkxokMkuFapXQ=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -268,11 +357,21 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
+github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
+github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
+github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
+github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
+github.com/opencontainers/runc v1.3.1 h1:c/yY0oh2wK7tzDuD56REnSxyU8ubh8hoAIOLGLrm4SM=
+github.com/opencontainers/runc v1.3.1/go.mod h1:9wbWt42gV+KRxKRVVugNP6D5+PQciRbenB4fLVsqGPs=
+github.com/ory/dockertest/v3 v3.12.0 h1:3oV9d0sDzlSQfHtIaB5k6ghUCVMVLpAY8hwrqoCyRCw=
+github.com/ory/dockertest/v3 v3.12.0/go.mod h1:aKNDTva3cp8dwOWwb9cWuX84aH5akkxXRvO7KCwWVjE=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/paulmach/orb v0.12.0 h1:z+zOwjmG3MyEEqzv92UN49Lg1JFYx0L9GpGKNVDKk1s=
github.com/paulmach/orb v0.12.0/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
+github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
+github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0=
github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
@@ -325,11 +424,13 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
@@ -349,6 +450,8 @@ github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ=
github.com/tkrajina/go-reflector v0.5.8/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
+github.com/trinodb/trino-go-client v0.333.0 h1:+bsW8/uLFNF00MEL9JZJym94LlUnle25VgDlWGPEZos=
+github.com/trinodb/trino-go-client v0.333.0/go.mod h1:91okdYtRUZoj3XJu/tqdzu11sNliQuN4A+vMFEB8GVE=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
@@ -367,6 +470,12 @@ github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
+github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.10.0 h1:8aKsP7JD39iKLc6dH5Tw3dgV3sPRh8uRVXu/fMstfW4=
@@ -413,6 +522,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -437,6 +547,7 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@@ -446,6 +557,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -483,6 +596,7 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
@@ -490,6 +604,7 @@ golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 h1:bTLqdHv7xrGlFbvf5/T
golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -498,6 +613,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/internal/app/db_context.go b/internal/app/db_context.go
index 3930394..a440852 100644
--- a/internal/app/db_context.go
+++ b/internal/app/db_context.go
@@ -26,7 +26,7 @@ func normalizeRunConfig(config connection.ConnectionConfig, dbName string) conne
if !isOceanBaseOracleProtocol(config) {
runConfig.Database = name
}
- case "mysql", "mariadb", "goldendb", "greatdb", "gdb", "diros", "starrocks", "sphinx", "postgres", "kingbase", "highgo", "vastbase", "opengauss", "gaussdb", "sqlserver", "iris", "intersystems", "intersystemsiris", "inter-systems", "inter-systems-iris", "mongodb", "tdengine", "iotdb", "clickhouse", "rabbitmq", "rabbit-mq", "rabbit_mq":
+ case "mysql", "mariadb", "goldendb", "greatdb", "gdb", "diros", "starrocks", "sphinx", "postgres", "kingbase", "highgo", "vastbase", "opengauss", "gaussdb", "sqlserver", "iris", "intersystems", "intersystemsiris", "inter-systems", "inter-systems-iris", "mongodb", "tdengine", "iotdb", "clickhouse", "trino", "rabbitmq", "rabbit-mq", "rabbit_mq":
// 这些类型的 dbName 表示"数据库",需要写入连接配置以选择目标库。
runConfig.Database = name
case "dameng":
@@ -57,7 +57,7 @@ func normalizeSchemaAndTable(config connection.ConnectionConfig, dbName string,
// Elasticsearch:索引名可能含多个点(如 iot_pro_biz_operate_log.index.20240626),
// 不能按点分割,直接返回原始数据库名和完整表名。
- if dbType == "elasticsearch" || dbType == "iotdb" || dbType == "rocketmq" || dbType == "mqtt" || dbType == "kafka" || dbType == "rabbitmq" {
+ if dbType == "elasticsearch" || dbType == "iotdb" || dbType == "rocketmq" || dbType == "mqtt" || dbType == "kafka" || dbType == "rabbitmq" || dbType == "trino" {
return rawDB, rawTable
}
@@ -116,7 +116,7 @@ func normalizeSchemaAndTable(config connection.ConnectionConfig, dbName string,
func normalizeMetadataSchemaAndTable(config connection.ConnectionConfig, dbName string, tableName string) (string, string) {
schema, table := normalizeSchemaAndTable(config, dbName, tableName)
switch resolveDDLDBType(config) {
- case "rocketmq", "mqtt", "kafka", "rabbitmq":
+ case "rocketmq", "mqtt", "kafka", "rabbitmq", "trino":
return schema, table
case "postgres", "kingbase", "highgo", "vastbase", "opengauss", "gaussdb":
rawTable := strings.TrimSpace(tableName)
diff --git a/internal/app/db_context_test.go b/internal/app/db_context_test.go
index 31a11cd..f1a3bdc 100644
--- a/internal/app/db_context_test.go
+++ b/internal/app/db_context_test.go
@@ -50,6 +50,18 @@ func TestNormalizeSchemaAndTable_PostgresStillSplitsQualifiedName(t *testing.T)
}
}
+func TestNormalizeSchemaAndTable_TrinoPreservesDottedTableName(t *testing.T) {
+ t.Parallel()
+
+ schema, table := normalizeSchemaAndTable(connection.ConnectionConfig{
+ Type: "trino",
+ }, "hive.default", "daily.events.v1")
+
+ if schema != "hive.default" || table != "daily.events.v1" {
+ t.Fatalf("expected trino table name to stay intact, got %q.%q", schema, table)
+ }
+}
+
func TestNormalizeSchemaAndTable_KingbaseNormalizesEscapedQualifiedName(t *testing.T) {
t.Parallel()
@@ -158,6 +170,18 @@ func TestNormalizeMetadataSchemaAndTable_NonPGLikeKeepsNormalBehavior(t *testing
}
}
+func TestNormalizeMetadataSchemaAndTable_TrinoPreservesDottedTableName(t *testing.T) {
+ t.Parallel()
+
+ schema, table := normalizeMetadataSchemaAndTable(connection.ConnectionConfig{
+ Type: "trino",
+ }, "iceberg.analytics", "ods.orders.v1")
+
+ if schema != "iceberg.analytics" || table != "ods.orders.v1" {
+ t.Fatalf("expected trino metadata table to stay intact, got %q.%q", schema, table)
+ }
+}
+
func TestNormalizeSchemaAndTable_PGLikePureTableStillSplitsKingbaseSearchPathOnlyInMetadata(t *testing.T) {
t.Parallel()
@@ -216,6 +240,19 @@ func TestNormalizeRunConfig_StarRocksUsesDatabaseFromTree(t *testing.T) {
}
}
+func TestNormalizeRunConfig_TrinoUsesNamespaceFromTree(t *testing.T) {
+ t.Parallel()
+
+ runConfig := normalizeRunConfig(connection.ConnectionConfig{
+ Type: "trino",
+ Database: "hive.default",
+ }, "iceberg.analytics")
+
+ if runConfig.Database != "iceberg.analytics" {
+ t.Fatalf("expected trino namespace from tree, got %q", runConfig.Database)
+ }
+}
+
func TestNormalizeRunConfig_GoldenDBUsesDatabaseFromTree(t *testing.T) {
t.Parallel()
diff --git a/internal/app/db_proxy.go b/internal/app/db_proxy.go
index fd07e80..22156eb 100644
--- a/internal/app/db_proxy.go
+++ b/internal/app/db_proxy.go
@@ -233,6 +233,8 @@ func defaultPortByType(driverType string) int {
return 27017
case "clickhouse":
return 9000
+ case "trino":
+ return 8080
case "highgo":
return 5866
case "iris":
diff --git a/internal/app/methods_db.go b/internal/app/methods_db.go
index 1a0a484..6c79970 100644
--- a/internal/app/methods_db.go
+++ b/internal/app/methods_db.go
@@ -496,8 +496,8 @@ func normalizeSchemaAndTableByType(dbType string, dbName string, tableName strin
return rawDB, rawTable
}
- // Elasticsearch / RocketMQ / MQTT / RabbitMQ / Kafka:对象名可能含多个点或路径,不能按点分割
- if dbType == "elasticsearch" || dbType == "rocketmq" || dbType == "mqtt" || dbType == "kafka" || dbType == "rabbitmq" {
+ // Elasticsearch / RocketMQ / MQTT / RabbitMQ / Kafka / Trino:对象名可能含多个点或路径,不能按点分割
+ if dbType == "elasticsearch" || dbType == "rocketmq" || dbType == "mqtt" || dbType == "kafka" || dbType == "rabbitmq" || dbType == "trino" {
return rawDB, rawTable
}
@@ -575,12 +575,35 @@ func resolveCreateStatementTargets(config connection.ConnectionConfig, dbType st
func quoteTableIdentByType(dbType string, schema string, table string) string {
s := strings.TrimSpace(schema)
t := strings.TrimSpace(table)
+ if dbType == "trino" {
+ catalog, namespace := splitTrinoNamespace(s)
+ switch {
+ case catalog == "" && namespace == "":
+ return quoteIdentByType(dbType, t)
+ case namespace == "":
+ return fmt.Sprintf("%s.%s", quoteIdentByType(dbType, catalog), quoteIdentByType(dbType, t))
+ default:
+ return fmt.Sprintf("%s.%s.%s", quoteIdentByType(dbType, catalog), quoteIdentByType(dbType, namespace), quoteIdentByType(dbType, t))
+ }
+ }
if s == "" {
return quoteIdentByType(dbType, t)
}
return fmt.Sprintf("%s.%s", quoteIdentByType(dbType, s), quoteIdentByType(dbType, t))
}
+func splitTrinoNamespace(raw string) (string, string) {
+ text := strings.TrimSpace(raw)
+ if text == "" {
+ return "", ""
+ }
+ parts := strings.SplitN(text, ".", 2)
+ if len(parts) == 1 {
+ return strings.TrimSpace(parts[0]), ""
+ }
+ return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
+}
+
func buildRunConfigForDDL(config connection.ConnectionConfig, dbType string, dbName string) connection.ConnectionConfig {
runConfig := normalizeRunConfig(config, dbName)
if strings.EqualFold(strings.TrimSpace(config.Type), "custom") {
diff --git a/internal/app/methods_db_create_statement_test.go b/internal/app/methods_db_create_statement_test.go
index 46d07fa..9323ff2 100644
--- a/internal/app/methods_db_create_statement_test.go
+++ b/internal/app/methods_db_create_statement_test.go
@@ -193,6 +193,24 @@ func TestNormalizeSchemaAndTableByType_RabbitMQPreservesDottedQueueName(t *testi
}
}
+func TestNormalizeSchemaAndTableByType_TrinoPreservesDottedTableName(t *testing.T) {
+ t.Parallel()
+
+ schema, table := normalizeSchemaAndTableByType("trino", "hive.default", "orders.events.v1")
+ if schema != "hive.default" || table != "orders.events.v1" {
+ t.Fatalf("expected trino table name to stay intact, got %q.%q", schema, table)
+ }
+}
+
+func TestQuoteTableIdentByType_TrinoKeepsCatalogSchemaAndDottedTable(t *testing.T) {
+ t.Parallel()
+
+ got := quoteTableIdentByType("trino", "hive.default", "orders.events.v1")
+ if got != `"hive"."default"."orders.events.v1"` {
+ t.Fatalf("unexpected trino quoted table: %s", got)
+ }
+}
+
func TestBuildRunConfigForDDL_CustomHighGoUsesDatabase(t *testing.T) {
t.Parallel()
diff --git a/internal/app/methods_db_transaction.go b/internal/app/methods_db_transaction.go
index a3c39ef..6089737 100644
--- a/internal/app/methods_db_transaction.go
+++ b/internal/app/methods_db_transaction.go
@@ -278,6 +278,9 @@ func executeManagedSQLTransactionStatements(ctx context.Context, session db.Stat
}
func shouldUseManagedSQLTransaction(dbType string, query string) bool {
+ if strings.EqualFold(strings.TrimSpace(dbType), "trino") {
+ return false
+ }
statements := splitSQLStatements(query)
hasManagedWrite := false
for _, stmt := range statements {
diff --git a/internal/app/methods_db_transaction_test.go b/internal/app/methods_db_transaction_test.go
new file mode 100644
index 0000000..23037e5
--- /dev/null
+++ b/internal/app/methods_db_transaction_test.go
@@ -0,0 +1,14 @@
+package app
+
+import "testing"
+
+func TestShouldUseManagedSQLTransaction_TrinoAlwaysUsesPlainExecution(t *testing.T) {
+ t.Parallel()
+
+ if shouldUseManagedSQLTransaction("trino", "UPDATE hive.default.orders SET status = 'done'") {
+ t.Fatal("expected trino DML to skip SQL editor managed transactions")
+ }
+ if shouldUseManagedSQLTransaction("trino", "BEGIN; UPDATE hive.default.orders SET status = 'done'; COMMIT;") {
+ t.Fatal("expected trino explicit transactions to stay unmanaged")
+ }
+}
diff --git a/internal/app/methods_driver.go b/internal/app/methods_driver.go
index ac13650..9aa9c83 100644
--- a/internal/app/methods_driver.go
+++ b/internal/app/methods_driver.go
@@ -395,7 +395,8 @@ const builtinDriverManifestJSON = `{
"tdengine": { "engine": "go", "version": "3.7.8", "checksumPolicy": "off", "downloadUrl": "builtin://activate/tdengine" },
"iotdb": { "engine": "go", "version": "1.3.7", "checksumPolicy": "off", "downloadUrl": "builtin://activate/iotdb" },
"clickhouse": { "engine": "go", "version": "2.43.1", "checksumPolicy": "off", "downloadUrl": "builtin://activate/clickhouse" },
- "elasticsearch": { "engine": "go", "version": "8.19.6", "checksumPolicy": "off", "downloadUrl": "builtin://activate/elasticsearch" }
+ "elasticsearch": { "engine": "go", "version": "8.19.6", "checksumPolicy": "off", "downloadUrl": "builtin://activate/elasticsearch" },
+ "trino": { "engine": "go", "version": "0.333.0", "checksumPolicy": "off", "downloadUrl": "builtin://activate/trino" }
}
}`
@@ -462,6 +463,7 @@ var latestDriverVersionMap = map[string]string{
"iotdb": "1.3.7",
"clickhouse": "2.43.1",
"elasticsearch": "8.19.6",
+ "trino": "0.333.0",
"oracle": "2.9.0",
"postgres": "1.11.2",
"redis": "9.17.3",
@@ -489,6 +491,7 @@ var driverGoModulePathMap = map[string]string{
"iotdb": "github.com/apache/iotdb-client-go",
"clickhouse": "github.com/ClickHouse/clickhouse-go/v2",
"elasticsearch": "github.com/elastic/go-elasticsearch/v8",
+ "trino": "github.com/trinodb/trino-go-client",
}
var driverGoModuleAliasPathMap = map[string][]string{
@@ -1745,6 +1748,7 @@ func allDriverDefinitionsWithPackages(packages map[string]pinnedDriverPackage) [
buildOptionalGoDriverDefinition("iotdb", "Apache IoTDB", packages),
buildOptionalGoDriverDefinition("clickhouse", "ClickHouse", packages),
buildOptionalGoDriverDefinition("elasticsearch", "Elasticsearch", packages),
+ buildOptionalGoDriverDefinition("trino", "Trino", packages),
}
}
@@ -4330,6 +4334,8 @@ func optionalDriverBuildTag(driverType string, selectedVersion string) (string,
return "gonavi_clickhouse_driver", nil
case "elasticsearch":
return "gonavi_elasticsearch_driver", nil
+ case "trino":
+ return "gonavi_trino_driver", nil
default:
return "", fmt.Errorf("未配置驱动构建标签:%s", driverType)
}
diff --git a/internal/app/methods_driver_agent_revision_test.go b/internal/app/methods_driver_agent_revision_test.go
index be16c7b..5bd2a27 100644
--- a/internal/app/methods_driver_agent_revision_test.go
+++ b/internal/app/methods_driver_agent_revision_test.go
@@ -231,6 +231,7 @@ func optionalDriverAgentRevisionTestDrivers(t *testing.T) []string {
"iotdb",
"clickhouse",
"elasticsearch",
+ "trino",
}
for _, driverType := range drivers {
if db.OptionalDriverAgentRevision(driverType) == "" {
diff --git a/internal/app/methods_driver_version_test.go b/internal/app/methods_driver_version_test.go
index 8e29651..f875f5d 100644
--- a/internal/app/methods_driver_version_test.go
+++ b/internal/app/methods_driver_version_test.go
@@ -504,6 +504,39 @@ func TestElasticsearchDriverDefinitionUsesOptionalAgent(t *testing.T) {
}
}
+func TestTrinoDriverDefinitionUsesOptionalAgent(t *testing.T) {
+ definition, ok := resolveDriverDefinition("trino")
+ if !ok {
+ t.Fatal("expected trino driver definition")
+ }
+ if definition.Name != "Trino" {
+ t.Fatalf("unexpected trino driver name: %q", definition.Name)
+ }
+ if definition.BuiltIn {
+ t.Fatal("expected trino to be an optional driver agent")
+ }
+ if driverGoModulePathMap["trino"] != "github.com/trinodb/trino-go-client" {
+ t.Fatalf("unexpected trino go module path: %q", driverGoModulePathMap["trino"])
+ }
+ if definition.PinnedVersion != "0.333.0" {
+ t.Fatalf("unexpected trino definition pinned version: %q", definition.PinnedVersion)
+ }
+ if definition.DefaultDownloadURL != "builtin://activate/trino" {
+ t.Fatalf("unexpected trino default download URL: %q", definition.DefaultDownloadURL)
+ }
+ if latestDriverVersionMap["trino"] != "0.333.0" {
+ t.Fatalf("unexpected trino pinned version: %q", latestDriverVersionMap["trino"])
+ }
+
+ tags, err := optionalDriverBuildTags("trino", "")
+ if err != nil {
+ t.Fatalf("resolve trino build tags failed: %v", err)
+ }
+ if tags != "gonavi_trino_driver" {
+ t.Fatalf("unexpected trino build tag: %q", tags)
+ }
+}
+
func TestIoTDBDriverDefinitionUsesOptionalAgent(t *testing.T) {
definition, ok := resolveDriverDefinition("iotdb")
if !ok {
diff --git a/internal/app/methods_file.go b/internal/app/methods_file.go
index ca0b924..59e4aa3 100644
--- a/internal/app/methods_file.go
+++ b/internal/app/methods_file.go
@@ -2850,6 +2850,20 @@ func quoteQualifiedIdentByType(dbType string, ident string) string {
}
dbType = resolveDDLDBType(connection.ConnectionConfig{Type: dbType})
+ if dbType == "trino" {
+ parts := strings.Split(raw, ".")
+ switch {
+ case len(parts) >= 3:
+ catalog := strings.TrimSpace(parts[0])
+ schema := strings.TrimSpace(parts[1])
+ table := strings.TrimSpace(strings.Join(parts[2:], "."))
+ if catalog != "" && schema != "" && table != "" {
+ return quoteIdentByType(dbType, catalog) + "." + quoteIdentByType(dbType, schema) + "." + quoteIdentByType(dbType, table)
+ }
+ case len(parts) <= 2:
+ return quoteIdentByType(dbType, raw)
+ }
+ }
if dbType == "kingbase" {
schema, table := db.SplitKingbaseQualifiedName(raw)
if table == "" {
diff --git a/internal/db/database_optional_factories_full.go b/internal/db/database_optional_factories_full.go
index bc90d16..05338b2 100644
--- a/internal/db/database_optional_factories_full.go
+++ b/internal/db/database_optional_factories_full.go
@@ -23,4 +23,5 @@ func registerOptionalDatabaseFactories() {
registerDatabaseFactory(newOptionalDriverAgentDatabase("iotdb"), "iotdb", "apache-iotdb", "apache_iotdb")
registerDatabaseFactory(newOptionalDriverAgentDatabase("clickhouse"), "clickhouse")
registerDatabaseFactory(newOptionalDriverAgentDatabase("elasticsearch"), "elasticsearch", "elastic")
+ registerDatabaseFactory(newOptionalDriverAgentDatabase("trino"), "trino")
}
diff --git a/internal/db/database_optional_factories_lite.go b/internal/db/database_optional_factories_lite.go
index 29bd12f..9db6ec8 100644
--- a/internal/db/database_optional_factories_lite.go
+++ b/internal/db/database_optional_factories_lite.go
@@ -23,4 +23,5 @@ func registerOptionalDatabaseFactories() {
registerDatabaseFactory(newOptionalDriverAgentDatabase("iotdb"), "iotdb", "apache-iotdb", "apache_iotdb")
registerDatabaseFactory(newOptionalDriverAgentDatabase("clickhouse"), "clickhouse")
registerDatabaseFactory(newOptionalDriverAgentDatabase("elasticsearch"), "elasticsearch", "elastic")
+ registerDatabaseFactory(newOptionalDriverAgentDatabase("trino"), "trino")
}
diff --git a/internal/db/driver_agent_revisions_gen.go b/internal/db/driver_agent_revisions_gen.go
index 01d1c81..e05b8c9 100644
--- a/internal/db/driver_agent_revisions_gen.go
+++ b/internal/db/driver_agent_revisions_gen.go
@@ -24,5 +24,6 @@ func init() {
"iotdb": "src-5ba9da13c6a272f9",
"clickhouse": "src-99c8babfefdf142c",
"elasticsearch": "src-36b2e2b5f49db9d1",
+ "trino": "src-d264ceca132c185c",
}
}
diff --git a/internal/db/driver_support.go b/internal/db/driver_support.go
index 664fd19..0ca7b58 100644
--- a/internal/db/driver_support.go
+++ b/internal/db/driver_support.go
@@ -48,6 +48,7 @@ var optionalGoDrivers = map[string]struct{}{
"iotdb": {},
"clickhouse": {},
"elasticsearch": {},
+ "trino": {},
}
// optionalDriverAgentRevisions 记录 GoNavi 对各可选 driver-agent 包装逻辑的兼容版本。
@@ -150,6 +151,8 @@ func driverDisplayName(driverType string) string {
return "ClickHouse"
case "elasticsearch":
return "Elasticsearch"
+ case "trino":
+ return "Trino"
case "chroma":
return "Chroma"
case "qdrant":
diff --git a/internal/db/trino_impl.go b/internal/db/trino_impl.go
new file mode 100644
index 0000000..2cd0603
--- /dev/null
+++ b/internal/db/trino_impl.go
@@ -0,0 +1,661 @@
+//go:build gonavi_full_drivers || gonavi_trino_driver
+
+package db
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "net"
+ "net/http"
+ "net/url"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "GoNavi-Wails/internal/connection"
+ "GoNavi-Wails/internal/logger"
+ "GoNavi-Wails/internal/ssh"
+
+ trinodriver "github.com/trinodb/trino-go-client/trino"
+)
+
+const (
+ defaultTrinoPort = 8080
+ defaultTrinoSource = "GoNavi"
+)
+
+type TrinoDB struct {
+ conn *sql.DB
+ pingTimeout time.Duration
+ forwarder *ssh.LocalForwarder
+ namespace string
+ customClientName string
+}
+
+func normalizeTrinoConfig(config connection.ConnectionConfig) connection.ConnectionConfig {
+ normalized := applyTrinoURI(config)
+ normalized = applyTrinoHostURI(normalized)
+ if strings.TrimSpace(normalized.Host) == "" {
+ normalized.Host = "localhost"
+ }
+ if normalized.Port <= 0 {
+ normalized.Port = defaultTrinoPort
+ }
+ return normalized
+}
+
+func applyTrinoURI(config connection.ConnectionConfig) connection.ConnectionConfig {
+ return applyTrinoEndpointURI(config, config.URI, false)
+}
+
+func applyTrinoHostURI(config connection.ConnectionConfig) connection.ConnectionConfig {
+ return applyTrinoEndpointURI(config, config.Host, true)
+}
+
+func applyTrinoEndpointURI(config connection.ConnectionConfig, raw string, fromHostField bool) connection.ConnectionConfig {
+ uriText := strings.TrimSpace(raw)
+ if uriText == "" {
+ return config
+ }
+ parsed, err := url.Parse(uriText)
+ if err != nil {
+ return config
+ }
+ scheme := strings.ToLower(strings.TrimSpace(parsed.Scheme))
+ if scheme != "trino" && scheme != "http" && scheme != "https" {
+ return config
+ }
+ if strings.TrimSpace(parsed.Host) == "" {
+ return config
+ }
+
+ if parsed.User != nil {
+ if strings.TrimSpace(config.User) == "" {
+ config.User = parsed.User.Username()
+ }
+ if pass, ok := parsed.User.Password(); ok && config.Password == "" {
+ config.Password = pass
+ }
+ }
+
+ params := url.Values{}
+ mergeConnectionParamValues(params, parsed.Query())
+ mergeConnectionParamValues(params, connectionParamsFromText(config.ConnectionParams))
+
+ catalog := strings.TrimSpace(parsed.Query().Get("catalog"))
+ schema := strings.TrimSpace(parsed.Query().Get("schema"))
+ if strings.TrimSpace(config.Database) == "" {
+ config.Database = joinTrinoNamespace(catalog, schema)
+ }
+
+ if scheme == "https" {
+ config.UseSSL = true
+ if normalizeSSLModeValue(config.SSLMode) == sslModeDisable || strings.TrimSpace(config.SSLMode) == "" {
+ config.SSLMode = sslModeRequired
+ }
+ }
+
+ defaultPort := config.Port
+ if defaultPort <= 0 {
+ defaultPort = defaultTrinoPort
+ }
+ if fromHostField || strings.TrimSpace(config.Host) == "" {
+ host, port, ok := parseHostPortWithDefault(parsed.Host, defaultPort)
+ if ok {
+ config.Host = host
+ config.Port = port
+ }
+ }
+ if config.Port <= 0 {
+ config.Port = defaultPort
+ }
+ config.ConnectionParams = params.Encode()
+ return config
+}
+
+func splitTrinoNamespace(raw string) (string, string) {
+ text := strings.TrimSpace(raw)
+ if text == "" {
+ return "", ""
+ }
+ parts := strings.SplitN(text, ".", 2)
+ catalog := strings.TrimSpace(parts[0])
+ if len(parts) == 1 {
+ return catalog, ""
+ }
+ return catalog, strings.TrimSpace(parts[1])
+}
+
+func joinTrinoNamespace(catalog, schema string) string {
+ c := strings.TrimSpace(catalog)
+ s := strings.TrimSpace(schema)
+ switch {
+ case c == "":
+ return s
+ case s == "":
+ return c
+ default:
+ return c + "." + s
+ }
+}
+
+func resolveTrinoNamespace(raw string, fallback string) (string, string) {
+ catalog, schema := splitTrinoNamespace(raw)
+ if catalog != "" || schema != "" {
+ return catalog, schema
+ }
+ return splitTrinoNamespace(fallback)
+}
+
+func quoteTrinoIdentifier(ident string) string {
+ return `"` + strings.ReplaceAll(strings.TrimSpace(ident), `"`, `""`) + `"`
+}
+
+func quoteTrinoQualifiedTable(catalog, schema, table string) string {
+ quoted := make([]string, 0, 3)
+ if trimmed := strings.TrimSpace(catalog); trimmed != "" {
+ quoted = append(quoted, quoteTrinoIdentifier(trimmed))
+ }
+ if trimmed := strings.TrimSpace(schema); trimmed != "" {
+ quoted = append(quoted, quoteTrinoIdentifier(trimmed))
+ }
+ quoted = append(quoted, quoteTrinoIdentifier(table))
+ return strings.Join(quoted, ".")
+}
+
+func escapeTrinoSQLLiteral(value string) string {
+ return "'" + strings.ReplaceAll(strings.TrimSpace(value), "'", "''") + "'"
+}
+
+func trinoRowValue(row map[string]interface{}, keys ...string) (interface{}, bool) {
+ if len(row) == 0 {
+ return nil, false
+ }
+ for _, key := range keys {
+ for current, value := range row {
+ if strings.EqualFold(strings.TrimSpace(current), strings.TrimSpace(key)) {
+ return value, true
+ }
+ }
+ }
+ return nil, false
+}
+
+func trinoRowString(row map[string]interface{}, keys ...string) string {
+ value, ok := trinoRowValue(row, keys...)
+ if !ok || value == nil {
+ return ""
+ }
+ text := strings.TrimSpace(fmt.Sprintf("%v", value))
+ if strings.EqualFold(text, "") {
+ return ""
+ }
+ return text
+}
+
+func firstTrinoMapValueAsString(row map[string]interface{}) string {
+ for _, value := range row {
+ text := strings.TrimSpace(fmt.Sprintf("%v", value))
+ if !strings.EqualFold(text, "") {
+ return text
+ }
+ }
+ return ""
+}
+
+func firstTrinoRowValueAsString(data []map[string]interface{}) string {
+ if len(data) == 0 {
+ return ""
+ }
+ return firstTrinoMapValueAsString(data[0])
+}
+
+func (t *TrinoDB) buildTrinoHTTPClient(config connection.ConnectionConfig) (*http.Client, error) {
+ transport := &http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ DialContext: (&net.Dialer{
+ Timeout: getConnectTimeout(config),
+ KeepAlive: 30 * time.Second,
+ }).DialContext,
+ MaxIdleConns: 32,
+ IdleConnTimeout: 90 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: time.Second,
+ }
+ tlsConfig, err := resolveGenericTLSConfig(config)
+ if err != nil {
+ return nil, err
+ }
+ if tlsConfig != nil {
+ transport.TLSClientConfig = tlsConfig
+ }
+ return &http.Client{Transport: transport}, nil
+}
+
+func (t *TrinoDB) registerTrinoCustomClient(config connection.ConnectionConfig) (string, error) {
+ client, err := t.buildTrinoHTTPClient(config)
+ if err != nil {
+ return "", err
+ }
+ name := fmt.Sprintf("gonavi-trino-%d", time.Now().UnixNano())
+ if err := trinodriver.RegisterCustomClient(name, client); err != nil {
+ return "", err
+ }
+ return name, nil
+}
+
+func buildTrinoDSN(config connection.ConnectionConfig, customClientName string) (string, error) {
+ user := strings.TrimSpace(config.User)
+ if user == "" {
+ return "", fmt.Errorf("Trino 用户名不能为空")
+ }
+
+ scheme := "http"
+ if config.UseSSL {
+ scheme = "https"
+ }
+ if config.Password != "" && scheme != "https" {
+ return "", fmt.Errorf("Trino 启用密码认证时必须使用 HTTPS")
+ }
+
+ params := connectionParamsFromText(config.ConnectionParams)
+ catalog, schema := resolveTrinoNamespace(config.Database, "")
+ if catalog != "" {
+ params.Set("catalog", catalog)
+ }
+ if schema != "" {
+ params.Set("schema", schema)
+ }
+ if strings.TrimSpace(params.Get("source")) == "" {
+ params.Set("source", defaultTrinoSource)
+ }
+ if strings.TrimSpace(params.Get("explicitPrepare")) == "" {
+ params.Set("explicitPrepare", "false")
+ }
+ if strings.TrimSpace(params.Get("query_timeout")) == "" {
+ params.Set("query_timeout", fmt.Sprintf("%ds", getConnectTimeoutSeconds(config)))
+ }
+ if strings.TrimSpace(customClientName) != "" {
+ params.Set("custom_client", strings.TrimSpace(customClientName))
+ }
+
+ endpoint := &url.URL{
+ Scheme: scheme,
+ Host: net.JoinHostPort(strings.TrimSpace(config.Host), strconv.Itoa(config.Port)),
+ RawQuery: params.Encode(),
+ }
+ if config.Password != "" {
+ endpoint.User = url.UserPassword(user, config.Password)
+ } else {
+ endpoint.User = url.User(user)
+ }
+ return endpoint.String(), nil
+}
+
+func (t *TrinoDB) Close() error {
+ if t.conn != nil {
+ if err := t.conn.Close(); err != nil {
+ return err
+ }
+ t.conn = nil
+ }
+ if t.forwarder != nil {
+ if err := t.forwarder.Close(); err != nil {
+ logger.Warnf("关闭 Trino SSH 端口转发失败:%v", err)
+ }
+ t.forwarder = nil
+ }
+ if t.customClientName != "" {
+ trinodriver.DeregisterCustomClient(t.customClientName)
+ t.customClientName = ""
+ }
+ t.namespace = ""
+ return nil
+}
+
+func (t *TrinoDB) Connect(config connection.ConnectionConfig) error {
+ _ = t.Close()
+
+ runConfig := normalizeTrinoConfig(config)
+ t.pingTimeout = getConnectTimeout(runConfig)
+
+ if runConfig.UseSSH {
+ forwarder, err := ssh.GetOrCreateLocalForwarder(runConfig.SSH, runConfig.Host, runConfig.Port)
+ if err != nil {
+ return fmt.Errorf("创建 SSH 隧道失败:%w", err)
+ }
+ t.forwarder = forwarder
+
+ host, portText, err := net.SplitHostPort(forwarder.LocalAddr)
+ if err != nil {
+ _ = t.Close()
+ return fmt.Errorf("解析本地转发地址失败:%w", err)
+ }
+ port, err := strconv.Atoi(portText)
+ if err != nil {
+ _ = t.Close()
+ return fmt.Errorf("解析本地端口失败:%w", err)
+ }
+ runConfig.Host = host
+ runConfig.Port = port
+ runConfig.UseSSH = false
+ logger.Infof("Trino 通过本地端口转发连接:%s -> %s:%d", forwarder.LocalAddr, config.Host, config.Port)
+ }
+
+ customClientName, err := t.registerTrinoCustomClient(runConfig)
+ if err != nil {
+ _ = t.Close()
+ return fmt.Errorf("注册 Trino 自定义 HTTP 客户端失败:%w", err)
+ }
+ t.customClientName = customClientName
+
+ dsn, err := buildTrinoDSN(runConfig, customClientName)
+ if err != nil {
+ _ = t.Close()
+ return err
+ }
+ conn, err := sql.Open("trino", dsn)
+ if err != nil {
+ _ = t.Close()
+ return err
+ }
+ t.conn = conn
+ t.namespace = strings.TrimSpace(runConfig.Database)
+ if err := t.Ping(); err != nil {
+ _ = t.Close()
+ return err
+ }
+ return nil
+}
+
+func (t *TrinoDB) Ping() error {
+ if t.conn == nil {
+ return fmt.Errorf("连接未打开")
+ }
+ timeout := t.pingTimeout
+ if timeout <= 0 {
+ timeout = 10 * time.Second
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ defer cancel()
+ rows, err := t.conn.QueryContext(ctx, "SELECT 1")
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ if !rows.Next() {
+ if err := rows.Err(); err != nil {
+ return err
+ }
+ return fmt.Errorf("连接查询验证未返回结果")
+ }
+ var value sql.NullInt64
+ if err := rows.Scan(&value); err != nil {
+ return err
+ }
+ return rows.Err()
+}
+
+func (t *TrinoDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
+ if t.conn == nil {
+ return nil, nil, fmt.Errorf("连接未打开")
+ }
+ rows, err := t.conn.QueryContext(ctx, query)
+ if err != nil {
+ return nil, nil, err
+ }
+ defer rows.Close()
+ return scanRows(rows)
+}
+
+func (t *TrinoDB) Query(query string) ([]map[string]interface{}, []string, error) {
+ return t.QueryContext(context.Background(), query)
+}
+
+func (t *TrinoDB) StreamQueryContext(ctx context.Context, query string, consumer QueryStreamConsumer) error {
+ if t.conn == nil {
+ return fmt.Errorf("连接未打开")
+ }
+ rows, err := t.conn.QueryContext(ctx, query)
+ if err != nil {
+ return err
+ }
+ defer rows.Close()
+ return streamRows(rows, consumer)
+}
+
+func (t *TrinoDB) StreamQuery(query string, consumer QueryStreamConsumer) error {
+ return t.StreamQueryContext(context.Background(), query, consumer)
+}
+
+func (t *TrinoDB) ExecContext(ctx context.Context, query string) (int64, error) {
+ if t.conn == nil {
+ return 0, fmt.Errorf("连接未打开")
+ }
+ res, err := t.conn.ExecContext(ctx, query)
+ if err != nil {
+ return 0, err
+ }
+ affected, err := res.RowsAffected()
+ if err != nil {
+ return 0, nil
+ }
+ return affected, nil
+}
+
+func (t *TrinoDB) Exec(query string) (int64, error) {
+ return t.ExecContext(context.Background(), query)
+}
+
+func (t *TrinoDB) queryTrinoSingleColumnStrings(query string) ([]string, error) {
+ data, _, err := t.Query(query)
+ if err != nil {
+ return nil, err
+ }
+ result := make([]string, 0, len(data))
+ for _, row := range data {
+ text := firstTrinoMapValueAsString(row)
+ if text != "" {
+ result = append(result, text)
+ }
+ }
+ return result, nil
+}
+
+func (t *TrinoDB) GetDatabases() ([]string, error) {
+ catalogs, err := t.queryTrinoSingleColumnStrings("SHOW CATALOGS")
+ if err != nil {
+ if strings.TrimSpace(t.namespace) != "" {
+ return []string{t.namespace}, nil
+ }
+ return nil, err
+ }
+
+ namespaces := make([]string, 0, len(catalogs)*2)
+ seen := make(map[string]struct{}, len(catalogs)*4)
+ var lastErr error
+ for _, catalog := range catalogs {
+ query := fmt.Sprintf("SHOW SCHEMAS FROM %s", quoteTrinoIdentifier(catalog))
+ schemas, schemaErr := t.queryTrinoSingleColumnStrings(query)
+ if schemaErr != nil {
+ lastErr = schemaErr
+ continue
+ }
+ for _, schema := range schemas {
+ namespace := joinTrinoNamespace(catalog, schema)
+ if namespace == "" {
+ continue
+ }
+ key := strings.ToLower(namespace)
+ if _, ok := seen[key]; ok {
+ continue
+ }
+ seen[key] = struct{}{}
+ namespaces = append(namespaces, namespace)
+ }
+ }
+
+ if len(namespaces) == 0 {
+ if strings.TrimSpace(t.namespace) != "" {
+ return []string{t.namespace}, nil
+ }
+ if lastErr != nil {
+ return nil, lastErr
+ }
+ }
+ sort.Strings(namespaces)
+ return namespaces, nil
+}
+
+func (t *TrinoDB) GetTables(dbName string) ([]string, error) {
+ catalog, schema := resolveTrinoNamespace(dbName, t.namespace)
+ if catalog == "" || schema == "" {
+ return nil, fmt.Errorf("Trino 默认命名空间必须使用 catalog.schema")
+ }
+ query := fmt.Sprintf("SHOW TABLES FROM %s.%s", quoteTrinoIdentifier(catalog), quoteTrinoIdentifier(schema))
+ tables, err := t.queryTrinoSingleColumnStrings(query)
+ if err != nil {
+ return nil, err
+ }
+ sort.Strings(tables)
+ return tables, nil
+}
+
+func (t *TrinoDB) GetCreateStatement(dbName, tableName string) (string, error) {
+ catalog, schema := resolveTrinoNamespace(dbName, t.namespace)
+ if catalog == "" || schema == "" {
+ return "", fmt.Errorf("Trino 默认命名空间必须使用 catalog.schema")
+ }
+ query := fmt.Sprintf("SHOW CREATE TABLE %s", quoteTrinoQualifiedTable(catalog, schema, tableName))
+ data, _, err := t.Query(query)
+ if err != nil {
+ return "", err
+ }
+ ddl := firstTrinoRowValueAsString(data)
+ if ddl == "" {
+ return "", fmt.Errorf("未返回建表语句")
+ }
+ return ddl, nil
+}
+
+func buildTrinoColumnsQuery(catalog, schema, tableName string) string {
+ return fmt.Sprintf(`SELECT
+ column_name,
+ data_type,
+ is_nullable,
+ column_default
+FROM %s.information_schema.columns
+WHERE table_schema = %s AND table_name = %s
+ORDER BY ordinal_position`,
+ quoteTrinoIdentifier(catalog),
+ escapeTrinoSQLLiteral(schema),
+ escapeTrinoSQLLiteral(tableName),
+ )
+}
+
+func buildTrinoColumnDefinitions(data []map[string]interface{}) []connection.ColumnDefinition {
+ result := make([]connection.ColumnDefinition, 0, len(data))
+ for _, row := range data {
+ column := connection.ColumnDefinition{
+ Name: trinoRowString(row, "column_name", "Column", "Field"),
+ Type: trinoRowString(row, "data_type", "Type"),
+ Nullable: strings.ToUpper(trinoRowString(row, "is_nullable", "Null")),
+ }
+ if rawDefault, ok := trinoRowValue(row, "column_default", "Default"); ok && rawDefault != nil {
+ def := strings.TrimSpace(fmt.Sprintf("%v", rawDefault))
+ if !strings.EqualFold(def, "") && def != "" {
+ column.Default = &def
+ }
+ }
+ if column.Nullable == "" {
+ column.Nullable = "YES"
+ }
+ result = append(result, column)
+ }
+ return result
+}
+
+func (t *TrinoDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error) {
+ catalog, schema := resolveTrinoNamespace(dbName, t.namespace)
+ if catalog == "" || schema == "" {
+ return nil, fmt.Errorf("Trino 默认命名空间必须使用 catalog.schema")
+ }
+ data, _, err := t.Query(buildTrinoColumnsQuery(catalog, schema, tableName))
+ if err == nil {
+ return buildTrinoColumnDefinitions(data), nil
+ }
+
+ describeQuery := fmt.Sprintf("DESCRIBE %s", quoteTrinoQualifiedTable(catalog, schema, tableName))
+ describeRows, _, describeErr := t.Query(describeQuery)
+ if describeErr != nil {
+ return nil, err
+ }
+ columns := make([]connection.ColumnDefinition, 0, len(describeRows))
+ for _, row := range describeRows {
+ name := trinoRowString(row, "Column", "column_name", "Field")
+ if name == "" || strings.HasPrefix(name, "#") {
+ continue
+ }
+ columns = append(columns, connection.ColumnDefinition{
+ Name: name,
+ Type: trinoRowString(row, "Type", "data_type"),
+ Nullable: "YES",
+ })
+ }
+ return columns, nil
+}
+
+func (t *TrinoDB) GetAllColumns(dbName string) ([]connection.ColumnDefinitionWithTable, error) {
+ catalog, schema := resolveTrinoNamespace(dbName, t.namespace)
+ if catalog == "" || schema == "" {
+ return nil, fmt.Errorf("Trino 默认命名空间必须使用 catalog.schema")
+ }
+ query := fmt.Sprintf(`SELECT
+ table_name,
+ column_name,
+ data_type
+FROM %s.information_schema.columns
+WHERE table_schema = %s
+ORDER BY table_name, ordinal_position`,
+ quoteTrinoIdentifier(catalog),
+ escapeTrinoSQLLiteral(schema),
+ )
+ data, _, err := t.Query(query)
+ if err != nil {
+ return nil, err
+ }
+ result := make([]connection.ColumnDefinitionWithTable, 0, len(data))
+ for _, row := range data {
+ result = append(result, connection.ColumnDefinitionWithTable{
+ TableName: trinoRowString(row, "table_name", "TABLE_NAME"),
+ Name: trinoRowString(row, "column_name", "COLUMN_NAME"),
+ Type: trinoRowString(row, "data_type", "DATA_TYPE"),
+ })
+ }
+ return result, nil
+}
+
+func (t *TrinoDB) GetIndexes(dbName, tableName string) ([]connection.IndexDefinition, error) {
+ return []connection.IndexDefinition{}, nil
+}
+
+func (t *TrinoDB) GetForeignKeys(dbName, tableName string) ([]connection.ForeignKeyDefinition, error) {
+ return []connection.ForeignKeyDefinition{}, nil
+}
+
+func (t *TrinoDB) GetTriggers(dbName, tableName string) ([]connection.TriggerDefinition, error) {
+ return []connection.TriggerDefinition{}, nil
+}
+
+func (t *TrinoDB) OpenSessionExecer(ctx context.Context) (StatementExecer, error) {
+ if t.conn == nil {
+ return nil, fmt.Errorf("连接未打开")
+ }
+ conn, err := t.conn.Conn(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return NewSQLConnStatementExecer(conn), nil
+}
diff --git a/tools/generate-driver-agent-revisions.sh b/tools/generate-driver-agent-revisions.sh
index 81d41d0..5fb20c3 100755
--- a/tools/generate-driver-agent-revisions.sh
+++ b/tools/generate-driver-agent-revisions.sh
@@ -7,7 +7,7 @@ cd "$SCRIPT_DIR"
SCRIPT_DIR_WINDOWS="$(pwd -W 2>/dev/null || true)"
SCRIPT_DIR_WINDOWS="${SCRIPT_DIR_WINDOWS//\\//}"
-DEFAULT_DRIVERS=(mariadb oceanbase diros starrocks sphinx sqlserver sqlite duckdb dameng kingbase highgo vastbase opengauss gaussdb iris mongodb tdengine iotdb clickhouse elasticsearch)
+DEFAULT_DRIVERS=(mariadb oceanbase diros starrocks sphinx sqlserver sqlite duckdb dameng kingbase highgo vastbase opengauss gaussdb iris mongodb tdengine iotdb clickhouse elasticsearch trino)
OUTPUT_FILE="internal/db/driver_agent_revisions_gen.go"
usage() {
@@ -31,7 +31,7 @@ normalize_driver() {
opengauss|open_gauss|open-gauss) echo "opengauss" ;;
gaussdb|gauss_db|gauss-db) echo "gaussdb" ;;
elasticsearch|elastic) echo "elasticsearch" ;;
- mariadb|diros|starrocks|sphinx|sqlserver|sqlite|duckdb|dameng|kingbase|highgo|vastbase|gaussdb|iris|mongodb|tdengine|iotdb|clickhouse)
+ mariadb|diros|starrocks|sphinx|sqlserver|sqlite|duckdb|dameng|kingbase|highgo|vastbase|gaussdb|iris|mongodb|tdengine|iotdb|clickhouse|trino)
echo "$value"
;;
*)
@@ -139,7 +139,8 @@ tdengine:internal/db/tdengine_impl.go|\
iotdb:internal/db/iotdb_impl.go|\
clickhouse:internal/db/clickhouse_impl.go|\
elasticsearch:internal/db/elasticsearch_impl.go|\
-elasticsearch:internal/db/elasticsearch_helpers.go)
+elasticsearch:internal/db/elasticsearch_helpers.go|\
+trino:internal/db/trino_impl.go)
return 0
;;
esac