mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-19 16:16:45 +00:00
feat(argo): add argodb connection type with routine browsing
This commit is contained in:
@@ -438,21 +438,129 @@ func showViewsUnsupported(err error) bool {
|
||||
}
|
||||
|
||||
func (server *server) listObjects(schema string, constraints metadataListConstraints) ([]objectInfo, error) {
|
||||
if !acceptsHiveTable(constraints.ObjectTypes) {
|
||||
if !acceptsHiveTable(constraints.ObjectTypes) && !(server.supportsRoutines() && acceptsHiveRoutine(constraints.ObjectTypes)) {
|
||||
return []objectInfo{}, nil
|
||||
}
|
||||
tables, err := server.listTables(schema, constraints)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema = firstNonEmpty(schema, server.config.Database)
|
||||
values := make([]objectInfo, 0, len(tables))
|
||||
for _, table := range tables {
|
||||
values = append(values, objectInfo{Name: table.Name, ObjectType: table.TableType, Schema: schema, Comment: table.Comment})
|
||||
values := make([]objectInfo, 0)
|
||||
if acceptsHiveTable(constraints.ObjectTypes) {
|
||||
tables, err := server.listTables(schema, constraints)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, table := range tables {
|
||||
values = append(values, objectInfo{Name: table.Name, ObjectType: table.TableType, Schema: schema, Comment: table.Comment})
|
||||
}
|
||||
}
|
||||
if server.supportsRoutines() && acceptsRoutineType(constraints.ObjectTypes, "PROCEDURE") {
|
||||
procedures, err := server.listRoutines(schema, constraints, "PROCEDURE")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values = append(values, procedures...)
|
||||
}
|
||||
if server.supportsRoutines() && acceptsRoutineType(constraints.ObjectTypes, "FUNCTION") {
|
||||
functions, err := server.listRoutines(schema, constraints, "FUNCTION")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values = append(values, functions...)
|
||||
}
|
||||
sort.Slice(values, func(first, second int) bool { return values[first].Name < values[second].Name })
|
||||
return applyMetadataWindow(values, constraints.Offset, constraints.Limit), nil
|
||||
}
|
||||
|
||||
// supportsRoutines reports whether the connected server is known to expose the
|
||||
// Hive procedure / function catalog views (system.procedures_v /
|
||||
// system.functions_v). ArgoDB / Inceptor / Transwarp forks ship these views;
|
||||
// vanilla Apache Hive does not, so routine listing is gated on the connection's
|
||||
// database_type to avoid firing unsupported queries against plain Hive.
|
||||
func (server *server) supportsRoutines() bool {
|
||||
switch strings.ToLower(strings.TrimSpace(server.params.DatabaseType)) {
|
||||
case "argo", "inceptor", "inceptor2", "transwarp":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// listRoutines queries the server's procedure / function catalog views when the
|
||||
// driver supports them. Hive and most forks (ArgoDB, Inceptor, Transwarp) expose
|
||||
// stored procedures / functions through the system.procedures_v / system.functions_v
|
||||
// views, with columns (procedure_name | function_name, database_name, full_text, ...).
|
||||
// The full_text column carries the routine source used by getObjectSource.
|
||||
//
|
||||
// The query is best-effort: when the view is missing or the server rejects it
|
||||
// (older Hive without procedure support), the call returns an empty slice and
|
||||
// nil error so the caller can fall back to listing tables.
|
||||
func (server *server) listRoutines(schema string, constraints metadataListConstraints, routineType string) ([]objectInfo, error) {
|
||||
nameColumn := "procedure_name"
|
||||
viewName := "system.procedures_v"
|
||||
if strings.EqualFold(routineType, "FUNCTION") {
|
||||
nameColumn = "function_name"
|
||||
viewName = "system.functions_v"
|
||||
}
|
||||
// Routine listings are pinned to the active schema (or the connection's default
|
||||
// database when the caller passes an empty schema). system.procedures_v /
|
||||
// system.functions_v only contain rows for one specific database per query;
|
||||
// there is no wildcard form. Falling back to the connection default keeps
|
||||
// callers like the sidebar schema tree happy when they pass "" as the schema.
|
||||
targetSchema := firstNonEmpty(schema, server.config.Database)
|
||||
likePattern := buildRoutineLikePattern(constraints.Filter)
|
||||
// database_name is a string column, so the schema filter must be a single-quoted
|
||||
// literal — not a backtick-quoted identifier. ArgoDB/Inceptor reject lower(`ods`)
|
||||
// against system.procedures_v with an ERROR_STATUS, while lower('ods') works.
|
||||
schemaLiteral := "'" + strings.ReplaceAll(targetSchema, "'", "''") + "'"
|
||||
sql := "SELECT " + nameColumn + " FROM " + viewName +
|
||||
" WHERE lower(database_name) = lower(" + schemaLiteral + ")" +
|
||||
" AND lower(" + nameColumn + ") LIKE " + likePattern +
|
||||
" ORDER BY " + nameColumn
|
||||
result, err := server.executeQuery(queryOptions{SQL: sql, MaxRows: metadataQueryLimit})
|
||||
if err != nil {
|
||||
// View missing or not supported — silently return empty list.
|
||||
return []objectInfo{}, nil
|
||||
}
|
||||
values := make([]objectInfo, 0, len(result.Rows))
|
||||
for _, row := range result.Rows {
|
||||
name := rowString(row, 0)
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
values = append(values, objectInfo{Name: name, ObjectType: strings.ToUpper(routineType), Schema: schema, Comment: nil})
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// buildRoutineLikePattern turns a user-supplied filter into a Hive-safe LIKE
|
||||
// literal: wraps with %, escapes \, %, _ (the LIKE metacharacters), and quotes
|
||||
// the whole literal so it can be concatenated directly into SQL.
|
||||
func buildRoutineLikePattern(filter string) string {
|
||||
escaped := strings.NewReplacer(`\`, `\\`, `%`, `\%`, `_`, `\_`).Replace(filter)
|
||||
return "'%" + escaped + "%'"
|
||||
}
|
||||
|
||||
// acceptsHiveRoutine reports whether the requested object types include any
|
||||
// routine (PROCEDURE / FUNCTION) that hive-go needs to surface through listObjects.
|
||||
func acceptsHiveRoutine(objectTypes []string) bool {
|
||||
for _, objectType := range objectTypes {
|
||||
if strings.EqualFold(objectType, "PROCEDURE") || strings.EqualFold(objectType, "FUNCTION") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// acceptsRoutineType reports whether the requested object types include the
|
||||
// given routine kind (case-insensitive).
|
||||
func acceptsRoutineType(objectTypes []string, routineType string) bool {
|
||||
for _, objectType := range objectTypes {
|
||||
if strings.EqualFold(objectType, routineType) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (server *server) getColumns(schema, table string) ([]columnInfo, error) {
|
||||
if strings.TrimSpace(table) == "" {
|
||||
return nil, errors.New("table is required")
|
||||
@@ -585,7 +693,18 @@ func (server *server) getTableDDL(schema, table string) (string, error) {
|
||||
}
|
||||
|
||||
func (server *server) getObjectSource(schema, name, objectType string) (objectSource, error) {
|
||||
source, err := server.getTableDDL(schema, name)
|
||||
schema = firstNonEmpty(schema, server.config.Database)
|
||||
var source string
|
||||
var err error
|
||||
switch strings.ToUpper(objectType) {
|
||||
case "PROCEDURE", "FUNCTION":
|
||||
if !server.supportsRoutines() {
|
||||
return objectSource{}, fmt.Errorf("routine source is not supported for %s connections", server.params.DatabaseType)
|
||||
}
|
||||
source, err = server.getRoutineSource(schema, name, strings.ToUpper(objectType))
|
||||
default:
|
||||
source, err = server.getTableDDL(schema, name)
|
||||
}
|
||||
if err != nil {
|
||||
return objectSource{}, err
|
||||
}
|
||||
@@ -597,6 +716,37 @@ func (server *server) getObjectSource(schema, name, objectType string) (objectSo
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getRoutineSource fetches a procedure or function's full source from the
|
||||
// server's system.procedures_v / system.functions_v view. Returns an empty
|
||||
// string when the view is missing or the routine is not found, so callers can
|
||||
// fall back to other sources. full_text may span multiple rows (the underlying
|
||||
// query driver splits long strings), so we join them like getTableDDL does.
|
||||
func (server *server) getRoutineSource(schema, name, routineType string) (string, error) {
|
||||
nameColumn := "procedure_name"
|
||||
viewName := "system.procedures_v"
|
||||
if strings.EqualFold(routineType, "FUNCTION") {
|
||||
nameColumn = "function_name"
|
||||
viewName = "system.functions_v"
|
||||
}
|
||||
sql := "SELECT full_text FROM " + viewName +
|
||||
" WHERE lower(database_name) = lower('" + strings.ReplaceAll(schema, "'", "''") + "')" +
|
||||
" AND " + nameColumn + " = '" + strings.ReplaceAll(name, "'", "''") + "'"
|
||||
result, err := server.executeQuery(queryOptions{SQL: sql, MaxRows: metadataQueryLimit})
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
lines := make([]string, 0, len(result.Rows))
|
||||
for _, row := range result.Rows {
|
||||
if line := firstRowValue(row); line != "" {
|
||||
lines = append(lines, line)
|
||||
}
|
||||
}
|
||||
if len(lines) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
return strings.Join(lines, "\n") + "\n", nil
|
||||
}
|
||||
|
||||
func (server *server) getExplainInfo(sqlText string) (string, error) {
|
||||
sqlText = trimStatementSQL(sqlText)
|
||||
if sqlText == "" {
|
||||
|
||||
@@ -479,3 +479,160 @@ func TestListViewsReturnsFallbackErrorWhenShowViewsIsUnsupported(t *testing.T) {
|
||||
func metadataResult(columns []string, rows ...[]driver.Value) gohive.MetadataResult {
|
||||
return gohive.MetadataResult{Columns: columns, Rows: rows}
|
||||
}
|
||||
|
||||
func TestListObjectsIncludesProceduresAndFunctionsFromSystemViews(t *testing.T) {
|
||||
proceduresQuery := "SELECT procedure_name FROM system.procedures_v WHERE lower(database_name) = lower('ods') AND lower(procedure_name) LIKE '%sp%' ORDER BY procedure_name"
|
||||
functionsQuery := "SELECT function_name FROM system.functions_v WHERE lower(database_name) = lower('ods') AND lower(function_name) LIKE '%sp%' ORDER BY function_name"
|
||||
behavior := &scriptedBehavior{
|
||||
query: func(_ context.Context, query string) (driver.Rows, error) {
|
||||
switch query {
|
||||
case proceduresQuery:
|
||||
return newScriptedRows(context.Background(), []string{"procedure_name"}, []string{"STRING"}, [][]driver.Value{
|
||||
{"sp_daily_etl"}, {"sp_hourly_agg"},
|
||||
}), nil
|
||||
case functionsQuery:
|
||||
return newScriptedRows(context.Background(), []string{"function_name"}, []string{"STRING"}, [][]driver.Value{
|
||||
{"fn_clean"},
|
||||
}), nil
|
||||
default:
|
||||
return nil, errors.New("unexpected query: " + query)
|
||||
}
|
||||
},
|
||||
}
|
||||
server := newScriptedServer(t, behavior)
|
||||
server.params.DatabaseType = "argo"
|
||||
defer server.disconnect()
|
||||
|
||||
values, err := server.listObjects("ods", metadataListConstraints{
|
||||
ObjectTypes: []string{"PROCEDURE", "FUNCTION"},
|
||||
Filter: "sp", // "sp" overlaps the procedure filter; both lists get "%sp%" applied
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("listObjects: %v", err)
|
||||
}
|
||||
t.Logf("values: %+v", values)
|
||||
// listObjects applies the same filter pattern to procedures and functions;
|
||||
// we passed "sp" so procedures match and functions don't (because the function
|
||||
// list returns rows regardless of filter — that is, listObjects calls each
|
||||
// listRoutines call with the same Filter). Verify both queries ran and
|
||||
// the procedure name is present.
|
||||
queries, _, _, _ := behavior.snapshot()
|
||||
t.Logf("queries count: %d", len(queries))
|
||||
for i, q := range queries {
|
||||
t.Logf("query[%d]: %q", i, q)
|
||||
}
|
||||
if len(queries) != 2 {
|
||||
t.Fatalf("expected exactly 2 routine queries, got %d: %v", len(queries), queries)
|
||||
}
|
||||
if values == nil {
|
||||
t.Fatal("expected non-nil object list")
|
||||
}
|
||||
names := make([]string, len(values))
|
||||
for i, v := range values {
|
||||
names[i] = v.Name + ":" + v.ObjectType
|
||||
}
|
||||
joined := strings.Join(names, ",")
|
||||
if !strings.Contains(joined, "sp_daily_etl:PROCEDURE") {
|
||||
t.Fatalf("expected procedures in result, got %v", values)
|
||||
}
|
||||
if !strings.Contains(joined, "fn_clean:FUNCTION") {
|
||||
t.Fatalf("expected functions in result, got %v", values)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetObjectSourceRoutesProceduresToSystemProceduresView(t *testing.T) {
|
||||
expectedSQL := "SELECT full_text FROM system.procedures_v WHERE lower(database_name) = lower('ods') AND procedure_name = 'sp_daily_etl'"
|
||||
behavior := &scriptedBehavior{
|
||||
query: func(_ context.Context, query string) (driver.Rows, error) {
|
||||
if query != expectedSQL {
|
||||
return nil, errors.New("unexpected query: " + query)
|
||||
}
|
||||
return newScriptedRows(context.Background(), []string{"full_text"}, []string{"STRING"}, [][]driver.Value{
|
||||
{"-- daily ETL pipeline"},
|
||||
{"INSERT OVERWRITE TABLE ods.daily_summary SELECT * FROM staging.events"},
|
||||
}), nil
|
||||
},
|
||||
}
|
||||
server := newScriptedServer(t, behavior)
|
||||
server.params.DatabaseType = "argo"
|
||||
defer server.disconnect()
|
||||
|
||||
result, _, err := server.dispatch("get_object_source", map[string]json.RawMessage{
|
||||
"schema": json.RawMessage(`"ods"`),
|
||||
"name": json.RawMessage(`"sp_daily_etl"`),
|
||||
"object_type": json.RawMessage(`"PROCEDURE"`),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
source, ok := result.(objectSource)
|
||||
if !ok {
|
||||
t.Fatalf("get_object_source returned %T instead of objectSource", result)
|
||||
}
|
||||
if source.Name != "sp_daily_etl" || source.ObjectType != "PROCEDURE" {
|
||||
t.Fatalf("unexpected object source metadata: %#v", source)
|
||||
}
|
||||
expected := "-- daily ETL pipeline\nINSERT OVERWRITE TABLE ods.daily_summary SELECT * FROM staging.events\n"
|
||||
if source.Source != expected {
|
||||
t.Fatalf("unexpected procedure source: %q", source.Source)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetObjectSourceRoutesFunctionsToSystemFunctionsView(t *testing.T) {
|
||||
expectedSQL := "SELECT full_text FROM system.functions_v WHERE lower(database_name) = lower('ods') AND function_name = 'fn_clean'"
|
||||
behavior := &scriptedBehavior{
|
||||
query: func(_ context.Context, query string) (driver.Rows, error) {
|
||||
if query != expectedSQL {
|
||||
return nil, errors.New("unexpected query: " + query)
|
||||
}
|
||||
return newScriptedRows(context.Background(), []string{"full_text"}, []string{"STRING"}, [][]driver.Value{
|
||||
{"-- cleanup helper"},
|
||||
}), nil
|
||||
},
|
||||
}
|
||||
server := newScriptedServer(t, behavior)
|
||||
server.params.DatabaseType = "argo"
|
||||
defer server.disconnect()
|
||||
|
||||
result, _, err := server.dispatch("get_object_source", map[string]json.RawMessage{
|
||||
"schema": json.RawMessage(`"ods"`),
|
||||
"name": json.RawMessage(`"fn_clean"`),
|
||||
"object_type": json.RawMessage(`"FUNCTION"`),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
source, ok := result.(objectSource)
|
||||
if !ok {
|
||||
t.Fatalf("get_object_source returned %T instead of objectSource", result)
|
||||
}
|
||||
if source.Source != "-- cleanup helper\n" {
|
||||
t.Fatalf("unexpected function source: %q", source.Source)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListObjectsDoesNotQueryRoutinesForVanillaHive(t *testing.T) {
|
||||
behavior := &scriptedBehavior{
|
||||
query: func(ctx context.Context, query string) (driver.Rows, error) {
|
||||
if strings.Contains(strings.ToUpper(query), "PROCEDURES_V") || strings.Contains(strings.ToUpper(query), "FUNCTIONS_V") {
|
||||
t.Fatalf("vanilla Hive must not query routine views, got: %s", query)
|
||||
}
|
||||
return newScriptedRows(ctx, []string{"tab_name"}, []string{"STRING"}, [][]driver.Value{{"events"}}), nil
|
||||
},
|
||||
}
|
||||
server := newScriptedServer(t, behavior)
|
||||
server.params.DatabaseType = "hive"
|
||||
defer server.disconnect()
|
||||
|
||||
values, err := server.listObjects("ods", metadataListConstraints{
|
||||
ObjectTypes: []string{"PROCEDURE", "FUNCTION"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("listObjects: %v", err)
|
||||
}
|
||||
for _, v := range values {
|
||||
if v.ObjectType == "PROCEDURE" || v.ObjectType == "FUNCTION" {
|
||||
t.Fatalf("vanilla Hive should not surface routines, got: %+v", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2428,7 +2428,7 @@ function applyProfile(val: string, preserveConnectionFields = false) {
|
||||
form.value.connection_string = undefined;
|
||||
form.value.url_params = "";
|
||||
}
|
||||
resetHiveKerberosFields(profile.type === "hive" || profile.type === "kyuubi" || profile.type === "impala" ? form.value : undefined);
|
||||
resetHiveKerberosFields(profile.type === "hive" || profile.type === "argo" || profile.type === "kyuubi" || profile.type === "impala" ? form.value : undefined);
|
||||
}
|
||||
if (profile.type === "meilisearch") {
|
||||
syncMeilisearchHostInput(form.value);
|
||||
@@ -2566,7 +2566,7 @@ watch(
|
||||
resetVictoriaMetricsFields();
|
||||
}
|
||||
resetElasticsearchProxyFields(config.db_type === "elasticsearch" ? config.external_config : undefined);
|
||||
resetHiveKerberosFields(config.db_type === "hive" || config.db_type === "kyuubi" || config.db_type === "impala" ? config : undefined);
|
||||
resetHiveKerberosFields(config.db_type === "hive" || config.db_type === "argo" || config.db_type === "kyuubi" || config.db_type === "impala" ? config : undefined);
|
||||
resetDamengJvmOptions(config.db_type === "dameng" ? config : undefined);
|
||||
h2ConnectionMode.value = h2ConnectionModeForConfig(config);
|
||||
customColorInput.value = config.color || "";
|
||||
@@ -3790,7 +3790,7 @@ function connectionConfigForSubmit(id: string, generatedName = ""): ConnectionCo
|
||||
config.ssl = !!config.ssl || damengSsl.enabled;
|
||||
config.url_params = applyDamengSslUrlParams(config.url_params, config.ssl, damengSsl.sslFilesPath, damengSsl.sslKeystorePassword, damengSsl.sslProtocol);
|
||||
}
|
||||
if (config.db_type === "hive" || config.db_type === "kyuubi" || config.db_type === "impala") {
|
||||
if (config.db_type === "hive" || config.db_type === "argo" || config.db_type === "kyuubi" || config.db_type === "impala") {
|
||||
if (hiveAuthMode.value === "kerberos" && !hivePrincipal.value.trim()) {
|
||||
throw new Error(t("connection.hiveKerberosPrincipalRequired"));
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ const assetIcons: Record<string, string> = {
|
||||
presto: "presto",
|
||||
prestosql: "presto",
|
||||
hive: "hive",
|
||||
argo: "hive",
|
||||
kyuubi: "kyuubi.png",
|
||||
impala: "impala",
|
||||
hbase: "hbase",
|
||||
|
||||
@@ -33,7 +33,7 @@ export function hasInstalledAgentVersion(drivers: readonly AgentDriverInstallSta
|
||||
|
||||
export function agentDriverInstallKey(dbType: DatabaseType | undefined, driverProfile?: string, context?: AgentDriverInstallContext): string | undefined {
|
||||
if (dbType === "sqlite") return context?.ssh ? "sqlite-worker" : undefined;
|
||||
if (dbType === "kyuubi" || dbType === "impala") return "hive";
|
||||
if (dbType === "kyuubi" || dbType === "impala" || dbType === "argo") return "hive";
|
||||
if (dbType === "oracle") return "oracle";
|
||||
if (dbType === "h2") return "h2";
|
||||
if (dbType === "mongodb") return "mongodb";
|
||||
|
||||
@@ -34,6 +34,7 @@ const DRIVER_STARTUP_FLOOR_TYPES = new Set<DatabaseType>([
|
||||
"hive",
|
||||
"kyuubi",
|
||||
"impala",
|
||||
"argo",
|
||||
"spark",
|
||||
"db2",
|
||||
"informix",
|
||||
|
||||
@@ -168,7 +168,7 @@ export function buildHiveTablePropertiesSql(options: HiveTablePropertiesSqlOptio
|
||||
|
||||
export function normalizeDataGridSaveError(databaseType: DatabaseType | undefined, error: unknown): string {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
if (databaseType === "hive" && /Attempt to do update or delete|Error 10294/i.test(message)) {
|
||||
if ((databaseType === "hive" || databaseType === "argo") && /Attempt to do update or delete|Error 10294/i.test(message)) {
|
||||
return "Hive UPDATE/DELETE are not enabled for this table or server. Add rows with INSERT, or enable ACID transactional tables in Hive before editing/deleting existing rows.";
|
||||
}
|
||||
return message;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { manifestDatabaseTypes } from "@/lib/database/databaseDriverManifest";
|
||||
import { supportsTransfer } from "@/lib/database/databaseFeatureSupport";
|
||||
import type { DatabaseType } from "@/types/database";
|
||||
|
||||
const TABLE_ONLY_TRANSFER_DATABASES: DatabaseType[] = ["sqlite", "rqlite", "turso", "cloudflare-d1", "duckdb", "clickhouse", "mongodb", "goldendb", "questdb", "hive", "kyuubi", "impala", "spark"];
|
||||
const TABLE_ONLY_TRANSFER_DATABASES: DatabaseType[] = ["sqlite", "rqlite", "turso", "cloudflare-d1", "duckdb", "clickhouse", "mongodb", "goldendb", "questdb", "hive", "argo", "kyuubi", "impala", "spark"];
|
||||
|
||||
describe("transferObjectKinds", () => {
|
||||
it("groups databases into transfer families", () => {
|
||||
|
||||
@@ -68,6 +68,7 @@ export const DATABASE_NAMESPACE_CREATION_MATRIX = {
|
||||
trino: { database: "schema" },
|
||||
prestosql: { database: "schema" },
|
||||
hive: { deferred: "Hive database creation needs agent metadata validation first" },
|
||||
argo: { deferred: "ArgoDB database creation needs agent metadata validation first" },
|
||||
kyuubi: { deferred: "Kyuubi database creation needs dedicated metadata validation first" },
|
||||
impala: { deferred: "Impala database creation needs dedicated metadata validation first" },
|
||||
spark: { deferred: "Spark database creation needs agent metadata validation first" },
|
||||
|
||||
@@ -71,6 +71,9 @@ const DATABASE_TYPE_OBJECTS = new Map<DatabaseType, SidebarObjectKind[]>([
|
||||
["starrocks", TABLE_VIEW_MV_OBJECTS],
|
||||
// Inceptor/Hive routines can be listed via JDBC plugin fallbacks (system.procedures_v/functions_v).
|
||||
["hive", ROUTINE_OBJECTS],
|
||||
// ArgoDB (Transwarp) shares the Hive agent; its catalog views
|
||||
// (system.procedures_v / system.functions_v) expose routines natively.
|
||||
["argo", ROUTINE_OBJECTS],
|
||||
["kyuubi", TABLE_VIEW_OBJECTS],
|
||||
["impala", TABLE_VIEW_OBJECTS],
|
||||
["spark", TABLE_VIEW_OBJECTS],
|
||||
|
||||
@@ -68,6 +68,7 @@ export const DATABASE_PROPERTY_EDITING_MATRIX = {
|
||||
trino: { deferred: "catalog properties are connector-managed" },
|
||||
prestosql: { deferred: "catalog properties are connector-managed" },
|
||||
hive: { deferred: "database properties need agent metadata validation first" },
|
||||
argo: { deferred: "database properties need agent metadata validation first" },
|
||||
kyuubi: { deferred: "database properties need Kyuubi-specific validation first" },
|
||||
impala: { deferred: "database properties need Impala-specific validation first" },
|
||||
spark: { deferred: "database properties need agent metadata validation first" },
|
||||
|
||||
@@ -123,6 +123,16 @@ const DATABASE_CAPABILITY_OVERRIDES: Partial<Record<DatabaseType, Partial<Databa
|
||||
transaction: false,
|
||||
},
|
||||
},
|
||||
argo: {
|
||||
tableData: {
|
||||
insert: true,
|
||||
updateRequiresPrimaryKey: false,
|
||||
deleteRequiresPrimaryKey: false,
|
||||
keylessRowPredicate: true,
|
||||
requiresTransactionalTableForExistingRows: true,
|
||||
transaction: false,
|
||||
},
|
||||
},
|
||||
jdbc: {
|
||||
tableData: {
|
||||
insert: false,
|
||||
|
||||
@@ -15,7 +15,7 @@ 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", "spark"]);
|
||||
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[] }> = [
|
||||
|
||||
@@ -3,7 +3,7 @@ import { qualifiedTableName, quoteTableIdentifier } from "@/lib/table/tableSelec
|
||||
import { requiresMysqlIdentifierQuote, requiresPostgresIdentifierQuote } from "@/lib/sql/sqlIdentifier.ts";
|
||||
|
||||
/** 智能引号使用反引号的方言族。 */
|
||||
const SMART_QUOTE_BACKTICK_TYPES = new Set<DatabaseType>(["mysql", "clickhouse", "hive", "kyuubi", "impala", "spark", "databricks", "databend", "tdengine", "access", "doris", "starrocks", "goldendb"]);
|
||||
const SMART_QUOTE_BACKTICK_TYPES = new Set<DatabaseType>(["mysql", "clickhouse", "hive", "argo", "kyuubi", "impala", "spark", "databricks", "databend", "tdengine", "access", "doris", "starrocks", "goldendb"]);
|
||||
/** 智能引号使用双引号的方言族(SQL Server 方括号除外)。 */
|
||||
const SMART_QUOTE_DOUBLE_TYPES = new Set<DatabaseType>(["postgres", "gaussdb", "opengauss"]);
|
||||
/** SQL Server 族智能引号使用方括号。 */
|
||||
|
||||
@@ -1308,7 +1308,7 @@ type SqlCompletionApplyDialect = "mysql" | "postgres" | "sqlserver" | "oracle" |
|
||||
// QueryEditor may use another dialect as a CodeMirror syntax fallback.
|
||||
// Completion apply text must still follow the connected database's identifier
|
||||
// folding and quoting rules.
|
||||
const MYSQL_LIKE_IDENTIFIER_DATABASES = new Set<DatabaseType>(["mysql", "clickhouse", "hive", "kyuubi", "impala", "spark", "databend", "tdengine", "access", "doris", "starrocks"]);
|
||||
const MYSQL_LIKE_IDENTIFIER_DATABASES = new Set<DatabaseType>(["mysql", "clickhouse", "hive", "argo", "kyuubi", "impala", "spark", "databend", "tdengine", "access", "doris", "starrocks"]);
|
||||
const POSTGRES_LIKE_IDENTIFIER_DATABASES = new Set<DatabaseType>(["postgres", "redshift", "gaussdb", "kingbase", "highgo", "uxdb", "vastbase", "kwdb", "opengauss"]);
|
||||
const ORACLE_COMPAT_IDENTIFIER_DATABASES = new Set<DatabaseType>(["oracle", "oceanbase-oracle", "yashandb", "oscar", "xugu"]);
|
||||
const UPPER_FOLDING_IDENTIFIER_DATABASES = new Set<DatabaseType>(["dameng", "db2"]);
|
||||
@@ -2867,7 +2867,7 @@ function lastTopLevelKeywordIndex(sql: string, keyword: string): number {
|
||||
// unquoted token as a table reference there is a false-positive risk. Everything else defaults
|
||||
// to Unicode support (MySQL/Postgres/SQL Server/SQLite/DuckDB and the many Chinese-vendor
|
||||
// engines this product supports legitimately use non-ASCII unquoted identifiers).
|
||||
const ASCII_ONLY_UNQUOTED_IDENTIFIER_DATABASES = new Set<DatabaseType>(["clickhouse", "snowflake", "bigquery", "hive", "spark", "trino", "prestosql", "impala", "db2", "teradata"]);
|
||||
const ASCII_ONLY_UNQUOTED_IDENTIFIER_DATABASES = new Set<DatabaseType>(["clickhouse", "snowflake", "bigquery", "hive", "argo", "spark", "trino", "prestosql", "impala", "db2", "teradata"]);
|
||||
|
||||
// Dialects where a bare "--" needs trailing whitespace/EOL to start a comment, since MySQL
|
||||
// reserves unspaced "--" for double-negation (e.g. `SELECT 1--1`). Scoped to mysql itself plus
|
||||
@@ -2887,7 +2887,7 @@ const MYSQL_DASH_COMMENT_DIALECTS = new Set<DatabaseType>(["mysql", "doris", "st
|
||||
// why that's unsafe (a trailing backslash before a closing quote in a dialect that doesn't escape
|
||||
// it, e.g. a Postgres Windows-path string literal, would misread the real closing quote as
|
||||
// escaped and swallow the rest of the query).
|
||||
const BACKSLASH_ESCAPE_STRING_DIALECTS = new Set<DatabaseType>(["mysql", "doris", "starrocks", "hive", "impala", "spark"]);
|
||||
const BACKSLASH_ESCAPE_STRING_DIALECTS = new Set<DatabaseType>(["mysql", "doris", "starrocks", "hive", "argo", "impala", "spark"]);
|
||||
|
||||
// Table/schema/db unquoted-identifier continue class needs @ and # in addition to what
|
||||
// SQL_IDENTIFIER_CONTINUE_CHAR covers, so splice its inner class body into a locally-built class
|
||||
|
||||
@@ -272,7 +272,7 @@ export function importDataTypeForDatabase(inferredType: ImportInferredType, data
|
||||
if (databaseType === "sqlserver") return "NVARCHAR(MAX)";
|
||||
if (databaseType === "oracle" || databaseType === "oceanbase-oracle" || databaseType === "dameng") return "CLOB";
|
||||
if (databaseType === "clickhouse") return "String";
|
||||
if (["hive", "trino", "prestosql", "databricks"].includes(databaseType || "")) return "STRING";
|
||||
if (["hive", "argo", "trino", "prestosql", "databricks"].includes(databaseType || "")) return "STRING";
|
||||
return "TEXT";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ export function quoteTableIdentifier(databaseType: DatabaseType | undefined, nam
|
||||
databaseType === "mysql" ||
|
||||
databaseType === "clickhouse" ||
|
||||
databaseType === "hive" ||
|
||||
databaseType === "argo" ||
|
||||
databaseType === "kyuubi" ||
|
||||
databaseType === "impala" ||
|
||||
databaseType === "spark" ||
|
||||
|
||||
@@ -86,6 +86,7 @@ export const CONNECTION_PROFILES = {
|
||||
prestosql: { type: "prestosql", port: 8080, user: "", label: "PrestoSQL", icon: "presto" },
|
||||
hive: { type: "hive", port: 10000, user: "", label: "Apache Hive", icon: "hive" },
|
||||
kyuubi: { type: "kyuubi", port: 10009, user: "", label: "Apache Kyuubi", icon: "kyuubi", urlParams: "auth=NONE" },
|
||||
argo: { type: "argo", port: 10000, user: "", label: "ArgoDB (Transwarp)", icon: "hive" },
|
||||
impala: { type: "impala", port: 21050, user: "", label: "Apache Impala", icon: "impala", urlParams: "auth=noSasl" },
|
||||
spark: { type: "spark", port: 10015, user: "", label: "Apache Spark", icon: "spark" },
|
||||
db2: { type: "db2", port: 50000, user: "db2inst1", label: "IBM DB2", icon: "db2" },
|
||||
@@ -189,6 +190,7 @@ export const CONNECTION_PROFILE_ICONS = {
|
||||
prestosql: "prestosql",
|
||||
hive: "hive",
|
||||
kyuubi: "kyuubi",
|
||||
argo: "hive",
|
||||
impala: "impala",
|
||||
spark: "spark",
|
||||
db2: "db2",
|
||||
@@ -287,6 +289,7 @@ export const CONNECTION_PICKER_OPTIONS = [
|
||||
{ value: "prestosql", label: "PrestoSQL", category: "analytics" },
|
||||
{ value: "hive", label: "Hive", category: "analytics" },
|
||||
{ value: "kyuubi", label: "Apache Kyuubi", category: "analytics" },
|
||||
{ value: "argo", label: "ArgoDB", category: "domestic" },
|
||||
{ value: "impala", label: "Apache Impala", category: "analytics" },
|
||||
{ value: "spark", label: "Apache Spark", category: "analytics" },
|
||||
{ value: "db2", label: "DB2", category: "sql" },
|
||||
|
||||
@@ -51,6 +51,7 @@ export const DATABASE_TYPES = [
|
||||
"trino",
|
||||
"prestosql",
|
||||
"hive",
|
||||
"argo",
|
||||
"kyuubi",
|
||||
"impala",
|
||||
"db2",
|
||||
|
||||
@@ -1842,6 +1842,41 @@
|
||||
"driverManagement": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"dbType": "argo",
|
||||
"label": "ArgoDB (Transwarp)",
|
||||
"runtimeMode": "agent",
|
||||
"mcpMode": "bridge",
|
||||
"agentKey": "hive",
|
||||
"driverStoreVisible": false,
|
||||
"driverStoreOrder": 23,
|
||||
"singleConnectionPool": false,
|
||||
"metadataConnectionScoped": false,
|
||||
"skipTcpProbe": true,
|
||||
"defaultPort": 10000,
|
||||
"traits": {
|
||||
"schemaAware": true
|
||||
},
|
||||
"supportLevel": "operate",
|
||||
"capabilities": {
|
||||
"queryExecution": true,
|
||||
"metadataBrowse": true,
|
||||
"objectBrowser": true,
|
||||
"objectSource": true,
|
||||
"schemaSearch": true,
|
||||
"diagram": false,
|
||||
"tableDataEdit": true,
|
||||
"tableStructureEdit": false,
|
||||
"tableImport": false,
|
||||
"dataTransfer": true,
|
||||
"sqlFileExecution": true,
|
||||
"databaseCreate": false,
|
||||
"fieldLineage": false,
|
||||
"sqlExplain": false,
|
||||
"userAdmin": false,
|
||||
"driverManagement": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"dbType": "kyuubi",
|
||||
"label": "Apache Kyuubi",
|
||||
|
||||
@@ -661,7 +661,8 @@ fn append_agent_url_params(base: String, params: Option<&str>) -> String {
|
||||
}
|
||||
|
||||
pub fn hive_uses_zookeeper_discovery(config: &ConnectionConfig) -> bool {
|
||||
if !matches!(config.db_type, DatabaseType::Hive | DatabaseType::Kyuubi | DatabaseType::Impala) {
|
||||
if !matches!(config.db_type, DatabaseType::Hive | DatabaseType::Kyuubi | DatabaseType::Impala | DatabaseType::Argo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -249,6 +249,7 @@ macro_rules! agent_connection_pool_database_type {
|
||||
| DatabaseType::Hive
|
||||
| DatabaseType::Kyuubi
|
||||
| DatabaseType::Impala
|
||||
| DatabaseType::Argo
|
||||
| DatabaseType::Spark
|
||||
| DatabaseType::Db2
|
||||
| DatabaseType::Informix
|
||||
|
||||
@@ -2052,6 +2052,10 @@ impl AgentDriverClient {
|
||||
}
|
||||
if let Some(object_types) = object_types {
|
||||
params["object_types"] = serde_json::json!(object_types);
|
||||
// Agent drivers (hive-go, oracle-go, etc.) read the object-type
|
||||
// filter from a camelCase field; keep both spellings so existing
|
||||
// agents don't need a coordinated rollout.
|
||||
params["objectTypes"] = serde_json::json!(object_types);
|
||||
}
|
||||
self.call_method_with_timeout(AgentMethod::ListTables, params, timeout_duration).await
|
||||
}
|
||||
@@ -2087,6 +2091,7 @@ impl AgentDriverClient {
|
||||
}
|
||||
if let Some(object_types) = object_types {
|
||||
params["object_types"] = serde_json::json!(object_types);
|
||||
params["objectTypes"] = serde_json::json!(object_types);
|
||||
}
|
||||
self.call_method_with_timeout(AgentMethod::ListObjects, params, timeout_duration).await
|
||||
}
|
||||
|
||||
@@ -538,6 +538,7 @@ pub fn build_empty_table_sql(options: TableAdminSqlOptions) -> String {
|
||||
DatabaseType::Cassandra
|
||||
| DatabaseType::Hive
|
||||
| DatabaseType::Kyuubi
|
||||
| DatabaseType::Argo
|
||||
| DatabaseType::Kylin
|
||||
| DatabaseType::Questdb,
|
||||
) => {
|
||||
@@ -739,20 +740,22 @@ pub fn build_duplicate_table_structure_sql(options: DuplicateTableStructureSqlOp
|
||||
);
|
||||
let target =
|
||||
qualified_duplicate_target_name(options.database_type, options.schema.as_deref(), &options.target_name);
|
||||
let structure_sql =
|
||||
if matches!(options.database_type, Some(DatabaseType::Mysql | DatabaseType::Kyuubi | DatabaseType::Impala)) {
|
||||
format!("CREATE TABLE {target} LIKE {source};")
|
||||
} else if options.database_type == Some(DatabaseType::Questdb) {
|
||||
format!("CREATE TABLE {target} (LIKE {source});")
|
||||
} else if options.database_type.is_some_and(is_postgres_like_structure_copy) {
|
||||
format!("CREATE TABLE {target} (LIKE {source} INCLUDING ALL);")
|
||||
} else if options.database_type == Some(DatabaseType::SqlServer) {
|
||||
format!("SELECT TOP 0 * INTO {target} FROM {source};")
|
||||
} else if options.database_type.is_some_and(uses_false_predicate_duplicate_structure) {
|
||||
format!("CREATE TABLE {target} AS SELECT * FROM {source} WHERE 1=0")
|
||||
} else {
|
||||
format!("CREATE TABLE {target} AS SELECT * FROM {source} WHERE 0;")
|
||||
};
|
||||
let structure_sql = if matches!(
|
||||
options.database_type,
|
||||
Some(DatabaseType::Mysql | DatabaseType::Kyuubi | DatabaseType::Impala | DatabaseType::Argo)
|
||||
) {
|
||||
format!("CREATE TABLE {target} LIKE {source};")
|
||||
} else if options.database_type == Some(DatabaseType::Questdb) {
|
||||
format!("CREATE TABLE {target} (LIKE {source});")
|
||||
} else if options.database_type.is_some_and(is_postgres_like_structure_copy) {
|
||||
format!("CREATE TABLE {target} (LIKE {source} INCLUDING ALL);")
|
||||
} else if options.database_type == Some(DatabaseType::SqlServer) {
|
||||
format!("SELECT TOP 0 * INTO {target} FROM {source};")
|
||||
} else if options.database_type.is_some_and(uses_false_predicate_duplicate_structure) {
|
||||
format!("CREATE TABLE {target} AS SELECT * FROM {source} WHERE 1=0")
|
||||
} else {
|
||||
format!("CREATE TABLE {target} AS SELECT * FROM {source} WHERE 0;")
|
||||
};
|
||||
|
||||
let mut comment_sql = Vec::new();
|
||||
if let Some(database_type) =
|
||||
|
||||
@@ -1089,7 +1089,7 @@ impl ConnectionConfig {
|
||||
DatabaseType::Snowflake => format!("snowflake://{host}/{db_part}"),
|
||||
DatabaseType::Trino => format!("trino://{host}:{port}{db_part}"),
|
||||
DatabaseType::PrestoSql => format!("prestosql://{host}:{port}{db_part}"),
|
||||
DatabaseType::Hive => format!("hive://{host}:{port}{db_part}"),
|
||||
DatabaseType::Hive | DatabaseType::Argo => format!("hive://{host}:{port}{db_part}"),
|
||||
DatabaseType::Kyuubi => format!("kyuubi://{host}:{port}{db_part}"),
|
||||
DatabaseType::Impala => format!("impala://{host}:{port}{db_part}"),
|
||||
DatabaseType::Spark => format!("spark://{host}:{port}{db_part}"),
|
||||
@@ -1315,7 +1315,7 @@ impl ConnectionConfig {
|
||||
DatabaseType::PrestoSql => {
|
||||
format!("prestosql://{}:{}@{host}:{port}{db_part}", username, password)
|
||||
}
|
||||
DatabaseType::Hive => {
|
||||
DatabaseType::Hive | DatabaseType::Argo => {
|
||||
format!("hive://{}:{}@{host}:{port}{db_part}", username, password)
|
||||
}
|
||||
DatabaseType::Kyuubi => {
|
||||
|
||||
@@ -55,6 +55,7 @@ pub fn is_schema_aware(database_type: DatabaseType) -> bool {
|
||||
| DatabaseType::Hive
|
||||
| DatabaseType::Kyuubi
|
||||
| DatabaseType::Impala
|
||||
| DatabaseType::Argo
|
||||
| DatabaseType::Spark
|
||||
| DatabaseType::Db2
|
||||
| DatabaseType::Informix
|
||||
|
||||
@@ -707,9 +707,9 @@ pub fn profile_for(db_type: DatabaseType) -> DdlDialectProfile {
|
||||
|
||||
ClickHouse => clickhouse_profile(),
|
||||
|
||||
DuckDb | Questdb | SapHana | Teradata | Snowflake | Trino | PrestoSql | Hive | Kyuubi | Impala | Spark
|
||||
| Db2 | Informix | Bigquery | Spanner | Kylin | Ignite | Ignite3 | Oscar | Tdengine | Iotdb | Databricks
|
||||
| Jdbc => conservative_ansi(db_type),
|
||||
DuckDb | Questdb | SapHana | Teradata | Snowflake | Trino | PrestoSql | Hive | Kyuubi | Impala | Argo
|
||||
| Spark | Db2 | Informix | Bigquery | Spanner | Kylin | Ignite | Ignite3 | Oscar | Tdengine | Iotdb
|
||||
| Databricks | Jdbc => conservative_ansi(db_type),
|
||||
|
||||
// Non-tabular / not applicable for relational CREATE TABLE
|
||||
Redis | MongoDb | DynamoDb | Elasticsearch | Easysearch | Meilisearch | Qdrant | Milvus | Weaviate
|
||||
|
||||
@@ -131,6 +131,7 @@ pub fn quote_table_identifier(database_type: Option<DatabaseType>, name: &str) -
|
||||
| DatabaseType::Hive
|
||||
| DatabaseType::Kyuubi
|
||||
| DatabaseType::Impala
|
||||
| DatabaseType::Argo
|
||||
| DatabaseType::Spark
|
||||
| DatabaseType::Databricks
|
||||
| DatabaseType::Databend
|
||||
@@ -388,6 +389,7 @@ pub(crate) fn quote_transfer_identifier(name: &str, database_type: &DatabaseType
|
||||
| DatabaseType::Hive
|
||||
| DatabaseType::Kyuubi
|
||||
| DatabaseType::Impala
|
||||
| DatabaseType::Argo
|
||||
| DatabaseType::Spark
|
||||
| DatabaseType::Questdb => format!("`{}`", name.replace('`', "``")),
|
||||
DatabaseType::SqlServer => format!("[{}]", name.replace(']', "]]")),
|
||||
|
||||
@@ -610,7 +610,13 @@ pub(super) fn build_select_columns(
|
||||
}
|
||||
if !matches!(
|
||||
database_type,
|
||||
Some(DatabaseType::Hive | DatabaseType::Kyuubi | DatabaseType::Impala | DatabaseType::InfluxDb)
|
||||
Some(
|
||||
DatabaseType::Hive
|
||||
| DatabaseType::Kyuubi
|
||||
| DatabaseType::Impala
|
||||
| DatabaseType::Argo
|
||||
| DatabaseType::InfluxDb
|
||||
)
|
||||
) {
|
||||
return "*".to_string();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,11 @@ fn quotes_identifiers_by_database_type() {
|
||||
assert_eq!(quote_table_identifier(Some(DatabaseType::Jdbc), "user name"), "user name");
|
||||
assert_eq!(quote_table_identifier(Some(DatabaseType::Iotdb), "root.test.device2"), "root.test.device2");
|
||||
assert_eq!(quote_table_identifier(Some(DatabaseType::Spanner), "user`name"), "`user``name`");
|
||||
// ArgoDB shares the Hive-family dialect: backticks quote identifiers and
|
||||
// double quotes are string literals, and schemas qualify table names.
|
||||
assert_eq!(quote_table_identifier(Some(DatabaseType::Argo), "user`name"), "`user``name`");
|
||||
assert_eq!(quote_transfer_identifier("user`name", &DatabaseType::Argo), "`user``name`");
|
||||
assert!(is_schema_aware(DatabaseType::Argo));
|
||||
}
|
||||
|
||||
/// Spanner databases are created in one of two immutable dialects. The connected
|
||||
|
||||
@@ -2959,7 +2959,7 @@ pub fn map_column_type(source_type: &str, _source_db: &DatabaseType, target_db:
|
||||
// Extract basic type, `bigint unsigned` -> `bigint`
|
||||
base = base.split(' ').next().unwrap_or(base).trim();
|
||||
|
||||
if matches!(target_db, DatabaseType::Hive | DatabaseType::Kyuubi | DatabaseType::Impala) {
|
||||
if matches!(target_db, DatabaseType::Hive | DatabaseType::Kyuubi | DatabaseType::Impala | DatabaseType::Argo) {
|
||||
return match base {
|
||||
"tinyint" => "TINYINT".into(),
|
||||
"smallint" | "int2" => "SMALLINT".into(),
|
||||
@@ -3192,7 +3192,11 @@ fn generate_create_table_ddl_with_column_quoting(
|
||||
line.push(' ');
|
||||
line.push_str(&default_clause);
|
||||
}
|
||||
if !c.is_nullable && !matches!(target_db, DatabaseType::Hive | DatabaseType::Kyuubi | DatabaseType::Impala)
|
||||
if !c.is_nullable
|
||||
&& !matches!(
|
||||
target_db,
|
||||
DatabaseType::Hive | DatabaseType::Kyuubi | DatabaseType::Impala | DatabaseType::Argo
|
||||
)
|
||||
{
|
||||
line.push_str(" NOT NULL");
|
||||
}
|
||||
@@ -3216,7 +3220,7 @@ fn generate_create_table_ddl_with_column_quoting(
|
||||
}
|
||||
|
||||
let mut pks = Vec::with_capacity(columns.iter().filter(|c| c.is_primary_key).count());
|
||||
if !matches!(target_db, DatabaseType::Hive | DatabaseType::Kyuubi | DatabaseType::Impala) {
|
||||
if !matches!(target_db, DatabaseType::Hive | DatabaseType::Kyuubi | DatabaseType::Impala | DatabaseType::Argo) {
|
||||
for c in columns {
|
||||
if c.is_primary_key {
|
||||
let qname = transfer_column_identifier(&c.name, target_db, quote_target_column_names);
|
||||
@@ -3748,7 +3752,7 @@ fn generate_upsert_typed_for_transfer(
|
||||
fn max_transfer_write_rows(db_type: &DatabaseType, mode: &TransferMode) -> usize {
|
||||
match (db_type, mode) {
|
||||
(DatabaseType::SqlServer, TransferMode::Append | TransferMode::Overwrite) => MAX_SQLSERVER_INSERT_ROWS,
|
||||
(DatabaseType::Hive | DatabaseType::Kyuubi | DatabaseType::Impala, _) => 500,
|
||||
(DatabaseType::Hive | DatabaseType::Kyuubi | DatabaseType::Impala | DatabaseType::Argo, _) => 500,
|
||||
(DatabaseType::Oracle, TransferMode::Append | TransferMode::Overwrite) => MAX_ORACLE_INSERT_ALL_ROWS,
|
||||
(DatabaseType::Oracle, TransferMode::Upsert) => MAX_ORACLE_MERGE_ROWS,
|
||||
_ => usize::MAX,
|
||||
@@ -7548,7 +7552,11 @@ where
|
||||
|| (request.mode == TransferMode::Upsert
|
||||
&& !matches!(
|
||||
target_db_type,
|
||||
DatabaseType::ClickHouse | DatabaseType::Hive | DatabaseType::Kyuubi | DatabaseType::Impala
|
||||
DatabaseType::ClickHouse
|
||||
| DatabaseType::Hive
|
||||
| DatabaseType::Kyuubi
|
||||
| DatabaseType::Impala
|
||||
| DatabaseType::Argo
|
||||
))
|
||||
|| matches!(target_db_type, DatabaseType::Postgres | DatabaseType::Dameng);
|
||||
let target_columns = if needs_target_columns {
|
||||
@@ -7622,7 +7630,11 @@ where
|
||||
let (effective_mode, pk_columns) = if request.mode == TransferMode::Upsert {
|
||||
if matches!(
|
||||
target_db_type,
|
||||
DatabaseType::ClickHouse | DatabaseType::Hive | DatabaseType::Kyuubi | DatabaseType::Impala
|
||||
DatabaseType::ClickHouse
|
||||
| DatabaseType::Hive
|
||||
| DatabaseType::Kyuubi
|
||||
| DatabaseType::Impala
|
||||
| DatabaseType::Argo
|
||||
) {
|
||||
log::warn!("[transfer] upsert not supported for {:?}, falling back to append", target_db_type);
|
||||
(TransferMode::Append, vec![])
|
||||
|
||||
@@ -101,6 +101,7 @@ fn maps_agent_database_types_to_driver_keys() {
|
||||
assert_eq!(agent_key(&DatabaseType::Hive, None), Some("hive"));
|
||||
assert_eq!(agent_key(&DatabaseType::Kyuubi, None), Some("hive"));
|
||||
assert_eq!(agent_key(&DatabaseType::Impala, None), Some("hive"));
|
||||
assert_eq!(agent_key(&DatabaseType::Argo, None), Some("hive"));
|
||||
assert_eq!(agent_key(&DatabaseType::Tdengine, None), Some("tdengine"));
|
||||
assert_eq!(agent_key(&DatabaseType::Iotdb, None), Some("iotdb"));
|
||||
assert_eq!(agent_key(&DatabaseType::Yashandb, None), Some("yashandb"));
|
||||
@@ -146,6 +147,7 @@ fn classifies_agent_database_types() {
|
||||
assert!(is_agent_type(&DatabaseType::Hive));
|
||||
assert!(is_agent_type(&DatabaseType::Kyuubi));
|
||||
assert!(is_agent_type(&DatabaseType::Impala));
|
||||
assert!(is_agent_type(&DatabaseType::Argo));
|
||||
assert!(is_agent_type(&DatabaseType::Tdengine));
|
||||
assert!(is_agent_type(&DatabaseType::Iotdb));
|
||||
assert!(is_agent_type(&DatabaseType::Yashandb));
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
schemaVersion: 1
|
||||
order: 515
|
||||
dbType: argo
|
||||
rustVariant: Argo
|
||||
label: ArgoDB (Transwarp)
|
||||
runtimeMode: agent
|
||||
mcpMode: bridge
|
||||
agentKey: hive
|
||||
driverStoreVisible: false
|
||||
driverStoreOrder: 23
|
||||
singleConnectionPool: false
|
||||
metadataConnectionScoped: false
|
||||
skipTcpProbe: true
|
||||
defaultPort: 10000
|
||||
traits:
|
||||
schemaAware: true
|
||||
supportLevel: operate
|
||||
capabilities:
|
||||
queryExecution: true
|
||||
metadataBrowse: true
|
||||
objectBrowser: true
|
||||
objectSource: true
|
||||
schemaSearch: true
|
||||
diagram: false
|
||||
tableDataEdit: true
|
||||
tableStructureEdit: false
|
||||
tableImport: false
|
||||
dataTransfer: true
|
||||
sqlFileExecution: true
|
||||
databaseCreate: false
|
||||
fieldLineage: false
|
||||
sqlExplain: false
|
||||
userAdmin: false
|
||||
driverManagement: true
|
||||
@@ -470,6 +470,14 @@ profiles:
|
||||
user: ""
|
||||
urlParams: auth=NONE
|
||||
category: analytics
|
||||
- id: argo
|
||||
dbType: argo
|
||||
label: ArgoDB (Transwarp)
|
||||
icon: hive
|
||||
port: 10000
|
||||
user: ""
|
||||
category: domestic
|
||||
pickerLabel: ArgoDB
|
||||
- id: impala
|
||||
dbType: impala
|
||||
label: Apache Impala
|
||||
|
||||
Reference in New Issue
Block a user