Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,17 @@ _tmp*
__debug_bin
.DS_Store

# Kernel (SEA) backend build artifacts — produced by `make kernel-lib`, never committed.
# Kernel (SEA) backend build artifacts.
#
# The distribution model (see README): this repo commits only the small,
# platform-independent C header (internal/backend/kernel/include/). The prebuilt
# per-platform archives live in the SEPARATE github.com/databricks/databricks-sql-kernel-bindings
# repo (one nested Go module per platform), which this driver requires in go.mod
# — so a kernel opt-in `go get` build needs no build step. The committed header
# is intentionally NOT ignored.
#
# The paths below remain ignored: they are local scratch dirs used only by
# `make kernel-lib` for source builds against a kernel checkout (dev/CI), and are
# never committed — the shipped archives come from the bindings modules.
/build/kernel-src/
/internal/backend/kernel/lib/
/internal/backend/kernel/include/
2 changes: 1 addition & 1 deletion KERNEL_REV
Original file line number Diff line number Diff line change
@@ -1 +1 @@
eff8950428f4e6cc9975c663ec919f334962f7d0
c403bfb8ff2361b5919ced1a45feb4cf3b63ab60
25 changes: 16 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,21 @@ KERNEL_INC_DIR = internal/backend/kernel/include
KERNEL_GO = CGO_ENABLED=1 go
KERNEL_TAGS = -tags databricks_kernel

# SHIPPED PATH (consumers): the kernel archives are NOT built here. They come from
# the external per-platform modules in github.com/databricks/databricks-sql-kernel-bindings
# that this repo requires in go.mod; a kernel opt-in `go get` build downloads only
# the target platform's archive (no Rust, no build step). The targets below are
# for LOCAL DEVELOPMENT against a kernel source checkout only.
#
# Local-dev flow: `make kernel-lib` builds the host-platform archive from the
# pinned KERNEL_REV into $(KERNEL_LIB_DIR) (a .gitignore'd scratch dir). To have a
# `-tags databricks_kernel` build actually LINK that freshly built archive instead
# of the published bindings module, point the matching lib/<platform> module at a
# local bindings checkout via a go.work whose lib/<platform>/ holds the built .a
# (see docs/RELEASING.md). TODO(dev-loop): wire this go.work step into the targets
# so `make test-kernel` links the local build end-to-end without manual setup.
.PHONY: kernel-lib
kernel-lib: ## Build the pinned kernel static lib + header into the cgo link dir (source build).
kernel-lib: ## Build the pinned kernel static lib + header locally (source build, dev only).
KERNEL_REPO="$(KERNEL_REPO)" KERNEL_REV="$(KERNEL_REV)" KERNEL_SRC="$(KERNEL_SRC)" \
KERNEL_LIB_DIR="$(KERNEL_LIB_DIR)" KERNEL_INC_DIR="$(KERNEL_INC_DIR)" \
KERNEL_GOOS="$(KERNEL_GOOS)" KERNEL_GOARCH="$(KERNEL_GOARCH)" \
Expand All @@ -112,15 +125,9 @@ kernel-lib: ## Build the pinned kernel static lib + header into the cgo link di
./build/kernel-lib.sh

.PHONY: build-kernel
build-kernel: kernel-lib ## Build the driver with the kernel backend linked.
build-kernel: ## Build the driver with the kernel backend linked (against the bindings modules).
$(KERNEL_GO) build $(KERNEL_TAGS) ./...

.PHONY: test-kernel
test-kernel: kernel-lib ## Run the kernel-tagged unit tests (no warehouse needed).
test-kernel: ## Run the kernel-tagged unit tests (no warehouse needed; links the bindings modules).
$(KERNEL_GO) test $(KERNEL_TAGS) ./...

.PHONY: kernel-lib-download
kernel-lib-download: ## Prod mode (download prebuilt .a): blocked until the kernel publishes release artifacts.
@echo "kernel-lib-download: blocked — the kernel does not yet publish per-platform .a release artifacts."
@echo "Use 'make kernel-lib' (source build) meanwhile. See the distribution design doc."
@false
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ standard `database/sql` interface.
## Contents

- [Quick start](#quick-start)
- [Cloning the repository](#cloning-the-repository)
- [Choosing a backend (Thrift vs SEA/kernel)](#choosing-a-backend-thrift-vs-seakernel)
- [Building](#building)
- [Connecting](#connecting)
Expand Down Expand Up @@ -46,6 +47,43 @@ defer rows.Close()
See [`doc.go`](./doc.go) for full package documentation or the Databricks documentation
for the [SQL Driver for Go](https://docs.databricks.com/dev-tools/go-sql-driver.html).

> **Using the driver in your own project?** You never clone this repository — you
> add it with `go get github.com/databricks/databricks-sql-go` and `go build`.
> `go get` fetches per-version module archives, not git history, and for a
> default Thrift build it pulls **no** kernel binaries at all. The guidance below
> is only for people who `git clone` this repo directly (contributors / CI).

## Cloning the repository

This driver repo itself is **small**: for the SEA/kernel backend it commits only
the platform-independent C header
(`internal/backend/kernel/include/databricks_kernel.h`). The **prebuilt kernel
binaries** (per-platform `libdatabricks_sql_kernel.a`, ~60–95 MB each) live in a
**separate** repository,
[`databricks-sql-kernel-bindings`](https://github.com/databricks/databricks-sql-kernel-bindings),
one nested Go module per platform. This driver `require`s those modules, so the
SEA/kernel backend works straight from `go get` with **no build step** (see
[SEA/kernel](#seakernel--cgo--a-linked-rust-static-library); for how the archives
are versioned and published, see [docs/RELEASING.md](./docs/RELEASING.md)). A
consumer's `go get` pulls only the **target platform's** archive at the
driver-pinned version — never all platforms.

A plain `git clone` of *this* repo is therefore cheap. The partial/sparse-clone
guidance matters instead for the **bindings** repo, whose committed archives
(which git cannot delta-compress) accumulate across releases:

```bash
# Cheap history + only your platform's archive materialized:
git clone --filter=blob:none --sparse https://github.com/databricks/databricks-sql-kernel-bindings
cd databricks-sql-kernel-bindings
git sparse-checkout set --no-cone '/*' '!/lib' \
'lib/darwin_arm64' # keep only your platform
```

`--filter=blob:none` fetches commits and trees immediately and pulls file blobs
lazily, keeping `.git` small; GitHub serves it by default. CI checkouts of the
bindings repo use `--filter=blob:none` for the same reason.

## Choosing a backend (Thrift vs SEA/kernel)

The driver has **two execution backends**, selected once per connection:
Expand Down
142 changes: 142 additions & 0 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Releasing the driver (with the kernel/SEA backend)

The kernel/SEA backend is delivered as **prebuilt, per-platform static archives**
carried by `go get` — no `make kernel-lib` step for consumers, no Rust toolchain.
The archives do **not** live in this repo: they live in the separate

```
github.com/databricks/databricks-sql-kernel-bindings
```

repository, which this driver `require`s. This document explains how the archives
are versioned and published so that a
`go get github.com/databricks/databricks-sql-go@vX.Y.Z` resolves the matching
kernel archives automatically, and how to cut a coordinated release across the
two repos.

## The module layout

The archives are distributed as nested Go modules in the **bindings** repo:

```
github.com/databricks/databricks-sql-kernel-bindings (root module)
├── include/databricks_kernel.h (C header, at the pinned kernel rev)
├── prebuilt_<os>_<arch>.go (root shim: blank-imports lib/<platform>)
└── lib/<os>_<arch>/ (one NESTED module per platform)
├── go.mod → github.com/databricks/databricks-sql-kernel-bindings/lib/<os>_<arch>
├── prebuilt.go → //go:build cgo && <os> && <arch> (+ #cgo LDFLAGS)
└── libdatabricks_sql_kernel.a (the committed prebuilt archive for this platform)
```

Each `lib/<platform>` directory is **its own Go module**. This is deliberate: Go
downloads a module's zip only when a build compiles a file from it, and each
`prebuilt.go` is build-tag-gated to one platform. In THIS driver, the
`internal/backend/kernel/cgo_<os>[_<arch>].go` files blank-import the matching
`lib/<platform>` module under the same constraint. So:

- a **Thrift build** (`CGO_ENABLED=0`, no tag) downloads **none** of them;
- a **kernel build** for, say, darwin/arm64 downloads **only** the
`lib/darwin_arm64` module's zip — never the other platforms' archives. The
other four appear in `go.mod`/`go.sum` (their tiny `go.mod` hashes) but their
multi-megabyte zips are never fetched.

The driver owns the cgo call layer (`internal/backend/kernel/cgo.go` does
`import "C"` and `#include "databricks_kernel.h"`), so the C **header is committed
in this repo** at `internal/backend/kernel/include/databricks_kernel.h`. The
bindings modules provide only the archives and their `#cgo LDFLAGS`. The header
here and the archives there MUST come from the **same kernel revision** — the one
recorded in the repo-root `KERNEL_REV` file (the bindings repo records the same
rev in its release notes/commit).

## How versioning works

The driver's `go.mod` `require`s each platform module at a **real version**, with
**no `replace`** (the modules are external):

```
require (
github.com/databricks/databricks-sql-kernel-bindings/lib/darwin_amd64 vX.Y.Z
github.com/databricks/databricks-sql-kernel-bindings/lib/darwin_arm64 vX.Y.Z
github.com/databricks/databricks-sql-kernel-bindings/lib/linux_amd64 vX.Y.Z
github.com/databricks/databricks-sql-kernel-bindings/lib/linux_arm64 vX.Y.Z
github.com/databricks/databricks-sql-kernel-bindings/lib/windows_amd64 vX.Y.Z
)
```

- The **`require` version pins the kernel.** `go get .../databricks-sql-go@vX.Y.Z`
reads that tag's `go.mod`, sees the pinned bindings versions, and resolves those
exact archive versions from the module proxy. **Upgrading the driver is what
moves the kernel version** — deterministic, per-driver-version pinning.
- **Local development** against a checkout of the bindings repo uses a `go.work`
(or a temporary `replace`), never a committed `replace` — so a downstream
`go get` always resolves the published versions from the proxy.

> The bindings repo is a **private** repo today. External `go get` from the public
> proxy requires it to be made **public** (OSS review + third-party NOTICE). Until
> then, internal builds fetch it with `GOPRIVATE=github.com/databricks/*` over
> authenticated git.

## Publishing: path-prefixed tags (in the bindings repo)

Go publishes a nested module using a **tag whose name is the module's
subdirectory path plus the version**. To release all platforms at `vX.Y.Z` in the
bindings repo:

```
git tag lib/darwin_amd64/vX.Y.Z
git tag lib/darwin_arm64/vX.Y.Z
git tag lib/linux_amd64/vX.Y.Z
git tag lib/linux_arm64/vX.Y.Z
git tag lib/windows_amd64/vX.Y.Z
git tag vX.Y.Z # the bindings root module
git push origin --tags
```

> Module versions are **immutable** once a public proxy has served them. While the
> repo is private and un-proxied you can re-cut a tag; once public, bump to a new
> version instead of moving a tag.

## Release steps (coordinated, kernel → bindings → driver)

1. **Pick the kernel revision** and set it in this repo's `KERNEL_REV`. Sync the
committed header here (`internal/backend/kernel/include/databricks_kernel.h`)
to that exact kernel commit's `include/databricks_kernel.h`.
2. **Build the archives** for all five platforms at that revision — via the kernel
repo's `build-c-abi-libs` workflow (native `linux_amd64` + `windows_amd64`
today; see databricks-sql-kernel#244) and/or a local cross-build for the
platforms CI cannot yet produce (`darwin_*`, `linux_arm64`). Build flags:
`cargo build --release --locked --no-default-features --features tls-rustls
--target <triple>`; set `MACOSX_DEPLOYMENT_TARGET` for the darwin targets so
the archive links cleanly on older macOS. Windows must be the **`-gnu`** triple
(Go cgo links a GNU `.a`, never an MSVC `.lib`).
3. **Commit the archives** into `lib/<platform>/libdatabricks_sql_kernel.a` in the
bindings repo, and sync that repo's `include/databricks_kernel.h` to the same
kernel rev.
4. **Tag the bindings repo** with the path-prefixed tags above plus the root tag.
5. **Bump the `require` versions** in this driver's `go.mod` to the new bindings
version.
6. **Refresh `go.sum`:** `GOFLAGS=-mod=mod GOPRIVATE=github.com/databricks/* go mod tidy`
so the new per-platform module checksums land in `go.sum`. Consumers verify
against these.
7. **Tag the driver** `vX.Y.Z` and push. `go get @vX.Y.Z` now resolves the driver
and, transitively, the matching per-platform kernel archives.

## Adding a new platform

1. In the **bindings** repo: create `lib/<os>_<arch>/` with its own `go.mod`, a
build-tag-gated `prebuilt.go` (matching `//go:build` + `#cgo LDFLAGS`), and the
committed archive; add a root `prebuilt_<os>_<arch>.go` shim; tag it alongside
the others.
2. In this **driver** repo: add a `require` for the new module in `go.mod`, add a
build-tagged `internal/backend/kernel/cgo_<os>_<arch>.go` that blank-imports it,
and drop the platform from the exclusion list in `cgo_unsupported.go`.

## Consumer experience (for reference)

- **Thrift (default):** `go get ...` + `go build` — pure Go, no cgo, no archive
downloaded.
- **Kernel/SEA:** `go get ...` + `CGO_ENABLED=1 go build -tags databricks_kernel`
— pulls only the target platform's archive at the driver-pinned version; no
`make kernel-lib`, no Rust.
- **Cloning the bindings repo directly** (contributors/CI): use
`git clone --filter=blob:none` to skip the committed-archive history.
19 changes: 19 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,22 @@ require (
github.com/rs/zerolog v1.28.0
golang.org/x/sys v0.45.0 // indirect
)

// Per-platform kernel library modules, distributed by the separate
// github.com/databricks/databricks-sql-kernel-bindings repo (one nested Go
// module per platform, lib/<os>_<arch>). Each carries that platform's prebuilt
// kernel static archive + its cgo link directive; a kernel build downloads only
// the archive for the platform it targets (build-tag gated), and a pure-Go
// Thrift build downloads none of them.
//
// The versions are pinned in lockstep with the driver release. A consumer's
// `go get github.com/databricks/databricks-sql-go@vX.Y.Z` transitively pins the
// matching per-platform kernel archive; upgrading the driver moves the kernel
// version. See docs/RELEASING.md and the bindings repo README.
require (
github.com/databricks/databricks-sql-kernel-bindings/lib/darwin_amd64 v0.1.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium — These five modules are now direct requirements of the driver, so Go must resolve each one's go.mod to compute the build list for any consumer build — including the default Thrift CUJ (CGO_ENABLED=0, no build tag). The build-tag gating on the blank imports (cgo_*.go) only prevents fetching the multi-megabyte archive zips; it does not exempt these modules from module-graph resolution. A fresh consumer with an empty module cache still fetches the (tiny) go.mod files and verifies them against go.sum.

Consequence: while databricks-sql-kernel-bindings remains private (as docs/RELEASING.md notes), an external go get github.com/databricks/databricks-sql-go@<this version> followed by go build will fail for Thrift-only consumers too — not just the kernel opt-in — because the proxy cannot serve the private go.mod files. This contradicts the README's framing that the default Thrift build is "completely unchanged" / pulls "no kernel binaries at all": no binaries, correct, but a new hard module-graph dependency on a currently-unreachable repo now gates every consumer.

Suggest: (a) do not tag a public driver release with these requires until the bindings repo is public, and (b) soften the README claim to make clear the go.mod dependency applies to all consumers, so the gating condition isn't buried in RELEASING.md.

github.com/databricks/databricks-sql-kernel-bindings/lib/darwin_arm64 v0.1.0
github.com/databricks/databricks-sql-kernel-bindings/lib/linux_amd64 v0.1.0
github.com/databricks/databricks-sql-kernel-bindings/lib/linux_arm64 v0.1.0
github.com/databricks/databricks-sql-kernel-bindings/lib/windows_amd64 v0.1.0
)
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ github.com/coreos/go-oidc/v3 v3.5.0 h1:VxKtbccHZxs8juq7RdJntSqtXFtde9YpNpGn0yqgE
github.com/coreos/go-oidc/v3 v3.5.0/go.mod h1:ecXRtV4romGPeO6ieExAsUK9cb/3fp9hXNz1tlv8PIM=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/databricks/databricks-sql-kernel-bindings/lib/darwin_amd64 v0.1.0 h1:zbVSYVi+Rn5QmIIEnqYH6YcBLIbxbiCHVDZOroKMZ6Q=
github.com/databricks/databricks-sql-kernel-bindings/lib/darwin_amd64 v0.1.0/go.mod h1:ceyJVgJAbNmkwc/2YMUNGuw2gd8nHZwZhP2xpN84VBg=
github.com/databricks/databricks-sql-kernel-bindings/lib/darwin_arm64 v0.1.0 h1:osBt1aVUtvPi1150e1tL4x/R7yQ1ALVFQToPCZhyb9Y=
github.com/databricks/databricks-sql-kernel-bindings/lib/darwin_arm64 v0.1.0/go.mod h1:Cr0N6/u4qDUvdbtqm4sVflN4ZXO8qdDcxEBhF5WB9PI=
github.com/databricks/databricks-sql-kernel-bindings/lib/linux_amd64 v0.1.0 h1:4yb4vqV3y/onUIa1I3WojufFlpg57izM0eg0tkpjJDo=
github.com/databricks/databricks-sql-kernel-bindings/lib/linux_amd64 v0.1.0/go.mod h1:KqeZQ/C/GoKAqoGL97/KT3BwLoqsxgSl3qJjje1F7TU=
github.com/databricks/databricks-sql-kernel-bindings/lib/linux_arm64 v0.1.0 h1:iXviBG39/hpzxlnUWl3W2LiwbIs57lLkU30JmH2+5m4=
github.com/databricks/databricks-sql-kernel-bindings/lib/linux_arm64 v0.1.0/go.mod h1:/1vEJKUPnrWx566kI/Ifl00GrhSyp798Ztl6rjtymCI=
github.com/databricks/databricks-sql-kernel-bindings/lib/windows_amd64 v0.1.0 h1:s9yEiNwNFeSE2DAaxHEFd6Z89xS+E5OcdKehDpPyz/Q=
github.com/databricks/databricks-sql-kernel-bindings/lib/windows_amd64 v0.1.0/go.mod h1:OaUVov84uK1IJz1gNk5TIoae0rj0vx+4N1qqRT88MdU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
28 changes: 15 additions & 13 deletions internal/backend/kernel/cgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@
// the error mapping to the driver's error surface, and the gated step logger.
// The backend, operation, and rows layers live in sibling files.
//
// Link contract (${SRCDIR}-relative, machine-independent). The header is
// included from ${SRCDIR}/include and the static lib is linked from
// ${SRCDIR}/lib/<os>_<arch>; the per-platform link flags live in the
// cgo_<os>.go files beside this one. Both directories are produced by the
// build step (`make kernel-lib`), which checks out the kernel at the commit
// pinned in the repo-root KERNEL_REV file and `cargo build`s a static lib —
// so the kernel revision is a reviewable pin, never baked into a #cgo line
// (those expand only ${SRCDIR} and cannot run git or read env). The dirs are
// .gitignore'd; nothing kernel-built is committed. For local development
// against an existing checkout, `make kernel-lib KERNEL_LOCAL_A=<path/to>.a
// KERNEL_LOCAL_HEADER=<path/to>databricks_kernel.h` copies those in instead of
// building. The eventual release path downloads a published .a at the pinned
// rev rather than building it (see the driver's distribution design).
// Link contract. This file owns the compile side: the C header is committed at
// ${SRCDIR}/include/databricks_kernel.h and pulled in via the #cgo CFLAGS below.
// The link side lives OUT of this repo: each cgo_<os>[_<arch>].go beside this
// file blank-imports the matching per-platform module from the separate
// github.com/databricks/databricks-sql-kernel-bindings repo, and cgo collects
// THAT module's `#cgo LDFLAGS` at final link time to pull in the prebuilt
// libdatabricks_sql_kernel.a. So a kernel opt-in `go get` build downloads only
// the archive for the platform it targets (build-tag gated) — no Rust toolchain
// and no build step. The committed header and the prebuilt archives are produced
// from the same pinned kernel revision (repo-root KERNEL_REV; the bindings repo
// records the same rev), which is the reviewable pin — never baked into a #cgo
// line. For local development against a kernel checkout, `make kernel-lib` builds
// the archive from source into the .gitignore'd internal/backend/kernel/lib/
// scratch dir (selected via a go.work / replace pointing at that path); the
// committed bindings modules are what ship.
package kernel

/*
Expand Down
Loading
Loading