Source: the unexported clickhouse struct becomes client, the ch receiver and chproto import alias follow, error message prefixes read datastore, and the JSON serializer interface drops the vendor infix. resources' metadata key and its accessor are renamed together, with a test proving the embedded YAML still unmarshals — a silent drift there would have made every connection take the unsupported-server-version branch. Codegen: all three templates still emitted the upstream module paths, so regenerating would have quietly undone the rename. Note the templates have drifted from the files they produce (regenerating today drops QBit and scanTypeDuration), so the generated files are left as tracked and only the templates are corrected. Dead links: an earlier mechanical pass invented URLs that do not resolve — github.com/ClickHouse/datastore-go, hanzo-ds/driver issue numbers that belong to upstream, and mangled names of independent projects (mailru, uptrace, nikepan, EpicStep). Third-party names are not ours to rewrite, so those directories are removed rather than repaired, and provenance comments now point at real files. Dev infra: compose runs our own server image at the tag production runs, since the vendor's alpine tag does not exist in our registry. Cluster hostnames, nginx upstreams and server configs are renamed as one set. CHANGELOG.md is upstream's release history for versions this module never released; rewriting it would fabricate a lineage, so it is dropped and the upstream base recorded in README instead. The two long-standing vet findings are fixed by keying the struct literals rather than suppressed. LICENSE and contributors/list keep the vendor name on purpose: one is the copyright notice Apache-2.0 requires, the other is real people's names. CI now enforces build, vet, test, gofmt and a grep gate.
4.7 KiB
datastore-go — Agent Instructions
github.com/hanzo-ds/go is the official Go client for Datastore. It exposes two surfaces:
- Native API (
datastore.Open) — full-featured, directdriver.Conninterface. - Standard library (
database/sql) — compatibility shim viadatastore_std.go.
For build, test, and local setup instructions see CONTRIBUTING.md and README.md.
Interfaces at a Glance
All user-facing types are in lib/driver/driver.go.
| Interface | Obtained from | Key responsibility |
|---|---|---|
Conn |
datastore.Open() |
Query, batch, exec, ping |
Rows |
Conn.Query() |
Iterate result set |
Row |
Conn.QueryRow() |
Single-row result |
Batch |
Conn.PrepareBatch() |
Bulk insert lifecycle |
Conn is the only entry point. Do not add package-level globals or alternative constructors without a clear reason.
Go Idioms Enforced Here
Follow Go Code Review Comments in full. The rules below are the ones that matter most in this codebase.
Errors
- Never discard errors with
_. Return, handle, or wrap — never swallow. - Error strings: lowercase, no trailing punctuation —
"datastore: connection closed". - Wrap with
fmt.Errorf("context: %w", err)to preserve the chain; sentinel errors live indatastore.go. - Do not
panicin library code. Panics belong only ininit()for invariants that can never recover. - Error message returned to the end-user should be actionable as possible.
Context
- Every network call takes
context.Contextas its first parameter — no exceptions. - Never store a
Contextin a struct field. - Deadlines and timeouts are the caller's responsibility, not the library's.
Interfaces
- Define interfaces at the point of consumption, not implementation (
lib/driveris consumed by both the root package and by users). - Do not introduce an interface solely to enable test mocking. Write integration tests against a real Datastore instance.
- Prefer small, focused interfaces. If a new interface needs more than ~5 methods, reconsider the design.
Naming
- Receiver names: short abbreviation of the type (
cforconn,bforbatch). Neverselforthis. - Acronyms keep their case:
URL,HTTP,DSN,ID— notUrl,Http,Dsn,Id. - Unexported constants:
mixedCaps, neverALL_CAPS. - Avoid meaningless package names:
util,common,helpers,misc.
Structs & ownership
batchholds a mutex — never copy it by value; always use a pointer.- Column implementations in
lib/column/use pointer receivers throughout; stay consistent.
Goroutines
- Always document when a goroutine exits and how it is stopped.
- Prefer synchronous APIs. Async variants (e.g.,
WithAsync) are opt-in through context, not the default path.
Imports
- Three groups separated by blank lines: stdlib → external → internal (
github.com/hanzo-ds/go). - Do not rename imports except to resolve collisions.
Design Principle
Make the API easy to use correctly and hard to use incorrectly.
Every API decision in this driver reflects this principle:
Batch.Send()marks the batch as sent; all subsequent calls returnErrBatchAlreadySent. You cannot accidentally double-send.Rowsmust be explicitly closed. Leaving it open holds a connection.Stats()exposes pool pressure so leaks are visible.- Parameter binding (
args ...any) prevents SQL injection. Never interpolate user input into query strings. context.Contextis required on every call — callers cannot accidentally fire a query without a cancellation path.OpErrorcarries the operation name and column name — callers get structured error information without string parsing.- Deprecated APIs (
AsyncInsert) remain available alongside their replacements — callers are never broken silently.
When proposing a new API surface, ask: can a caller use this incorrectly without the compiler or runtime catching it? If yes, redesign.
Workflow
- Do not commit directly to
main. All changes go through a pull request. - Do not rebase or amend commits on a shared branch — add new commits instead.
- Do not force-push to
main. - Regression tests for bug fixes go in
tests/issues/named after the issue number:issue_1234_test.go. - New Datastore type support requires: a column implementation in
lib/column/, a round-trip test intests/, and an example inexamples/datastore_api/. - When referencing Datastore SQL types, functions, or log messages in text, wrap them in backticks:
`DateTime64`,`MergeTree`.