parsesqlite

package
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Nov 9, 2026 License: MIT Imports: 5 Imported by: 0

README

parse/parsesqlite

Registers the SQL functions parse's SQLite plans compare decimals through, with modernc.org/sqlite. SQLite has no exact decimal type: a NUMERIC column converts text past 64-bit integers to a double, and json_extract reads a JSON number as one. filter.SQLiteDialect therefore reads every decimal column and decimal path member through two deterministic functions, so equality, sets, ranges, sorts, keyset seeks and facets on SQLite agree with PostgreSQL's numeric and with filter.Evaluate to core.MaxDecimalDigits (1000) digits.

Quick Start

func openDB(ctx context.Context, dsn string) (*sql.DB, error) {
	if err := parsesqlite.Register(); err != nil {
		return nil, err
	}
	return sql.Open("sqlite", dsn)
}

Register before the database opens its first connection: modernc.org/sqlite installs registered functions on each connection it opens afterwards. Declare decimal columns TEXT so they hold every decimal exactly (decimals). The example server registers in openDB.

Architecture

flowchart LR
    plan["filter.SQLiteDialect<br/>parse_decimal(amount) = ?1<br/>parse_decimal_key(doc -> '$.price') > parse_decimal_key(?2)"] --> sqlite["SQLite (modernc.org/sqlite)"]
    sqlite -->|"per row: column value or JSON literal"| fns["parse_decimal / parse_decimal_key<br/>(this package)"]
    fns --> dec["internal/decimals<br/>Value · Key"]
    dec --> core["core.DecimalText"]

Entry points

  • Register(): registers both functions with the driver, once per process.
  • Decimal(v), DecimalKey(v): the functions over one SQLite value, for registering with another driver.

Exit points

  • Function results: TEXT, a BLOB or NULL. Nothing is written or logged.

Failure modes

  • A connection opened before Register, or a database whose driver never registered the functions: every statement on a decimal field fails with SQLite's no such function: parse_decimal (or parse_decimal_key). parse never falls back to a rounded comparison.
  • Register failing: parsesqlite: register parse_decimal: a function named "parse_decimal" is already registered means another package registered that name with modernc.org/sqlite first. Register returns its first result on every call.
  • A value that holds no decimal (a JSON boolean, a string with an exponent, text that is no number, a BLOB): NULL, so a filter treats it as missing, as filter.PathValue does.

Dependencies

  • Imports: parse/internal/decimals, parse/core, modernc.org/sqlite
  • Imported by: applications running parse plans on SQLite, parse/internal/dbtest, the example server
  • Package boundary: filter does not import this package, so the compiler stays driver-free; both read the function names from internal/decimals

Functions

SQL Returns Used for
parse_decimal(x) x's canonical decimal text (core.DecimalText): plain notation, no exponent, no leading or trailing zeros, 0 for either zero; NULL when x holds no decimal equality, $in / $nin against the set's canonical texts, facet terms
parse_decimal_key(x) a BLOB whose bytes order as x's value, equal for equal values; NULL when x holds no decimal ranges, ORDER BY, keyset seeks, range facets

x is read as:

SQLite value Decimal
INTEGER its digits
REAL its shortest decimal form
TEXT holding a JSON number literal (10.50, 1.05e1) its value: a TEXT column's exact decimal, or a document member's number as -> returns it
TEXT holding a JSON string literal ("9.50") its content, if it is a decimal in plain notation of at most 1000 characters: a document member's string, as filter.PathValue reads a decimal member
anything else (NULL, a BLOB, JSON null / true / an object, other text) none: NULL

The key is a sign byte (negative, zero, positive), then for a nonzero value 0.d₁…dₙ × 10^e the exponent e biased to two big-endian bytes and the digits; a negative's exponent and digits are complemented and end with 0xFF, so a greater magnitude orders lower.

Configuration

None. The functions are pure and deterministic; Register takes no options.

Build and test

From the parse module directory:

go test -tags=unit -race ./parsesqlite/ ./internal/decimals/
go test -tags=unit -race -run 'TestDecimalMatrix_SQLite|TestEvaluateDecimalConformance' ./ ./filter/

internal/decimals checks over generated literals that the key orders as core.CompareDecimals does. TestDecimalMatrix_SQLite runs filtertest.DecimalCases, the decimal sorts, keyset walks and facets on SQLite; the PostgreSQL and filter.Evaluate runs of the same cases must agree with it.

Operations

  • Cost: one Go call per row for each decimal expression a statement evaluates. A key is the value's significant digits plus three bytes.
  • Indexes: SQLite indexes an expression of deterministic functions. CREATE INDEX … ON t (parse_decimal_key(amount)) serves decimal ranges, sorts and keyset seeks; (parse_decimal(amount)) serves equality and sets. An index on them needs the functions on every connection that writes the table, the sqlite3 shell included, since its maintenance calls them.
  • Health and metrics: none; a missing registration surfaces as the statement error above.

Security

The functions read only their argument, allocate at most the value's digits (bounded by core.MaxDecimalDigits), and have no side effects. A string member longer than 1000 characters is NULL before it is parsed.

Code Map

File Responsibility
parsesqlite.go Register, Decimal, DecimalKey, DecimalFunc, DecimalKeyFunc
../internal/decimals Value (a SQLite value as a decimal), Member (a document member as a decimal), Key, the function names

Documentation

Overview

Package parsesqlite registers the SQL functions parse's SQLite dialect calls, with the modernc.org/sqlite driver. SQLite has no exact decimal type: a NUMERIC column or a json_extract result holds a double, so filter.SQLiteDialect compares, orders and facets decimals through two deterministic functions instead.

  • parse_decimal(x) is the canonical decimal text (core.DecimalText) of x, or NULL: equality, sets and facet keys compare it.
  • parse_decimal_key(x) is a BLOB that orders bytewise as the decimal x, or NULL: ranges, ORDER BY and keyset seeks compare it.

x is a decimal column's value (an INTEGER, a REAL, or TEXT holding a JSON number literal) or a document member as SQLite's `->` operator returns it: a JSON number literal as written, or a JSON string literal holding a decimal in plain notation, as filter.PathValue reads a decimal member. Anything else is NULL, so a filter treats it as missing.

Call Register before the database opens its first connection. A statement on a connection without the functions fails with SQLite's "no such function: parse_decimal" rather than comparing a rounded double.

Index

Constants

View Source
const (
	DecimalFunc    = decimals.SQLiteValueFunc
	DecimalKeyFunc = decimals.SQLiteKeyFunc
)

The functions' SQL names, as filter.SQLiteDialect calls them.

Variables

This section is empty.

Functions

func Decimal

func Decimal(v driver.Value) (driver.Value, error)

Decimal is parse_decimal for one SQLite value: its canonical decimal text, or nil (SQL NULL) when it holds no decimal. It is exported for a SQLite driver other than modernc.org/sqlite to register under DecimalFunc.

func DecimalKey

func DecimalKey(v driver.Value) (driver.Value, error)

DecimalKey is parse_decimal_key for one SQLite value: a BLOB whose bytes order as the decimal does, equal for equal decimals, or nil (SQL NULL) when it holds no decimal. It is exported for a SQLite driver other than modernc.org/sqlite to register under DecimalKeyFunc.

func Register

func Register() error

Register registers parse_decimal and parse_decimal_key with modernc.org/sqlite, for every connection the "sqlite" driver opens afterwards; connections already open lack them. It registers once per process: later calls, from any goroutine or package, return the first call's result.

Types

This section is empty.

Jump to

Keyboard shortcuts

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