Documentation
¶
Overview ¶
Package sqlite implements ports.ExampleStore using a SQLite database via modernc.org/sqlite (pure Go, no CGO). The schema is versioned through the schema_migrations table shared with other contexts when using the same DB.
Index ¶
- Constants
- func Migrations() []sqlitestore.Migration
- type Store
- func (s *Store) Close() error
- func (s *Store) FindBySymbol(ctx context.Context, symbol string, pipelineVersion string, ...) ([]ports.ExampleRef, error)
- func (s *Store) FindBySymbolInModule(ctx context.Context, coord coordinate.ModuleCoordinate, symbol string, ...) ([]ports.ExampleRef, error)
- func (s *Store) GetExampleRecord(ctx context.Context, coord coordinate.ModuleCoordinate, pipelineVersion string) (domain2.ExampleRecord, bool, error)
- func (s *Store) IdenticalGeneration(ctx context.Context, rec domain2.ExampleRecord) (domain2.ExampleRecord, bool, error)
- func (s *Store) InternalDB() sqlitestore.DB
- func (s *Store) ListExampleRecords(ctx context.Context, filter ports.ExampleFilter) ([]ports.ExampleSummary, error)
- func (s *Store) ListExampleRecordsFor(ctx context.Context, coord coordinate.ModuleCoordinate, pipelineVersion string) ([]domain2.ExampleRecord, error)
- func (s *Store) PipelinesHeld(ctx context.Context, coord coordinate.ModuleCoordinate) ([]string, error)
- func (s *Store) PutExampleRecord(ctx context.Context, r domain2.ExampleRecord) error
- func (s *Store) ReportSetAside(r func([]recordseal.SetAsideRow))
- func (s *Store) WithSupersededArtefacts(fn fetchports.SupersededArtefacts) *Store
Constants ¶
const RecordKind = "example record"
RecordKind is how a set-aside example generation names its kind.
Variables ¶
This section is empty.
Functions ¶
func Migrations ¶
func Migrations() []sqlitestore.Migration
Migrations returns the schema migrations for the example module.
Types ¶
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is the SQLite-backed example store.
func New ¶
func New(db sqlitestore.DB) *Store
New returns a new Store using the provided database handle.
func Open ¶
Open opens (or creates) the SQLite database at dsn and runs migrations. Use ":memory:" for tests.
func (*Store) FindBySymbol ¶
func (s *Store) FindBySymbol(ctx context.Context, symbol string, pipelineVersion string, scope coordinate.ModuleSet) ([]ports.ExampleRef, error)
FindBySymbol returns index entries for all examples associated with the given symbol, filtered by pipeline version and restricted to the modules in scope (the zero ModuleSet imposes no restriction, matching every stored version — see ports.ExampleStore). The symbol may be qualified with a package name (e.g. "modfile.File") or unqualified (e.g. "File"); both forms are matched against the stored associated_symbol column which holds unqualified names like "File" or "Client.Do".
func (*Store) FindBySymbolInModule ¶
func (s *Store) FindBySymbolInModule(ctx context.Context, coord coordinate.ModuleCoordinate, symbol string, pipelineVersion string) ([]ports.ExampleRef, error)
FindBySymbolInModule returns index entries for examples associated with the given symbol within a specific module@version. Applies the same package-qualification stripping as FindBySymbol.
func (*Store) GetExampleRecord ¶
func (s *Store) GetExampleRecord(ctx context.Context, coord coordinate.ModuleCoordinate, pipelineVersion string) (domain2.ExampleRecord, bool, error)
GetExampleRecord returns the composed example answer for the coordinate and pipeline version. Returns (zero, false, nil) when the ledger holds none.
Composition serves a completed extraction over a failed one, then the fewest parse failures, then the most recent. Recency alone never wins: an extraction that regresses to more parse failures must not displace a cleaner earlier one merely by running later. See domain.Compose for the ladder and for the disagreement it refuses to resolve by picking.
A stored record whose bytes do not hash to their seal is ErrExampleIntegrity, and it stops the read rather than being skipped: dropping it would serve a composition computed over fewer records than the ledger holds and report it as the whole answer. A record whose bytes hash to their seal but which this build cannot reproduce is set aside, named through ReportSetAside, and composed around; see ListExampleRecordsFor.
func (*Store) IdenticalGeneration ¶
func (s *Store) IdenticalGeneration(ctx context.Context, rec domain2.ExampleRecord) (domain2.ExampleRecord, bool, error)
IdenticalGeneration returns the generation the ledger already holds that states the same measurement as rec. See ports.IdenticalGenerationReader for why a run asks this after extracting rather than before.
The SQL is a PREFILTER and nothing else. The table records only the status and the example count, which two different sets of examples of the same size share, so the decision is domain.SameMeasurement over the decoded records: every example body, every parse failure and the artefact that was read. The blob carries all of it, so no index row is touched to answer this.
Newest first, stopping at the first match, because the generation a re-read of an unchanged tree matches is the one it wrote last time; the content hash breaks a tie so two rows sharing a second cannot order differently between runs.
A row whose bytes do not hash to their seal stops the read rather than being skipped, on the same terms as every other read leg of this store. The caller treats a failure as "nothing held" and appends, so an unverifiable row costs a redundant generation, never a suppressed measurement. A row this build cannot reproduce cannot be shown to state this measurement, so it is skipped and named through ReportSetAside.
func (*Store) InternalDB ¶
func (s *Store) InternalDB() sqlitestore.DB
InternalDB returns the underlying sqlite.DB for testing purposes.
func (*Store) ListExampleRecords ¶
func (s *Store) ListExampleRecords(ctx context.Context, filter ports.ExampleFilter) ([]ports.ExampleSummary, error)
ListExampleRecords returns one summary per module, pipeline version pair — the generation composition serves — ordered by extracted_at descending.
One summary per module, not one per row. The ledger holds a row per extraction, so listing rows would show a re-extracted module once per generation, and an operator reading the list has no way to tell a second generation from a second module.
Limit and offset are applied AFTER the collapse, so they count modules rather than rows. Applying them in SQL would let a module with three generations consume three places of a --limit 50, and the page an operator sees would depend on how many times each module happened to be re-extracted.
func (*Store) ListExampleRecordsFor ¶
func (s *Store) ListExampleRecordsFor(ctx context.Context, coord coordinate.ModuleCoordinate, pipelineVersion string) ([]domain2.ExampleRecord, error)
ListExampleRecordsFor returns every generation the ledger holds for one coordinate and pipeline version, in the order they were appended.
This is what makes the ledger observable: the earlier extraction is still here, naming the artefact it was computed from, after a later one has changed the served answer.
The secondary sort is the row id, not the content hash. extracted_at persists at second precision — that is the precision the canonical hash covers, so widening the column would put the stored hashes and the stored time out of step — and two extractions within one second carry the same timestamp. The ledger is append-only, so insertion order is the sequence it actually has, and composition relies on it for local coordinates.
A generation this build cannot reproduce is left out and named through ReportSetAside. When every generation is, the answer is *recordseal.NothingServable: records are held, none this build can serve.
func (*Store) PipelinesHeld ¶
func (s *Store) PipelinesHeld(ctx context.Context, coord coordinate.ModuleCoordinate) ([]string, error)
PipelinesHeld lists the pipeline versions coord holds example records at, oldest first, from the generation index alone: no blob is decoded.
func (*Store) PutExampleRecord ¶
PutExampleRecord appends an extraction to the ledger, together with the index rows belonging to it.
It never updates. The record key carries the time of measurement and the record's own content hash, so two distinct extractions are always two rows; the index rows carry the parent record's content hash, so each generation's children are its own. The only collision left is the same record written twice, which the conflict clauses make a no-op because it is one measurement rather than two — and it must not be an error, or a retried write would fail a run that had already succeeded.
The index rows are appended, never deleted. Deleting the previous generation's children is what an overwriting store had to do; here it would strand the earlier record with no examples to resolve, which is the orphaning this conversion exists to prevent.
func (*Store) ReportSetAside ¶
func (s *Store) ReportSetAside(r func([]recordseal.SetAsideRow))
ReportSetAside states where the store names a generation it set aside.
func (*Store) WithSupersededArtefacts ¶
func (s *Store) WithSupersededArtefacts(fn fetchports.SupersededArtefacts) *Store
WithSupersededArtefacts makes the store's reads of a published coordinate leave out generations derived from a directory read under it before local code had its own identity. The generations stay in the ledger.