Skip to content

Commit ef9332c

Browse files
authored
Merge pull request #737 from Ewan-Keith/add-databricks-support
Add databricks support
2 parents 174ce55 + 4e025ca commit ef9332c

30 files changed

+3266
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
tbls
22
tbls.exe
3+
tbls.yml
34
dist/
45
dbdoc/
56
coverage.out

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ db_sqlite:
3737
sqlite3 $(PWD)/testdata/testdb.sqlite3 < testdata/ddl/sqlite.sql
3838

3939
test:
40-
go test ./... -tags 'bq clickhouse dynamo mariadb mongodb mssql mysql postgres redshift snowflake spanner sqlite' -coverprofile=coverage.out -covermode=count
40+
go test ./... -tags 'bq clickhouse databricks dynamo mariadb mongodb mssql mysql postgres redshift snowflake spanner sqlite' -coverprofile=coverage.out -covermode=count
4141

4242
test-no-db:
4343
go test ./... -coverprofile=coverage.out -covermode=count

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,32 @@ dsn: clickhouse://dbuser:dbpass@hostname:9000/dbname
607607

608608
See also: https://pkg.go.dev/github.com/ClickHouse/clickhouse-go
609609

610+
**Databricks (Experimental):**
611+
612+
Personal Access Token (PAT) Authentication:
613+
```yaml
614+
# .tbls.yml
615+
dsn: databricks://your_databricks_workspace_id.cloud.databricks.com:443/sql/1.0/warehouses/your_warehouse_id?catalog=your_catalog&schema=your_schema&token=your_token
616+
```
617+
618+
OAuth Client Credentials Authentication:
619+
```yaml
620+
# .tbls.yml
621+
dsn: databricks://your_databricks_workspace_id.cloud.databricks.com:443/sql/1.0/warehouses/your_warehouse_id?catalog=your_catalog&schema=your_schema&client_id=your_client_id&client_secret=your_client_secret
622+
```
623+
624+
Required parameters:
625+
- `your_databricks_workspace_id`: databricks workspace ID
626+
- `your_warehouse_id`: databricks sql warehouse ID
627+
- `your_catalog`: Unity Catalog name
628+
- `your_schema`: Schema/database name within the catalog
629+
630+
Authentication (choose one):
631+
- PAT Authentication: `token` - Personal access token for authentication
632+
- OAuth Authentication: `client_id` and `client_secret` - OAuth M2M client credentials
633+
634+
See also: https://pkg.go.dev/github.com/databricks/databricks-sql-go
635+
610636
**JSON:**
611637

612638
The JSON file output by the `tbls out -t json` command can be read as a datasource (JSON Schema is [here](spec/tbls.schema.json_schema.json)).

datasource/databricks.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package datasource
2+
3+
import (
4+
"database/sql"
5+
"errors"
6+
"fmt"
7+
"net/url"
8+
9+
"github.com/k1LoW/tbls/drivers/databricks"
10+
"github.com/k1LoW/tbls/schema"
11+
)
12+
13+
// AnalyzeDatabricks analyze `databricks://`
14+
func AnalyzeDatabricks(urlstr string) (_ *schema.Schema, err error) {
15+
s := &schema.Schema{}
16+
17+
u, err := url.Parse(urlstr)
18+
if err != nil {
19+
return nil, err
20+
}
21+
22+
catalog := u.Query().Get("catalog")
23+
if catalog == "" {
24+
return nil, errors.New("no catalog name in the connection string")
25+
}
26+
schemaName := u.Query().Get("schema")
27+
if schemaName == "" {
28+
return nil, errors.New("no schema name in the connection string")
29+
}
30+
31+
// Extract authentication parameters
32+
token := u.Query().Get("token")
33+
clientID := u.Query().Get("client_id")
34+
clientSecret := u.Query().Get("client_secret")
35+
36+
// Validate authentication parameter combinations
37+
hasToken := token != ""
38+
hasOAuth := clientID != "" && clientSecret != ""
39+
40+
if !hasToken && !hasOAuth {
41+
return nil, errors.New("authentication required: provide either 'token' for PAT authentication or both 'client_id' and 'client_secret' for OAuth authentication")
42+
}
43+
44+
if hasToken && hasOAuth {
45+
return nil, errors.New("conflicting authentication methods: provide either 'token' for PAT authentication OR 'client_id'/'client_secret' for OAuth authentication, not both")
46+
}
47+
48+
if clientID != "" && clientSecret == "" {
49+
return nil, errors.New("incomplete OAuth credentials: 'client_secret' is required when 'client_id' is provided")
50+
}
51+
52+
if clientSecret != "" && clientID == "" {
53+
return nil, errors.New("incomplete OAuth credentials: 'client_id' is required when 'client_secret' is provided")
54+
}
55+
56+
s.Name = fmt.Sprintf("%s.%s", catalog, schemaName)
57+
58+
// Build databricks driver DSN based on authentication method
59+
var databricksDSN string
60+
if hasToken {
61+
// PAT token authentication: token:TOKEN@host:port/path?catalog=CATALOG&schema=SCHEMA
62+
databricksDSN = fmt.Sprintf("token:%s@%s%s?catalog=%s&schema=%s", token, u.Host, u.Path, catalog, schemaName)
63+
} else {
64+
// OAuth client credentials authentication: host:port/path?authType=OauthM2M&clientID=ID&clientSecret=SECRET&catalog=CATALOG&schema=SCHEMA
65+
databricksDSN = fmt.Sprintf("%s%s?authType=OauthM2M&clientID=%s&clientSecret=%s&catalog=%s&schema=%s",
66+
u.Host, u.Path, clientID, clientSecret, catalog, schemaName)
67+
}
68+
69+
db, err := sql.Open("databricks", databricksDSN)
70+
if err != nil {
71+
return nil, err
72+
}
73+
defer func() {
74+
_ = db.Close()
75+
}()
76+
77+
driver := databricks.New(db)
78+
if err := driver.Analyze(s); err != nil {
79+
return nil, err
80+
}
81+
82+
return s, nil
83+
}

datasource/datasource.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ func Analyze(dsn config.DSN) (_ *schema.Schema, err error) {
6868
if strings.HasPrefix(urlstr, "mongodb://") || strings.HasPrefix(urlstr, "mongo://") {
6969
return AnalyzeMongodb(urlstr)
7070
}
71+
if strings.HasPrefix(urlstr, "databricks://") {
72+
return AnalyzeDatabricks(urlstr)
73+
}
7174
s := &schema.Schema{}
7275
u, err := dburl.Parse(urlstr)
7376
if err != nil || !slices.Contains(supportDriversWithDburl, u.Driver) {

0 commit comments

Comments
 (0)