Documentation
¶
Index ¶
- Constants
- Variables
- func BoundaryScope(rows []Row, index int) map[string]any
- func CELTypeName(value any) string
- func ClickyColumns(profile Profile) ([]api.ColumnDef, error)
- func ColumnFilterKindValues() []string
- func ColumnTypeValues() []string
- func CursorWalkClock(c Cursor) (time.Time, bool)
- func DecodeOptions[T any](opts map[string]any) (T, error)
- func ErrorPage(err error) iter.Seq2[Page, error]
- func EvalJSONPath(expression, source string, row Row) ([]any, error)
- func ExecutePages(ctx context.Context, p Profile, page PageRequest, params ...map[string]any) iter.Seq2[Page, error]
- func FlushResourceSelectorCache()
- func InferInspectionSemanticType(databaseType string) string
- func Limit(rows iter.Seq2[Row, error], n int) iter.Seq2[Row, error]
- func LookupFilterValues(ctx context.Context, request FilterValueLookupRequest) ([]FilterOption, *Total, error)
- func MarshalDiagnosticPreview(value any) []byte
- func MergeScope(group []Row, keep string) map[string]any
- func NamedProcessorNames() []string
- func NormalizeKeyValue(value any) string
- func OrQueries(db *gorm.DB, queries ...*gorm.DB) *gorm.DB
- func PagesNatively(providerType string) bool
- func ParseFilteringQuery(query string, decodeURL bool) (grammar.FilteringQuery, error)
- func ProcessPages(ctx context.Context, specs []ProcessorSpec, carried map[string][]byte, ...) iter.Seq2[Page, error]
- func QueryResourceSelectors[T any](ctx context.Context, queryModel QueryModel, selectColumns []string, limit int, ...) ([]T, error)
- func RegisterNamedProcessor(p NamedProcessor)
- func RegisterProcessor(p Processor)
- func RegisterProvider(p Provider)
- func RegisteredProcessors() []string
- func RegisteredProviders() []string
- func RenderParamsJSON(ctx context.Context, doc []byte, params map[string]any) ([]byte, []string, error)
- func Rows(pages iter.Seq2[Page, error]) iter.Seq2[Row, error]
- func SampleFilterValues(ctx context.Context, profile Profile, options SampleFilterValuesOptions) ([]FilterOption, *Total, error)
- func SanitizeDiagnosticValues(values map[string]any) map[string]any
- func SetResourceSelectorClause(ctx context.Context, resourceSelector types.ResourceSelector, query *gorm.DB, ...) (*gorm.DB, error)
- func SlicePages(rows []Row) iter.Seq2[Page, error]
- func StreamableProcessors(specs []ProcessorSpec) (bool, error)
- func SupportsNativeFilters(providerType string) bool
- func SupportsNestedFilters(providerType string) bool
- func SupportsRequestSort(providerType string) bool
- func SupportsStreaming(providerType string) bool
- func ValidateReadOnlySQL(sql string) error
- func WithDiagnosticSink(ctx context.Context, d *ProviderDiagnostics) context.Context
- func WithDiagnostics(err error, diagnostics *ProviderDiagnostics) error
- func WithRecorder(ctx context.Context, r *Recorder) context.Context
- type AliasDef
- type CardinalityProbe
- type ColumnDef
- type ColumnFilterBinding
- type ColumnFilterDef
- type ColumnFilterKind
- type ColumnFilterValue
- type ColumnInspectionProvider
- type ColumnInspectionResult
- type ColumnKind
- type ColumnType
- type Cursor
- type CursorEncoding
- type CursorPosition
- type CursorScope
- type DiagnosticDetail
- type DiagnosticError
- type DiagnosticOptions
- type Event
- type ExecutionDetail
- type ExecutionSource
- type ExecutionSummary
- type ExpressionOptions
- type ExpressionResult
- type ExpressionScope
- type FilterBound
- type FilterDef
- type FilterLookupProvider
- type FilterOption
- type FilterRange
- type FilterValueLookupRequest
- type FinishOptions
- type InspectionCacheSummary
- type InspectionCardinality
- type InspectionField
- type InspectionFilterResolution
- type InspectionLimit
- type InspectionOptions
- type InspectionPaging
- type InspectionRecord
- type InspectionResult
- type InspectionStatus
- type JSONPathFilterTarget
- type KeyFunc
- type KeyRange
- type KeySpec
- type LogLine
- type NamedProcessor
- type Operation
- type OperationResult
- type OperationSummary
- type Order
- type OrderBy
- type OrderingProvider
- type Page
- type PageInfo
- type PageProcessor
- type PageRequest
- type PagingMode
- type PagingProvider
- type ParamDef
- type ParamRole
- type ParamType
- type ParameterizedQuery
- type Processor
- type ProcessorPreview
- type ProcessorPreviewStage
- type ProcessorSpec
- type Profile
- func (p Profile) ColumnFilterBindings() ([]ColumnFilterBinding, error)
- func (p Profile) ColumnFilterKeys() (map[string]string, error)
- func (p Profile) ColumnSortKeys() (map[string]string, error)
- func (p Profile) EffectiveOrder() (Order, error)
- func (p Profile) FilterBindings() ([]ColumnFilterBinding, error)
- func (p Profile) HasParamRoleName(role ParamRole, name string) bool
- func (p Profile) HasTimeRangeParams() bool
- func (p Profile) Kind() ProfileKind
- func (p Profile) Pageable() error
- func (p Profile) ParamFilterBindings() []ColumnFilterBinding
- func (p Profile) ParamNameForRole(role ParamRole, fallback string) string
- func (p Profile) RenderMode() string
- func (p Profile) RequestedOrder(sort string, desc bool) (Order, error)
- func (p Profile) RowLimits() RowLimits
- func (p Profile) RuntimeFilterBindings() ([]ColumnFilterBinding, error)
- func (p Profile) SortBindings() ([]SortBinding, error)
- func (p Profile) Streamable() (bool, error)
- func (p Profile) TimeRangeParams() map[ParamRole]ParamDef
- func (p Profile) Validate() error
- func (p Profile) ValidateKind() error
- func (p Profile) ValidateQuerySource() error
- type ProfileKind
- type Provider
- type ProviderConfig
- type ProviderDiagnosticRequest
- type ProviderDiagnosticResponse
- type ProviderDiagnostics
- func (d *ProviderDiagnostics) HTTPTransport(base http.RoundTripper) http.RoundTripper
- func (d *ProviderDiagnostics) RecordConnection(connection string)
- func (d *ProviderDiagnostics) RecordError(err error)
- func (d *ProviderDiagnostics) RecordPreview(contentType string, body []byte)
- func (d *ProviderDiagnostics) RecordRendered(query string, options map[string]any)
- func (d *ProviderDiagnostics) RecordRequest(query string, arguments []any, details map[string]any)
- func (d *ProviderDiagnostics) RecordResponse(started time.Time, rows int, details map[string]any)
- func (d *ProviderDiagnostics) Snapshot() *ProviderDiagnostics
- func (d *ProviderDiagnostics) WantsPreview() bool
- type ProviderRequest
- type QueryModel
- type QueryParameterizationRequest
- type QueryParameterizer
- type ReconcileConfig
- type ReconcileMode
- type ReconcileProvenance
- type ReconcileResult
- func (r *ReconcileResult) Bounded() bool
- func (r *ReconcileResult) Columns() []api.ColumnDef
- func (r *ReconcileResult) Flatten() []Row
- func (r *ReconcileResult) Pretty() api.Text
- func (r *ReconcileResult) Render(format string) (string, error)
- func (r *ReconcileResult) SnapshotColumns() []ColumnDef
- func (r *ReconcileResult) SnapshotRows() []Row
- func (r *ReconcileResult) Table() api.TextTable
- type ReconcileRow
- type ReconcileRun
- type ReconcileSideExecution
- type ReconcileSpec
- type ReconcileStats
- type ReconcileStatus
- type RecordCounts
- type Recorder
- func (r *Recorder) Detail() ExecutionDetail
- func (r *Recorder) DiagnosticDetail() DiagnosticDetail
- func (r *Recorder) Finish(options FinishOptions)
- func (r *Recorder) ID() string
- func (r *Recorder) Level() logger.LogLevel
- func (r *Recorder) Log(line LogLine)
- func (r *Recorder) Operation(provider string) *Operation
- func (r *Recorder) RecordInspection(record InspectionRecord)
- func (r *Recorder) RecordProbe(probe CardinalityProbe)
- func (r *Recorder) RefreshInspection() bool
- func (r *Recorder) Summary() ExecutionSummary
- type RecorderOptions
- type RegistryOptions
- type ReplayBuildOptions
- type ReplayExecuteResult
- type ReplayPreview
- type ReplayRowSummary
- type ReplaySpec
- type Result
- type ResultColumn
- type ResultColumnFilter
- type ResultColumnFilterOption
- type ResultColumnOptions
- type Row
- type RowExpr
- type RowLimits
- type SampleFilterValuesOptions
- type SampleOptions
- type SampleResolution
- type SampleResult
- type Session
- func (s *Session) Abort(err error)
- func (s *Session) Emit(e Event)
- func (s *Session) Events() []Event
- func (s *Session) ID() string
- func (s *Session) Latest() *Result
- func (s *Session) Result(ctx context.Context) (*Result, error)
- func (s *Session) Snapshot() SessionInfo
- func (s *Session) Stop()
- func (s *Session) Subscribe() (replay []Event, live <-chan Event, cancel func())
- func (s *Session) SubscribeFrom(after int64) (replay []Event, live <-chan Event, cancel func())
- type SessionInfo
- type SessionOptions
- type SessionRegistry
- func (r *SessionRegistry) Add(s *Session) error
- func (r *SessionRegistry) ClampDuration(d time.Duration) time.Duration
- func (r *SessionRegistry) ClampEvents(n int) int
- func (r *SessionRegistry) Get(id string) (*Session, bool)
- func (r *SessionRegistry) List() []SessionInfo
- func (r *SessionRegistry) StopAll()
- type SessionState
- type SortBinding
- type SortingProvider
- type StreamProvider
- type SubQuery
- type TopSpec
- type Total
- type TraceBufferSpec
- type TraceSpec
Constants ¶
const ( // DefaultFilterLookupLimit is how many distinct values a profile's filter // offers before the rest must be reached by typing. It is small on purpose: // a list nobody can scan is not a list anyone picks from, and the values past // it are one debounced search away. DefaultFilterLookupLimit = 50 // MaxFilterLookupLimit is the largest head a filter may ask for. It mirrors // clicky's entity.MaxLookupOptions, which caps the lookup response either // way — declaring it here is what turns a silent reduction at request time // into a loud rejection when the profile is written. MaxFilterLookupLimit = 200 )
const ( KeepFirst = "first" KeepLast = "last" )
Keep values selecting which row of a group survives the merge.
const ( // KubernetesDefaultStart is how far back a Kubernetes log profile reads when // nothing bounds it. An unbounded read is every line the container still // retains, from every pod in scope, buffered before the first row is served — // a default that cannot be what anyone meant. KubernetesDefaultStart = "now-1h" // KubernetesDefaultTimeRange is that same floor written in the filter grammar // the generated time control carries. KubernetesDefaultTimeRange = ">=" + KubernetesDefaultStart )
const ( RenderLogs = "logs" RenderTrace = "trace" RenderTop = "top" )
Render values the frontend keys presentation off (x-clicky-render): RenderLogs selects the canonical LogsTable; RenderTrace and RenderTop select the session-backed live views and are derived from the profile kind when Render is not set explicitly.
const ( // DefaultRecorderLogLines bounds the lines one request retains. A request // that logs more than this is one whose interesting lines are at the start. DefaultRecorderLogLines = 500 // DefaultRecorderHAREntries mirrors observability.DefaultCollectorEntries, // which is what the per-operation collectors are already capped at. DefaultRecorderHAREntries = 100 )
const ( // DefaultPageSize is the page a caller gets when it asks for no size. DefaultPageSize = 100 // DefaultMaxPageSize is the largest single page a caller may ask for. // Exporting more than this is what MaxExportRows is for. DefaultMaxPageSize = 1000 // DefaultMaxExportRows is where an all-row export stops. DefaultMaxExportRows = 100_000 // DefaultSampleLimit is how many rows column inference reads. DefaultSampleLimit = 100 )
The row caps a caller has to keep apart. They answer different questions, so none of them substitutes for another:
- a profile's own limit — the `limit` provider option or a limit-role param — is how many rows the *query* asks the source for. It belongs to the query and travels with it.
- PageSize/MaxPageSize bound one *response*: the page a caller gets when it asks for no size, and the largest page it may ask for.
- MaxExportRows bounds a whole *export*: the point past which "give me everything" stops, so an unbounded source still terminates.
- DefaultSampleLimit bounds a *look at the shape of the data*, which is not a read of it at all: sampling infers columns, so it takes the same few rows however many the query would return.
The first three are what a profile may set for itself; the constants below are what applies when it does not.
const ( // DefaultMaxDuration bounds a trace or top session when the spec omits one. DefaultMaxDuration = 15 * time.Minute // DefaultMaxEvents caps a trace session's event ring buffer. DefaultMaxEvents = 10000 // DefaultTopInterval is the sampling interval when a TopSpec omits one. DefaultTopInterval = 5 * time.Second // MinTopInterval is the floor for a TopSpec interval. MinTopInterval = time.Second )
const DiagnosticPreviewLimit = 256 << 10
const MaxCursorBytes = 7 << 10
MaxCursorBytes bounds an issued cursor. A cursor travels as a query parameter and is echoed in a response header, so this leaves room for the request path, other parameters and header name within common 8 KiB HTTP line budgets.
It is a ceiling rather than a trimming point. The only thing that grows a cursor is a processor's carried state, and that state is what stops an already-emitted group being emitted again — so silently dropping part of it would not shorten the answer, it would corrupt it. Refusing says which walk outgrew the mechanism, which is a thing an author can act on.
const ( // ReplayKindHTTP is the only supported replay transport today. The field // exists so a future kind can be added without breaking stored profiles. ReplayKindHTTP = "http" )
Variables ¶
var CommonFields = map[string]func(ctx context.Context, tx *gorm.DB, val string) (*gorm.DB, error){ "limit": func(ctx context.Context, tx *gorm.DB, val string) (*gorm.DB, error) { if i, err := strconv.Atoi(val); err == nil { return tx.Limit(i), nil } else { return nil, err } }, "sort": func(ctx context.Context, tx *gorm.DB, sort string) (*gorm.DB, error) { return tx.Order(clause.OrderByColumn{Column: clause.Column{Name: sort}}), nil }, "offset": func(ctx context.Context, tx *gorm.DB, val string) (*gorm.DB, error) { if i, err := strconv.Atoi(val); err == nil { return tx.Offset(i), nil } else { return nil, err } }, }
CommonFields provides built-in query field handlers for common operations
var DateMapper = func(ctx context.Context, val string) (any, error) { if expr, err := datemath.Parse(val); err != nil { return nil, fmt.Errorf("invalid date '%s': %s", val, err) } else { return expr.Time(), nil } }
DateMapper maps date expressions (including datemath like "now-7d") to actual time values
var ErrCursorStale = errors.New("cursor no longer matches this query")
ErrCursorStale is returned when a cursor is replayed against a query it did not come from — a changed filter, param or order. The position it holds is real but no longer locatable, so it is refused rather than served: resuming "after" a row this query never produces silently skips or repeats an unknowable number of rows.
var ErrMaxSessions = errors.New("max sessions reached")
ErrMaxSessions is returned by Add when the active-session cap is reached.
var JSONPathMapper = func(ctx context.Context, tx *gorm.DB, column string, op grammar.QueryOperator, path string, val string) *gorm.DB { if !slices.Contains([]grammar.QueryOperator{grammar.Eq, grammar.Neq}, op) { op = grammar.Eq } values := strings.Split(val, ",") for _, v := range values { tx = tx.Where(fmt.Sprintf(`TRIM(BOTH '"' from jsonb_path_query_first(%s, '$.%s')::TEXT) %s ?`, column, path, op), v) } return tx }
JSONPathMapper handles JSONPath queries against JSONB columns
Functions ¶
func BoundaryScope ¶ added in v0.1.29
BoundaryScope is the environment a grouping predicate sees for one row: the row, the row above it, and its position.
All three are always bound, matching `startsBatch`. Omitting one would not read as null — gomplate declares its variables from this map, so an absent binding makes the expression fail to compile with "undeclared reference" rather than evaluate to nothing.
func CELTypeName ¶ added in v0.1.29
CELTypeName names a result the way an author reads it, so a column declared `type: number` can be checked against what its expression actually returns.
func ClickyColumns ¶ added in v0.1.15
ClickyColumns maps a profile's declared columns to the shared Clicky contract.
func ColumnFilterKindValues ¶ added in v0.1.29
func ColumnFilterKindValues() []string
ColumnFilterKindValues returns every kind an author may declare, for the profile schema's enum. The order is the order the profile editor offers them, grouped by family so that a reader reaching for "duration" finds it beside "range" rather than wherever it happened to be appended.
func ColumnTypeValues ¶ added in v0.1.29
func ColumnTypeValues() []string
ColumnTypeValues returns every type an author may declare, for the profile schema's enum.
func CursorWalkClock ¶ added in v0.1.29
CursorWalkClock is the instant the walk that issued c resolved its date math at. Resolving a later page against it is what lets a rolling window like "now-2d" name one result set for the whole walk, rather than a new one — and so a new fingerprint — on every request.
It reads the clock without validating the cursor, because the params the validation compares cannot be resolved until the clock is known. DecodeCursor still runs immediately afterwards, so a cursor this server did not issue is refused exactly as before; the only thing an unchecked clock can move is the caller's own window. Reports false for no cursor, an unreadable one, and one minted before this field existed.
func DecodeOptions ¶ added in v0.1.13
DecodeOptions decodes a ProviderRequest.Options map into a provider-specific options struct T via a JSON round-trip (T's json tags drive the mapping). Returns the zero T when opts is empty.
func ErrorPage ¶ added in v0.1.29
ErrorPage returns a sequence that yields nothing but err, so a provider can fail out of a setup step without a second return value.
func EvalJSONPath ¶ added in v0.1.29
EvalJSONPath resolves an ad-hoc path against a row for profile authoring tools, which need to show an author what their half-written path selects.
Unlike evalRowJSONPath it hands back every match rather than collapsing 0/1/N into nil/value/slice: the collapse is what a column cell wants, and a preview that showed nil for both "no match" and "matched null" would hide the one mistake it exists to catch.
func ExecutePages ¶ added in v0.1.29
func ExecutePages(ctx context.Context, p Profile, page PageRequest, params ...map[string]any) iter.Seq2[Page, error]
ExecutePages resolves profile parameters and templates exactly like Execute, then yields the requested page and every page after it. Each raw provider page runs through page-capable processors before aliases, filters, columns, and styles. A processor that needs the whole result makes paging invalid.
The sequence ends when the source is exhausted or the consumer stops ranging. A caller wanting a single page breaks after the first, which is also what releases the backend cursor.
func FlushResourceSelectorCache ¶
func FlushResourceSelectorCache()
FlushResourceSelectorCache flushes all resource selector caches
func InferInspectionSemanticType ¶ added in v0.1.30
func Limit ¶ added in v0.1.29
Limit stops a row sequence after n rows.
It exists because PageRequest.Limit is a request, not a guarantee: a provider may round it up to a batch size or ignore it in favour of its own, so the ceiling a caller is entitled to is enforced here as well as asked for there.
func LookupFilterValues ¶ added in v0.1.29
func LookupFilterValues(ctx context.Context, request FilterValueLookupRequest) ([]FilterOption, *Total, error)
func MarshalDiagnosticPreview ¶ added in v0.1.29
func MergeScope ¶ added in v0.1.29
MergeScope is the CEL environment a group expression is evaluated in: the bindings a processor's `set`, `when` and `emit` see.
It lives here rather than in the processor package so that previewing one of those expressions and running it cannot drift apart.
func NamedProcessorNames ¶ added in v0.1.29
func NamedProcessorNames() []string
NamedProcessorNames returns the library keys, sorted.
func NormalizeKeyValue ¶ added in v0.1.29
NormalizeKeyValue renders one key component. The CEL and SQL layers spell an absent value several different ways; they all collapse to the empty key so a missing identity never masquerades as a distinct one.
func PagesNatively ¶ added in v0.1.29
PagesNatively reports whether this provider serves a page without producing the whole result first.
SupportsPaging answers a different question — which strategies a caller may ask for — and answers PagingOffset for a provider that has no paging at all, because offset paging over a materialized result is still correct. Only this separates a walk that streams from one that buffers, which is what decides whether an all-row export can be served forward and what X-Export-Mode may truthfully call it.
func ParseFilteringQuery ¶
func ParseFilteringQuery(query string, decodeURL bool) (grammar.FilteringQuery, error)
ParseFilteringQuery parses a filtering query string
func ProcessPages ¶ added in v0.1.29
func ProcessPages( ctx context.Context, specs []ProcessorSpec, carried map[string][]byte, pages iter.Seq2[Page, error], ) iter.Seq2[Page, error]
ProcessPages applies a streamable processor chain to each page while preserving its cursor, totals and provider metadata.
carried is the state each processor left on the previous page, keyed by label, and each page's own state is written back onto Page.State for the cursor to carry. ExecutePages calls this before the cursor is minted; a preview or a sample calls it with no carried state because it reads one bounded batch rather than walking.
func QueryResourceSelectors ¶
func QueryResourceSelectors[T any]( ctx context.Context, queryModel QueryModel, selectColumns []string, limit int, clauses []clause.Expression, resourceSelectors ...types.ResourceSelector, ) ([]T, error)
QueryResourceSelectors queries a table using multiple resource selectors. It returns the combined results from all selectors, respecting the limit.
Example usage:
model := QueryModel{
Table: "my_resources",
Columns: []string{"id", "name", "type"},
HasTags: true,
}
results, err := QueryResourceSelectors[MyResource](ctx, model, []string{"id", "name"}, 100, nil, selectors...)
func RegisterNamedProcessor ¶ added in v0.1.29
func RegisterNamedProcessor(p NamedProcessor)
RegisterNamedProcessor adds p to the global library, keyed by p.Name.
func RegisterProcessor ¶ added in v0.1.13
func RegisterProcessor(p Processor)
RegisterProcessor adds p to the global processor registry, keyed by p.Type().
func RegisterProvider ¶ added in v0.1.13
func RegisterProvider(p Provider)
RegisterProvider adds p to the global provider registry, keyed by p.Type(). A later registration for the same type replaces the earlier one.
func RegisteredProcessors ¶ added in v0.1.13
func RegisteredProcessors() []string
RegisteredProcessors returns the registered processor types, sorted.
func RegisteredProviders ¶ added in v0.1.13
func RegisteredProviders() []string
RegisteredProviders returns the registered provider types, sorted.
func RenderParamsJSON ¶ added in v0.1.29
func RenderParamsJSON(ctx context.Context, doc []byte, params map[string]any) ([]byte, []string, error)
RenderParamsJSON templates every string in a JSON document with the resolved parameters under `params`, and reports which parameters it consumed. Object keys and non-string scalars are left alone. It is how a caller holding a JSON body — the query builder's compile endpoint, for instance — reaches the same interpolation a profile gets at execution time.
func Rows ¶ added in v0.1.29
Rows flattens pages for consumers that do not care about batch boundaries. Stopping the returned sequence stops the underlying one, and with it the backend cursor.
func SampleFilterValues ¶ added in v0.1.29
func SampleFilterValues( ctx context.Context, profile Profile, options SampleFilterValuesOptions, ) ([]FilterOption, *Total, error)
SampleFilterValues lists one draft profile filter's values without weakening the read-only contract used by Sample.
func SanitizeDiagnosticValues ¶ added in v0.1.29
SanitizeDiagnosticValues blanks the values whose key names a credential, so a caller reporting its own map beside a provider's diagnostics holds both to the same rule rather than re-deriving it.
func SetResourceSelectorClause ¶
func SetResourceSelectorClause( ctx context.Context, resourceSelector types.ResourceSelector, query *gorm.DB, queryModel QueryModel, ) (*gorm.DB, error)
SetResourceSelectorClause applies a ResourceSelector to a GORM query. The caller must provide a QueryModel that defines the table structure and capabilities.
Returns the modified query and any error encountered.
func SlicePages ¶ added in v0.1.29
SlicePages adapts a complete, already-materialized result to the contract — the buffered pipeline, and providers that cannot stream.
The Total it reports is exact because the whole result is in hand. Callers holding only one page of a larger set must not use this: it would claim the page length as the size of everything.
func StreamableProcessors ¶ added in v0.1.29
func StreamableProcessors(specs []ProcessorSpec) (bool, error)
StreamableProcessors reports whether every processor in specs can run page by page.
func SupportsNativeFilters ¶ added in v0.1.29
SupportsNativeFilters reports whether a provider type turns ProviderRequest.Filters into backend query clauses. Column-filter bindings and tri-state list params both gate on it, so a profile can never accept an exclusion the provider would quietly drop.
It is a declared list rather than a registry probe on purpose: the schema and OpenAPI generators, and every test in the external query_test package, run without linking query/providers, and a probe would report false there and silently delete every binding. The SQL entries are the registry keys the sql provider registers under, not connection types — "sqlserver", not models.ConnectionTypeSQLServer. The generic "sql" key is included even though its engine is unknown until the connection is hydrated: a binding is dialect-agnostic, and only quoting the identifier needs to know which engine it is for.
postgrest stays out. It has no filter builder, its filter syntax is PostgREST's own, and its Execute never sees a dialect — so listing it would advertise controls that silently do nothing, which is what this list exists to prevent.
func SupportsNestedFilters ¶ added in v0.1.29
SupportsNestedFilters reports whether a provider type can scope a selection to one entry of a repeated field. It is the document stores: `nested` is their mapping, and their query language is the only one here with a clause for it.
func SupportsRequestSort ¶ added in v0.1.30
SupportsRequestSort reports whether this provider type orders by a column the request names. An unregistered or non-sorting provider reports false.
func SupportsStreaming ¶ added in v0.1.15
SupportsStreaming reports whether this provider can follow its source — tail a log, subscribe to an event feed — rather than answer one question and stop.
It is the third question in this family and deliberately separate from the other two: paging walks a result set that already exists, and streaming waits for one that does not yet. A provider can do either, both or neither, and a surface offering a Follow control against a provider that cannot is offering a button that can only fail.
func ValidateReadOnlySQL ¶ added in v0.1.29
ValidateReadOnlySQL reports why sql may write, or nil when the statement can only read.
It is deliberately the whole answer rather than a hint a caller refines: the question "does this statement write" cannot be answered from the opening keyword, because postgres spells a delete that returns rows as a WITH and spells an insert that runs under EXPLAIN as an EXPLAIN. Anything that decides what a read-only connection may run has to ask this, and nothing else.
The decision is by token over a comment- and literal-aware scan, so a keyword inside a string or a quoted identifier is text rather than a verb, and a second statement after a semicolon cannot ride along behind a SELECT.
func WithDiagnosticSink ¶ added in v0.1.29
func WithDiagnosticSink(ctx context.Context, d *ProviderDiagnostics) context.Context
WithDiagnosticSink returns a context whose executions record the provider request they issue into d.
ExecutePages takes its recorder from the PageRequest it is handed, which is the whole story for a paged read. Execute has none: a buffered profile's result comes from a walk the caller never describes, and a debug run that can explain the paged half of the profiles and not the buffered half explains the wrong half. The context is the only thing both paths already carry.
func WithDiagnostics ¶ added in v0.1.29
func WithDiagnostics(err error, diagnostics *ProviderDiagnostics) error
func WithRecorder ¶ added in v0.1.29
WithRecorder returns a context whose executions record themselves into r.
It does not install r as the context's HAR collector. A collector installed there would be the parent every operation forwards to, and the recorder reads each operation's own collector at Finish — so doing both would deliver every entry twice, and doing only the former would silently displace a CLI --har export that had already installed one.
Types ¶
type CardinalityProbe ¶ added in v0.1.29
type CardinalityProbe struct {
Provider string `json:"provider"`
Connection string `json:"connection,omitempty"`
Column string `json:"column"`
Field string `json:"field,omitempty"`
Distinct int64 `json:"distinct"`
Limit int `json:"limit"`
Kind string `json:"kind"` // the filter kind the count chose
Cached bool `json:"cached"`
}
CardinalityProbe records a column-cardinality question and the filter kind its answer chose.
The decision is currently unanswerable from outside: "why is this column a free-text box instead of a dropdown" has no answer short of reading the provider's source and re-running the count by hand.
type ColumnDef ¶ added in v0.1.13
type ColumnDef struct {
// Name is the public row key and default header label.
Name string `json:"name" yaml:"name"`
// Source is the provider row key copied into Name. The original key is
// removed after all column expressions have been evaluated.
Source string `json:"source,omitempty" yaml:"source,omitempty"`
// Label overrides the column header. Defaults to a prettified Name.
Label string `json:"label,omitempty" yaml:"label,omitempty"`
// Type is the semantic type used for formatting. Defaults to string.
Type ColumnType `json:"type,omitempty" yaml:"type,omitempty"`
// Kind enables specialized table behavior. In particular, timestamp marks
// the column used by the table's date-range control.
Kind ColumnKind `json:"kind,omitempty" yaml:"kind,omitempty"`
// Format overrides the clicky format string (e.g. "date", "bytes",
// "duration", "currency"). When empty it is derived from Type.
Format string `json:"format,omitempty" yaml:"format,omitempty"`
// Unit is an optional display unit (e.g. "ms", "bytes", "percentunit").
Unit string `json:"unit,omitempty" yaml:"unit,omitempty"`
// Width is an optional max display width in characters.
Width int `json:"width,omitempty" yaml:"width,omitempty"`
// CEL is an optional expression computing the cell value from the row.
// The row is exposed as `row` in the CEL environment.
CEL string `json:"cel,omitempty" yaml:"cel,omitempty"`
// JSONPath is an optional path computing the cell value. It is rooted at the
// row, or at Source when that is set — and a Source holding JSON as a string
// is parsed first, so an encoded and a native column read the same way. A
// path matching nothing yields nil: on a scan, a row that lacks the field is
// ordinary rather than exceptional. Alternative to CEL, not a companion.
//
// Source names the root here; it does not rename. Several columns read one
// JSON column, and that column stays in the row rather than being consumed
// by whichever of them ran first.
JSONPath string `json:"jsonpath,omitempty" yaml:"jsonpath,omitempty"`
// Filter overrides how this column is filtered at the backend. Direct
// columns, simple row/span CEL lookups and literal-key-chain JSONPaths infer
// the field automatically and Type infers the control; computed CEL, and a
// JSONPath that selects rather than addresses, need an explicit field to be
// filterable at all.
Filter *ColumnFilterDef `json:"filter,omitempty" yaml:"filter,omitempty"`
// Hidden excludes the column from rendered output while keeping it available
// to later column and style CEL expressions.
Hidden bool `json:"hidden,omitempty" yaml:"hidden,omitempty"`
// Style is an optional CEL expression returning this cell's presentation
// classes (e.g. `level == "ERROR" ? "text-red-500" : ""`). It reads the row
// the same way CEL does and affects rendering only: the row itself is
// unchanged, so an export carries the value without the styling.
Style string `json:"style,omitempty" yaml:"style,omitempty"`
}
ColumnDef declares one output column of a Profile.
func InferSampleColumns ¶ added in v0.1.15
InferSampleColumns infers stable, compact ColumnDefs from top-level row keys.
func (ColumnDef) InspectedField ¶ added in v0.1.29
InspectedField is the field an inspection probe asks about on behalf of this column: the filter's explicit field if it names one, then the source the column maps from, then the column's own name.
type ColumnFilterBinding ¶ added in v0.1.29
type ColumnFilterBinding struct {
Column string
Key string
Field string
Label string
Kind ColumnFilterKind
Options []string
Lookup bool
Multi bool
// Unit is the unit the column stores its values in (ColumnDef.Unit), which
// a duration bound is resolved into. Empty means milliseconds, the unit an
// unannotated duration column is read under. No other kind consults it.
Unit string
// Nested and Where carry the container the selection is compiled inside and
// the constants that address one entry of it. See ColumnFilterDef.
Nested string
Where map[string]string
// Limit is the author's declared cap on the lookup, or zero when they
// declared none. Zero is not "no values": it is what leaves the choice to
// whoever asks, which is why an inferred binding never fills it in.
Limit int
// Default is the selection a request that names no value is executed under,
// written in the same wire grammar a request would send. It exists for a
// generated control whose absence is not a sensible query — a log profile
// with no time bound reads every line a container ever wrote — and it is
// resolved through the same parse a supplied value takes, so the two can
// never mean different things. Empty means the filter is simply absent when
// nobody selects anything.
Default string
}
ColumnFilterBinding is one filterable column or param as every consumer sees it: the request key it answers to, the backend field it applies to, and the control it offers.
func (ColumnFilterBinding) ControlType ¶ added in v0.1.29
func (b ColumnFilterBinding) ControlType() string
ControlType is the clicky filter type this binding registers as.
It refines the kind's own answer, because whether a value selection is picked or typed is not a property of the kind: a selection with no option list and nothing to enumerate would open an empty dropdown, so it asks for an input instead. That is what a UUID column gets — the values still compare exactly, they are just written rather than chosen.
type ColumnFilterDef ¶ added in v0.1.29
type ColumnFilterDef struct {
// Field is the backend field the selection is applied to. For a document
// store it is the indexed field; for SQL it is the result column the query
// returns. Required only when the column's own definition implies none.
Field string `json:"field,omitempty" yaml:"field,omitempty"`
// Nested names the `nested` mapping Field lives inside. A document store
// indexes each element of such a field as its own document, so a selection on
// one has to be compiled inside a nested query; a flat clause on it matches no
// document at all, and says nothing about why.
//
// It cannot be inferred from the column, because a tag list mapped `nested`
// and a plain array of objects report identical fields — only the index
// mapping tells them apart. Declare it for a profile; the connection browser
// reads it from the mapping itself.
Nested string `json:"nested,omitempty" yaml:"nested,omitempty"`
// Where pins the constants the selection also requires, keyed by backend
// field. It is what narrows to one entry of a key/value tag list: the key is
// fixed here and the value is what the operator picks.
//
// It requires Nested. Outside a nested query the two clauses are ANDed across
// the whole document, which matches a document carrying the key on one entry
// and the value on another — the wrong rows, returned confidently.
Where map[string]string `json:"where,omitempty" yaml:"where,omitempty"`
// Kind overrides the control and the value grammar. Empty derives it from
// Type: string, status and health select values; number, duration and bytes
// take numeric bounds; datetime takes a time range; boolean is a yes/no
// toggle; key_value, key_values and json offer nothing. Set it where the
// rendered type and the backend storage disagree — a status code shown as a
// badge but stored as a number filters by range.
Kind ColumnFilterKind `json:"kind,omitempty" yaml:"kind,omitempty"`
// Options enumerates the selectable values, replacing the backend lookup. It
// is what a low-cardinality field the backend cannot aggregate needs: the
// question the lookup asks has no answer there. Value selections only.
Options []string `json:"options,omitempty" yaml:"options,omitempty"`
// Lookup asks the backend for this field's distinct values. Defaults to true
// for a value selection with no Options. Turning it off leaves the values
// typed rather than picked, which is the only workable control over a
// high-cardinality field like a trace id.
Lookup *bool `json:"lookup,omitempty" yaml:"lookup,omitempty"`
// Multi allows several values at once, defaulting to true. A single-valued
// filter still excludes: "!eu" is one value with a sign, not two.
Multi *bool `json:"multi,omitempty" yaml:"multi,omitempty"`
// Limit caps how many distinct values one lookup offers, defaulting to
// DefaultFilterLookupLimit. Everything past it is reached by typing rather
// than scrolling, so raise it for a field whose whole range is worth seeing
// at once and lower it for one where even fifty is noise. Value selections
// only — the other kinds have no list to cap.
Limit *int `json:"limit,omitempty" yaml:"limit,omitempty"`
// Disabled offers no filter for this column while leaving the column itself
// rendered, which Hidden does not.
Disabled bool `json:"disabled,omitempty" yaml:"disabled,omitempty"`
}
ColumnFilterDef declares how a column is filtered at the backend. Every field is an override: a direct column, one whose CEL is a plain row lookup, or one whose JSONPath is a literal key chain infers its field from the column itself and its control from Type. This exists for the columns inference cannot reach — a computed CEL, or a JSONPath that selects rather than addresses — and for the ones where inference has the shape right and the backend wrong.
func (ColumnFilterDef) Validate ¶ added in v0.1.29
func (d ColumnFilterDef) Validate(column string) error
Validate rejects a filter declaration that cannot behave as written.
type ColumnFilterKind ¶ added in v0.1.29
type ColumnFilterKind string
ColumnFilterKind is the control a column filter offers and the grammar its wire value is read under. It is derived from ColumnType and overridable per column, because the shape a value renders in and the shape the backend compares it in are different questions: an HTTP status renders as a badge and is stored as a number.
const ( // ColumnFilterKindTerms selects exact values, with exclusions. It is what a // column filters by unless its type says otherwise, and the only kind whose // values the backend can be asked to enumerate. ColumnFilterKindTerms ColumnFilterKind = "terms" // ColumnFilterKindExact matches whole values like terms, and is never // offered as a list. It is what an identifier gets: enumerating one is a // scan of the whole result that answers with a page of the rows, so the // values are typed rather than picked. Same wire grammar as terms — a // comma-separated list whose "!" prefix excludes. ColumnFilterKindExact ColumnFilterKind = "exact" // ColumnFilterKindRange bounds a numeric field from below, above, or both. ColumnFilterKindRange ColumnFilterKind = "range" // ColumnFilterKindDuration bounds an elapsed-time field. Its operands are // Go duration strings ("500ms", "2m30s") or bare numbers, and both resolve // to the unit the column stores — ColumnDef.Unit, milliseconds when unset. // A bare number is already in that unit and passes through unchanged, so a // bound written before this kind existed still means what it did. ColumnFilterKindDuration ColumnFilterKind = "duration" // ColumnFilterKindTime bounds a date-and-time field. Its operands are // RFC3339 instants or date math ("now-15m"); OpenSearch resolves those // itself and SQL resolves them at bind time. ColumnFilterKindTime ColumnFilterKind = "time" // ColumnFilterKindDate bounds a date field to whole days. Its operands and // its compiled clause are a time bound's exactly; only the control differs, // because a field nobody reads the clock off is a calendar rather than a // timestamp. It is never inferred — a datetime column infers time. ColumnFilterKindDate ColumnFilterKind = "date" // ColumnFilterKindBoolean is a yes/no/any toggle. Unset is the third arm and // is not the same as false: a row missing the value is neither. ColumnFilterKindBoolean ColumnFilterKind = "boolean" // ColumnFilterKindText matches a substring rather than a whole value. It is // what an analyzed field with no exact-value sibling can offer, and it is // never inferred — a column asks for it explicitly. ColumnFilterKindText ColumnFilterKind = "text" // ColumnFilterKindWorkload selects one Kubernetes workload inside the broad // target scope declared by a profile query. ColumnFilterKindWorkload ColumnFilterKind = "workload" // ColumnFilterKindLabels selects Kubernetes label key/value pairs, grouped // by key in the browser and compiled into the target selector. ColumnFilterKindLabels ColumnFilterKind = "labels" // ColumnFilterKindNone marks a column that offers no filter. It is what a // structured type infers, and what an author sets to suppress one. ColumnFilterKindNone ColumnFilterKind = "none" )
func (ColumnFilterKind) CompilesAs ¶ added in v0.1.29
func (k ColumnFilterKind) CompilesAs() ColumnFilterKind
CompilesAs is the kind whose backend clause this kind produces, and the only question a provider has to ask. An exact match and a value selection differ in how the browser offers them, not in the predicate they compile to, and a switch that told them apart would be two arms doing one thing — and would refuse to merge a list param with an identifier column bound to one field.
Callers deciding a control or a grammar must not use it: whether a bound is written "5s" or "now-1h" is exactly what this throws away.
func (ColumnFilterKind) ControlType ¶ added in v0.1.29
func (k ColumnFilterKind) ControlType() string
ControlType is the clicky filter type this kind registers as, which decides the FilterBar control the browser renders. The vocabulary is clicky's own (entity.FilterSpec.Type), not one this package invents. An empty result means the filter needs no named component and travels as a plain parameter.
A time bound is "date-range" rather than "date": its wire value carries both edges under one key, and "date" is clicky's single-instant input. A date bound is "day-range", the same control with the clock taken off it.
Callers with a binding in hand should ask it instead — whether a value selection is picked or typed is not a property of the kind alone.
func (ColumnFilterKind) Lookupable ¶ added in v0.1.29
func (k ColumnFilterKind) Lookupable() bool
Lookupable reports whether the backend can be asked for this filter's values. Only a value selection has a list to offer; a range, a toggle, a substring and an exact match are typed, not picked — the last because the values it compares are identifiers rather than a vocabulary.
func (ColumnFilterKind) Normalized ¶ added in v0.1.29
func (k ColumnFilterKind) Normalized() ColumnFilterKind
Normalized resolves the zero value, which means a value selection. Spelling the common case out in every literal would buy nothing.
func (ColumnFilterKind) Valid ¶ added in v0.1.29
func (k ColumnFilterKind) Valid() bool
Valid reports whether k names a kind this package compiles.
type ColumnFilterValue ¶ added in v0.1.29
type ColumnFilterValue struct {
Column string
Key string
Field string
// Nested and Where carry the container this selection is compiled inside and
// the constants that address one entry of it. See ColumnFilterDef.
Nested string
Where map[string]string
// Kind is the grammar the value was parsed under and the one a provider
// compiles it back out of. Empty means a value selection.
Kind ColumnFilterKind
// Include and Exclude carry a value or substring selection.
Include []string
Exclude []string
// Range carries a numeric or time selection.
Range *FilterRange
// Bool carries a yes/no selection. Nil is the "any" arm.
Bool *bool
}
ColumnFilterValue is one resolved selection on its way to a provider.
func ResolveColumnFilters ¶ added in v0.1.29
func ResolveColumnFilters(profile Profile, input map[string]any) ([]ColumnFilterValue, error)
ResolveColumnFilters resolves filter.<column> request values against a profile's columns, for a caller that assembled the profile itself rather than loading a stored one — the connection browser, which infers its columns from the rows a first, unfiltered run returned. Params are not resolved here: an assembled profile declares none.
func (ColumnFilterValue) IsZero ¶ added in v0.1.29
func (v ColumnFilterValue) IsZero() bool
IsZero reports that nothing was selected, which is the same as the filter being absent.
type ColumnInspectionProvider ¶ added in v0.1.29
type ColumnInspectionProvider interface {
InspectColumnFilters(context.Context, ProviderRequest, []ColumnDef) (ColumnInspectionResult, error)
}
type ColumnInspectionResult ¶ added in v0.1.29
type ColumnInspectionResult struct {
Filters map[string]*ColumnFilterDef
Cache []inspection.CacheMetadata
// Counts is the distinct count per inspected *field* that the filter kinds
// above were chosen from. It is reported rather than kept internal because
// the count is the whole reason for the choice: without it, "why is this a
// text box and not a dropdown" can only be answered by re-running the probe
// by hand.
Counts map[string]int64
}
type ColumnKind ¶ added in v0.1.15
type ColumnKind string
ColumnKind enables semantic table behavior beyond value formatting.
const ( ColumnKindTimestamp ColumnKind = "timestamp" ColumnKindTags ColumnKind = "tags" ColumnKindStatus ColumnKind = "status" )
type ColumnType ¶ added in v0.1.13
type ColumnType string
ColumnType is the semantic type of a column. It drives default formatting in the render layer (see render.go) and the clicky-ui contract.
The set mirrors duty/view.ColumnType so view specs port cleanly; it is expanded with format/filter/badge metadata in Phase 2.
const ( ColumnTypeString ColumnType = "string" ColumnTypeNumber ColumnType = "number" ColumnTypeBoolean ColumnType = "boolean" ColumnTypeDateTime ColumnType = "datetime" ColumnTypeDuration ColumnType = "duration" ColumnTypeBytes ColumnType = "bytes" ColumnTypeStatus ColumnType = "status" ColumnTypeHealth ColumnType = "health" ColumnTypeUUID ColumnType = "uuid" ColumnTypeKeyValue ColumnType = "key_value" ColumnTypeKeyValues ColumnType = "key_values" ColumnTypeJSON ColumnType = "json" )
func (ColumnType) Enumerable ¶ added in v0.1.29
func (t ColumnType) Enumerable() bool
Enumerable reports whether asking the backend to list this column's distinct values would answer anything. An identifier is unique per row, so its value list is a list of the rows — a scan of the whole result to offer twenty values nobody recognizes. Such a column is typed into, not picked from.
type Cursor ¶ added in v0.1.29
type Cursor string
Cursor is a position in an ordered result set, resumed from by the request that follows.
It is opaque on purpose. A caller able to read a cursor is a caller able to build one, and a hand-built position is wrong in the one way this package cannot tolerate: quietly. Keeping it closed means the only positions in circulation are ones this package issued, so the checks below are checks on data that was once true rather than on a stranger's arithmetic.
func EncodeCursor ¶ added in v0.1.29
func EncodeCursor(encoding CursorEncoding) (Cursor, error)
EncodeCursor issues a cursor resuming after the last row of a page.
type CursorEncoding ¶ added in v0.1.30
type CursorEncoding struct {
Scope CursorScope
Keys []any
PIT string
Scroll string
State map[string][]byte
}
CursorEncoding is everything carried by one opaque cursor.
type CursorPosition ¶ added in v0.1.29
type CursorPosition struct {
// Keys are the ordered sort values of the last row of the previous page.
// A provider resumes strictly after them.
Keys []any
// PIT identifies the backend snapshot this walk is reading, when it has
// one. Empty means the walk sees the index as it changes.
PIT string
// Scroll identifies the OpenSearch scroll context this walk is reading.
// It is mutually exclusive with PIT.
Scroll string
// State is what each processor carried forward from the previous page,
// keyed by its label. A processor that folds rows across the whole result —
// a dedupe, say — is only correct page by page if it can remember what it
// has already emitted, and the cursor is where that memory lives, because
// the cursor is the only thing a resumed request brings with it.
State map[string][]byte
}
CursorPosition is a validated cursor: where to resume, and the point-in-time the walk is pinned to when the backend supports one.
func DecodeCursor ¶ added in v0.1.29
func DecodeCursor(c Cursor, scope CursorScope) (CursorPosition, error)
DecodeCursor validates c against the query it is being replayed on and returns the position to resume from. Every mismatch is ErrCursorStale: from the caller's side a forged cursor and an outdated one are the same event — this request cannot honour this position — and both must be refused.
func (CursorPosition) IsZero ¶ added in v0.1.29
func (p CursorPosition) IsZero() bool
IsZero reports whether this position names the start of the result set.
type CursorScope ¶ added in v0.1.29
type CursorScope struct {
Profile string
Provider string
Connection string
Query string
Options map[string]any
Order Order
Params map[string]any
Roles map[string]ParamRole
Filters []ColumnFilterValue
// Now is the clock Params were resolved against. One walk resolves its date
// math once — the first page picks the instant, every later page reads it
// back off the cursor — so a rolling window names one result set for as long
// as the walk lasts.
Now time.Time
}
CursorScope is everything a cursor is valid against: the query that issued it, the order it was cut from, and the inputs deciding which rows exist.
It is the resolved inputs rather than the built ProviderRequest because a keyset profile templates the cursor's own keys into its query text — so the request a cursor is checked against is one the cursor helped build, and checking against it would be circular.
type DiagnosticDetail ¶ added in v0.1.29
type DiagnosticDetail int
DiagnosticDetail says how much a run is willing to pay to be explained.
const ( // DiagnosticRendered records what was sent and what came back, and nothing a // backend has to be asked for. It is what a run that is not a debug run can // afford — a reconciliation records itself on every execution, not only when // someone is watching. DiagnosticRendered DiagnosticDetail = iota // DiagnosticFull additionally records response previews and whatever // backend-side instrumentation the provider can switch on. DiagnosticFull )
func DetailForLevel ¶ added in v0.1.29
func DetailForLevel(level logger.LogLevel) DiagnosticDetail
DetailForLevel maps a capture level onto how much a run pays to be explained.
Trace is the boundary because trace is where the connection policy first asks a backend for something it would not otherwise say — the SQL statement, the HTTP headers, ClickHouse's own log stream.
type DiagnosticError ¶ added in v0.1.29
type DiagnosticError struct {
Err error
Diagnostics *ProviderDiagnostics
}
func (*DiagnosticError) Error ¶ added in v0.1.29
func (e *DiagnosticError) Error() string
func (*DiagnosticError) Unwrap ¶ added in v0.1.29
func (e *DiagnosticError) Unwrap() error
type DiagnosticOptions ¶ added in v0.1.29
type DiagnosticOptions struct {
Provider string
Query string
Options map[string]any
Detail DiagnosticDetail
Walk bool
}
DiagnosticOptions describes what a recorder records and how much it pays to.
Walk records a many-page read as one request rather than as forty: the first statement issued is the one reported, and every page's rows and duration are summed into one response. A walk's last page is not what it ran — recording page forty the way a single request is recorded reports `OFFSET 19500` as the query, which is true of that page and false of the read.
Detail and Walk are independent, which is the whole reason this is one constructor rather than two. Ordinary paged execution is a walk at DiagnosticRendered, because a profile read must not switch on per-page previews nobody asked for; an armed debug run of the same profile is the same walk at DiagnosticFull.
type Event ¶ added in v0.1.15
type Event struct {
SessionID string `json:"sessionId"`
Sequence int64 `json:"sequence"`
Time time.Time `json:"time"`
Row Row `json:"row,omitempty"`
Rows []Row `json:"rows,omitempty"`
Error string `json:"error,omitempty"`
}
Event is one emission from a session: a single streamed row (trace) or a full snapshot for one tick (top).
type ExecutionDetail ¶ added in v0.1.29
type ExecutionDetail struct {
Summary ExecutionSummary `json:"summary"`
Operations []*ProviderDiagnostics `json:"operations,omitempty"`
HAR *har.File `json:"har,omitempty"`
Logs []LogLine `json:"logs,omitempty"`
Probes []CardinalityProbe `json:"probes,omitempty"`
Inspections []InspectionRecord `json:"inspections,omitempty"`
// HARSensitive reports that credential capture was switched on, so the
// entries above hold live secrets. Stated rather than refused: it is a
// deliberate operator setting for replay, and a console that hands the file
// to a colleague has to know which kind of file it is.
HARSensitive bool `json:"harSensitive,omitempty"`
}
ExecutionDetail is the expensive half, served for one record on demand.
type ExecutionSource ¶ added in v0.1.29
type ExecutionSource struct {
Surface string `json:"surface"` // profile | browser | sample | reconcile
Profile string `json:"profile,omitempty"`
Method string `json:"method,omitempty"`
Path string `json:"path,omitempty"`
// Query is the client's query string with credential-shaped values blanked,
// kept so a console can offer "re-run with bodies" without the caller having
// to reconstruct the request by hand.
Query string `json:"query,omitempty"`
}
ExecutionSource says what asked for this execution. Surface is the seam the request came in on; a record whose Surface is empty means something armed a request the recorder does not understand, which is a bug rather than a row.
type ExecutionSummary ¶ added in v0.1.29
type ExecutionSummary struct {
ID string `json:"id"`
Sequence int64 `json:"sequence"` // assigned by the store; the SSE event id
Source ExecutionSource `json:"source"`
StartedAt time.Time `json:"startedAt"`
DurationMS float64 `json:"durationMs"`
Rows int `json:"rows"`
Status int `json:"status,omitempty"`
Error string `json:"error,omitempty"`
// Level names the capture level this run was armed at. A console showing a
// record it did not arm needs it to explain why the bodies are missing,
// rather than implying the request made none.
Level string `json:"level"`
Operations []OperationSummary `json:"operations,omitempty"`
Counts RecordCounts `json:"counts"`
}
ExecutionSummary is the cheap half: what ran, against what, how long, and how much came back.
type ExpressionOptions ¶ added in v0.1.29
type ExpressionOptions struct {
// Scope selects the binding environment.
Scope ExpressionScope
// Rows are the rows the caller already holds. In ScopeRow and ScopeBoundary
// each row produces a result; in ScopeBatch they are one group producing one.
Rows []Row
// Keep chooses the kept row of a ScopeBatch group: "first" (default) or
// "last".
Keep string
}
ExpressionOptions describes what an expression is being evaluated against.
type ExpressionResult ¶ added in v0.1.29
type ExpressionResult struct {
// Index is the row this result came from, or 0 for a batch.
Index int `json:"index"`
Value any `json:"value,omitempty"`
Type string `json:"type,omitempty"`
Error string `json:"error,omitempty"`
}
ExpressionResult is one evaluation, successful or not.
A failure is a field rather than a returned error because a half-written expression is the normal state of the input this serves, and because the per-row outcome is the entire point: `applyRowTransforms` aborts a whole sample on the first row a column expression throws on, so an author asking "does this work" currently learns only that some row did not.
func EvalExpression ¶ added in v0.1.29
func EvalExpression(ctx context.Context, expression string, options ExpressionOptions) ([]ExpressionResult, error)
EvalExpression evaluates expression once per row, or once per group for ScopeBatch, and reports each outcome separately.
The rows travel in from the caller because they came out of /profile/sample moments earlier. Re-running someone's backend query on every keystroke to fetch rows the caller is already holding would make the preview cost money.
type ExpressionScope ¶ added in v0.1.29
type ExpressionScope string
ExpressionScope names the binding environment a profile's CEL is evaluated in.
A profile carries expressions in three different environments and the document gives no sign of which is which — `columns[].cel` and `processors[].config.set.x` are two strings in the same YAML that compile against different worlds. Anything offering to evaluate one has to be told which it is holding.
const ( // ScopeRow is cel.go's environment: `row`, `span`, and every row key that is // a valid identifier bound bare. Used by columns, aliases, filters, styles, // replay and the reconcile key. ScopeRow ExpressionScope = "row" // ScopeBatch is the merge environment: `batch`, `first`, `last`, `count` and // the kept `row`. Used by a processor's `set`, `when` and `emit`. ScopeBatch ExpressionScope = "batch" // ScopeBoundary is the grouping environment: `row`, `prev` and `index`. Used // by a batch processor's `continuation` and `boundary` predicates. ScopeBoundary ExpressionScope = "boundary" )
type FilterBound ¶ added in v0.1.29
FilterBound is one edge of a range. Value is carried as the kind stores it: a float64 for a numeric field and for a duration field (already resolved into the column's own unit), and for a date field the operand as written — either an RFC3339 instant or date math, which the backend resolves.
type FilterDef ¶ added in v0.1.29
type FilterDef struct {
// Name identifies the filter in the UI and in error messages. Required for a
// quick filter, which a UI has to be able to select by name.
Name string `json:"name,omitempty" yaml:"name,omitempty"`
// Description surfaces in the UI tooltip and listing.
Description string `json:"description,omitempty" yaml:"description,omitempty"`
// Fields holds CEL predicates keyed by a label, AND-ed together. Each is
// evaluated with the row bound as `row`.
Fields map[string]string `json:"fields,omitempty" yaml:"fields,omitempty"`
// Exclude inverts the match: matching rows are dropped rather than kept.
Exclude bool `json:"exclude,omitempty" yaml:"exclude,omitempty"`
// Hidden marks the filter always-on rather than togglable.
Hidden bool `json:"hidden,omitempty" yaml:"hidden,omitempty"`
}
FilterDef is a named row predicate evaluated after aliases. Profile authors use it to trim known noise — access logs, health checks, per-second heartbeats — without wrapping every column in a conditional.
A filter matches when every entry in Fields evaluates to true. Exclude inverts what a match means: false (the default) keeps matching rows and drops the rest, true drops matching rows.
Hidden decides whether the filter applies at all. Hidden filters are always-on server-side noise suppression. A non-hidden filter is a declared quick filter for a UI to offer as a toggle: it is carried on the profile and never applied here, because applying one unconditionally would hide useful default output — which is what a predicate like `row.level == "ERROR"` would do to every INFO line.
type FilterLookupProvider ¶ added in v0.1.29
type FilterLookupProvider interface {
LookupFilterValues(ctx context.Context, req ProviderRequest, binding ColumnFilterBinding, search string, limit int) ([]FilterOption, *Total, error)
}
FilterLookupProvider resolves distinct backend values for one bound column.
The total is a *Total rather than an int because not every backend can count exactly: a SQL COUNT is the number, an OpenSearch cardinality aggregation is an estimate, and nil is "the backend did not say". Collapsing the three into an int is what lets an estimate be rendered as a count.
type FilterOption ¶ added in v0.1.29
type FilterRange ¶ added in v0.1.29
type FilterRange struct {
Min *FilterBound
Max *FilterBound
}
FilterRange is a bounded selection. Either edge may be absent, leaving that side open; both absent is not a selection.
type FilterValueLookupRequest ¶ added in v0.1.29
type FinishOptions ¶ added in v0.1.29
FinishOptions is what the transport knows about a finished request. Row counts are deliberately absent: the operations report their own, and a middleware that had to be told would be guessing for any request that ran more than one.
type InspectionCacheSummary ¶ added in v0.1.30
type InspectionCacheSummary struct {
Policy string `json:"policy"`
State string `json:"state"`
Age string `json:"age"`
Cached bool `json:"cached"`
}
func SummarizeInspectionCache ¶ added in v0.1.30
func SummarizeInspectionCache(cache []inspection.CacheMetadata) InspectionCacheSummary
type InspectionCardinality ¶ added in v0.1.30
type InspectionField ¶ added in v0.1.30
type InspectionField struct {
ID string `json:"id"`
Name string `json:"name"`
Source string `json:"source,omitempty"`
DatabaseType string `json:"databaseType"`
SemanticType string `json:"semanticType"`
Cardinality *InspectionCardinality `json:"cardinality,omitempty"`
Filter InspectionFilterResolution `json:"filter"`
}
func (InspectionField) Columns ¶ added in v0.1.30
func (InspectionField) Columns() []api.ColumnDef
func (InspectionField) Row ¶ added in v0.1.30
func (field InspectionField) Row() map[string]any
type InspectionFilterResolution ¶ added in v0.1.30
type InspectionLimit ¶ added in v0.1.30
type InspectionLimit struct {
Label string `json:"label"`
Value int `json:"value"`
Origin string `json:"origin"`
}
func (InspectionLimit) Columns ¶ added in v0.1.30
func (InspectionLimit) Columns() []api.ColumnDef
func (InspectionLimit) Row ¶ added in v0.1.30
func (limit InspectionLimit) Row() map[string]any
type InspectionOptions ¶ added in v0.1.29
type InspectionOptions struct {
Refresh bool
}
type InspectionPaging ¶ added in v0.1.30
type InspectionRecord ¶ added in v0.1.29
type InspectionRecord struct {
// Policy is the cache class (inspect.CacheClass) and Key the entry within
// it. The key is a digest of the request, so it correlates two lookups
// without disclosing what was in them.
Policy string `json:"policy"`
Key string `json:"key"`
// ElapsedMS is what this caller waited: ~0 for a hit, the whole fill for a
// miss. That difference is the reason this record exists.
ElapsedMS float64 `json:"elapsedMs"`
Cached bool `json:"cached"`
State string `json:"state,omitempty"` // fresh | stale
// Refreshing says a background fill is running behind the value served.
Refreshing bool `json:"refreshing,omitempty"`
AgeMS int64 `json:"ageMs"`
// RefreshError is a failed refresh behind a value that was still served;
// Error is a failure the caller actually got. Distinct because the first is
// a warning about staleness and the second is why the page has no filters.
RefreshError string `json:"refreshError,omitempty"`
Error string `json:"error,omitempty"`
}
InspectionRecord is one inspection-cache lookup this request made.
Column filters, OpenSearch field mappings and SQL catalogs are all memoised behind inspect.Memo, and the cache already returns how the lookup went — it simply had no one to tell. Without it a first page and a warm page differ by seconds with nothing in the record to explain the gap, and "why did opening this profile take four seconds once" stays a mystery.
type InspectionResult ¶ added in v0.1.30
type InspectionResult struct {
Name string `json:"name"`
Provider string `json:"provider"`
Connection string `json:"connection"`
ScopeLabel string `json:"scopeLabel"`
Scope string `json:"scope"`
Query string `json:"query"`
Status string `json:"status"`
StatusNote string `json:"statusNote"`
DurationMS float64 `json:"durationMs,omitempty"`
Cache InspectionCacheSummary `json:"cache"`
Fields []InspectionField `json:"fields"`
Paging InspectionPaging `json:"paging"`
}
func NewProfileInspectionResult ¶ added in v0.1.30
func NewProfileInspectionResult(profile Profile, sample *SampleResult) *InspectionResult
func (*InspectionResult) Pretty ¶ added in v0.1.30
func (result *InspectionResult) Pretty() api.Text
func (*InspectionResult) PrettyFull ¶ added in v0.1.30
func (result *InspectionResult) PrettyFull() api.Textable
type InspectionStatus ¶ added in v0.1.29
type InspectionStatus struct {
Status string `json:"status"`
Message string `json:"message,omitempty"`
Cache []inspection.CacheMetadata `json:"cache,omitempty"`
Counts map[string]int64 `json:"counts,omitempty"`
}
type JSONPathFilterTarget ¶ added in v0.1.29
type JSONPathFilterTarget struct {
// Field is the backend field the selection applies to.
Field string
// Container names the field the path descends through to reach Field, when it
// reaches it by picking one entry rather than by naming it. It is set only
// alongside Where.
//
// Whether the backend can actually correlate Where with Field inside it is
// not something the path can know — it depends on how the container is
// mapped, which is why a profile declares it as filter.nested and the
// connection browser reads it from the index mapping.
Container string
// Where is the constants the selection also requires, keyed by backend field.
// It is what addresses one entry of a key/value tag list: the path fixes the
// key and the operator picks the value.
Where map[string]string
}
JSONPathFilterTarget is how a jsonpath column pushes a selection down to the backend: the field the picked values compare against, the container that field sits inside, and the constants that address one entry of that container.
func FilterTargetForJSONPath ¶ added in v0.1.29
func FilterTargetForJSONPath(expression, source string) (JSONPathFilterTarget, bool)
FilterTargetForJSONPath resolves the backend selection a jsonpath filters through, for the two shapes that address a field rather than merely select values.
A plain chain of literal keys addresses exactly one field of the document, so the dotted join of its segments is the name a document store indexes it under — which is what makes a promoted JSON column filterable without the author writing filter.field by hand.
A chain that steps through one equality filter and carries on — the shape of `$.tags[?(@.key == 'app')].value` — addresses one field too, but only of the entry the equality picks. That is the shape tags arrive in from Jaeger and OpenTelemetry, and reading it here is what lets such a column be narrowed at the index instead of after the rows are home.
Anything else — a wildcard, a descent, an array index, an inequality, a second filter — matches a set whose size depends on the row, so it names no field and the column stays unfilterable unless the author declares one.
type KeyRange ¶ added in v0.1.29
type KeyRange struct {
From string `json:"from,omitempty" yaml:"from,omitempty"`
To string `json:"to,omitempty" yaml:"to,omitempty"`
}
KeyRange narrows a reconcile to the keys in [From, To). An empty end is open, so an empty range is everything.
It is half-open on purpose: consecutive ranges that share a boundary cover every key exactly once, which is what lets a large reconcile be split into pieces — by hand or across workers — without a key falling into both or neither.
func (*KeyRange) After ¶ added in v0.1.29
After reports whether key sorts at or past the end of the range. On an ordered walk it means the rest of that side cannot contribute, so the walk stops rather than reading to the end of the dataset.
func (*KeyRange) Before ¶ added in v0.1.29
Before reports whether key sorts before the range, which on an ordered walk means it can be skipped without reading further.
func (*KeyRange) Clone ¶ added in v0.1.29
Clone returns a copy, so a merged config never aliases a stored range.
type KeySpec ¶ added in v0.1.29
type KeySpec struct {
// Columns names the row keys whose values, joined in order, form the key.
Columns []string `json:"columns,omitempty" yaml:"columns,omitempty"`
// CEL is an expression evaluated against the row. The row is exposed as
// both `row` and `span`, and every row key that is a valid CEL identifier
// is bound as a top-level variable.
CEL string `json:"cel,omitempty" yaml:"cel,omitempty"`
}
KeySpec derives a comparison key from a Row. Exactly one of Columns or CEL must be set.
Columns is the right choice when both sides of a comparison share a schema — the same profile at two points in time. CEL is required when they do not: a join across two profiles reads the same logical identity out of differently named or nested fields on each side.
type LogLine ¶ added in v0.1.29
type LogLine struct {
Sequence int64 `json:"sequence"`
Time time.Time `json:"time"`
Level string `json:"level"`
Source string `json:"source"` // request | process
Logger string `json:"logger,omitempty"`
Event string `json:"event,omitempty"` // an observability.Event, when it came from one
Message string `json:"message"`
Values map[string]any `json:"values,omitempty"`
// RecordID and Operation say which execution, and which operation within it,
// this line belongs to. Operation is 0 when the line is not attributable to
// one — a line the request logged before any provider ran.
RecordID string `json:"recordId,omitempty"`
Operation int `json:"operation,omitempty"`
}
LogLine is one line a request logged, or one the process logged while no request owned it.
Source distinguishes the two because they are not equally trustworthy: a request line is captured structurally, with its values intact, while a process line is scraped from the writer the logger was already using and has been rendered to text by the time it arrives.
type NamedProcessor ¶ added in v0.1.29
type NamedProcessor struct {
// Name is the library key referenced by ProcessorSpec.Use (e.g.
// "java.stacktrace").
Name string `json:"name" yaml:"name"`
// Title is the human label shown in the profile editor.
Title string `json:"title,omitempty" yaml:"title,omitempty"`
// Description explains what the preset does to a result set.
Description string `json:"description,omitempty" yaml:"description,omitempty"`
// Spec is the processor type and its default configuration.
Spec ProcessorSpec `json:"spec" yaml:"spec"`
}
NamedProcessor is a reusable, named ProcessorSpec preset — the processor equivalent of Profile.Imports. A profile references it as `processors: [{use: <name>}]` instead of restating the type and its whole configuration, and can still override individual config keys.
Implementations register their presets alongside the processor itself, in the subpackage consumers blank-import.
func GetNamedProcessor ¶ added in v0.1.29
func GetNamedProcessor(name string) (NamedProcessor, error)
GetNamedProcessor returns the library entry for name, or an error listing the available names.
func NamedProcessors ¶ added in v0.1.29
func NamedProcessors() []NamedProcessor
NamedProcessors returns every library entry, sorted by name. The profile schema uses this to offer the presets as an enum.
type Operation ¶ added in v0.1.29
type Operation struct {
// contains filtered or unexported fields
}
Operation is one provider operation's handle back to the recorder.
func (*Operation) Complete ¶ added in v0.1.29
func (o *Operation) Complete(result OperationResult)
Complete files the operation's result. The caller guarantees it runs once — connectionOperation.Finish is sync.Once-guarded — so this does not re-guard.
type OperationResult ¶ added in v0.1.29
type OperationResult struct {
Diagnostics *ProviderDiagnostics
Entries []har.Entry
Dropped int
Sensitive bool
Duration time.Duration
Rows int
Err error
}
OperationResult is everything one finished operation contributes.
type OperationSummary ¶ added in v0.1.29
type OperationSummary struct {
Index int `json:"index"` // 1-based, in the order they started
Provider string `json:"provider"`
Connection string `json:"connection,omitempty"`
Query string `json:"query,omitempty"`
Method string `json:"method,omitempty"`
URL string `json:"url,omitempty"`
Status int `json:"status,omitempty"`
DurationMS float64 `json:"durationMs"`
Rows int `json:"rows"`
Pages int `json:"pages,omitempty"`
Error string `json:"error,omitempty"`
}
OperationSummary is one provider operation inside a request: ProviderDiagnostics with everything expensive left out.
A request is routinely more than one operation — a profile with context sub-queries runs several — and reporting them as one row is what makes "which of these three queries was slow" unanswerable today.
type Order ¶ added in v0.1.29
type Order []OrderBy
Order is a Profile's declared result order.
It exists because paging of either kind is meaningless without one. An offset names a position in a sequence, and a cursor names a row to resume after; neither is well defined when consecutive executions may return rows in different orders, which is what both backends are free to do when nothing asks them otherwise.
func NaturalOrder ¶ added in v0.1.29
func NaturalOrder(config ProviderConfig) (Order, error)
NaturalOrder returns the order the configured provider can page by, or nil when it has none to offer.
A nil order is an answer rather than a failure: the profile stays un-pageable and Pageable() keeps reporting why, which is the same outcome every provider had before any of them could answer this. An error means the provider knows an order but could not read the options to build it, which is a broken profile and is reported as one.
func (Order) Fingerprint ¶ added in v0.1.29
Fingerprint identifies this order for cursor validation. A cursor holds the sort values of the row it resumes after, so it is only meaningful against the order that produced them — replaying it against a different one would resume from a position that never existed.
type OrderBy ¶ added in v0.1.29
type OrderBy struct {
// Column is the row key to order by.
Column string `json:"column" yaml:"column"`
// Desc orders this column descending.
Desc bool `json:"desc,omitempty" yaml:"desc,omitempty"`
// Unique asserts this column breaks all remaining ties, which is what makes
// the order total. Paging requires the order to end in one.
//
// We validate that the author asserted a tiebreaker, not that the assertion
// is true: whether a column is unique is a fact about their schema, not
// about anything reachable from here. An untrue assertion produces the same
// unstable paging an undeclared order does — but it does so having been
// claimed, which is the difference between a bug and a silent default.
Unique bool `json:"unique,omitempty" yaml:"unique,omitempty"`
}
OrderBy is one column of a Profile's result order.
type OrderingProvider ¶ added in v0.1.29
type OrderingProvider interface {
// NaturalOrder returns the order this provider would page by, given the
// options the profile configured it with.
NaturalOrder(config ProviderConfig) (Order, error)
}
OrderingProvider is implemented by a provider that can name a total order for a profile which declares none.
Paging of either kind needs a total order, and a profile without one dead-ends at its first page — the surface describing it cannot offer a position, so the caller is left with a row count and nowhere to step. For most providers that is the only honest answer: nothing reachable from a SQL profile says which of its columns is unique. A document store is different. OpenSearch indexes every hit with a position that is unique by construction, so the order a profile declined to declare is one the provider already knows, and requiring it to be typed out is a toll rather than a question.
The order returned is a paging device, not a claim about what the author meant. It is used to cut and resume pages; it is deliberately not used where an order carries meaning the author must own — the reconcile merge join reads the declared order and only that.
type Page ¶ added in v0.1.29
type Page struct {
Rows []Row
// Styles are the evaluated cell styles for Rows, positionally parallel to
// it. Nil when no column declares a Style.
Styles []map[string]string
// Next resumes after this Page's last row. Empty when the source is
// exhausted, and never set by a provider that cannot cursor.
//
// A provider does not set this: it sets NextKeys and the engine mints the
// token, so the format and its validation stay in one place rather than in
// every backend.
Next Cursor
// NextKeys are the ordered sort values of this Page's last row, in the
// order's column order. A cursoring provider sets them; ExecutePages turns
// them into Next.
NextKeys []any
// PIT is the backend snapshot this page was read from, when the provider
// opened one. It travels inside Next so the next request lands on the same
// snapshot rather than on the index as it has since become.
PIT string
// Scroll is the OpenSearch scroll context that resumes this page. It is
// mutually exclusive with PIT and travels inside Next for the same reason:
// the next request must continue the backend cursor that produced this page.
Scroll string
// State is what each processor asked to carry into the next page, keyed by
// label. Like PIT it travels inside Next rather than being returned to the
// caller, so a resumed walk hands each processor back its own memory.
// Providers never set it; ProcessPages does.
State map[string][]byte
// HasMore reports that the source had rows beyond this Page.
//
// It is the provider's answer rather than an inference from len(Rows),
// because a short page and the end of the data are different facts that
// look identical from the outside — and reading one as the other is how a
// partial answer starts being presented as a complete one.
HasMore bool
// Total is the size of the whole result set when the source knows it.
Total *Total
// Truncated reports that the provider applied a cap of its own — a
// configured limit, a backend default, a result-window ceiling — so a short
// Page is never mistaken for the end of the data.
//
// This is distinct from HasMore: HasMore says the caller may ask for more,
// Truncated says the source already decided not to give it. A caller that
// asked for everything needs to know the difference.
Truncated bool
}
Page is one batch of an ordered result set together with the position that resumes after it.
A batch rather than a row, because a batch is the unit both backends actually fetch and the only place a cursor is well defined: a position between pages is something the source can hand back, whereas a position between rows of one fetch is something it would have to be asked to invent.
type PageInfo ¶ added in v0.1.29
type PageInfo struct {
Mode string `json:"mode"`
Limit int `json:"limit"`
Offset int `json:"offset,omitempty"`
Cursor Cursor `json:"cursor,omitempty"`
NextCursor Cursor `json:"nextCursor,omitempty"`
HasMore bool `json:"hasMore"`
Total *int64 `json:"total,omitempty"`
TotalRelation string `json:"totalRelation"`
Consistency string `json:"consistency"`
}
func NewPageInfo ¶ added in v0.1.29
func NewPageInfo(request PageRequest, page Page) PageInfo
type PageProcessor ¶ added in v0.1.29
type PageProcessor interface {
Processor
ProcessPage(ctx context.Context, spec ProcessorSpec, page Page, state []byte) (Page, []byte, error)
}
PageProcessor is an optional Processor capability: a processor that can run on one page at a time, so a profile using nothing else can still be served page by page.
state is what this processor returned from the previous page of the same walk, and is nil on the first. It travels inside the cursor, which is the only thing a resumed request carries — so a processor that folds rows across the whole result can still run incrementally by remembering what it has already emitted. Whatever it returns is handed back on the next page; returning nil keeps the processor stateless, which is all a per-row transform needs.
A processor that does not implement this is not deficient — a merge or a reconcile genuinely needs every row before any row is correct. It just means the profile answering with it has to run its query in full, which is a cost worth being able to name rather than discover.
type PageRequest ¶ added in v0.1.29
type PageRequest struct {
// Limit is the most rows one Page may carry. Required and positive.
Limit int
// Offset skips rows from the start of the order. Offset paging only.
Offset int
// Cursor resumes immediately after the position it encodes. Cursor paging
// only.
Cursor Cursor
// Sort names the column this request orders by, overriding the order the
// profile declared. It is a public column name, not a backend field: the
// profile resolves it through the same target inference its filters use, so
// a caller never has to know what the backend calls anything.
//
// The requested column leads the order rather than replacing it — the
// profile's declared tiebreaker still has to end it, or paging past the
// first page would have no stable meaning. See Profile.RequestedOrder.
Sort string
// Desc orders Sort descending. It is inert without one.
Desc bool
// Strategy chooses how to page when the request does not already say.
//
// It is needed because the first page of a cursor walk carries no cursor,
// and so is indistinguishable from an offset page at position zero. Zero
// means offset, which is the strategy a caller gets by not thinking about
// it — and the one that can jump to an arbitrary page.
Strategy PagingMode
// Ceiling bounds the whole walk rather than one page: an export reads
// forward to it and stops. Zero leaves the walk unbounded. A provider that
// can push it down to the backend should, so the read stops where the export
// does instead of continuing past it into rows nobody will receive.
Ceiling int
// SkipTotal releases the provider from reporting the size of the whole
// result, and a provider that takes it up must report no total rather than a
// zero one — Total.Relation tells those apart, and a caller reading "exactly
// 0" while rows stream past is worse served than one reading "unknown".
//
// It exists because stating an exact total can cost the whole result: see
// buildPagedSQL, where it is the difference between a walk that streams and
// one that materializes. A page cannot waive it — a table has to say what it
// is a page of — so this is an export's trade to make.
SkipTotal bool
// Diagnostics is non-nil only for an explicit debug execution. It is not a
// paging input and is excluded from cursor scope.
Diagnostics *ProviderDiagnostics `json:"-"`
Inspection InspectionOptions `json:"-"`
}
PageRequest is the caller's position in an ordered result set.
Cursor and Offset are mutually exclusive. A cursor already encodes where it resumes, so a request carrying both states its position twice, and the two statements can disagree — there is no reading of that request which is obviously right, so it is refused instead of resolved.
func (PageRequest) Mode ¶ added in v0.1.29
func (r PageRequest) Mode() PagingMode
Mode reports which strategy this request asks for.
func (PageRequest) Validate ¶ added in v0.1.29
func (r PageRequest) Validate() error
Validate rejects a request no provider could serve unambiguously.
type PagingMode ¶ added in v0.1.29
type PagingMode uint8
PagingMode is the set of paging strategies a provider can serve.
const ( // PagingOffset skips rows by position. It is sound only under a total // order, and costs O(offset) against a backend that cannot push the skip // down — which it must be assumed cannot, since the alternative is // rewriting a query whose text belongs to its author. PagingOffset PagingMode = 1 << iota // PagingCursor resumes from the position carried by the previous page. It // requires a total order and is O(1) at any depth, which is what makes the // hundredth page cost the same as the first. PagingCursor )
func SupportsPaging ¶ added in v0.1.29
func SupportsPaging(providerType string) PagingMode
SupportsPaging reports which paging strategies the registered provider can serve natively. A provider with none is still pageable — the engine slices a buffered result for it — but only by running the whole query per page, which is why the distinction is worth being able to ask about.
func (PagingMode) String ¶ added in v0.1.29
func (m PagingMode) String() string
String names the modes for errors and for the API description.
func (PagingMode) Supports ¶ added in v0.1.29
func (m PagingMode) Supports(mode PagingMode) bool
Supports reports whether m can serve mode.
type PagingProvider ¶ added in v0.1.29
type PagingProvider interface {
Provider
Pages(ctx context.Context, req ProviderRequest, page PageRequest) iter.Seq2[Page, error]
// PagingModes reports which strategies this provider can serve, so a
// request for one it cannot is refused before it runs rather than quietly
// answered with another.
PagingModes() PagingMode
}
PagingProvider is the result contract implemented by every provider that can serve a page.
Pages yields consecutive Pages starting at page and keeps yielding until the source is exhausted or the consumer stops ranging. One page and every page are therefore the same call — a caller wanting one breaks after the first — and ending the range is what releases the backend cursor, so the release cannot be forgotten the way an explicit Close can.
Errors are yielded in band as the second value. The sequence yields at most one error and then stops, and a setup failure surfaces on the first iteration rather than through a second return value, so a consumer has one error path instead of two.
type ParamDef ¶ added in v0.1.13
type ParamDef struct {
// Name is the parameter key, referenced as `{{.params.<Name>}}` (or
// `$(.params.<Name>)`) in the query, in any provider option, or in the
// connection.
Name string `json:"name" yaml:"name"`
// Label is the human-facing name for the FilterBar. Defaults to Name.
Label string `json:"label,omitempty" yaml:"label,omitempty"`
// Type drives validation/coercion. Defaults to string. Identifier is for a
// SQL database, schema, table, or column name and is dialect-quoted rather
// than bound as a value.
Type ParamType `json:"type,omitempty" yaml:"type,omitempty"`
// Role maps the parameter to a first-class table control. Empty defaults to
// filter. A profile can rename the pager parameters by assigning limit and
// offset roles; time-from/time-to form one server-backed date-range picker.
Role ParamRole `json:"role,omitempty" yaml:"role,omitempty"`
// Default is used when no value is supplied.
Default any `json:"default,omitempty" yaml:"default,omitempty"`
// Options enumerates the allowed values (an enum). When set, a supplied value
// must be one of these. A list parameter validates every selected value
// against them, and leaving them empty asks the provider for its distinct
// values instead.
Options []string `json:"options,omitempty" yaml:"options,omitempty"`
// Field is the backend field an include/exclude selection binds to. Declaring
// it makes a list parameter tri-state: a value prefixed with "!" excludes,
// and both halves resolve into the same native filter clauses a column filter
// produces. `params.<Name>` still carries only the includes, so a query
// template and an esdsl multi-operand condition see a plain list either way.
// Only a provider that applies native filters may declare it — Validate
// rejects the rest, so an exclusion can never be silently dropped.
Field string `json:"field,omitempty" yaml:"field,omitempty"`
// Required fails execution when no value (and no Default) is supplied.
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
// Description is shown as the FilterBar tooltip.
Description string `json:"description,omitempty" yaml:"description,omitempty"`
// Template optionally rewrites the resolved value; "{value}" is replaced with
// the supplied value (e.g. "{value}-api").
Template string `json:"template,omitempty" yaml:"template,omitempty"`
}
ParamDef declares one server-side filter parameter of a Profile. Supplied values are validated and coerced against the declaration, then exposed under `params.<Name>` to everything the provider is handed — the query, the provider options and the connection — before it runs. This mirrors legacy trace-profile params.
func (ParamDef) DisplayLabel ¶ added in v0.1.13
DisplayLabel returns the Label when set, otherwise the Name.
type ParamRole ¶ added in v0.1.15
type ParamRole string
ParamRole assigns a profile parameter to a first-class table control. Filter is the default; limit/offset drive the pager, sort/order are paired into the table's column sort and time-from/time-to are paired into its date-range control. Cursor is a reserved transport parameter because its provider-owned position must never be rendered into query text.
type ParamType ¶ added in v0.1.13
type ParamType string
ParamType is the declared type of a Profile parameter. It drives validation, coercion of incoming (string) values, and the per-profile JSON schema.
const ( ParamTypeString ParamType = "string" ParamTypeNumber ParamType = "number" ParamTypeBoolean ParamType = "boolean" ParamTypeDate ParamType = "date" ParamTypeDateTime ParamType = "datetime" ParamTypeDuration ParamType = "duration" ParamTypeEnum ParamType = "enum" ParamTypeIdentifier ParamType = "identifier" // ParamTypeList accepts several values at once. `params.<Name>` holds the // included values as a []string, which an esdsl multi-operand condition // (terms, ids) binds directly. SQL providers expand a direct reference into // separately bound values; other providers may template the list explicitly. ParamTypeList ParamType = "list" // ParamTypeLabels is a Kubernetes label value selector bound to one // labels.<key> field. It has the same include/exclude wire form as list, but // lets the browser render a label-aware control. ParamTypeLabels ParamType = "labels" )
type ParameterizedQuery ¶ added in v0.1.29
type Processor ¶ added in v0.1.13
type Processor interface {
// Type is the registry key (e.g. "sqlite.merge", "sqlite.recon").
Type() string
// Process transforms in according to spec and returns the new Result.
Process(ctx context.Context, spec ProcessorSpec, in *Result) (*Result, error)
}
Processor is a post-query step applied to a Result (e.g. sqlite merge, reconciliation). Implementations self-register via RegisterProcessor and are selected by ProcessorSpec.Type. Like providers, processors live in a subpackage that consumers blank-import.
func GetProcessor ¶ added in v0.1.13
GetProcessor returns the registered Processor for typ, or an error listing the available types.
type ProcessorPreview ¶ added in v0.1.29
type ProcessorPreview struct {
Input []Row `json:"input"`
Stages []ProcessorPreviewStage `json:"stages"`
}
ProcessorPreview carries the source sample and the output after each ordered processor. A whole-result processor sees only Input: this is a bounded preview, not a claim about the complete query result.
type ProcessorPreviewStage ¶ added in v0.1.29
type ProcessorSpec ¶ added in v0.1.13
type ProcessorSpec struct {
// Type is the registered processor key (e.g. "sqlite.merge", "sqlite.recon").
// Optional when Use names a library entry, which supplies it.
Type string `json:"type,omitempty" yaml:"type,omitempty"`
// Use names a library processor (see RegisterNamedProcessor) whose type and
// configuration this spec starts from — the processor equivalent of
// Profile.Imports. Config set here is merged over the preset's, so a profile
// can adopt a shared transform and still override a single key.
Use string `json:"use,omitempty" yaml:"use,omitempty"`
// Config is the processor-specific configuration.
Config map[string]any `json:"config,omitempty" yaml:"config,omitempty"`
}
ProcessorSpec names a post-query processor and carries its raw config, which the processor decodes for itself.
func (ProcessorSpec) Label ¶ added in v0.1.29
func (s ProcessorSpec) Label() string
Label names the processor in errors: the library entry when there is one, since that is what the author wrote.
func (ProcessorSpec) Resolve ¶ added in v0.1.29
func (s ProcessorSpec) Resolve() (ProcessorSpec, error)
Resolve expands a library reference into a self-contained spec: the library entry's type, with the caller's config deep-merged over the preset's. A spec that names no library entry is returned unchanged.
type Profile ¶ added in v0.1.13
type Profile struct {
// Name identifies the Profile (e.g. "SQL Server trace").
Name string `json:"profile" yaml:"profile"`
// Virtual profiles are generated runtime views over temporary data. They use
// the normal profile execution surfaces but cannot be persisted.
Virtual bool `json:"virtual,omitempty" yaml:"-"`
ReadOnly bool `json:"read_only,omitempty" yaml:"-"`
ExpiresAt *time.Time `json:"expires_at,omitempty" yaml:"-"`
// Imports names profiles to merge from left to right before this profile is
// executed. The authored profile remains unchanged in the profile store.
Imports []string `json:"imports,omitempty" yaml:"imports,omitempty"`
// Namespace scopes Kubernetes secret/configmap lookups and workload URLs used
// by inline provider connections. When empty, the caller's namespace is used.
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
// Icon overrides the glyph the UI shows for this Profile. It is an opaque
// icon name resolved by the frontend's icon provider (e.g. "kubernetes",
// "activemq"). When empty the provider type's own mark is used, so this only
// needs setting where the backend type is not what the Profile is *about*.
Icon string `json:"icon,omitempty" yaml:"icon,omitempty"`
// Provider selects and configures the backend the Profile reads from.
Provider ProviderConfig `json:"provider" yaml:"provider"`
// Query is the provider-native query (SQL, PromQL, HTTP path, etc.). It may
// reference declared params as `{{.params.<name>}}` (or `$(...)`), which are
// rendered before the provider runs.
Query string `json:"query,omitempty" yaml:"query,omitempty"`
// Params declares the server-side filter parameters the Profile accepts. Their
// resolved values are templated into Query (and context sub-queries) and drive
// the per-profile FilterBar schema.
Params []ParamDef `json:"params,omitempty" yaml:"params,omitempty"`
// Columns declares the output columns in display order. When empty, the
// provider's raw row keys are used.
Columns []ColumnDef `json:"columns,omitempty" yaml:"columns,omitempty"`
// Aliases are ordered CEL projections applied after processors. Later aliases
// can reference values produced by earlier aliases.
Aliases []AliasDef `json:"aliases,omitempty" yaml:"aliases,omitempty"`
// Ignore removes processed row fields after aliases have been evaluated.
Ignore []string `json:"ignore,omitempty" yaml:"ignore,omitempty"`
// Filters are named row predicates evaluated after aliases, dropping rows
// before they reach the columns. Hidden filters always apply; the rest are
// togglable and inert until named in FilterEnabledParam.
Filters []FilterDef `json:"filters,omitempty" yaml:"filters,omitempty"`
// Processors consume raw provider rows in order before aliases, ignores, row
// filters, columns, and styles.
Processors []ProcessorSpec `json:"processors,omitempty" yaml:"processors,omitempty"`
// Context defines secondary queries whose single result becomes a named side
// object on the Result (e.g. Policy, Plan, Integrations).
Context map[string]SubQuery `json:"context,omitempty" yaml:"context,omitempty"`
// Output lists the render targets (e.g. table, html, xlsx, json).
Output []string `json:"output,omitempty" yaml:"output,omitempty"`
// Render selects how the frontend presents the result. "table" (the default,
// when empty) uses the generic data table; "logs" maps the columns onto the
// canonical LogsTable view (timestamp/level/pod/logger/thread/message, plus an
// optional duration column) for trace/log profiles. Filtering stays server-side
// via Params regardless of render mode.
Render string `json:"render,omitempty" yaml:"render,omitempty"`
// Trace declares the Profile as a long-running streaming session with
// explicit setup/teardown; the provider must implement StreamProvider.
// Mutually exclusive with Top.
Trace *TraceSpec `json:"trace,omitempty" yaml:"trace,omitempty"`
// Top declares the Profile as interval-sampled: the engine re-executes the
// query per tick and each snapshot replaces the last. Mutually exclusive
// with Trace.
Top *TopSpec `json:"top,omitempty" yaml:"top,omitempty"`
// Replay describes how a single result row is turned back into an outbound
// HTTP request, so a failed or dropped record can be re-sent to its
// destination without hand-assembling the call.
Replay *ReplaySpec `json:"replay,omitempty" yaml:"replay,omitempty"`
// Reconcile is the profile this one is normally joined against, and how:
// the shared identity, the event-time column, and the per-side row bound.
// It supplies the defaults of the `reconcile` action, so the join a profile
// is habitually checked against is stored with it rather than retyped.
Reconcile *ReconcileConfig `json:"reconcile,omitempty" yaml:"reconcile,omitempty"`
// Limits are the row caps this Profile sets for itself: the page it returns
// by default, the largest page a caller may ask for, and where an all-row
// export stops. Each unset cap takes its default. None of them is the
// query's own limit, which is a provider option.
Limits *RowLimits `json:"limits,omitempty" yaml:"limits,omitempty"`
// Order is the total order this Profile's rows are returned in, ending in a
// column declared unique. It is what makes a page identifiable twice
// running, so paging past the first page requires it: without one, two
// executions of the same query may interleave rows differently and a second
// page can repeat or skip rows from the first.
Order Order `json:"order,omitempty" yaml:"order,omitempty"`
}
Profile is a declarative, CEL-driven view over a data provider. It names the backend to read from, the provider-native query, raw-row processors, output columns (with optional CEL formatting), and named context objects.
A Profile is the unifying abstraction across legacy "trace profiles", duty View specs, and ad-hoc reports.
func Follow ¶ added in v0.1.29
Follow rewrites p as a session that tails its source from here onward: the promotion a plain query profile gets when a caller asks to follow it rather than run it once. The provider must implement StreamProvider — SupportsStreaming answers that, and the transport is expected to have asked before promoting, so a surface never offers a Follow control it cannot honour.
Dropping the time-to parameter is the whole of "from here onward". It reads as the opposite of what a cursor walk does, and it is: a walk pins the instant its date math resolved against (see the clock stamped into the cursor by ExecutePages) because every page after the first must name the same result set, and a rolling window that moved between pages would stale the token. A follow has no result set to name — it is waiting for rows that do not exist yet — so an upper bound resolved at start is simply the moment it stops tailing. Only that edge goes; the lower bound is where "here" begins, and a follow that replayed from the beginning of retention every time would be a different feature.
The returned profile shares nothing mutable with p: the caller's profile came out of a store other requests read too.
func (Profile) ColumnFilterBindings ¶ added in v0.1.29
func (p Profile) ColumnFilterBindings() ([]ColumnFilterBinding, error)
func (Profile) ColumnFilterKeys ¶ added in v0.1.29
func (Profile) ColumnSortKeys ¶ added in v0.1.30
ColumnSortKeys maps each sortable column to the public name a request orders it by. It is the sort counterpart of ColumnFilterKeys, and the two are shaped alike so a renderer reads both the same way.
func (Profile) EffectiveOrder ¶ added in v0.1.29
EffectiveOrder is the order this Profile's rows are actually returned in: the one it declares, or the one its provider supplies when it declares none.
It resolves on read and never writes back, like RowLimits above. That is what keeps a derived order out of the stored profile: the editor pre-fills from the record as saved, and a rename re-marshals sibling profiles, so an order written into the struct would be persisted into profiles nobody edited.
Everything that cuts, resumes, or describes a page reads this rather than the declared field, and they must all read the same value — a cursor is scoped by the order it was cut from, so a caller minting cursors under one order and validating them under another would reject every cursor it issued.
func (Profile) FilterBindings ¶ added in v0.1.29
func (p Profile) FilterBindings() ([]ColumnFilterBinding, error)
FilterBindings returns every server-backed filter a profile exposes: rendered-column filters, bound parameters, and provider-generated runtime controls.
func (Profile) HasParamRoleName ¶ added in v0.1.15
HasParamRoleName reports whether name is a profile-declared transport parameter for role.
func (Profile) HasTimeRangeParams ¶ added in v0.1.29
HasTimeRangeParams reports whether the profile declares its own time bound.
func (Profile) Kind ¶ added in v0.1.15
func (p Profile) Kind() ProfileKind
Kind derives the Profile's execution kind from its Trace/Top blocks.
func (Profile) Pageable ¶ added in v0.1.29
Pageable reports whether a caller can ask this Profile for a position past its first page, and says why not when it cannot.
It is the same question ExecutePages enforces before it serves an offset or a cursor, asked early enough to be answered in a capability declaration: a surface that advertises paging it will refuse sends the caller to a page that can only fail. Whoever describes the profile — OpenAPI parameters, export headers — asks here rather than re-deriving the rule.
func (Profile) ParamFilterBindings ¶ added in v0.1.29
func (p Profile) ParamFilterBindings() []ColumnFilterBinding
ParamFilterBindings returns the bindings the profile's tri-state list params contribute. They carry no Column — the value never comes from a result column — but every consumer downstream reads only Field, so a param and a column ask the backend for their distinct values through one path. The key is the bare param name: the "filter." prefix is what routes a request key to the column table, and a param's key must stay the name its query-string entry uses.
func (Profile) ParamNameForRole ¶ added in v0.1.15
ParamNameForRole returns the first parameter assigned to role, or fallback when the profile uses the built-in transport parameter.
func (Profile) RenderMode ¶ added in v0.1.15
RenderMode returns the effective render value: the explicit Render when set, otherwise the profile kind for trace/top profiles, otherwise empty (generic table).
func (Profile) RequestedOrder ¶ added in v0.1.30
RequestedOrder is the order this Profile's rows are returned in when a request names a sort column, or its effective order when none is named.
The requested column leads the declared order rather than replacing it. That is not a courtesy to the author: Order.Pageable requires the order to end in a column declared unique, so replacing it would cost every profile the ability to serve a second page the moment anyone sorted. Leading it keeps the tiebreaker where it has to be and leaves the sort meaning what the caller asked.
A cursor is scoped by the order it was cut from, so changing the sort stales the cursors minted under the previous one. That is the honest outcome: the position they name does not exist in the new sequence.
func (Profile) RowLimits ¶ added in v0.1.29
RowLimits resolves this Profile's row caps against the defaults, so callers never reach for a default constant themselves.
func (Profile) RuntimeFilterBindings ¶ added in v0.1.29
func (p Profile) RuntimeFilterBindings() ([]ColumnFilterBinding, error)
RuntimeFilterBindings returns generated controls that narrow a provider's immutable profile query for one execution.
The workload control is offered even when the profile query already names one exact target, because the choice it presents there is a different one: the lookup lists the pods that target resolves to, and reading one pod of a Deployment is the whole point of picking. Where the scope leaves a single option the browser renders it as a plain label rather than a picker, so an unnarrowable scope costs no control.
The time control is generated only for a profile that declares no time bound of its own. A profile carrying a time-from/time-to parameter pair already has one, and the provider reads that pair — generating a second control would put two pickers on one window, each writing a bound the other cannot see.
func (Profile) SortBindings ¶ added in v0.1.30
func (p Profile) SortBindings() ([]SortBinding, error)
SortBindings are the columns a request may order this Profile by.
Sortability asks the same question filtering does — does this column address a real backend field, or is its value only known once the row has been read — so it is answered by the same target inference, and the two can never point at different fields.
It deliberately does not ask the filter's other questions. A column whose filter is disabled, or whose filter kind resolves to none, still names a field that can be ordered by; and a timestamp column served by the date-range control is excluded from the filter bindings while remaining the column most worth sorting on.
func (Profile) Streamable ¶ added in v0.1.29
Streamable reports whether this Profile can be served page by page.
A Top sorts the whole result before its first row is correct, and a processor that is not a PageProcessor needs every row for the same reason. Either one means a page is cut from a full run of the query rather than read from a cursor — which is correct but costs the whole query per page, so callers ask rather than assume.
func (Profile) TimeRangeParams ¶ added in v0.1.29
TimeRangeParams returns the parameters a profile declares as its own time bound, keyed by role. Either edge may be absent — a profile that only bounds where a read starts is as valid as one that bounds both ends.
func (Profile) Validate ¶ added in v0.1.29
Validate rejects invalid execution and column presentation metadata.
func (Profile) ValidateKind ¶ added in v0.1.15
ValidateKind rejects a Profile that declares both trace and top.
func (Profile) ValidateQuerySource ¶ added in v0.1.29
ValidateQuerySource rejects a Profile that carries both a raw query and a structured search specification. The provider refuses the pair too, but that happens at execution — by then the profile is already stored, and the author who introduced the conflict is long gone.
type ProfileKind ¶ added in v0.1.15
type ProfileKind string
ProfileKind classifies how a Profile executes: a single-shot query, a long-running trace session, or an interval-sampled top session.
const ( KindQuery ProfileKind = "query" KindTrace ProfileKind = "trace" KindTop ProfileKind = "top" )
type Provider ¶ added in v0.1.13
type Provider interface {
// Type is the registry key (e.g. "sql", "http", "prometheus").
Type() string
// Execute runs req against the backend and returns the raw rows.
Execute(ctx context.Context, req ProviderRequest) ([]Row, error)
}
Provider executes a Profile's query against a single backend type and returns the raw rows. Implementations register themselves via RegisterProvider and are selected by ProviderConfig.Type.
func GetProvider ¶ added in v0.1.13
GetProvider returns the registered Provider for typ, or an error listing the available types when none is registered.
type ProviderConfig ¶ added in v0.1.13
type ProviderConfig struct {
// Type is the registered provider key (e.g. "sql", "http", "prometheus").
Type string `json:"type" yaml:"type"`
// Connection references a connection (connection://name) or an inline DSN/URL.
// It is templated with the resolved params before the provider runs.
Connection string `json:"connection,omitempty" yaml:"connection,omitempty"`
// Options carries provider-specific knobs. Every string in it — however
// deeply nested — is templated with the resolved params before the provider
// runs, so `{{.params.x}}` and `$(.params.x)` work in any provider's options.
Options map[string]any `json:"options,omitempty" yaml:"options,omitempty"`
}
ProviderConfig selects a registered Provider and supplies the connection and provider-specific options.
type ProviderDiagnosticRequest ¶ added in v0.1.29
type ProviderDiagnosticRequest struct {
// Query is the statement the provider actually issued — the first page's,
// for a walk. Rendered is the profile's query once the engine templated it.
// A provider that records nothing of its own leaves them equal.
Query string `json:"query,omitempty"`
Rendered string `json:"rendered,omitempty"`
// Connection is the reference or DSN the request was rendered against,
// stripped of credentials.
Connection string `json:"connection,omitempty"`
Arguments []any `json:"arguments,omitempty"`
Options map[string]any `json:"options,omitempty"`
Details map[string]any `json:"details,omitempty"`
// Method, URL and Headers are the HTTP exchange the request rode on, for a
// provider that speaks HTTP. The endpoint is stated nowhere the author can
// read it — a connection names a host and the provider builds the path — so
// it is the first thing anyone reproducing the call by hand has to guess.
Method string `json:"method,omitempty"`
URL string `json:"url,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
}
type ProviderDiagnosticResponse ¶ added in v0.1.29
type ProviderDiagnosticResponse struct {
DurationMS float64 `json:"durationMs,omitempty"`
ReturnedRows int `json:"returnedRows,omitempty"`
Pages int `json:"pages,omitempty"`
Details map[string]any `json:"details,omitempty"`
Preview string `json:"preview,omitempty"`
ContentType string `json:"contentType,omitempty"`
Truncated bool `json:"truncated,omitempty"`
// Status and Headers are what the HTTP exchange answered, for a provider
// that speaks HTTP.
Status int `json:"status,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
}
type ProviderDiagnostics ¶ added in v0.1.29
type ProviderDiagnostics struct {
Provider string `json:"provider"`
Request ProviderDiagnosticRequest `json:"request"`
Response ProviderDiagnosticResponse `json:"response,omitempty"`
Error string `json:"error,omitempty"`
// contains filtered or unexported fields
}
func DiagnosticSink ¶ added in v0.1.29
func DiagnosticSink(ctx context.Context) *ProviderDiagnostics
DiagnosticSink returns the recorder WithDiagnosticSink attached, or nil when this is an ordinary run.
func DiagnosticsFromError ¶ added in v0.1.29
func DiagnosticsFromError(err error) *ProviderDiagnostics
func NewDiagnostics ¶ added in v0.1.29
func NewDiagnostics(options DiagnosticOptions) *ProviderDiagnostics
func (*ProviderDiagnostics) HTTPTransport ¶ added in v0.1.29
func (d *ProviderDiagnostics) HTTPTransport(base http.RoundTripper) http.RoundTripper
HTTPTransport wraps base so the exchange carrying this run's provider request is recorded: where it went, what headers it took with it, and what the backend answered.
Exactly one exchange is recorded — the first after RecordRequest, which is the statement the provider has just described. Everything a client does on the way there, a ping or a mapping lookup or a token fetch, happens before that and is left alone; a walk's later pages go to the same endpoint with the same headers and differ only in the body already reported.
It returns base unchanged for an ordinary run, which has no diagnostics to record into and must not pay to be watched.
func (*ProviderDiagnostics) RecordConnection ¶ added in v0.1.29
func (d *ProviderDiagnostics) RecordConnection(connection string)
RecordConnection records the connection the request was rendered against. A connection:// reference is kept verbatim; an inline DSN is stripped of its credentials, because provenance that cannot be shared is provenance nobody reads.
func (*ProviderDiagnostics) RecordError ¶ added in v0.1.29
func (d *ProviderDiagnostics) RecordError(err error)
func (*ProviderDiagnostics) RecordPreview ¶ added in v0.1.29
func (d *ProviderDiagnostics) RecordPreview(contentType string, body []byte)
func (*ProviderDiagnostics) RecordRendered ¶ added in v0.1.29
func (d *ProviderDiagnostics) RecordRendered(query string, options map[string]any)
RecordRendered seeds the diagnostics with the request the engine built — the templated query and the options it resolved to — so a provider that records nothing of its own still says what ran. A provider that does record replaces the query with the statement it actually issued.
func (*ProviderDiagnostics) RecordRequest ¶ added in v0.1.29
func (d *ProviderDiagnostics) RecordRequest(query string, arguments []any, details map[string]any)
func (*ProviderDiagnostics) RecordResponse ¶ added in v0.1.29
func (*ProviderDiagnostics) Snapshot ¶ added in v0.1.29
func (d *ProviderDiagnostics) Snapshot() *ProviderDiagnostics
func (*ProviderDiagnostics) WantsPreview ¶ added in v0.1.29
func (d *ProviderDiagnostics) WantsPreview() bool
WantsPreview reports whether this run pays for a response body preview and for backend-side instrumentation.
type ProviderRequest ¶ added in v0.1.13
type ProviderRequest struct {
// Provider is the resolved registry key. It is carried so optional
// diagnostics can identify the native backend without re-deriving it.
Provider string
// Connection references a connection (connection://name) or an inline DSN/URL.
Connection string
// Query is the provider-native query string.
Query string
// QueryArgs are values bound to placeholders derived from Query parameter
// references. They never form part of Query itself.
QueryArgs []any
// QueryIdentifiers are validated names represented by opaque markers in
// Query. The SQL provider quotes them for the resolved connection dialect.
QueryIdentifiers []string
// Options carries provider-specific knobs from ProviderConfig.Options.
Options map[string]any
// Params contains the validated profile parameters. Providers use this for
// native query builders that cannot be expressed as a query template.
Params map[string]any
// ParamRoles maps each declared param name to its ParamRole, so a structural
// query builder can fold role-carrying params (time-from, limit, …) into the
// backend's native constructs instead of treating them as plain filters.
ParamRoles map[string]ParamRole
// TemplatedParams names the params consumed while templating Query, Options
// or Connection. A provider with its own structural param binding counts
// them as referenced, so a param interpolated into the options is not
// reported as unused.
TemplatedParams []string
// Filters contains native include/exclude clauses bound to profile columns.
Filters []ColumnFilterValue
// Order is the Profile's declared result order. A provider that pages by
// cursor needs it to sort by, and to cut the next position out of the last
// row it returned.
Order Order
// Position is the decoded cursor this request resumes after, empty at the
// start of a walk. The engine validates and decodes it, so a provider works
// in key values and never in the token format.
Position CursorPosition
// Diagnostics is populated only for an explicitly requested debug run.
// Providers record their final native request and response details here so
// failures can return the same evidence as successful executions.
Diagnostics *ProviderDiagnostics
Inspection InspectionOptions
}
ProviderRequest is the resolved input handed to a Provider by the engine.
type QueryModel ¶
type QueryModel struct {
// Table name
Table string
// Custom functions to map fields to clauses
// Example: map["custom_field"] = func(ctx, tx, val) { ... custom query logic ... }
Custom map[string]func(ctx context.Context, tx *gorm.DB, val string) (*gorm.DB, error)
// List of jsonb columns that store a map.
// These columns can be addressed using dot notation to access the JSON fields directly
// Example: tags.cluster or tags.namespace.
JSONMapColumns []string
// List of columns that can be addressed on the search query.
// Any other fields will be treated as a property lookup.
Columns []string
// Alias maps fields from the search query to the table columns
// Example: map["created"] = "created_at"
Aliases map[string]string
// True when the table has a "tags" column
HasTags bool
// True when the table has a "labels" column
HasLabels bool
// True when the table has properties column
HasProperties bool
// FieldMapper maps the value of these fields
// Example: map["created_at"] = DateMapper
FieldMapper map[string]func(ctx context.Context, id string) (any, error)
}
QueryModel defines the structure and capabilities of a queryable table/resource. Consumers can create their own QueryModel instances for their specific tables.
func (QueryModel) Apply ¶
func (qm QueryModel) Apply(ctx context.Context, q grammar.QueryField, tx *gorm.DB) (*gorm.DB, []clause.Expression, error)
Apply processes a query field and converts it to GORM clauses. It handles: - Field aliases - Field value mapping (via FieldMapper) - Common field operations (limit, sort, offset) - Custom field handlers - JSON path queries - Property filtering Returns the modified transaction, clauses to add, and any error
type QueryParameterizationRequest ¶ added in v0.1.29
type QueryParameterizer ¶ added in v0.1.29
type QueryParameterizer interface {
ParameterizeQuery(request QueryParameterizationRequest) (ParameterizedQuery, error)
}
QueryParameterizer replaces provider-native parameter references with opaque markers while carrying their values separately to the backend driver.
type ReconcileConfig ¶ added in v0.1.29
type ReconcileConfig struct {
// Dest names the profile the source is reconciled against.
Dest string `json:"dest" yaml:"dest"`
// SourceFilters and DestFilters are independent because both profiles can
// expose the same filter name with different values, or entirely different
// filter surfaces.
SourceFilters map[string]string `json:"sourceFilters,omitempty" yaml:"sourceFilters,omitempty"`
DestFilters map[string]string `json:"destFilters,omitempty" yaml:"destFilters,omitempty"`
// Key and TimeColumn are promoted, so a stored reconcile reads as one flat
// block rather than nesting the join spec inside itself.
ReconcileSpec `yaml:",inline"`
}
ReconcileConfig is a whole reconcile: which profile to join against, how to filter each side, and how to derive the identity. It is what a Profile stores under `reconcile:` and what the action's flags override, whereas ReconcileSpec is only what the join itself needs.
func MergeReconcileConfig ¶ added in v0.1.29
func MergeReconcileConfig(base, override *ReconcileConfig) *ReconcileConfig
MergeReconcileConfig layers an imported profile's reconcile with the importing profile's, field by field — the same way a replay block merges, so a profile can inherit a join and change only its bound or one filter.
func (*ReconcileConfig) Clone ¶ added in v0.1.29
func (c *ReconcileConfig) Clone() *ReconcileConfig
Clone returns a deep copy, so merging two profiles never aliases a stored config's filters or key columns.
type ReconcileMode ¶ added in v0.1.29
type ReconcileMode string
ReconcileMode says how a run was joined, because the two ways differ in what they cost and in what they can promise.
const ( // ReconcileMerged walked both sides in key order and joined them as they // arrived. Memory is one key's rows, not one dataset. ReconcileMerged ReconcileMode = "merged" // ReconcileBuffered read both sides in full and joined them in memory. It is // what a run falls back to when the key is not the order — a CEL key, or a // profile ordered by something else — because then no two rows can be known // to be adjacent without reading everything. ReconcileBuffered ReconcileMode = "buffered" )
type ReconcileProvenance ¶ added in v0.1.29
type ReconcileProvenance struct {
Mode ReconcileMode `json:"mode,omitempty"`
BufferedReason string `json:"buffered_reason,omitempty"`
Source *ReconcileSideExecution `json:"source,omitempty"`
Dest *ReconcileSideExecution `json:"dest,omitempty"`
RanAt time.Time `json:"ran_at"`
}
ReconcileProvenance is how a run happened: the mode it took and what each side asked. The config it ran under lives on the result, not here, so the two cannot disagree.
type ReconcileResult ¶ added in v0.1.29
type ReconcileResult struct {
// Config is what the run was asked to do — destination, key, per-side
// filters, and the span of keys it covered (nil range means all of them).
// Both sides are cut at the same keys, so a one-sided key inside the range
// is a finding rather than an artefact of where the read stopped.
Config ReconcileConfig `json:"config"`
SourceProfile Profile `json:"-"`
DestProfile Profile `json:"-"`
Source string `json:"source"`
Dest string `json:"dest"`
Rows []ReconcileRow `json:"rows"`
Stats ReconcileStats `json:"stats"`
// Mode says how the two sides were joined, and BufferedReason says why a run
// could not be merged — which is always something the author can change.
Mode ReconcileMode `json:"mode,omitempty"`
BufferedReason string `json:"buffered_reason,omitempty"`
// Provenance is what each side actually ran. It is a record, not an input:
// nothing in the join reads it, and it is the only trace of a query that
// existed only at execution time.
Provenance *ReconcileProvenance `json:"provenance,omitempty"`
// The Truncated flags report a side whose backend cut the read short. A
// one-sided key from an incomplete read is not a finding, which is why they
// travel with the result.
SourceTruncated bool `json:"source_truncated,omitempty"`
DestTruncated bool `json:"dest_truncated,omitempty"`
}
ReconcileResult is a completed join, carrying both profiles so the table renderer can label and order each side's columns.
func Reconcile ¶ added in v0.1.29
func Reconcile(ctx context.Context, source, dest *Result, sourceProfile, destProfile Profile, spec ReconcileSpec) (*ReconcileResult, error)
Reconcile joins two Results on spec.Key.
Every key present on either side produces at least one row. When a key repeats on a side, the pairs are expanded cartesian-style — 2 source rows against 3 destination rows emit 6 rows — because with a duplicated identity there is no principled way to decide which source belongs to which destination, and silently picking one hides the ambiguity that is usually the actual bug.
func ReconcileProfiles ¶ added in v0.1.29
func ReconcileProfiles(ctx context.Context, run ReconcileRun) (*ReconcileResult, error)
ReconcileProfiles runs both sides and joins them.
When the key is a prefix of both orders the join is a merge: both sides are walked in key order and each key is emitted once it can no longer grow, so a run holds one key's rows rather than two datasets. Otherwise there is no way to know a key is finished without having read everything, and the run says which it did.
func (*ReconcileResult) Bounded ¶ added in v0.1.29
func (r *ReconcileResult) Bounded() bool
Bounded reports whether either side was cut short by its backend, leaving keys unaccounted for.
func (*ReconcileResult) Columns ¶ added in v0.1.29
func (r *ReconcileResult) Columns() []api.ColumnDef
Columns returns the joined column schema: the fixed join columns, then each side's visible profile columns under a side-qualified key.
func (*ReconcileResult) Flatten ¶ added in v0.1.29
func (r *ReconcileResult) Flatten() []Row
Flatten renders the result as plain Rows keyed by Columns(), so a reconcile can be handed to anything that consumes a Result.
func (*ReconcileResult) Pretty ¶ added in v0.1.29
func (r *ReconcileResult) Pretty() api.Text
Pretty renders the summary line plus the joined table, and is what clicky's formatter picks up on the CLI.
func (*ReconcileResult) Render ¶ added in v0.1.29
func (r *ReconcileResult) Render(format string) (string, error)
Render formats the result in the given clicky format (e.g. "pretty", "csv", "json", "html").
func (*ReconcileResult) SnapshotColumns ¶ added in v0.1.29
func (r *ReconcileResult) SnapshotColumns() []ColumnDef
SnapshotColumns describes the raw reconciliation rows persisted for paging, transformations, and exports. Unlike Columns, it contains no api.Text values.
func (*ReconcileResult) SnapshotRows ¶ added in v0.1.29
func (r *ReconcileResult) SnapshotRows() []Row
SnapshotRows is the unstyled, typed projection stored in SQLite.
func (*ReconcileResult) Table ¶ added in v0.1.29
func (r *ReconcileResult) Table() api.TextTable
Table builds the side-by-side clicky table.
type ReconcileRow ¶ added in v0.1.29
type ReconcileRow struct {
Key string `json:"key"`
Status ReconcileStatus `json:"status"`
Source Row `json:"source,omitempty"`
Dest Row `json:"dest,omitempty"`
SourceTime *time.Time `json:"source_time,omitempty"`
DestTime *time.Time `json:"dest_time,omitempty"`
TimeDiff *time.Duration `json:"time_diff,omitempty"`
// Dup counters expose the cartesian expansion: index i of N rows sharing
// this key on that side. Both are 1/1 when the key is unique.
SourceDupIndex int `json:"source_dup_index,omitempty"`
SourceDupCount int `json:"source_dup_count,omitempty"`
DestDupIndex int `json:"dest_dup_index,omitempty"`
DestDupCount int `json:"dest_dup_count,omitempty"`
}
ReconcileRow is one joined pair. Source and Dest are nil on the side the key is missing from.
type ReconcileRun ¶ added in v0.1.29
type ReconcileRun struct {
Source Profile
Dest Profile
Config ReconcileConfig
SourceFilters map[string]any
DestFilters map[string]any
}
ReconcileRun is one execution of a ReconcileConfig: the two resolved profiles and each side's independently validated filter values.
func (ReconcileRun) Mergeable ¶ added in v0.1.29
func (r ReconcileRun) Mergeable() (bool, string)
Mergeable reports whether a run can be joined by walking both sides, and says why not when it cannot.
The requirement is that every processor can run page by page and the key is a prefix of both orders. Then rows sharing a key are adjacent on both sides, which is the whole basis of a merge join — a key can be finished and emitted as soon as a larger one appears, without holding anything else.
type ReconcileSideExecution ¶ added in v0.1.29
type ReconcileSideExecution struct {
Side string `json:"side"`
Profile string `json:"profile"`
Provider string `json:"provider"`
// Query is the profile's query as authored, templates and all; the rendered
// and native forms live on Diagnostics.Request.
Query string `json:"query,omitempty"`
// Filters are the resolved per-side filter values, sanitized the same way a
// provider's options are.
Filters map[string]any `json:"filters,omitempty"`
Diagnostics *ProviderDiagnostics `json:"diagnostics,omitempty"`
// Rows is what the join consumed from this side — after column transforms
// and processors, and after a key range stopped the walk early.
// Diagnostics.Response.ReturnedRows is what the provider handed over, so the
// two differ whenever the profile drops or the range truncates.
Rows int `json:"rows"`
Pages int `json:"pages,omitempty"`
// DurationMS is wall clock spent reading this side. On a merged run the two
// sides interleave and this includes the join's own time between pulls, so
// BackendMS — the summed provider response time — is the number that
// compares across modes.
DurationMS float64 `json:"durationMs"`
BackendMS float64 `json:"backendMs,omitempty"`
Truncated bool `json:"truncated,omitempty"`
}
ReconcileSideExecution is what one side of a reconciliation actually ran.
It exists because a reconciliation's findings are only as trustworthy as the two reads behind them, and neither read is visible in the joined rows. "This key never arrived" is a claim about a query that ran once, against a connection, with filters, at a moment — and the rows alone record none of it.
type ReconcileSpec ¶ added in v0.1.29
type ReconcileSpec struct {
// Range narrows the join to a span of keys. An empty range covers all of
// them.
//
// It replaced a per-side row cap, which could not be made correct: two sides
// cut at N rows each are two different sets of keys unless they happen to be
// ordered identically, so the bound itself produced the one-sided keys the
// run then reported as findings. A range cuts both sides at the same keys by
// construction, so a key missing from one side inside it is missing.
Range *KeyRange `json:"range,omitempty" yaml:"range,omitempty"`
// Key derives the join identity from a row on either side. Cross-profile
// joins normally need KeySpec.CEL, since the two sides rarely name the same
// field the same way.
Key KeySpec `json:"key" yaml:"key"`
// TimeColumn names the row key holding each side's event time, used for the
// source/dest timestamps and their difference. When empty it is discovered
// from the profile column declared with Kind: timestamp; when no such column
// exists, the time fields are simply omitted.
TimeColumn string `json:"timeColumn,omitempty" yaml:"timeColumn,omitempty"`
}
ReconcileSpec configures a join between two Results.
Reconcile answers a different question from the sqlite.recon processor: recon diffs two snapshots of the *same* schema cell by cell, whereas Reconcile joins two *different* profiles on a shared identity and reports presence and latency. Their statuses are deliberately not interchangeable.
type ReconcileStats ¶ added in v0.1.29
type ReconcileStats struct {
Matched int `json:"matched"`
OnlySource int `json:"only_source"`
OnlyDest int `json:"only_dest"`
DupKeys int `json:"dup_keys"`
}
ReconcileStats summarises a reconcile run. Counts are per key, not per emitted row: a key with 2 source and 3 destination rows counts once as matched while emitting 6 rows.
type ReconcileStatus ¶ added in v0.1.29
type ReconcileStatus string
ReconcileStatus is the outcome of joining one key across two Results.
const ( // ReconcileMatched means the key appears on both sides. ReconcileMatched ReconcileStatus = "matched" // ReconcileOnlySource means the key appears in the source only — it left A // and never arrived at B. ReconcileOnlySource ReconcileStatus = "only_source" // ReconcileOnlyDest means the key appears in the destination only — it // arrived at B without a matching record in A. ReconcileOnlyDest ReconcileStatus = "only_dest" )
type RecordCounts ¶ added in v0.1.29
type RecordCounts struct {
Operations int `json:"operations"`
HAREntries int `json:"harEntries"`
HARDropped int `json:"harDropped"`
LogLines int `json:"logLines"`
LogDropped int `json:"logDropped"`
Probes int `json:"probes"`
Inspections int `json:"inspections"`
}
RecordCounts says what detail exists to fetch, so a badge can be rendered without paying for the payload behind it.
type Recorder ¶ added in v0.1.29
type Recorder struct {
// contains filtered or unexported fields
}
Recorder accumulates one request's record.
func NewRecorder ¶ added in v0.1.29
func NewRecorder(options RecorderOptions) *Recorder
func RecorderFrom ¶ added in v0.1.29
RecorderFrom returns the recorder this request was armed with, or nil for an ordinary run.
func (*Recorder) Detail ¶ added in v0.1.29
func (r *Recorder) Detail() ExecutionDetail
Detail is the expensive half, assembled for one caller that asked for it.
Every slice is copied: the recorder outlives the response, and a caller ranging over the live slices while a late operation completes would race.
func (*Recorder) DiagnosticDetail ¶ added in v0.1.29
func (r *Recorder) DiagnosticDetail() DiagnosticDetail
DiagnosticDetail is how much the executions under this recorder should pay to explain themselves. An unarmed run records what it was going to record anyway.
func (*Recorder) Finish ¶ added in v0.1.29
func (r *Recorder) Finish(options FinishOptions)
Finish closes the record. It is idempotent: a request that both errors and then unwinds through a deferred close must not be recorded twice.
func (*Recorder) ID ¶ added in v0.1.29
ID is the correlation handle the response carries so a console can join its own request to this record.
func (*Recorder) Log ¶ added in v0.1.29
Log appends one line. Sequence is assigned here so a console can order lines that arrived from two sources.
func (*Recorder) Operation ¶ added in v0.1.29
Operation registers a provider operation that is about to start and returns the handle it reports back through. It returns nil for an unarmed run, which every method on the handle tolerates.
func (*Recorder) RecordInspection ¶ added in v0.1.29
func (r *Recorder) RecordInspection(record InspectionRecord)
RecordInspection appends one inspection-cache lookup.
func (*Recorder) RecordProbe ¶ added in v0.1.29
func (r *Recorder) RecordProbe(probe CardinalityProbe)
RecordProbe appends one cardinality probe.
func (*Recorder) RefreshInspection ¶ added in v0.1.29
RefreshInspection reports that this request asked to rebuild every metadata lookup it makes rather than read what is cached.
func (*Recorder) Summary ¶ added in v0.1.29
func (r *Recorder) Summary() ExecutionSummary
Summary is the cheap half of the record, safe to push to every open console.
type RecorderOptions ¶ added in v0.1.29
type RecorderOptions struct {
ID string
Level logger.LogLevel
Source ExecutionSource
// RefreshInspection makes every metadata lookup this request performs rebuild
// rather than read what is cached — the console's "re-run inspection".
//
// Scoped to the request on purpose: it costs this caller the rebuild and
// leaves everyone else's reads alone, unlike flushing the cache.
RefreshInspection bool
// MaxLogLines and MaxHAREntries bound what one record retains; zero takes
// the defaults above.
MaxLogLines int
MaxHAREntries int
}
RecorderOptions configures NewRecorder.
type RegistryOptions ¶ added in v0.1.15
type RegistryOptions struct {
// MaxSessions caps concurrently active (starting/running) sessions.
MaxSessions int // default 5
// MaxDuration caps any session's run duration.
MaxDuration time.Duration // default 15m
// MaxEvents caps any session's ring buffer.
MaxEvents int // default 10000
// RetainDone is how many terminal sessions stay in memory before the
// oldest are pruned.
RetainDone int // default 50
// OnEvent/OnTransition are installed on every session started through
// ExecuteStream — the persistence hooks.
OnEvent func(Event)
OnTransition func(SessionInfo)
}
RegistryOptions bounds a SessionRegistry. Zero values take the defaults; profile-declared limits are clamped to these server caps, never raised.
type ReplayBuildOptions ¶ added in v0.1.29
type ReplayBuildOptions struct {
// Profile supplies the ReplaySpec and the column metadata used to describe
// the selected row.
Profile Profile
// Rows is the executed result the row is selected from.
Rows []Row
// Select filters Rows by exact column value. It must narrow to exactly one
// row; an ambiguous selection is an error, never a silent first-match.
Select map[string]string
// DefaultTarget is used when neither the profile nor TargetOverride names
// one — typically an application-level default connection.
DefaultTarget connection.HTTPConnection
// TargetOverride is a connection reference (name or connection://...) or a
// direct http(s) URL.
TargetOverride string
MethodOverride string
URLOverride string
BodyOverride string
// Headers are merged over the profile's rendered headers.
Headers map[string]string
// MaxBodyPreview caps the previewed body. Defaults to 64KiB.
MaxBodyPreview int
}
ReplayBuildOptions selects the row to replay and carries the caller's overrides. Every override wins over the profile's CEL expression, so an operator can retarget or reshape a single request without editing the profile.
type ReplayExecuteResult ¶ added in v0.1.29
type ReplayExecuteResult struct {
Preview ReplayPreview `json:"preview"`
StatusCode int `json:"statusCode,omitempty"`
Status string `json:"status,omitempty"`
DurationMS int64 `json:"durationMs"`
ResponseHeaders map[string]string `json:"responseHeaders,omitempty"`
ResponsePreview string `json:"responsePreview,omitempty"`
ResponseBytes int `json:"responseBytes,omitempty"`
}
ReplayExecuteResult is the outcome of actually sending the request.
func ExecuteReplay ¶ added in v0.1.29
func ExecuteReplay(ctx context.Context, preview *ReplayPreview) (*ReplayExecuteResult, error)
ExecuteReplay sends a previewed request. The caller is responsible for having checked the preview hash first.
type ReplayPreview ¶ added in v0.1.29
type ReplayPreview struct {
Profile string `json:"profile"`
Row ReplayRowSummary `json:"row"`
Target string `json:"target"`
Method string `json:"method"`
URL string `json:"url"`
Headers map[string]string `json:"headers"`
BodyPreview string `json:"bodyPreview,omitempty"`
BodyBytes int `json:"bodyBytes,omitempty"`
BodyTruncated bool `json:"bodyTruncated,omitempty"`
// Hash covers the method, URL, headers and body. A caller previews, shows
// the user what will be sent, then sends the hash back with the execute
// request; a changed hash means the underlying data moved and the user
// approved something other than what would now be sent.
Hash string `json:"hash"`
// contains filtered or unexported fields
}
ReplayPreview is the fully resolved request, safe to show a user: the URL has its credentials stripped and sensitive headers are masked. The unredacted values are kept unexported for ExecuteReplay.
func BuildReplayPreview ¶ added in v0.1.29
func BuildReplayPreview(ctx context.Context, opts ReplayBuildOptions) (*ReplayPreview, error)
BuildReplayPreview resolves one row into a concrete HTTP request without sending it.
type ReplayRowSummary ¶ added in v0.1.29
type ReplayRowSummary struct {
Index int `json:"index"`
Values map[string]any `json:"values,omitempty"`
}
ReplayRowSummary identifies the selected row in preview output without dumping every column.
type ReplaySpec ¶ added in v0.1.29
type ReplaySpec struct {
// Kind selects the replay transport. Defaults to http.
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
// Target is the connection the request is sent to. A relative URL requires
// one; an absolute URL falls back to its own origin.
Target connection.HTTPConnection `json:"target,omitempty" yaml:"target,omitempty"`
// Method is a CEL expression yielding the HTTP method. Defaults to POST.
Method string `json:"method,omitempty" yaml:"method,omitempty"`
// URL is a CEL expression yielding an absolute URL or a path relative to
// Target.
URL string `json:"url,omitempty" yaml:"url,omitempty"`
// Body is a CEL expression yielding the request body. Non-string values are
// JSON-encoded.
Body string `json:"body,omitempty" yaml:"body,omitempty"`
// Headers maps header names to CEL expressions. A header whose expression
// yields blank is omitted rather than sent empty.
Headers map[string]string `json:"headers,omitempty" yaml:"headers,omitempty"`
}
ReplaySpec describes how one result row becomes a replayable HTTP request. Method, URL, Body and Headers are CEL expressions evaluated against the row after the profile's aliases and columns have been applied, so they see the same field names the table does.
func MergeReplaySpec ¶ added in v0.1.29
func MergeReplaySpec(base, override *ReplaySpec) *ReplaySpec
MergeReplaySpec overlays override onto base field by field, so a profile can import a replay block and change only its target or one header.
func (*ReplaySpec) Clone ¶ added in v0.1.29
func (s *ReplaySpec) Clone() *ReplaySpec
Clone returns a deep copy, so profile merging never aliases a stored spec.
type Result ¶ added in v0.1.13
type Result struct {
// Profile is the name of the Profile that produced this Result.
Profile string `json:"profile,omitempty" yaml:"profile,omitempty"`
// Rows are the primary tabular records.
Rows []Row `json:"rows" yaml:"rows"`
// Context holds named side objects keyed by SubQuery name.
Context map[string]any `json:"context,omitempty" yaml:"context,omitempty"`
// ColumnFilterKeys binds rendered columns to native server filter params.
ColumnFilterKeys map[string]string `json:"-" yaml:"-"`
// ColumnSortKeys binds rendered columns to the public name a request sorts
// them by. A column absent from it cannot be ordered on, which is what makes
// the table render its header inert rather than offering a sort the server
// would refuse.
ColumnSortKeys map[string]string `json:"-" yaml:"-"`
// Styles holds each row's evaluated ColumnDef.Style classes, keyed by column
// name and positionally parallel to Rows. It is render-only and deliberately
// off the wire: styling a cell must not change the value an export carries.
Styles []map[string]string `json:"-" yaml:"-"`
// Truncated reports that this result is short of what the query had to
// give — the Profile's export ceiling stopping the read, or the backend
// applying a cap of its own. It travels with the Result so a partial answer
// is never mistaken for a complete one.
Truncated bool `json:"truncated,omitempty" yaml:"truncated,omitempty"`
}
Result is the output of executing a Profile: the tabular rows plus any named context objects (Policy/Plan/Integrations side panels, each produced by a SubQuery).
func Execute ¶ added in v0.1.13
Execute runs a Profile end-to-end: resolve the supplied params, render the query, dispatch to the provider, run processors, and then evaluate aliases, filters, columns, and styles. Context SubQueries are available to processors.
params carries the server-side filter values for the Profile's declared Params (omit when there are none). They are validated/coerced against the declarations and exposed to the query template as `params`.
The read stops at the Profile's own MaxExportRows, and Result.Truncated says whether stopping there left rows behind. Every buffered caller — the CLI, a replay, a reconcile that cannot merge — is asking for the whole result, and "the whole result" against an unbounded source is not a number any of them can hold. The ceiling is the profile's to raise.
func MaterializeEvents ¶ added in v0.1.15
MaterializeEvents turns a session's event log into a Result: the last snapshot for a top profile, or the final streamed trace rows. It also serves persisted events after the live session is gone.
type ResultColumn ¶ added in v0.1.29
type ResultColumn struct {
Name string `json:"name"`
Label string `json:"label,omitempty"`
DatabaseType string `json:"databaseType,omitempty"`
Kind ColumnKind `json:"kind,omitempty"`
FilterKey string `json:"filterKey,omitempty"`
Filter *ResultColumnFilter `json:"filter,omitempty"`
}
ResultColumn is the transport contract consumed by clicky-ui's QueryBrowser. It is intentionally separate from ColumnDef: ColumnDef is authoring metadata, while this type carries the resolved filter key and control the server can actually honour.
func DescribeResultColumns ¶ added in v0.1.29
func DescribeResultColumns(options ResultColumnOptions) ([]ResultColumn, error)
type ResultColumnFilter ¶ added in v0.1.29
type ResultColumnFilter struct {
Kind string `json:"kind"`
Options []ResultColumnFilterOption `json:"options,omitempty"`
Lookup bool `json:"lookup,omitempty"`
Multi bool `json:"multi,omitempty"`
Unit string `json:"unit,omitempty"`
}
type ResultColumnFilterOption ¶ added in v0.1.29
type ResultColumnFilterOption struct {
Value string `json:"value"`
}
type ResultColumnOptions ¶ added in v0.1.29
ResultColumnOptions describes the profile output columns for an interactive result surface. DatabaseTypes is optional display-only metadata reported by SQL drivers.
type Row ¶ added in v0.1.13
Row is a single result record keyed by column name. It is a type alias for the generic map so provider code (ported from duty/dataquery) and CEL evaluation can treat rows uniformly.
func CollectRows ¶ added in v0.1.29
CollectRows drains a row sequence, returning the first error it yields.
type RowExpr ¶ added in v0.1.29
type RowExpr struct {
// contains filtered or unexported fields
}
RowExpr is a CEL expression evaluated against an explicit, caller-fixed set of bindings.
It deliberately differs from the column and alias expressions in cel.go, which flatten every row key into a top-level variable. Those run once per profile column; batch predicates run once per row, and a per-row variable set both defeats gomplate's compiled-program cache (its key includes the variable names) and turns a sparse row into an "undeclared reference" failure halfway through a scan. With a fixed binding set the program is compiled once and a missing field reads as null, which gomplate's nilsafe library then folds into the zero value of whatever it is used as.
func CompileRowExpr ¶ added in v0.1.29
CompileRowExpr prepares expression for repeated evaluation. CEL compilation itself happens on the first Eval, inside gomplate's program cache.
func (*RowExpr) Bool ¶ added in v0.1.29
Bool evaluates the expression as a predicate. A non-boolean result is an error rather than a truthiness guess, so a typo like `count` instead of `count > 1` fails loudly.
func (*RowExpr) Eval ¶ added in v0.1.29
Eval runs the expression with bindings as the variable environment.
func (*RowExpr) Expression ¶ added in v0.1.29
Expression returns the source text, for error messages.
type RowLimits ¶ added in v0.1.29
type RowLimits struct {
// PageSize is the page this profile returns when a caller asks for no size.
PageSize int `json:"pageSize,omitempty" yaml:"pageSize,omitempty"`
// MaxPageSize is the largest page a caller may ask this profile for.
MaxPageSize int `json:"maxPageSize,omitempty" yaml:"maxPageSize,omitempty"`
// MaxExportRows is where an all-row export of this profile stops.
MaxExportRows int `json:"maxExportRows,omitempty" yaml:"maxExportRows,omitempty"`
}
RowLimits are the caps a profile sets for itself. Each is optional: an unset cap takes its default, and a set one wins outright — a profile that exports a large table raises its own ceiling rather than asking the server to raise it for everyone.
func (*RowLimits) Resolve ¶ added in v0.1.29
Resolve fills each unset cap from its default, so callers work with three real numbers rather than deciding what a zero meant.
func (*RowLimits) Validate ¶ added in v0.1.29
Validate rejects caps that would return nothing or contradict each other. A pair is refused rather than narrowed: an author who asked for a default page larger than the page a caller may request meant one of the two numbers, and picking for them hides the mistake.
type SampleFilterValuesOptions ¶ added in v0.1.29
type SampleOptions ¶ added in v0.1.29
type SampleOptions struct {
Params map[string]any
Filters map[string]string
FilterColumns []ColumnDef
InspectionColumns []ColumnDef
Page PageRequest
PreviewProcessors bool
Inspection InspectionOptions
}
type SampleResolution ¶ added in v0.1.30
type SampleResult ¶ added in v0.1.15
type SampleResult struct {
Rows []Row `json:"rows"`
Columns []ColumnDef `json:"columns"`
ResultColumns []ResultColumn `json:"resultColumns"`
RenderedQuery string `json:"renderedQuery"`
Truncated bool `json:"truncated,omitempty"`
DurationMS float64 `json:"durationMs"`
Pagination PageInfo `json:"pagination"`
Diagnostics *ProviderDiagnostics `json:"diagnostics,omitempty"`
ProcessorPreview *ProcessorPreview `json:"processorPreview,omitempty"`
Inspection *InspectionStatus `json:"inspection,omitempty"`
Resolution SampleResolution `json:"resolution"`
}
SampleResult is the bounded output used by profile authoring tools. It skips processors unless PreviewProcessors was explicitly requested, but always applies the profile's row mapping. Columns are inferred from top-level keys.
func Sample ¶ added in v0.1.15
func Sample(ctx context.Context, p Profile, options SampleOptions) (*SampleResult, error)
Sample renders and executes a profile through its provider while bypassing context queries. Processors are bypassed by default; PreviewProcessors runs them over the bounded raw page and records every stage. Row mapping runs only after that optional processor chain, and only providers whose request can be proven read-only are allowed.
type Session ¶ added in v0.1.15
type Session struct {
// contains filtered or unexported fields
}
Session is one running (or finished) trace/top execution: a capped ring buffer of events, live subscribers, and a state machine starting → running → {completed|failed|stopped}.
func ExecuteStream ¶ added in v0.1.15
func ExecuteStream(ctx context.Context, reg *SessionRegistry, p Profile, params ...map[string]any) (*Session, error)
ExecuteStream starts a trace or top session and returns immediately with the session in the starting state. ctx must be a long-lived application context; the run is bounded only by the session's clamped MaxDuration or Stop().
func NewSession ¶ added in v0.1.15
func NewSession(opts SessionOptions) *Session
NewSession creates a session in the starting state. The caller is expected to have validated the profile and clamped MaxEvents.
func (*Session) Abort ¶ added in v0.1.15
Abort forces an active session into the failed state (e.g. when its durable event log cannot be written), cancelling the run and closing subscribers.
func (*Session) Emit ¶ added in v0.1.15
Emit stamps the event with the next sequence, appends it to the ring (evicting the oldest at capacity), and fans it out to subscribers without blocking. Events emitted after the session is terminal are discarded.
func (*Session) Events ¶ added in v0.1.15
Events returns a copy of the buffered events, oldest first.
func (*Session) Latest ¶ added in v0.1.15
Latest returns the most recent top snapshot (nil for traces or before the first tick).
func (*Session) Result ¶ added in v0.1.15
Result materializes the session: the latest snapshot for top, or the final rows already emitted by the trace pipeline.
func (*Session) Snapshot ¶ added in v0.1.15
func (s *Session) Snapshot() SessionInfo
Snapshot returns a JSON-safe copy of the session's current state.
func (*Session) Stop ¶ added in v0.1.15
func (s *Session) Stop()
Stop requests cancellation. A live runner flushes its pending trace buffer before markDone makes the stopped state terminal.
func (*Session) Subscribe ¶ added in v0.1.15
Subscribe atomically returns the buffered events and a live channel for subsequent ones — no gap, no duplication. The channel is closed when the session reaches a terminal state; cancel detaches the subscriber.
func (*Session) SubscribeFrom ¶ added in v0.1.29
SubscribeFrom is Subscribe for a consumer that already holds every event up to and including after — a reconnecting SSE client naming its Last-Event-ID. Sequences start at 1, so 0 replays the whole ring and is what Subscribe asks for.
A sequence older than the ring's oldest surviving event replays what is left rather than failing: the evicted span is unrecoverable whichever way it is answered, and the alternative is a client that reconnects into silence.
type SessionInfo ¶ added in v0.1.15
type SessionInfo struct {
ID string `json:"id"`
Profile string `json:"profile"`
Kind ProfileKind `json:"kind"`
State SessionState `json:"state"`
Params map[string]any `json:"params,omitempty"`
Error string `json:"error,omitempty"`
EventCount int64 `json:"eventCount"`
StartedAt time.Time `json:"startedAt"`
StoppedAt *time.Time `json:"stoppedAt,omitempty"`
}
SessionInfo is a JSON-safe snapshot of a session's state.
type SessionOptions ¶ added in v0.1.15
type SessionOptions struct {
ID string
Profile Profile
Params map[string]any
// MaxEvents caps the in-memory ring buffer (already clamped by the caller).
MaxEvents int
// OnEvent is called synchronously for every emitted event (before ring
// eviction can drop it) — the persistence hook.
OnEvent func(Event)
// OnTransition is called synchronously after every state change.
OnTransition func(SessionInfo)
}
SessionOptions configures NewSession.
type SessionRegistry ¶ added in v0.1.15
type SessionRegistry struct {
// contains filtered or unexported fields
}
SessionRegistry tracks live and recently finished sessions in memory.
func NewSessionRegistry ¶ added in v0.1.15
func NewSessionRegistry(opts RegistryOptions) *SessionRegistry
NewSessionRegistry creates a registry, applying defaults to zero options.
func (*SessionRegistry) Add ¶ added in v0.1.15
func (r *SessionRegistry) Add(s *Session) error
Add registers s, failing fast when MaxSessions active sessions already exist, and prunes the oldest terminal sessions beyond RetainDone.
func (*SessionRegistry) ClampDuration ¶ added in v0.1.15
func (r *SessionRegistry) ClampDuration(d time.Duration) time.Duration
ClampDuration lowers d to the server cap when it exceeds it.
func (*SessionRegistry) ClampEvents ¶ added in v0.1.15
func (r *SessionRegistry) ClampEvents(n int) int
ClampEvents lowers n to the server cap when it exceeds it.
func (*SessionRegistry) Get ¶ added in v0.1.15
func (r *SessionRegistry) Get(id string) (*Session, bool)
Get returns the session with the given id.
func (*SessionRegistry) List ¶ added in v0.1.15
func (r *SessionRegistry) List() []SessionInfo
List returns snapshots of all tracked sessions, oldest first.
func (*SessionRegistry) StopAll ¶ added in v0.1.15
func (r *SessionRegistry) StopAll()
StopAll stops every active session (serve shutdown hook).
type SessionState ¶ added in v0.1.15
type SessionState string
SessionState is the lifecycle state of a trace or top session.
const ( SessionStarting SessionState = "starting" SessionRunning SessionState = "running" SessionCompleted SessionState = "completed" SessionFailed SessionState = "failed" SessionStopped SessionState = "stopped" SessionInterrupted SessionState = "interrupted" )
func (SessionState) Terminal ¶ added in v0.1.15
func (s SessionState) Terminal() bool
Terminal reports whether no further transitions or events can occur.
type SortBinding ¶ added in v0.1.30
type SortBinding struct {
// Column is the public column name, which is what the client sorts by.
Column string
// Field is the backend field the order is compiled against.
Field string
}
SortBinding is one column a request may order a Profile by: the public name a caller sends, and the backend field it is applied to.
type SortingProvider ¶ added in v0.1.30
type SortingProvider interface {
// SupportsRequestSort reports whether an order named by the request is
// applied rather than ignored.
SupportsRequestSort() bool
}
SortingProvider is implemented by a provider that applies an order the request names, rather than only the one the profile declared.
It is opt-in and the default is no, which is the answer that keeps a sort control honest. A provider with no paging implementation is served by bufferedPages, which slices rows without ever applying ProviderRequest.Order; offering a sort there would reorder nothing while claiming otherwise. A provider whose order is fixed answers no for the same reason — see k8slogs, which can only read forward from a timestamp.
type StreamProvider ¶ added in v0.1.15
type StreamProvider interface {
Provider
// Stream runs req, calling emit for each row until ctx is cancelled or the
// source ends. It blocks; a nil return means the source ended normally.
Stream(ctx context.Context, req ProviderRequest, emit func(Row)) error
}
StreamProvider is an optional provider capability for continuous sources (log tails, event streams). Trace profiles require it.
type SubQuery ¶ added in v0.1.13
type SubQuery struct {
Provider ProviderConfig `json:"provider" yaml:"provider"`
Query string `json:"query,omitempty" yaml:"query,omitempty"`
}
SubQuery is a secondary provider query whose result is attached to the Result as a named context object.
type TopSpec ¶ added in v0.1.15
type TopSpec struct {
// Interval is the sampling period (default 5s, floor 1s).
Interval types.Duration `json:"interval,omitempty" yaml:"interval,omitempty"`
// MaxDuration bounds the session; the server may clamp it lower.
MaxDuration types.Duration `json:"maxDuration,omitempty" yaml:"maxDuration,omitempty"`
// SortBy names the column each snapshot is sorted by (descending).
SortBy string `json:"sortBy,omitempty" yaml:"sortBy,omitempty"`
// Limit truncates each snapshot after sorting.
Limit int `json:"limit,omitempty" yaml:"limit,omitempty"`
}
TopSpec declares a Profile as a top: the engine re-executes the query on an interval and each tick replaces the previous snapshot. Any provider works.
func (TopSpec) DurationLimit ¶ added in v0.1.15
DurationLimit returns MaxDuration, defaulted when unset.
func (TopSpec) TickInterval ¶ added in v0.1.15
TickInterval returns Interval, defaulted and floored.
type Total ¶ added in v0.1.29
Total is a row count and whether the source could state it exactly.
Exactness is carried rather than assumed because one of the backends cannot promise it: past its tracking threshold OpenSearch reports a lower bound, and a caller shown that number as a total would be reading "10,000" where the truth is "at least 10,000". The distinction belongs to whoever renders it.
func (*Total) Relation ¶ added in v0.1.29
Relation names how a caller may read Value: "eq" when the number is the count, "gte" when it is a lower bound, and "unknown" when there is no total at all.
The nil case is the reason this is a method rather than a field: a missing total and a total of zero are different facts that serialize identically, and every surface that reports one has to say which it means.
type TraceBufferSpec ¶ added in v0.1.29
type TraceBufferSpec struct {
MaxRows int `json:"maxRows,omitempty" yaml:"maxRows,omitempty"`
MaxWait types.Duration `json:"maxWait,omitempty" yaml:"maxWait,omitempty"`
}
TraceBufferSpec bounds a raw-row processor batch by count, elapsed time, or both. When both are set, the first bound reached flushes the batch.
func (TraceBufferSpec) Validate ¶ added in v0.1.29
func (s TraceBufferSpec) Validate() error
type TraceSpec ¶ added in v0.1.15
type TraceSpec struct {
// MaxDuration bounds the session; the server may clamp it lower.
MaxDuration types.Duration `json:"maxDuration,omitempty" yaml:"maxDuration,omitempty"`
// MaxEvents caps the in-memory event ring buffer.
MaxEvents int `json:"maxEvents,omitempty" yaml:"maxEvents,omitempty"`
// Buffer batches raw provider rows before running the full processor chain.
// Without it every trace processor must implement PageProcessor.
Buffer *TraceBufferSpec `json:"buffer,omitempty" yaml:"buffer,omitempty"`
// Follow marks a session promoted from a plain query profile by ?follow=true
// rather than one an author declared. It is set by Follow and is not part of
// the document, which is why it does not serialize: a profile cannot ask to
// be followed, a request can.
//
// It exists because the two kinds want opposite things from a closing time
// bound. A followed profile is being read from now onward, so an end instant
// is the moment it would stop rather than a bound on what it reads, and
// openTailWindow drops it. A declared trace is an author's whole statement of
// what the session is, and silently discarding half of a window they wrote
// down would be a worse answer than honouring it.
Follow bool `json:"-" yaml:"-"`
}
TraceSpec declares a Profile as a trace: a long-running streaming session with explicit setup (start) and teardown (stop). The provider must implement StreamProvider.
func (TraceSpec) DurationLimit ¶ added in v0.1.15
DurationLimit returns MaxDuration, defaulted when unset.
func (TraceSpec) EventLimit ¶ added in v0.1.15
EventLimit returns MaxEvents, defaulted when unset.
Source Files
¶
- caches.go
- cel.go
- column.go
- column_filter.go
- column_filter_kind.go
- column_filter_parse.go
- column_filter_resolve.go
- column_filter_target.go
- column_inspection.go
- commons.go
- connection_log_format.go
- connection_logging.go
- cursor.go
- diagnostics.go
- diagnostics_http.go
- diagnostics_redact.go
- engine.go
- execute_pages.go
- expression.go
- filters.go
- inspection_result.go
- jsonpath.go
- key.go
- keyrange.go
- kubernetes_filter.go
- library.go
- models.go
- order.go
- order_derive.go
- order_request.go
- paging.go
- param.go
- param_validate.go
- processor.go
- profile.go
- provider.go
- reconcile.go
- reconcile_merge.go
- reconcile_provenance.go
- reconcile_snapshot.go
- reconcile_table.go
- recorder.go
- recorder_har.go
- recorder_types.go
- render.go
- replay.go
- resource_selector.go
- result_column.go
- row.go
- rowexpr.go
- rowlimits.go
- sample.go
- sample_filter_values.go
- sample_input.go
- sample_inspection_columns.go
- sample_processors.go
- sample_resolution.go
- sample_value.go
- session.go
- session_registry.go
- spec.go
- stream.go
- template.go
- value_graph.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package datetime parses the date values accepted by query parameters.
|
Package datetime parses the date values accepted by query parameters. |
|
Package esdsl models an OpenSearch search as a structured specification and compiles it to a Query DSL request body.
|
Package esdsl models an OpenSearch search as a structured specification and compiles it to a Query DSL request body. |
|
Package processor contains built-in post-query processors for the query engine: sqlite-backed merge, key-based reconciliation, and CEL batch transforms that fold runs of adjacent rows together.
|
Package processor contains built-in post-query processors for the query engine: sqlite-backed merge, key-based reconciliation, and CEL batch transforms that fold runs of adjacent rows together. |
|
Package providers contains the built-in data providers for the query engine.
|
Package providers contains the built-in data providers for the query engine. |
|
Package schema generates JSON Schema (Draft 2020-12) documents that drive the clicky-ui forms and tables of the query app:
|
Package schema generates JSON Schema (Draft 2020-12) documents that drive the clicky-ui forms and tables of the query app: |