client

package
v0.5.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 4, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package client is a small Go client for the quicSQL native-JSON API (POST /<db>/query). One constructor per transport — H1, H2C, H2TLS, H3, Unix — returns a *Client that speaks that wire; the SQL surface (Query/Exec) is identical across them, which is what the cross-protocol benchmark relies on.

It is intentionally thin (no connection pooling knobs, no Hrana). For interactive transactions use the Hrana pipeline endpoint directly, or a libSQL client library.

Index

Constants

View Source
const DefaultMaxResponse int64 = 1 << 30

DefaultMaxResponse is the client-side ceiling on a single server-supplied response body (1 GiB). It bounds a client allocation a hostile/buggy server could otherwise force; raise it with WithMaxResponse for genuinely large blobs or exports, or pass WithMaxResponse(0) to disable the cap entirely.

Variables

This section is empty.

Functions

This section is empty.

Types

type AdminDatabase

type AdminDatabase struct {
	Name string `json:"name"`
	Kind string `json:"kind"`
	Open bool   `json:"open"`
	Refs int    `json:"refs"`
}

AdminDatabase is one entry from the control plane's database listing.

type AdminInfo

type AdminInfo struct {
	UptimeSeconds  int64  `json:"uptime_seconds"`
	Goroutines     int    `json:"goroutines"`
	HeapBytes      uint64 `json:"heap_bytes"`
	Databases      int    `json:"databases"`
	OpenDatabases  int    `json:"open_databases"`
	ActiveSessions int    `json:"active_sessions"`
}

AdminInfo is the server-wide runtime snapshot (server-admin only).

type AdminSession

type AdminSession struct {
	ID          string `json:"id"`
	Database    string `json:"database"`
	Principal   string `json:"principal"`
	ReadOnly    bool   `json:"read_only"`
	InFlight    bool   `json:"in_flight"`
	AgeSeconds  int64  `json:"age_seconds"`
	IdleSeconds int64  `json:"idle_seconds"`
}

AdminSession is one live interactive-transaction session.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client talks to one quicSQL server over one transport, presenting at most one credential (bearer / basic / mTLS client cert / ed25519 challenge-response).

func H1

func H1(addr string, opts ...Option) *Client

H1 talks plain HTTP/1.1 to addr (host:port).

func H2C

func H2C(addr string, opts ...Option) *Client

H2C talks cleartext HTTP/2 (prior knowledge) to addr (host:port).

func H2TLS

func H2TLS(addr string, insecure bool, opts ...Option) *Client

H2TLS talks HTTP/2 over TLS to addr; insecure skips certificate verification (for the dev self-signed cert). WithClientCert enables mTLS.

func H3

func H3(addr string, insecure bool, opts ...Option) *Client

H3 talks HTTP/3 over QUIC to addr; insecure skips certificate verification. WithClientCert enables mTLS.

func Unix

func Unix(socketPath string, opts ...Option) *Client

Unix talks HTTP/1.1 over a Unix-domain socket.

func (*Client) AdminCreate

func (c *Client) AdminCreate(ctx context.Context, request any) error

AdminCreate provisions a new database at runtime (server-admin only). The request is marshaled verbatim as the `/_admin/create` body, whose shape is

{"database": {<config.Database fields>}, "grants": [{"principal": …, "level": …}]}

It is typed `any` so callers assemble the spec (a map, or their own structs) without this package depending on the server's config types.

func (*Client) AdminDatabases

func (c *Client) AdminDatabases(ctx context.Context) ([]AdminDatabase, error)

AdminDatabases lists the databases the caller may administer: all of them for a server-admin, otherwise the ones the caller holds an `admin` grant on.

func (*Client) AdminDetach

func (c *Client) AdminDetach(ctx context.Context, database string) error

AdminDetach detaches the named database from the registry (server-admin only). The underlying file is left in place.

func (*Client) AdminInfo

func (c *Client) AdminInfo(ctx context.Context) (*AdminInfo, error)

AdminInfo fetches `/_admin/info` (server-admin only).

func (*Client) AdminKill

func (c *Client) AdminKill(ctx context.Context, session string) error

AdminKill force-closes the session with the given id (server-admin only). A session with a request in flight is refused with a 409.

func (*Client) AdminMaintenance

func (c *Client) AdminMaintenance(ctx context.Context, database, op string, maxBytes int64, dest string) (json.RawMessage, error)

AdminMaintenance runs a maintenance op on a database: "compact" (offline, vault), "compact_online"/"trim" (online, vault; maxBytes caps a reclaim), or "snapshot" (any backend; dest is a server-side path inside data_dir). It returns the server's raw JSON status for display.

func (*Client) AdminSessions

func (c *Client) AdminSessions(ctx context.Context) ([]AdminSession, error)

AdminSessions lists live sessions, filtered to the databases the caller may administer.

func (*Client) ApplyChangeset

func (c *Client) ApplyChangeset(ctx context.Context, db string, changeset []byte) error

ApplyChangeset applies a SQLite changeset (as produced by Stream.SessionChangeset) to db. It requires write access on the server.

func (*Client) Batch

func (c *Client) Batch(ctx context.Context, db string, stmts []Statement) ([]*Result, error)

Batch runs several statements in ONE HTTP request — a single authentication and a single network round trip — instead of one request per statement. The statements run in order on one server-side connection, each autocommitting.

It returns one Result per statement (in order). The batch is NOT atomic: a failing statement does not roll back the ones before it, and Batch returns the first statement's error (wrapped with its index) without the later results. For all-or-nothing semantics, make the first and last statements BEGIN and COMMIT, or use Client.OpenStream for an interactive transaction.

This is the fix for "I have N statements to run": it saves N-1 round trips for every auth method, and with the keyring method it also collapses N challenge fetches into (at most) one. Batch uses the Hrana pipeline endpoint, so the server must have sessions enabled (it is, in every standard deployment).

func (*Client) BlobCreate

func (c *Client) BlobCreate(ctx context.Context, db, store string) (int64, error)

BlobCreate allocates a new large object in the named blobstore and returns its id. Store and load its bytes with BlobWrite / BlobRead. Requires write access.

func (*Client) BlobDelete

func (c *Client) BlobDelete(ctx context.Context, db, store string, id int64) error

BlobDelete removes object id from store (write access).

func (*Client) BlobProvision

func (c *Client) BlobProvision(ctx context.Context, db, store string, chunk int, compress string, dedup bool) error

BlobProvision provisions (idempotently) the named store with the given options, so objects created in it thereafter honor them. chunk<=0 / compress=="" / dedup=false keep server defaults. Requires write access.

func (*Client) BlobRead

func (c *Client) BlobRead(ctx context.Context, db, store string, id int64) ([]byte, error)

BlobRead returns the whole content of object id in store (read access).

func (*Client) BlobSize

func (c *Client) BlobSize(ctx context.Context, db, store string, id int64) (int64, error)

BlobSize returns the byte length of object id in store (read access).

func (*Client) BlobWrite

func (c *Client) BlobWrite(ctx context.Context, db, store string, id int64, r io.Reader) (int64, error)

BlobWrite streams r as the whole content of object id in store and returns the number of bytes written. The body is streamed (bounded memory), so an object can be large. Requires write access.

func (*Client) Close

func (c *Client) Close() error

Close releases the client's transport resources (idle connections, QUIC sessions). It is safe to call more than once.

func (*Client) ConcatChangesets

func (c *Client) ConcatChangesets(ctx context.Context, db string, a, b []byte) ([]byte, error)

ConcatChangesets returns the concatenation of a then b. Read access.

func (*Client) Exec

func (c *Client) Exec(ctx context.Context, db, sql string, args ...any) (*Result, error)

Exec runs sql (a write/DDL) against db. The native endpoint auto-dispatches read vs write, so Exec and Query are interchangeable; both are provided for call-site clarity.

func (*Client) Export

func (c *Client) Export(ctx context.Context, db string) ([]byte, error)

Export downloads the entire database as a SQLite serialization — the byte image a backup contains, which sqlite.Deserialize (or the sqlite3 CLI) can open. It requires read access on the server.

func (*Client) Health

func (c *Client) Health(ctx context.Context) error

Health probes the server-scoped `/_health` endpoint (public, unauthenticated on every transport). A nil error means the server answered "ok".

func (*Client) InvertChangeset

func (c *Client) InvertChangeset(ctx context.Context, db string, changeset []byte) ([]byte, error)

InvertChangeset returns the inverse (undo) of a changeset. Read access.

func (*Client) OpenStream

func (c *Client) OpenStream(db string) *Stream

OpenStream returns a Stream bound to db. No network call happens until the first Exec.

func (*Client) Query

func (c *Client) Query(ctx context.Context, db, sql string, args ...any) (*Result, error)

Query runs sql (a read) against db and returns the result.

type Error

type Error struct {
	Message string
	// contains filtered or unexported fields
}

Error is a SQL execution error returned by the server. It carries SQLite's primary and extended result codes so callers — notably an ORM's error normalization — can classify constraint violations (unique / foreign-key / not-null / check) and busy/locked contention. It satisfies the Code() int / ExtendedCode() int interfaces those callers assert on.

func (*Error) Code

func (e *Error) Code() int

func (*Error) Error

func (e *Error) Error() string

func (*Error) ExtendedCode

func (e *Error) ExtendedCode() int

type Option

type Option func(*clientOpts)

Option customizes a Client. Auth options set the single credential the client presents; mutual-TLS is set via WithClientCert on a TLS transport.

func WithBasicAuth

func WithBasicAuth(user, password string) Option

WithBasicAuth sends HTTP basic credentials on every request.

func WithBearer

func WithBearer(token string) Option

WithBearer sends "Authorization: Bearer <token>" on every request.

func WithClientCert

func WithClientCert(cert tls.Certificate) Option

WithClientCert presents a client certificate for mTLS. It applies only to the TLS transports (H2TLS, H3); it is ignored on H1/H2C/Unix.

func WithEd25519

func WithEd25519(pubLine string, priv ed25519.PrivateKey) Option

WithEd25519 authenticates via the keyring challenge/response: the client fetches a challenge from /_auth/challenge and signs it with priv, caching and reusing the challenge within its window so a burst of requests does not fetch one each. pubLine is the matching ssh-ed25519 authorized_keys line.

func WithMaxResponse added in v0.5.3

func WithMaxResponse(n int64) Option

WithMaxResponse caps the size (bytes) of a server-supplied response body the client will read into memory — BlobRead, changeset invert/concat, Export, and query/pipeline results. Passing n<=0 disables the cap (unbounded). Unset, the client uses DefaultMaxResponse.

func WithRootCA

func WithRootCA(pool *x509.CertPool) Option

WithRootCA verifies the server's TLS certificate against pool instead of the system roots — for a private/dev CA, so the TLS transports (H2TLS, H3) can be used verified rather than with insecure=true. Composes with WithClientCert.

type Result

type Result struct {
	Columns      []string
	Rows         [][]any
	RowsAffected int64
	LastInsertID int64
	Truncated    bool
}

Result is one statement's outcome. Rows holds decoded cells: JSON numbers as json.Number, text as string, NULL as nil, a blob as []byte (decoded from the {"base64":…} box).

type Statement

type Statement struct {
	SQL  string
	Args []any
}

Statement is one SQL statement plus its bound arguments, for Client.Batch.

type Stream

type Stream struct {
	// contains filtered or unexported fields
}

Stream is a session-pinned sequence of statements over the server's Hrana pipeline endpoint (/<db>/v3/pipeline). Every statement on a Stream runs on the same server-side connection, held for the Stream's lifetime — which is what a database/sql transaction (BEGIN … COMMIT, with SAVEPOINT nesting) requires. The stateless Query/Exec on *Client cannot give that guarantee.

A Stream is opened lazily: the first Exec sends a null baton, the server pins a connection and returns a baton, and each subsequent Exec threads the rotated baton to resume the same connection. Close ends the server-side session. A Stream is not safe for concurrent use; drive it from one goroutine (the database/sql contract for a connection in a transaction).

func (*Stream) Close

func (s *Stream) Close(ctx context.Context) error

Close ends the server-side session (a Hrana "close" request), returning the pinned connection to the server's pool. It is safe to call more than once and on a never-used stream (no baton yet → no-op).

func (*Stream) Exec

func (s *Stream) Exec(ctx context.Context, sql string, args []any) (*Result, error)

Exec runs one statement on the stream's pinned connection and returns its result. sql may be any statement, including BEGIN / SAVEPOINT / RELEASE / COMMIT / ROLLBACK.

func (*Stream) SessionChangeset

func (s *Stream) SessionChangeset(ctx context.Context) ([]byte, error)

SessionChangeset serializes and returns the changeset captured since SessionStart (consuming the capture; a second call needs a fresh SessionStart).

func (*Stream) SessionStart

func (s *Stream) SessionStart(ctx context.Context, tables []string) error

SessionStart begins capturing a SQLite changeset on the stream's pinned connection: every write executed on this stream afterward is recorded. tables names the tables to track (empty tracks all). Pair with SessionChangeset. This is a quicSQL extension to the Hrana pipeline.

Directories

Path Synopsis
Package sqldriver registers a database/sql driver named "quicsql" that speaks to a quicSQL server, so ordinary database/sql code connects to a remote database the same way it opens a local one:
Package sqldriver registers a database/sql driver named "quicsql" that speaks to a quicSQL server, so ordinary database/sql code connects to a remote database the same way it opens a local one:

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL