feat(d1): add Cloudflare D1 database support

This commit is contained in:
Anubis authored and GitHub committed 2026-07-12 22:03:57 +08:00
1 parent 977e7f78ca
commit 2a92746dc0
61 files changed
+1732 -69

No files matched your search

@@ -0,0 +1,33 @@
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import PasswordInput from "@/components/ui/PasswordInput.vue";
const accountId = defineModel<string>("accountId", { required: true });
const databaseId = defineModel<string | undefined>("databaseId");
const apiToken = defineModel<string>("apiToken", { required: true });
const { t } = useI18n();
</script>
<template>
<div class="grid grid-cols-4 items-center gap-4">
<Label class="justify-self-start text-left">{{ t("connection.d1AccountId") }}</Label>
<Input v-model="accountId" class="col-span-3" placeholder="023e105f4ecef8ad9ca31a8372d0c353" />
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label class="justify-self-start text-left">{{ t("connection.d1DatabaseId") }}</Label>
<Input v-model="databaseId" class="col-span-3" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label class="justify-self-start text-left">{{ t("connection.d1ApiToken") }}</Label>
<PasswordInput v-model="apiToken" class="col-span-3" placeholder="Cloudflare API Token" />
</div>
<div class="grid grid-cols-4 items-start gap-4">
<span />
<p class="col-span-3 text-xs text-muted-foreground">{{ t("connection.d1TokenHint") }}</p>
</div>
</template>
@@ -50,9 +50,11 @@ import { buildDraftVisibleDatabasesConnectionId, connectionCanChooseVisibleDatab
import { canSaveVisibleDatabaseSelection, connectionUsesVisibleSchemaFilter, filterDatabaseNamesForVisiblePicker, isSystemDatabaseName, normalizeVisibleDatabaseSelection, buildDraftVisibleSchemasConnectionId, normalizeVisibleSchemaSelection } from "@/lib/database/visibleDatabases";
import { isSchemaAware, isSingleDatabase } from "@/lib/database/databaseFeatureSupport";
import VisibleSchemasDialog from "@/components/sidebar/VisibleSchemasDialog.vue";
import CloudflareD1ConnectionFields from "@/components/connection/CloudflareD1ConnectionFields.vue";
import { oceanbaseModeConnectionPatch, oceanbaseSubModeFromConfig } from "@/lib/database/oceanbaseConnectionMode";
import { translateBackendError } from "@/i18n/backend-errors";
import { applyHiveKerberosSubmitConfig, hiveKerberosFormConfig, type HiveKerberosAuthMode } from "@/lib/database/hiveKerberosOptions";
import { hasCloudflareD1Credentials, isCloudflareD1Connection, normalizeCloudflareD1Connection } from "@/lib/connection/cloudflareD1";
type DbOption = { value: string; label: string };
type DbCategory = { key: string; title: string; options: DbOption[] };
@@ -547,6 +549,7 @@ const driverProfiles: Record<
sqlite: { type: "sqlite", port: 0, user: "", label: "SQLite", icon: "sqlite" },
rqlite: { type: "rqlite", port: 4001, user: "", label: "RQLite", icon: "rqlite" },
turso: { type: "turso", port: 443, user: "", label: "Turso", icon: "turso" },
"cloudflare-d1": { type: "cloudflare-d1", port: 443, user: "", label: "Cloudflare D1", icon: "cloudflare-d1" },
duckdb: { type: "duckdb", port: 0, user: "", label: "DuckDB", icon: "duckdb" },
access: { type: "access", port: 0, user: "", label: "Microsoft Access", icon: "access" },
mongodb: { type: "mongodb", port: 27017, user: "", label: "MongoDB", icon: "mongodb" },
@@ -1635,6 +1638,7 @@ const iconTypeMap: Record<string, string> = {
sqlite: "sqlite",
rqlite: "rqlite",
turso: "turso",
"cloudflare-d1": "cloudflare-d1",
access: "access",
redis: "redis",
mongodb: "mongodb",
@@ -1724,6 +1728,7 @@ const dbOptions: DbOption[] = [
{ value: "dm", label: "DM (Dameng)" },
{ value: "opengauss", label: "openGauss" },
{ value: "turso", label: "Turso" },
{ value: "cloudflare-d1", label: "Cloudflare D1" },
{ value: "duckdb", label: "DuckDB" },
{ value: "rqlite", label: "RQLite" },
{ value: "access", label: "Microsoft Access" },
@@ -1915,7 +1920,7 @@ const zookeeperConnectString = computed({
form.value.connection_string = normalizeZooKeeperConnectString(value);
},
});
const canUseTransportLayers = computed(() => form.value.db_type !== "sqlite" && form.value.db_type !== "access" && !isH2FileMode.value);
const canUseTransportLayers = computed(() => form.value.db_type !== "sqlite" && form.value.db_type !== "access" && !isCloudflareD1Connection(form.value) && !isH2FileMode.value);
const shouldShowAgentDriverInstallHint = computed(() => showAgentDriverInstallHint(form.value.db_type, agentDrivers.value, form.value.driver_profile));
const h2DriverMissing = computed(() => form.value.db_type === "h2" && isH2FileMode.value && agentDrivers.value.find((d) => d.db_type === "h2")?.installed !== true);
const canChooseVisibleDatabases = computed(() => connectionCanChooseVisibleDatabases(form.value));
@@ -2040,6 +2045,7 @@ const hasRequiredConnectionTarget = computed(() => {
}
if (form.value.db_type === "zookeeper") return !!(form.value.host || form.value.connection_string || connectionUrlInput.value.trim());
if (form.value.db_type === "nacos") return !!nacosServerAddr.value.trim();
if (isCloudflareD1Connection(form.value)) return hasCloudflareD1Credentials(form.value);
if (isH2FileMode.value) return !!(form.value.host.trim() || h2FilePathFromJdbcUrl(form.value.connection_string));
return !!(form.value.host || (mongoUseUrl.value && form.value.connection_string) || (form.value.db_type === "jdbc" && form.value.connection_string) || connectionUrlInput.value.trim());
});
@@ -2290,6 +2296,12 @@ function connectionConfigForSubmit(id: string): ConnectionConfig {
throw new Error(t("connection.kingbaseDatabaseRequired"));
}
}
if (isCloudflareD1Connection(config)) {
normalizeCloudflareD1Connection(config);
if (!hasCloudflareD1Credentials(config)) {
throw new Error(t("connection.d1FieldsRequired"));
}
}
config.transport_layers = (config.transport_layers || []).map(normalizeTransportLayer);
config.transport_layers = config.transport_layers.map((layer) => {
if (layer.type !== "ssh") return layer;
@@ -4744,6 +4756,10 @@ function openExternalUrl(url: string) {
</div>
</template>
<template v-else-if="form.db_type === 'cloudflare-d1'">
<CloudflareD1ConnectionFields v-model:account-id="form.host" v-model:database-id="form.database" v-model:api-token="form.password" />
</template>
<!-- MySQL / PostgreSQL: host, port, user, password, database -->
<template v-else>
<div class="grid grid-cols-4 items-center gap-4">
@@ -14,6 +14,7 @@ const assetIcons: Record<string, string> = {
sqlite: "sqlite",
rqlite: "rqlite.png",
turso: "turso.png",
cloudflare_d1: "cloudflare-d1",
redis: "redis",
mongodb: "mongodb",
mongodb_legacy: "mongodb",
@@ -474,6 +474,8 @@ function connectionTooltipScheme(config: Pick<ConnectionConfig, "db_type" | "ssl
case "turso":
case "mq":
return config.ssl ? "https" : "http";
case "cloudflare-d1":
return "https";
case "dameng":
return "dm";
default:
@@ -4825,7 +4827,7 @@ function treeItemMenuItems(): ContextMenuItem[] {
items.push({ label: t("contextMenu.newQuery"), action: newQuery, icon: TerminalSquare });
const sqlHistoryMenu = savedSqlHistorySubmenu();
if (sqlHistoryMenu) items.push(sqlHistoryMenu);
if (node.type === "database") {
if (node.type === "database" && currentDatabaseType() !== "cloudflare-d1") {
if (!isNodeDefaultDatabase.value) {
items.push({ label: t("contextMenu.setDefaultDatabase"), action: setNodeAsDefaultDatabase, icon: Database });
} else {
+5
View File
@@ -198,6 +198,11 @@ export default {
driverName: "Driver Name",
driverNamePlaceholder: "Vendor or environment name",
urlParams: "URL Params",
d1AccountId: "Account ID",
d1DatabaseId: "Database ID",
d1ApiToken: "API Token",
d1TokenHint: "The token needs D1 Read permission; writes, DDL, and imports also require D1 Write.",
d1FieldsRequired: "Cloudflare Account ID, D1 Database ID, and API Token are required.",
hiveAuthMode: "Auth",
hiveAuthNone: "None",
hivePrincipal: "Principal",
+5
View File
@@ -200,6 +200,11 @@ export default withEnglishFallback({
driverName: "Nombre del driver",
driverNamePlaceholder: "Nombre del proveedor o entorno",
urlParams: "Parámetros de URL",
d1AccountId: "ID de cuenta",
d1DatabaseId: "ID de base de datos",
d1ApiToken: "Token de API",
d1TokenHint: "El token necesita al menos el permiso D1 Read; las escrituras, el DDL y las importaciones también requieren D1 Write.",
d1FieldsRequired: "Se requieren el ID de cuenta de Cloudflare, el ID de la base de datos D1 y el token de API.",
gbaseServer: "GBASEDBTSERVER",
informixServer: "INFORMIXSERVER",
sslEnable: "Habilitar conexión cifrada",
+5
View File
@@ -199,6 +199,11 @@ export default withEnglishFallback({
driverName: "Nome Driver",
driverNamePlaceholder: "Nome del fornitore o dell'ambiente",
urlParams: "Parametri URL",
d1AccountId: "ID account",
d1DatabaseId: "ID database",
d1ApiToken: "Token API",
d1TokenHint: "Il token richiede almeno l'autorizzazione D1 Read; per scritture, DDL e importazioni è necessaria anche D1 Write.",
d1FieldsRequired: "Sono obbligatori l'ID account Cloudflare, l'ID database D1 e il token API.",
gbaseServer: "GBASEDBTSERVER",
informixServer: "INFORMIXSERVER",
sslEnable: "Abilita connessione crittografata",
+5
View File
@@ -199,6 +199,11 @@ export default withEnglishFallback({
driverName: "ドライバー名",
driverNamePlaceholder: "ベンダー名または環境名",
urlParams: "URLパラメータ",
d1AccountId: "アカウントID",
d1DatabaseId: "データベースID",
d1ApiToken: "APIトークン",
d1TokenHint: "トークンには少なくともD1 Read権限が必要です。書き込み、DDL、インポートにはD1 Write権限も必要です。",
d1FieldsRequired: "CloudflareアカウントID、D1データベースID、APIトークンは必須です。",
gbaseServer: "GBASEDBTSERVER",
informixServer: "INFORMIXSERVER",
sslEnable: "暗号化接続を有効にする",
+5
View File
@@ -200,6 +200,11 @@ export default withEnglishFallback({
driverName: "Nome do Driver",
driverNamePlaceholder: "Nome do fornecedor ou ambiente",
urlParams: "Parâmetros da URL",
d1AccountId: "ID da conta",
d1DatabaseId: "ID do banco de dados",
d1ApiToken: "Token da API",
d1TokenHint: "O token precisa de pelo menos a permissão D1 Read; gravações, DDL e importações também exigem D1 Write.",
d1FieldsRequired: "O ID da conta Cloudflare, o ID do banco de dados D1 e o token da API são obrigatórios.",
gbaseServer: "GBASEDBTSERVER",
informixServer: "INFORMIXSERVER",
sslEnable: "Habilitar conexão criptografada",
+5
View File
@@ -200,6 +200,11 @@ export default withEnglishFallback({
driverName: "驱动名称",
driverNamePlaceholder: "厂商或环境名称",
urlParams: "URL 参数",
d1AccountId: "Account ID",
d1DatabaseId: "Database ID",
d1ApiToken: "API Token",
d1TokenHint: "Token 至少需要 D1 Read 权限;执行写入、DDL 或导入时还需要 D1 Write 权限。",
d1FieldsRequired: "必须填写 Cloudflare Account ID、D1 Database ID 和 API Token。",
hiveAuthMode: "认证",
hiveAuthNone: "无",
hivePrincipal: "Principal",
+5
View File
@@ -200,6 +200,11 @@ export default withEnglishFallback({
driverName: "驅動程式名稱",
driverNamePlaceholder: "廠商或環境名稱",
urlParams: "URL 參數",
d1AccountId: "帳戶 ID",
d1DatabaseId: "資料庫 ID",
d1ApiToken: "API Token",
d1TokenHint: "Token 至少需要 D1 Read 權限;執行寫入、DDL 或匯入時還需要 D1 Write 權限。",
d1FieldsRequired: "必須填寫 Cloudflare Account ID、D1 Database ID 和 API Token。",
gbaseServer: "GBASEDBTSERVER",
informixServer: "INFORMIXSERVER",
sslEnable: "啟用加密連線",
@@ -16,6 +16,7 @@ describe("supportsTransaction", () => {
expect(supportsTransaction("duckdb")).toBe(false);
expect(supportsTransaction("qdrant")).toBe(false);
expect(supportsTransaction("turso")).toBe(false);
expect(supportsTransaction("cloudflare-d1")).toBe(false);
expect(supportsTransaction("sqlite")).toBe(false);
expect(supportsTransaction("clickhouse")).toBe(false);
expect(supportsTransaction("sqlserver")).toBe(false);
+1 -1
View File
@@ -9,7 +9,7 @@ describe("sqlFormatter", () => {
});
it("maps SQLite-compatible database types to the sqlite formatter dialect", () => {
for (const dbType of ["sqlite", "rqlite", "turso"]) {
for (const dbType of ["sqlite", "rqlite", "turso", "cloudflare-d1"]) {
expect(sqlFormatDialectForDbType(dbType)).toBe("sqlite");
}
});
@@ -0,0 +1,23 @@
import type { ConnectionConfig } from "@/types/database";
type CloudflareD1Config = Pick<ConnectionConfig, "db_type" | "host" | "database" | "password">;
type MutableCloudflareD1Config = CloudflareD1Config & Pick<ConnectionConfig, "port" | "username" | "ssl" | "url_params" | "transport_layers">;
export function isCloudflareD1Connection(config: Pick<ConnectionConfig, "db_type">): boolean {
return config.db_type === "cloudflare-d1";
}
export function hasCloudflareD1Credentials(config: CloudflareD1Config): boolean {
return !!config.host.trim() && !!config.database?.trim() && !!config.password.trim();
}
export function normalizeCloudflareD1Connection(config: MutableCloudflareD1Config): void {
config.host = config.host.trim();
config.database = config.database?.trim() || undefined;
config.password = config.password.trim();
config.port = 443;
config.username = "";
config.ssl = true;
config.url_params = "";
config.transport_layers = [];
}
@@ -17,6 +17,7 @@ export function connectionDriverLabel(connection?: Pick<ConnectionConfig, "db_ty
export function connectionEndpointLabel(connection?: ConnectionPresentationConfig): string {
if (!connection) return "";
if (connection.db_type === "cloudflare-d1") return [connection.host, connection.database].filter(Boolean).join("/");
if (LOCAL_DATABASE_TYPES.has(connection.db_type) || (connection.db_type === "h2" && connection.port === 0)) {
return connection.host || connection.database || "local";
}
@@ -45,6 +46,7 @@ function redactConnectionHost(host: string): string {
export function connectionRedactedEndpointLabel(connection?: ConnectionPresentationConfig): string {
if (!connection) return "";
if (connection.db_type === "cloudflare-d1") return `${REDACTED_HOST_SEGMENT}/${REDACTED_HOST_SEGMENT}`;
if (LOCAL_DATABASE_TYPES.has(connection.db_type) || (connection.db_type === "h2" && connection.port === 0)) {
return connectionEndpointLabel(connection);
}
@@ -111,6 +113,9 @@ export function connectionUrlPlaceholder(dbType: DatabaseType): string {
case "turso":
return "https://[your-db]-[org].turso.io";
case "cloudflare-d1":
return "https://api.cloudflare.com/client/v4/accounts/{account_id}/d1/database/{database_id}";
case "duckdb":
return "duckdb:///absolute/path/to/database.duckdb";
@@ -3,7 +3,7 @@ import { filterDatabaseNamesForVisiblePicker, normalizeVisibleDatabaseSelection
const DRAFT_VISIBLE_DATABASES_PREFIX = "__visible_draft_";
const UNSUPPORTED_VISIBLE_DATABASE_TYPES = new Set<DatabaseType>(["elasticsearch", "qdrant", "milvus", "weaviate", "chromadb", "etcd", "zookeeper"]);
const UNSUPPORTED_VISIBLE_DATABASE_TYPES = new Set<DatabaseType>(["cloudflare-d1", "elasticsearch", "qdrant", "milvus", "weaviate", "chromadb", "etcd", "zookeeper"]);
type VisibleDatabaseConnectionFields = Pick<
ConnectionConfig,
@@ -75,4 +75,4 @@ export const DATABASE_OBJECT_TREE_TYPES = new Set<DatabaseType>(["jdbc"]);
export const PG_LIKE_STRUCTURE_TYPES = new Set<DatabaseType>(["postgres", "redshift", "gaussdb", "kwdb", "opengauss", "questdb"]);
export const DIAGRAM_SQL_TYPES = new Set<DatabaseType>(["mysql", "postgres", "sqlite", "rqlite", "turso", "sqlserver", "oracle", "redshift", "dameng", "gaussdb", "kwdb", "opengauss", "questdb", "oceanbase-oracle"]);
export const DIAGRAM_SQL_TYPES = new Set<DatabaseType>(["mysql", "postgres", "sqlite", "rqlite", "turso", "cloudflare-d1", "sqlserver", "oracle", "redshift", "dameng", "gaussdb", "kwdb", "opengauss", "questdb", "oceanbase-oracle"]);
@@ -118,7 +118,7 @@ export function supportsObjectBrowserTreeNode(dbType: DatabaseType | undefined,
}
export function supportsTableTruncate(dbType?: DatabaseType): boolean {
return !!dbType && dbType !== "sqlite" && dbType !== "rqlite" && dbType !== "turso" && dbType !== "duckdb" && dbType !== "influxdb" && dbType !== "manticoresearch";
return !!dbType && dbType !== "sqlite" && dbType !== "rqlite" && dbType !== "turso" && dbType !== "cloudflare-d1" && dbType !== "duckdb" && dbType !== "influxdb" && dbType !== "manticoresearch";
}
export function usesPostgresLikeStructureCopy(dbType?: DatabaseType): boolean {
@@ -20,6 +20,7 @@ export const DATABASE_NAMESPACE_CREATION_MATRIX = {
sqlite: { deferred: "file-backed; create a new connection/file instead" },
rqlite: { deferred: "single SQLite-compatible database per node" },
turso: { deferred: "remote libSQL database lifecycle is provider-managed" },
"cloudflare-d1": { deferred: "Cloudflare D1 database lifecycle is provider-managed" },
redis: { deferred: "numbered logical databases are server-configured" },
duckdb: { connection: "attach" },
clickhouse: { connection: "database" },
@@ -35,6 +35,7 @@ const DATABASE_TYPE_OBJECTS = new Map<DatabaseType, SidebarObjectKind[]>([
["sqlite", TABLE_VIEW_OBJECTS],
["rqlite", TABLE_VIEW_OBJECTS],
["turso", TABLE_VIEW_OBJECTS],
["cloudflare-d1", TABLE_VIEW_OBJECTS],
["duckdb", TABLE_VIEW_OBJECTS],
["clickhouse", TABLE_VIEW_OBJECTS],
["doris", TABLE_VIEW_OBJECTS],
@@ -21,6 +21,7 @@ export const DATABASE_PROPERTY_EDITING_MATRIX = {
sqlite: { deferred: "file-backed database properties are not edited in-place" },
rqlite: { deferred: "single SQLite-compatible database per node" },
turso: { deferred: "remote libSQL database lifecycle is provider-managed" },
"cloudflare-d1": { deferred: "Cloudflare D1 database lifecycle is provider-managed" },
redis: { deferred: "numbered logical databases are server-configured" },
duckdb: { deferred: "attached database file properties need a dedicated DuckDB workflow" },
clickhouse: { deferred: "database property editing not verified for first pass" },
@@ -52,6 +52,7 @@ const NAVICAT_STYLE_TABLE_DATA_TYPES = new Set<DatabaseType>([
"sqlite",
"rqlite",
"turso",
"cloudflare-d1",
"duckdb",
"sqlserver",
"oracle",
@@ -4,7 +4,8 @@ import { usesTreeSchemaMode } from "@/lib/database/databaseCapabilities";
export const TREE_SCHEMA_DEFAULT_DATABASE_SELECT_VALUE = "__dbx_tree_schema_default_database__";
export const EMPTY_DATABASE_SELECT_VALUE = "__dbx_empty_database__";
export function resolveDefaultDatabase(connection: Pick<ConnectionConfig, "database">, options: string[]): string {
export function resolveDefaultDatabase(connection: Pick<ConnectionConfig, "database"> & Partial<Pick<ConnectionConfig, "db_type">>, options: string[]): string {
if (connection.db_type === "cloudflare-d1") return "main";
return connection.database || options[0] || "";
}
@@ -29,6 +30,7 @@ export function formatDatabaseLabel(connection: Pick<ConnectionConfig, "db_type"
return database || labels.noDatabase;
}
export function isDefaultDatabase(connection: Pick<ConnectionConfig, "database"> | undefined, database: string): boolean {
export function isDefaultDatabase(connection: (Pick<ConnectionConfig, "database"> & Partial<Pick<ConnectionConfig, "db_type">>) | undefined, database: string): boolean {
if (connection?.db_type === "cloudflare-d1") return database === "main";
return !!connection?.database && !!database && connection.database === database;
}
+1
View File
@@ -0,0 +1 @@
export const CLOUDFLARE_D1_COMMON_FUNCTION_NAMES = new Set(["ABS", "AVG", "CAST", "CEIL", "COALESCE", "CONCAT", "COUNT", "FLOOR", "LENGTH", "LOWER", "LTRIM", "MAX", "MIN", "MOD", "NULLIF", "REPLACE", "ROUND", "RTRIM", "SIGN", "SQRT", "SUBSTR", "SUBSTRING", "SUM", "TRIM", "UPPER"]);
@@ -155,6 +155,7 @@ export function sqlSemanticDialectFor(options: { databaseType?: DatabaseType; di
case "sqlite":
case "rqlite":
case "turso":
case "cloudflare-d1":
return SQL_SEMANTIC_DIALECTS.sqlite;
case "duckdb":
return SQL_SEMANTIC_DIALECTS.duckdb;
+7 -1
View File
@@ -1,6 +1,7 @@
import { Cassandra, MariaSQL, MSSQL, MySQL, PLSQL, PostgreSQL, SQLite, StandardSQL } from "@codemirror/lang-sql";
import type { DatabaseType, SqlSnippet } from "@/types/database";
import { buildMongoCompletionItemsFromContext, type MongoCompletionItem } from "@/lib/mongo/mongoCompletion";
import { CLOUDFLARE_D1_COMMON_FUNCTION_NAMES } from "@/lib/sql/cloudflareD1";
const SQL_KEYWORDS = [
"SELECT",
@@ -470,6 +471,7 @@ const DATABASE_SQL_KEYWORDS: Partial<Record<DatabaseType, string[]>> = {
sqlite: SQLITE_SQL_KEYWORDS,
rqlite: SQLITE_SQL_KEYWORDS,
turso: SQLITE_SQL_KEYWORDS,
"cloudflare-d1": SQLITE_SQL_KEYWORDS,
sqlserver: SQLSERVER_SQL_KEYWORDS,
manticoresearch: MANTICORESEARCH_SQL_KEYWORDS,
};
@@ -896,6 +898,8 @@ const SQLITE_FUNCTION_SIGNATURES = new Map<string, string[]>([
["NOW", []],
]);
const CLOUDFLARE_D1_FUNCTION_SIGNATURES = new Map(Array.from(SQLITE_FUNCTION_SIGNATURES.entries()).filter(([name]) => name !== "NOW"));
const SQLSERVER_FUNCTION_SIGNATURES = new Map<string, string[]>([
["TRY_CAST", ["expression AS type"]],
["TRY_CONVERT", ["type", "expression"]],
@@ -958,6 +962,7 @@ const DATABASE_FUNCTION_SIGNATURES: Partial<Record<DatabaseType, Map<string, str
sqlite: SQLITE_FUNCTION_SIGNATURES,
rqlite: SQLITE_FUNCTION_SIGNATURES,
turso: SQLITE_FUNCTION_SIGNATURES,
"cloudflare-d1": CLOUDFLARE_D1_FUNCTION_SIGNATURES,
sqlserver: SQLSERVER_FUNCTION_SIGNATURES,
manticoresearch: MANTICORESEARCH_FUNCTION_SIGNATURES,
};
@@ -3727,7 +3732,8 @@ function buildSnippetItems(prefix: string, snippets: SqlSnippet[], keywordCase?:
}
function activeFunctionSignatures(databaseType?: DatabaseType): Map<string, string[]> {
const signatures = databaseType ? new Map(Array.from(SQL_FUNCTION_SIGNATURES.entries()).filter(([name]) => COMMON_SQL_FUNCTION_NAMES.has(name))) : new Map(SQL_FUNCTION_SIGNATURES);
const commonFunctionNames = databaseType === "cloudflare-d1" ? CLOUDFLARE_D1_COMMON_FUNCTION_NAMES : COMMON_SQL_FUNCTION_NAMES;
const signatures = databaseType ? new Map(Array.from(SQL_FUNCTION_SIGNATURES.entries()).filter(([name]) => commonFunctionNames.has(name))) : new Map(SQL_FUNCTION_SIGNATURES);
const databaseSignatures = databaseType ? DATABASE_FUNCTION_SIGNATURES[databaseType] : undefined;
if (databaseSignatures) {
for (const [name, parameters] of databaseSignatures) signatures.set(name, parameters);
+1
View File
@@ -30,6 +30,7 @@ export function sqlFormatDialectForDbType(dbType: string | null | undefined): Sq
case "sqlite":
case "rqlite":
case "turso":
case "cloudflare-d1":
return "sqlite";
case "sqlserver":
return "sqlserver";
@@ -21,7 +21,7 @@ export function supportsObjectRename(databaseType: DatabaseType | undefined, obj
if (objectType === "PROCEDURE" || objectType === "FUNCTION") {
return false;
}
if (databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "turso") return objectType === "TABLE";
if (databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "turso" || databaseType === "cloudflare-d1") return objectType === "TABLE";
if (databaseType === "mysql" || databaseType === "goldendb") return objectType === "TABLE" || objectType === "VIEW";
if (postgresLikeRenameTypes.has(databaseType)) return objectType === "TABLE" || objectType === "VIEW" || objectType === "MATERIALIZED_VIEW";
if (oracleLikeRenameTypes.has(databaseType)) return objectType === "TABLE" || objectType === "VIEW" || objectType === "MATERIALIZED_VIEW";
+5 -5
View File
@@ -151,29 +151,29 @@ export function importDataTypeForDatabase(inferredType: ImportInferredType, data
case "boolean":
if (["mysql", "doris", "starrocks", "goldendb", "sundb", "databend"].includes(databaseType || "")) return "TINYINT(1)";
if (databaseType === "sqlserver") return "BIT";
if (databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "turso") return "INTEGER";
if (databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "turso" || databaseType === "cloudflare-d1") return "INTEGER";
if (databaseType === "oracle" || databaseType === "oceanbase-oracle" || databaseType === "dameng") return "NUMBER(1)";
if (databaseType === "clickhouse") return "UInt8";
return "BOOLEAN";
case "integer":
if (databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "turso") return "INTEGER";
if (databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "turso" || databaseType === "cloudflare-d1") return "INTEGER";
if (databaseType === "oracle" || databaseType === "oceanbase-oracle" || databaseType === "dameng") return "NUMBER(19)";
if (databaseType === "clickhouse") return "Int64";
return "BIGINT";
case "decimal":
if (["postgres", "gaussdb", "opengauss", "redshift", "kingbase", "highgo", "kwdb", "vastbase"].includes(databaseType || "")) return "DOUBLE PRECISION";
if (databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "turso") return "REAL";
if (databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "turso" || databaseType === "cloudflare-d1") return "REAL";
if (databaseType === "oracle" || databaseType === "oceanbase-oracle" || databaseType === "dameng") return "BINARY_DOUBLE";
if (databaseType === "clickhouse") return "Float64";
return "DOUBLE";
case "date":
if (databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "turso") return "TEXT";
if (databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "turso" || databaseType === "cloudflare-d1") return "TEXT";
if (databaseType === "clickhouse") return "Date";
return "DATE";
case "timestamp":
if (["mysql", "doris", "starrocks", "goldendb", "sundb", "databend"].includes(databaseType || "")) return "DATETIME";
if (databaseType === "sqlserver") return "DATETIME2";
if (databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "turso") return "TEXT";
if (databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "turso" || databaseType === "cloudflare-d1") return "TEXT";
if (databaseType === "clickhouse") return "DateTime64";
return "TIMESTAMP";
case "json":
@@ -129,7 +129,7 @@ function columnPlaceholderValue(column: ColumnInfo, databaseType?: DatabaseType)
const colName = column.name;
if (typeLooksNumeric(dataType)) return "0";
if (typeLooksBoolean(dataType)) return databaseType === "mysql" || databaseType === "sqlite" || databaseType === "rqlite" ? "1" : "TRUE";
if (typeLooksBoolean(dataType)) return databaseType === "mysql" || databaseType === "sqlite" || databaseType === "rqlite" || databaseType === "cloudflare-d1" ? "1" : "TRUE";
if (typeLooksDateOnly(dataType)) return "'2024-01-01'";
if (typeLooksTimeOnly(dataType)) return "'12:00:00'";
if (typeLooksTimestamp(dataType)) return databaseType === "tdengine" ? "NOW" : "CURRENT_TIMESTAMP";
+5 -1
View File
@@ -1692,6 +1692,7 @@ export const useConnectionStore = defineStore("connection", () => {
async function setDefaultDatabase(connectionId: string, database: string) {
const config = getConfig(connectionId);
if (config?.db_type === "cloudflare-d1") return;
if (!config || config.database === database) return;
await updateConnection({
...config,
@@ -1701,6 +1702,7 @@ export const useConnectionStore = defineStore("connection", () => {
async function clearDefaultDatabase(connectionId: string) {
const config = getConfig(connectionId);
if (config?.db_type === "cloudflare-d1") return;
if (!config || !config.database) return;
await updateConnection({
...config,
@@ -1709,7 +1711,9 @@ export const useConnectionStore = defineStore("connection", () => {
}
function isDefaultDatabase(connectionId: string, database: string): boolean {
return getConfig(connectionId)?.database === database && database !== "";
const config = getConfig(connectionId);
if (config?.db_type === "cloudflare-d1") return database === "main";
return config?.database === database && database !== "";
}
async function setVisibleDatabases(connectionId: string, databaseNames: string[]) {
+1
View File
@@ -4,6 +4,7 @@ export type DatabaseType =
| "sqlite"
| "rqlite"
| "turso"
| "cloudflare-d1"
| "redis"
| "duckdb"
| "clickhouse"