embedded

package
v0.0.0-...-b45679d Latest Latest
Warning

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

Go to latest
Published: Jun 30, 2026 License: Apache-2.0 Imports: 51 Imported by: 0

Documentation

Overview

Package embedded implements the embedded (in-process) SQL execution engine for the FoundationDB relational layer.

EmbeddedConnection is the Go equivalent of Java's EmbeddedRelationalConnection. It parses SQL, routes DDL statements through the MetadataOperationsFactory, and (eventually) routes DML through the query planner.

Index

Constants

View Source
const MaxLoggedSQLLength = 1024

MaxLoggedSQLLength bounds the SQL text carried in a PlanGenerationInfo so a pathological query can't blow up a log line.

Variables

This section is empty.

Functions

func NewExplainOnlyGenerator

func NewExplainOnlyGenerator() query.Generator

NewExplainOnlyGenerator constructs a Generator suitable for capturing Plan.Explain() output without executing. The returned Generator is backed by a zero-value EmbeddedConnection — Plan.Execute on the returned plans is unsupported (no FDB, no catalog, no session state). Used by the plan-equivalence harness (RFC-022 section 4.-1) to produce plan trees for diffing against Java's planner output.

Catalog-aware predicate trees (buildLogicalPlanFor*WithCatalog paths) require non-nil RecordMetaData; this constructor always produces text-only logical plans. Use NewExplainOnlyGeneratorWithSchema to unlock the catalog-aware branch.

func NewExplainOnlyGeneratorWithSchema

func NewExplainOnlyGeneratorWithSchema(schemaDDL string) (query.Generator, error)

NewExplainOnlyGeneratorWithSchema is the catalog-aware companion to NewExplainOnlyGenerator. It parses the supplied CREATE SCHEMA TEMPLATE DDL into an in-memory RecordLayerSchemaTemplate (no FDB write), wraps it in an api.Schema bound to a synthetic database + schema, and seeds the connection's SchemaCache. Subsequent statements planned through the returned Generator route through the buildLogicalPlanFor*WithCatalog paths so WHERE clauses appear as real cascades.predicates.QueryPredicate trees in the Explain output.

schemaDDL must contain exactly one CREATE SCHEMA TEMPLATE statement. Multiple-statement DDL or any non-CREATE-SCHEMA-TEMPLATE shape returns an error — callers should isolate the schema DDL from the SELECT/DML they intend to plan.

func PlanQueryForTest

func PlanQueryForTest(sql, schemaDDL string, stats properties.StatisticsProvider) (string, error)

PlanQueryForTest runs the full Cascades pipeline on a SQL query against a schema defined by DDL, with optional table statistics. Returns the physical plan's Explain string. No FDB connection needed.

schemaDDL is a CREATE SCHEMA TEMPLATE statement (or just the table/index definitions — "CREATE SCHEMA TEMPLATE auto_template" is prepended if missing).

stats may be nil to use default statistics (LeafScanCardinality for all record types).

func PlanQueryWithMetadata

func PlanQueryWithMetadata(sql string, md *recordlayer.RecordMetaData, stats properties.StatisticsProvider) (string, error)

PlanQueryWithMetadata is like PlanQueryForTest but accepts pre-built RecordMetaData instead of DDL. Used for testing features that require index types not expressible in DDL (aggregate indexes).

func PlanRecordQueryWithMetadata

func PlanRecordQueryWithMetadata(sql string, md *recordlayer.RecordMetaData, stats properties.StatisticsProvider) (plans.RecordQueryPlan, error)

PlanRecordQueryWithMetadata is like PlanQueryWithMetadata but returns the physical RecordQueryPlan (so callers can execute it against a real store), not just its Explain string. The session schema defaults to the embedded planner's "s".

func PlanRecordQueryWithMetadataSchema

func PlanRecordQueryWithMetadataSchema(sql string, md *recordlayer.RecordMetaData, schemaName string, stats properties.StatisticsProvider) (plans.RecordQueryPlan, error)

PlanRecordQueryWithMetadataSchema is PlanRecordQueryWithMetadata bound to a specific session schema (the real CONNECT schema on the session path — cascades_generator.go uses g.c.sess.Schema for the same threading). A non-default schema flows through NewPlanVisitorWithSchema AND the schema-qualified-table demotion/resolution, so a schema-qualified source — including INSIDE a subquery (`… EXISTS (SELECT 1 FROM PA AS main, main.PB AS B)` with session schema `main`) — is resolved against the ACTIVE schema, not the hardcoded default. RFC-142 (P2b).

func ResultColumnLabelsForPlan

func ResultColumnLabelsForPlan(plan plans.RecordQueryPlan, md *recordlayer.RecordMetaData) []string

ResultColumnLabelsForPlan returns the user-visible result-set column labels a plan would advertise — the metadata-only (no-FDB) analog of the driver's paginatingRows.Columns(): it runs the SAME production column derivation (deriveColumnsFromPlan, the function the live Execute() path calls) and maps each ColumnDef to its label exactly as Columns() does (Label, or Name when the label is empty), upper-cased. This lets the planner harness assert the result COLUMN SET — distinct from the per-row datum map (which carries extra resolution-convenience keys) — for shapes that cannot be seeded through the SQL driver (e.g. non-empty array columns for a lateral unnest, which have no SQL array-literal form). RFC-142.

func ResultColumnNullabilityForPlan

func ResultColumnNullabilityForPlan(plan plans.RecordQueryPlan, md *recordlayer.RecordMetaData) []int

ResultColumnNullabilityForPlan returns the JDBC NULLABILITY flag advertised for each result-set column, in order — the metadata-only (no-FDB) analog of the driver's ResultSetMetaData.isNullable. It runs the SAME production column derivation (deriveColumnsFromPlan → ColumnDef.Nullable) the live Execute() path uses, so the harness can assert column nullability for shapes that cannot be seeded through the SQL driver (e.g. a lateral unnest over a non-empty array column, which has no SQL array-literal form). The WITH-ORDINALITY ordinal column must report api.ColumnNoNulls here (Java's INT NOT NULL ordinal), even though it has no backing proto descriptor field. RFC-142.

func ResultColumnTypesForPlan

func ResultColumnTypesForPlan(plan plans.RecordQueryPlan, md *recordlayer.RecordMetaData) []string

ResultColumnTypesForPlan returns the SQL TYPE NAME advertised for each result-set column, in order — the metadata-only (no-FDB) analog of the driver's column-type metadata. It runs the SAME production column derivation (deriveColumnsFromPlan → ColumnDef.TypeName) the live Execute() path uses, so the harness can assert column types for shapes that cannot be seeded through the SQL driver (e.g. a lateral unnest over a non-empty array column, which has no SQL array-literal form). The element column of a non-ordinal unnest over a STRING array must report STRING here, not the UnknownType→BIGINT fallback. RFC-142.

Types

type CorrelatedExistsError

type CorrelatedExistsError struct {
	Message string
	Cause   error
}

CorrelatedExistsError is returned when buildCorrelatedExists fails. Detected via errors.As at the caller to propagate as ErrCodeUndefinedColumn for fallback to a richer outer scope.

func (*CorrelatedExistsError) Error

func (e *CorrelatedExistsError) Error() string

func (*CorrelatedExistsError) Unwrap

func (e *CorrelatedExistsError) Unwrap() error

type EmbeddedConnection

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

EmbeddedConnection is an in-process SQL connection backed by FDB.

Implements driver.Conn and driver.ExecerContext so DDL statements can execute without a Prepare round-trip.

Transaction model:

Auto-commit: every statement runs in its own FDB transaction via fdbDB.Run().
Explicit transaction: BeginTx opens an FDB transaction; all statements in
the transaction share it. Commit/Rollback close it.

func New

New returns a ready-to-use embedded connection.

func (*EmbeddedConnection) Begin

func (c *EmbeddedConnection) Begin() (driver.Tx, error)

Begin implements driver.Conn by delegating to BeginTx with default options.

func (*EmbeddedConnection) BeginTx

BeginTx implements driver.ConnBeginTx. Opens an FDB transaction that spans all subsequent statements until Commit or Rollback is called. Isolation levels other than the default and ReadCommitted return an error. Read-only transactions are not separately enforced at the FDB level.

KNOWN GAP (database/sql ctx not honored): the caller's ctx is dropped here, and beginTransaction → NewFDBRecordContext pins context.Background() (recordlayer/ database.go), so a bounded ExecContext(ctx)/QueryContext(ctx) deadline never reaches the FDB read path. Against a wedged/unreachable cluster a read therefore retries forever (correct C++/Java behaviour — neither sets a default transaction timeout — but it means a caller deadline cannot interrupt it). Honoring ctx here needs care: database/sql ctx is per-statement, an FDB transaction timeout is whole-tx (anchored at creation, bounded by the 5s MVCC window), and txns are pooled — so it is a deliberate follow-up, not a drive-by. The client CAN be bounded today via tx.SetTimeout (see TestRead_BoundedByTimeout_NoHang); what's missing is the SQL layer wiring a caller deadline through to it.

func (*EmbeddedConnection) CheckNamedValue

func (c *EmbeddedConnection) CheckNamedValue(nv *driver.NamedValue) error

CheckNamedValue implements driver.NamedValueChecker. Converts custom Go types to driver-compatible values before they reach substituteParams. Accepts: uuid.UUID → string (canonical 36-char form). All standard types (int64, float64, string, bool, []byte, time.Time) pass through unchanged.

func (*EmbeddedConnection) Close

func (c *EmbeddedConnection) Close() error

Close marks the connection as closed and cancels any open FDB transaction.

func (*EmbeddedConnection) ExecContext

func (c *EmbeddedConnection) ExecContext(ctx context.Context, sql string, args []driver.NamedValue) (res driver.Result, err error)

ExecContext executes SQL (DDL/DML/transaction) and returns the row- count result. Routes through cascadesGenerator in exec mode, which dispatches DML/DDL/transaction through execStatement and returns a Plan whose Execute aggregates RowsAffected across a multi-statement batch.

func (*EmbeddedConnection) GetDBPath

func (c *EmbeddedConnection) GetDBPath() string

GetDBPath returns the current database path.

func (*EmbeddedConnection) GetSchema

func (c *EmbeddedConnection) GetSchema() string

GetSchema returns the current schema label.

func (*EmbeddedConnection) IsValid

func (c *EmbeddedConnection) IsValid() bool

IsValid implements driver.Validator. Returns true if the connection is open; the FDB client is stateless so a non-closed connection is always usable (catalog init is lazy, not a validity condition).

func (*EmbeddedConnection) Options

func (c *EmbeddedConnection) Options() *api.Options

Options returns the connection's api.Options, or api.NoOptions() when none have been set. Used by the execution path to read the per-page scan-limit options and the statement-wide MAX_ROWS cap (RFC-106a).

func (*EmbeddedConnection) Ping

func (c *EmbeddedConnection) Ping(ctx context.Context) error

Ping implements driver.Pinger. Bootstraps the catalog on first call.

func (*EmbeddedConnection) PlanExplain

func (c *EmbeddedConnection) PlanExplain(ctx context.Context, sql string) (string, error)

PlanExplain runs the SQL through the Cascades planner and returns the physical plan's Explain string without executing the query. Useful for testing plan structure (e.g. verifying sort elimination).

func (*EmbeddedConnection) Prepare

func (c *EmbeddedConnection) Prepare(query string) (driver.Stmt, error)

Prepare returns a prepared statement. DDL statements have no bind parameters.

func (*EmbeddedConnection) PrepareContext

func (c *EmbeddedConnection) PrepareContext(_ context.Context, query string) (driver.Stmt, error)

PrepareContext implements driver.ConnPrepareContext.

func (*EmbeddedConnection) QueryContext

func (c *EmbeddedConnection) QueryContext(ctx context.Context, sql string, args []driver.NamedValue) (rows driver.Rows, err error)

QueryContext handles read-only queries (SELECT / SHOW). Routes through the query.Generator seam. Rejects multi-statement batches and non-row-returning Plans — behaviour matches the pre-seam QueryContext.

func (*EmbeddedConnection) ResetSession

func (c *EmbeddedConnection) ResetSession(_ context.Context) error

ResetSession implements driver.SessionResetter. Resets per-request state so pooled connections start clean:

  • schema → defaultSchema (original CONNECT value)
  • activeTx → rolled back (prevents a leaked transaction bleeding into the next checkout)
  • schemaCache → cleared (schema evolution between checkouts would otherwise serve a stale descriptor)

func (*EmbeddedConnection) SetDefaultSchema

func (c *EmbeddedConnection) SetDefaultSchema(s string)

SetDefaultSchema sets the initial schema that is restored by ResetSession. Called by the driver when the DSN contains ?schema=.

func (*EmbeddedConnection) SetFailOnScanLimitReached

func (c *EmbeddedConnection) SetFailOnScanLimitReached(v bool)

SetFailOnScanLimitReached toggles the Java setFailOnScanLimitReached(true) behavior (RFC-106a): when true a leaf cursor hitting a scan/byte limit errors (54F01) instead of paginating. Default false.

func (*EmbeddedConnection) SetMaxResultBytes

func (c *EmbeddedConnection) SetMaxResultBytes(n int64)

SetMaxResultBytes sets the statement-wide returned-row byte cap (RFC-106a §5). A non-positive value disables it. The accounted size is the cheap tuple-encoded length of each returned row, not exact heap — a non-exact egress ceiling.

func (*EmbeddedConnection) SetOptions

func (c *EmbeddedConnection) SetOptions(o *api.Options)

SetOptions installs the per-connection api.Options (RFC-106a scan-limit + MAX_ROWS wiring). Passing nil resets to defaults. Not safe to call concurrently with query execution on the same connection (matches database/sql's per-Conn threading contract).

func (*EmbeddedConnection) SetPlanLogger

func (c *EmbeddedConnection) SetPlanLogger(l PlanGenerationLogger)

SetPlanLogger installs a planning-metrics logger (RFC-034). Passing nil disables planning logging. Not safe to call concurrently with query planning on the same connection (matches database/sql's per-Conn threading contract).

func (*EmbeddedConnection) SetSchema

func (c *EmbeddedConnection) SetSchema(s string)

SetSchema sets the current schema label used when no schema is specified in SQL.

func (*EmbeddedConnection) SetSlowQueryThresholdMicros

func (c *EmbeddedConnection) SetSlowQueryThresholdMicros(micros int64)

SetSlowQueryThresholdMicros sets the slow-query threshold in microseconds. A non-positive value disables the slow-query flag.

func (*EmbeddedConnection) SetStatementTimeout

func (c *EmbeddedConnection) SetStatementTimeout(d time.Duration)

SetStatementTimeout sets the per-Execute wall-clock deadline (RFC-106a). A non-positive duration disables it. PER-REQUEST semantics: it bounds a single Execute (all its pages); a continuation resumed by a new request starts a fresh deadline. There is intentionally no `SET statement_timeout = …` SQL path — the parser grammar has no generic SET <var> = <val> rule (only SET TRANSACTION), so a grammar change would be required; this connection-field setter is the Go-local config instead (RFC-106a §3).

type PlanCache

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

PlanCache caches Cascades query plans keyed by normalized SQL text. Thread-safe for concurrent access. Uses an LRU eviction strategy with a configurable maximum size.

LRU order is tracked with a doubly-linked list (front = least recently used, back = most recently used) paired with a map from key to list element. Promotion on hit/update and eviction of the oldest entry are all O(1), matching Java's Caffeine-backed plan cache (RelationalPlanCache / MultiStageCache, which uses maximumSize LRU eviction). The previous slice-based order tracking linear-scanned on every hit — O(n) under the lock — which became a contention point at large cache sizes.

See RFC-029: keys on the full normalized SQL string to eliminate hash-collision correctness bugs (previously keyed on uint64 FNV-64a). See RFC-033: O(1) LRU via container/list.

func NewPlanCache

func NewPlanCache(maxSize int) *PlanCache

NewPlanCache creates a plan cache with the given maximum number of entries. If maxSize <= 0, it defaults to 256.

func (*PlanCache) Get

func (c *PlanCache) Get(sql string) (plans.RecordQueryPlan, []scalarSubqueryBinding, bool)

Get looks up a cached plan by SQL text. The SQL is normalized internally (case-folded, whitespace-collapsed, comments stripped) before lookup. Returns the plan, scalar subquery bindings, and true on a cache hit; nil, nil, false on miss.

func (*PlanCache) Invalidate

func (c *PlanCache) Invalidate()

Invalidate clears all cached entries. Must be called when schema metadata changes (DDL: CREATE/DROP TABLE, CREATE/DROP INDEX, etc.).

func (*PlanCache) Len

func (c *PlanCache) Len() int

Len returns the number of entries currently held by the cache. O(1).

func (*PlanCache) Put

func (c *PlanCache) Put(sql string, plan plans.RecordQueryPlan, subs []scalarSubqueryBinding)

Put stores a plan in the cache keyed by normalized SQL text. If the cache is at capacity, the least recently used entry is evicted.

func (*PlanCache) Stats

func (c *PlanCache) Stats() (hits, misses int64)

Stats returns the cumulative hit and miss counts.

type PlanCacheEvent

type PlanCacheEvent int

PlanCacheEvent classifies how the plan cache participated in a single Plan() call. Mirrors Java's RelationalLoggingUtil.PlanCacheEvent ({SKIP, HIT, MISS, INCONCLUSIVE}).

const (
	// PlanCacheInconclusive is the zero value: planning errored before a
	// cache decision was reached (Java: INCONCLUSIVE).
	PlanCacheInconclusive PlanCacheEvent = iota
	// PlanCacheSkip means the plan was produced but deliberately not cached
	// (LIMIT/OFFSET query) or no cache is configured.
	PlanCacheSkip
	// PlanCacheHit means the plan was served from the cache.
	PlanCacheHit
	// PlanCacheMiss means the plan was freshly built and stored in the cache.
	PlanCacheMiss
)

func (PlanCacheEvent) String

func (e PlanCacheEvent) String() string

type PlanGenerationInfo

type PlanGenerationInfo struct {
	// SQL is the original whitespace-preserved query text (from the parse
	// tree's token interval, not GetText()), truncated to MaxLoggedSQLLength.
	SQL string
	// PlanHash is the deterministic hash of the chosen physical plan tree,
	// or 0 when no physical plan was produced (e.g. planning error).
	PlanHash uint64
	// PlanExplain is the plan's Explain() text, or "" when no plan was produced.
	PlanExplain string
	// PlanningDuration is the wall-clock time spent in this planning call.
	PlanningDuration time.Duration
	// Cache records how the plan cache participated.
	Cache PlanCacheEvent
	// CacheNumEntries is the plan cache's current size (Java:
	// primaryCacheNumEntries), or 0 when no cache is configured.
	CacheNumEntries int
	// SlowQuery is true when PlanningDuration exceeded the connection's
	// slow-query threshold.
	SlowQuery bool
	// Err is the planning error, or nil on success.
	Err error
}

PlanGenerationInfo is the diagnostic record emitted once per Plan() call. The field set mirrors the keys Java adds to its KeyValueLogMessage in RelationalLoggingUtil.publishPlanGenerationLogs. There is deliberately no scalar "estimated cost": the Cascades cost model is a comparator, not a number — plan identity is PlanHash + PlanExplain, matching Java.

type PlanGenerationLogger

type PlanGenerationLogger interface {
	LogPlanGeneration(ctx context.Context, info PlanGenerationInfo)
}

PlanGenerationLogger receives one callback per Plan() call. A nil logger is silent. Sampling and log-level policy are the handler's responsibility: the engine always emits, and the handler decides volume and sink. (Java keeps the same split — the engine builds the record unconditionally; SLF4J level and any sampling live outside the planner.)

type PlanVisitor

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

PlanVisitor builds LogicalOperator trees from ANTLR parse nodes. It holds the metadata needed for catalog-aware resolution (predicate upgrade, column validation, sort-key resolution) and any CTE column schemas accumulated from WITH clause processing.

func NewPlanVisitor

func NewPlanVisitor(md *recordlayer.RecordMetaData) *PlanVisitor

NewPlanVisitor creates a PlanVisitor with the given metadata, defaulting the session schema to the embedded planner's "s". md may be nil; all catalog-aware upgrades degrade to text fallback.

func NewPlanVisitorWithSchema

func NewPlanVisitorWithSchema(md *recordlayer.RecordMetaData, schemaName string) *PlanVisitor

NewPlanVisitorWithSchema creates a PlanVisitor bound to a specific session schema (the real CONNECT schema on the session path). RFC-142.

func (*PlanVisitor) VisitQuery

VisitQuery is the top-level entry point. It handles WITH (CTE) wrapping and then delegates to VisitQueryBody for the main query.

Mirrors buildLogicalPlanForQueryWithCatalog: pre-scans CTE definitions to extract column schemas, then recursively builds the main query body with CTE scopes in context.

func (*PlanVisitor) VisitQueryBody

VisitQueryBody dispatches simple SELECT vs UNION, threading metadata and CTE scopes through both arms.

func (*PlanVisitor) VisitSimpleTable

func (v *PlanVisitor) VisitSimpleTable(termCtx *antlrgen.QueryTermDefaultContext) (logical.LogicalOperator, error)

VisitSimpleTable is the main SELECT visitor. It walks the ANTLR tree incrementally, building the LogicalOperator tree step by step in the same order as Java's QueryVisitor.visitSimpleTable:

  1. FROM clause → visitFrom → scan/derived/join operator
  2. WHERE clause → visitWhere → wrap with filter
  3. SELECT+GROUP BY+HAVING → visitSelectGroupBy → wrap with aggregate
  4. ORDER BY → visitOrderBy → wrap with sort (ANTLR direct)
  5. LIMIT/OFFSET → visitLimit → wrap with limit (ANTLR direct)
  6. Projection → visitFinalProjection + DISTINCT (ANTLR direct)
  7. Catalog-aware upgrades (inline) → predicate resolution, column validation, Value resolution for projections/aggregates/sort keys, qualified star expansion, EXISTS/scalar subquery planning.

Aggregate classification delegates to classifySelectElements which returns a selectClassification. When metadata is available, the classification is bridged to a selectQuery for the upgrade functions that consume it — the operator tree itself is built directly by the visit methods.

Jump to

Keyboard shortcuts

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