Skip to content

Commit 2229188

Browse files
authored
feat: add Cloudflare D1 database driver plugin (#376)
* feat: add Cloudflare D1 database driver plugin (#206) * fix: make D1 plugin registry-distributed, not built-in * ci: add Cloudflare D1 to plugin build workflow * docs: add Cloudflare D1 documentation (en, vi, zh) * fix: address PR review findings for Cloudflare D1 plugin * fix: restore nil param handling, remove unused executeQuery
1 parent 9c08063 commit 2229188

File tree

21 files changed

+3191
-6
lines changed

21 files changed

+3191
-6
lines changed

.github/workflows/build-plugin.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ jobs:
155155
DISPLAY_NAME="Redis Driver"; SUMMARY="Redis in-memory data store driver via hiredis"
156156
DB_TYPE_IDS='["Redis"]'; ICON="redis-icon"; BUNDLE_NAME="RedisDriver"
157157
CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/redis" ;;
158+
cloudflare-d1)
159+
TARGET="CloudflareD1DriverPlugin"; BUNDLE_ID="com.TablePro.CloudflareD1DriverPlugin"
160+
DISPLAY_NAME="Cloudflare D1 Driver"; SUMMARY="Cloudflare D1 serverless SQLite-compatible database driver via REST API"
161+
DB_TYPE_IDS='["Cloudflare D1"]'; ICON="cloudflare-d1-icon"; BUNDLE_NAME="CloudflareD1DriverPlugin"
162+
CATEGORY="database-driver"; HOMEPAGE="https://docs.tablepro.app/databases/cloudflare-d1" ;;
158163
xlsx)
159164
TARGET="XLSXExport"; BUNDLE_ID="com.TablePro.XLSXExportPlugin"
160165
DISPLAY_NAME="XLSX Export"; SUMMARY="Export data to Microsoft Excel XLSX format"
@@ -174,8 +179,8 @@ jobs:
174179
esac
175180
}
176181
177-
PLUGIN_NAME=$(echo "$TAG" | sed -E 's/^plugin-([a-z]+)-v.*$/\1/')
178-
VERSION=$(echo "$TAG" | sed -E 's/^plugin-[a-z]+-v(.*)$/\1/')
182+
PLUGIN_NAME=$(echo "$TAG" | sed -E 's/^plugin-([a-z0-9-]+)-v([0-9].*)$/\1/')
183+
VERSION=$(echo "$TAG" | sed -E 's/^plugin-([a-z0-9-]+)-v([0-9].*)$/\2/')
179184
180185
resolve_plugin_info "$PLUGIN_NAME"
181186

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- Cloudflare D1 database support
1213
- Match highlighting in autocomplete suggestions (matched characters shown in bold)
1314
- Loading spinner in autocomplete popup while fetching column metadata
1415

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//
2+
// CloudflareD1Plugin.swift
3+
// TablePro
4+
//
5+
6+
import Foundation
7+
import os
8+
import TableProPluginKit
9+
10+
final class CloudflareD1Plugin: NSObject, TableProPlugin, DriverPlugin {
11+
static let pluginName = "Cloudflare D1 Driver"
12+
static let pluginVersion = "1.0.0"
13+
static let pluginDescription = "Cloudflare D1 serverless SQLite-compatible database support via REST API"
14+
static let capabilities: [PluginCapability] = [.databaseDriver]
15+
16+
static let databaseTypeId = "Cloudflare D1"
17+
static let databaseDisplayName = "Cloudflare D1"
18+
static let iconName = "cloudflare-d1-icon"
19+
static let defaultPort = 0
20+
21+
// MARK: - UI/Capability Metadata
22+
23+
static let connectionMode: ConnectionMode = .apiOnly
24+
static let supportsSSH = false
25+
static let supportsSSL = false
26+
static let isDownloadable = true
27+
static let supportsImport = false
28+
static let supportsSchemaEditing = false
29+
static let databaseGroupingStrategy: GroupingStrategy = .flat
30+
static let brandColorHex = "#F6821F"
31+
static let urlSchemes: [String] = ["d1"]
32+
33+
static let explainVariants: [ExplainVariant] = [
34+
ExplainVariant(id: "plan", label: "Query Plan", sqlPrefix: "EXPLAIN QUERY PLAN")
35+
]
36+
37+
static let structureColumnFields: [StructureColumnField] = [.name, .type, .nullable, .defaultValue]
38+
39+
static let columnTypesByCategory: [String: [String]] = [
40+
"Integer": ["INTEGER", "INT", "TINYINT", "SMALLINT", "MEDIUMINT", "BIGINT"],
41+
"Float": ["REAL", "DOUBLE", "FLOAT", "NUMERIC", "DECIMAL"],
42+
"String": ["TEXT", "VARCHAR", "CHARACTER", "CHAR", "CLOB", "NVARCHAR", "NCHAR"],
43+
"Date": ["DATE", "TIME", "DATETIME", "TIMESTAMP"],
44+
"Binary": ["BLOB"],
45+
"Boolean": ["BOOLEAN"]
46+
]
47+
48+
static let sqlDialect: SQLDialectDescriptor? = SQLDialectDescriptor(
49+
identifierQuote: "\"",
50+
keywords: [
51+
"SELECT", "FROM", "WHERE", "JOIN", "INNER", "LEFT", "RIGHT", "OUTER", "CROSS",
52+
"ON", "AND", "OR", "NOT", "IN", "LIKE", "GLOB", "BETWEEN", "AS",
53+
"ORDER", "BY", "GROUP", "HAVING", "LIMIT", "OFFSET",
54+
"INSERT", "INTO", "VALUES", "UPDATE", "SET", "DELETE",
55+
"CREATE", "ALTER", "DROP", "TABLE", "INDEX", "VIEW", "TRIGGER",
56+
"PRIMARY", "KEY", "FOREIGN", "REFERENCES", "UNIQUE", "CONSTRAINT",
57+
"ADD", "COLUMN", "RENAME",
58+
"NULL", "IS", "ASC", "DESC", "DISTINCT", "ALL",
59+
"CASE", "WHEN", "THEN", "ELSE", "END", "COALESCE", "IFNULL", "NULLIF",
60+
"UNION", "INTERSECT", "EXCEPT",
61+
"AUTOINCREMENT", "WITHOUT", "ROWID", "PRAGMA",
62+
"REPLACE", "ABORT", "FAIL", "IGNORE", "ROLLBACK",
63+
"TEMP", "TEMPORARY", "VACUUM", "EXPLAIN", "QUERY", "PLAN"
64+
],
65+
functions: [
66+
"COUNT", "SUM", "AVG", "MAX", "MIN", "GROUP_CONCAT", "TOTAL",
67+
"LENGTH", "SUBSTR", "SUBSTRING", "LOWER", "UPPER", "TRIM", "LTRIM", "RTRIM",
68+
"REPLACE", "INSTR", "PRINTF",
69+
"DATE", "TIME", "DATETIME", "JULIANDAY", "STRFTIME",
70+
"ABS", "ROUND", "RANDOM",
71+
"CAST", "TYPEOF",
72+
"COALESCE", "IFNULL", "NULLIF", "HEX", "QUOTE"
73+
],
74+
dataTypes: [
75+
"INTEGER", "REAL", "TEXT", "BLOB", "NUMERIC",
76+
"INT", "TINYINT", "SMALLINT", "MEDIUMINT", "BIGINT",
77+
"UNSIGNED", "BIG", "INT2", "INT8",
78+
"CHARACTER", "VARCHAR", "VARYING", "NCHAR", "NATIVE",
79+
"NVARCHAR", "CLOB",
80+
"DOUBLE", "PRECISION", "FLOAT",
81+
"DECIMAL", "BOOLEAN", "DATE", "DATETIME"
82+
],
83+
tableOptions: [
84+
"WITHOUT ROWID", "STRICT"
85+
],
86+
regexSyntax: .unsupported,
87+
booleanLiteralStyle: .numeric,
88+
likeEscapeStyle: .explicit,
89+
paginationStyle: .limit
90+
)
91+
92+
static let additionalConnectionFields: [ConnectionField] = [
93+
ConnectionField(
94+
id: "cfAccountId",
95+
label: String(localized: "Account ID"),
96+
placeholder: "Cloudflare Account ID",
97+
required: true,
98+
section: .authentication
99+
)
100+
]
101+
102+
func createDriver(config: DriverConnectionConfig) -> any PluginDatabaseDriver {
103+
CloudflareD1PluginDriver(config: config)
104+
}
105+
}

0 commit comments

Comments
 (0)