kernel

package
v0.18.43 Latest Latest
Warning

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

Go to latest
Published: Sep 5, 2026 License: AGPL-3.0 Imports: 14 Imported by: 0

Documentation

Overview

Package kernel provides type-specialized vectorized operations for the query engine. Generic functions are monomorphized at compile time and dispatch is resolved once at query init time (not per-row), eliminating type-switch overhead from hot loops.

Index

Constants

View Source
const (
	NumConstOK     = IntConstOK
	NumConstSyntax = IntConstSyntax
	NumConstRange  = IntConstRange
)

Variables

This section is empty.

Functions

func BoolFilterConst added in v0.18.4

func BoolFilterConst(v any) (val, ok bool)

BoolFilterConst resolves a BOOL-column filter constant to a Go bool. A Go bool arrives from a parameter; a SQL text literal (Go string, or []byte from a binary parameter) is read through PostgreSQL's boolean input grammar. ok is false only for a string that names no boolean, and a nil kernel is how this package asks the caller (exec.boolConstError) to raise 22P02 — the same "nil kernel, caller raises" convention the network and DECIMAL arms use.

func CanonicalFloat32 added in v0.18.1

func CanonicalFloat32(f float32) float32

CanonicalFloat32 is CanonicalFloat64 for float32.

func CanonicalFloat64 added in v0.18.1

func CanonicalFloat64(f float64) float64

CanonicalFloat64 / CanonicalFloat32 fold a value onto the one bit pattern the order above treats as canonical for it: every NaN payload onto one NaN, and -0.0 onto +0.0. CompareFloat64/32 call both of those pairs EQUAL, and the standing invariant (ADR-0012 item 8) is that two values the comparator calls equal must also SERIALIZE alike — otherwise a GROUP BY splits one group in two, a hash join misses a pair the comparator matches, or a shuffle routes two equal keys to different partitions and the distributed answer stops agreeing with the single-process one.

func CidrOrderKey added in v0.18.3

func CidrOrderKey(s string) string

CidrOrderKey is CidrSortKey for every CIDR consumer that needs a definite key for EVERY stored value, not just the ones that parse: ORDER BY, GROUP BY / DISTINCT / COUNT(DISTINCT) / hash-join hash keys, MIN/MAX, and the distributed shuffle router (#520 — the residual ADR-0012 item 10 left open after #492 fixed WHERE-clause comparison: those consumers still keyed a CIDR column on its raw stored TEXT, so '10.0.0.1' and '10.0.0.1/32' were two GROUP BY groups, two DISTINCT values and could land in two different hash-join buckets or shuffle partitions, while `=` already called them one value).

A stored value that names no address at all falls back to its own raw text: the column is unvalidated (internal/storage/ingest), so a malformed row still needs a stable, total-order position instead of a panic or an arbitrary collision — it groups/sorts with other byte-identical garbage, which is the same answer every OTHER type gives an unparseable value it cannot special-case either. This is deliberately NOT the same rule as a FILTER LITERAL that names no address (kernel.ResolveFilterKernel's TypeCIDR arm, exec.networkConstError): a bad literal in a WHERE/comparison is a query the engine cannot answer at all (22P02); a bad STORED value is just one row with no defined place in the order, same as a NULL.

func CidrSortKey added in v0.18.2

func CidrSortKey(s string) (string, bool)

CidrSortKey re-keys a CIDR/inet TEXT value ("192.168.1.0/24", "10.0.0.1/8", or a bare "10.0.0.1") into PostgreSQL's `inet` order — network_cmp — as a byte string two keys compare LEXICALLY in exactly that order.

PostgreSQL's network_cmp_internal compares, in this sequence:

  1. the address FAMILY (v4 before v6),
  2. the common bits under the SMALLER of the two prefix lengths,
  3. the prefix length itself,
  4. the FULL, UNMASKED address.

The key is [family][address masked to its own prefix, full width][prefix length][full unmasked address], which reproduces that order exactly. Step 2 needs both operands and no single-value key can hold it directly, but the masked address is equivalent: if the first min(len) bits differ, both keys retain the differing bit and compare the same way; if they agree, the shorter prefix's key has zeros where the longer one may have ones, so it sorts first — which is step 3's answer — and when those bits are zero too the keys tie and the explicit prefix-length byte decides. The trailing full address is step 4.

Verified against live PostgreSQL 17 over host-bearing and canonical values, v4 and v6, at mixed prefix lengths — the whole table is TestCidrSortKeyMatchesPostgresInetOrder's fixture. Three of its consequences are worth naming because a simpler key gets them wrong:

'9.255.255.255/32' < '10.0.0.0/8'   — common bits decide before the mask
'192.168.1.5/24'   < '192.168.1.0/32' — the MASK outranks the address
'10.0.0.0/8'       < '10.0.0.1/8'   — host bits are kept, and ordered last

That last one is why the key cannot be built from net.ParseCIDR's MASKED network alone, which is what this function did when #492 introduced it: keying only ipnet.IP threw the host bits away, so '10.0.0.1/8' and '10.0.0.0/8' became the SAME value and `= '10.0.0.1/8'` answered rows holding a different address. Wadjet's CIDR column is unvalidated text (internal/storage/ingest), and host-bearing prefixes are ordinary in the network data this type exists for, so those are not edge values.

A BARE address with no "/" is a /32 (v4) or /128 (v6), which is what PostgreSQL's inet does with the same input — `'10.0.0.1'::inet = '10.0.0.1/32'::inet` is true. A v4-MAPPED v6 address ("::ffff:10.0.0.2") keeps the v6 family, also matching PostgreSQL (`family()` answers 6).

ok is false when s is not an address at all. Callers must turn that into a query ERROR, never a match-nothing kernel: see ResolveFilterKernel's TypeCIDR arm.

Exported — unlike this file's other literal parse helpers (parseIPv4ToInt64, parseMACToInt64), which internal/engine/expr duplicates locally rather than importing — because this one is not a trivial re-encode: expr.CmpNetworkLit's CIDR literal and this kernel's per-row CIDR key MUST agree bit for bit, and two structural parsers maintained separately is exactly the shape #492 already is (the kernel path numeric, the expr path lexical). One implementation, shared, is what keeps them from drifting apart again.

func CompareDecimalAt

func CompareDecimalAt(a *batch.Vector, ai int, b *batch.Vector, bi int) int

CompareDecimalAt orders two DECIMAL values by NUMERIC value, which is what PostgreSQL's `numeric` ordering means and what every other comparator in this file already does for its type. Before this arm existed, DECIMAL fell through the three resolvers' defaults to a comparator that reports every row equal, so `ORDER BY dec_col` was a stable no-op that returned input order, and a sort-merge join on a DECIMAL key matched every row against every row. The other path in the tree — compareAny over Vector.GetValue — compares the FORMATTED string instead, where "10.001" sorts before "2.0002". Same query, three different sequences depending on which path answered (#394).

The comparison is EXACT at every scale. Equal scales compare the unscaled Int128s directly — that is every sort over one column, every sorted run and every k-way merge over runs. Unequal scales, reachable where two separately declared DECIMAL columns meet, rescale the smaller-scale operand by 10^(delta) and compare the unscaled integers; if that product overflows Int128 the two are compared as big.Int rather than approximated.

Exactness is not a nicety here: SortMergeJoin uses this comparator for key EQUALITY (sort_merge_join.go), so an approximate answer is a spurious JOIN MATCH. The float64 rescale this replaced held to 2^53 unscaled units and then started reporting 9007199254740993 and 9007199254740992.0 — which differ by one unscaled unit at the common scale — as the same key.

func CompareDecimalValues added in v0.18.1

func CompareDecimalValues(av batch.Int128, as int, bv batch.Int128, bs int) int

CompareDecimalValues is CompareDecimalAt on values already read out of their columns — the form the col-col FILTER kernel needs, which reads its two slices once per batch rather than per row. One function so the sort comparator, the sort-merge join key and the filter cannot drift apart.

func CompareFloat32 added in v0.18.1

func CompareFloat32(a, b float32) int

CompareFloat32 orders two float32 values with NaN greatest and NaN == NaN.

Native float32 comparisons, not a widen-to-float64-and-delegate: widening every element cost ZZSortFloat32NoNulls +2.03% (benchmarked against CompareFloat64(float64(a), float64(b)), the form this replaced) for a rule that needs nothing float64 offers — float32's `<`/`>`/`==`/self-inequality already carry the same PostgreSQL order this function documents for float64.

func CompareFloat64 added in v0.18.1

func CompareFloat64(a, b float64) int

CompareFloat64 orders two float64 values with NaN greatest and NaN == NaN.

func CompareValuesAt added in v0.18.1

func CompareValuesAt(a *batch.Vector, ai int, b *batch.Vector, bi int) int

CompareValuesAt orders two values of the same type, WITHOUT consulting either row's null bit. Callers that can see a NULL use compareElemAt (for a container's elements) or one of the resolvers (for a column).

The type comes from a: two vectors reaching one comparator always carry the same type — the sort compares one column against itself and the join's planner gate requires identical key types.

func DateLiteralDays added in v0.18.3

func DateLiteralDays(v any) (int32, error)

DateLiteralDays is toDateInt32 exported for exec.dateConstError to recover the same failure ResolveFilterKernel/ResolveInFilterKernel already saw when they returned a nil kernel for a DATE constant, mirroring CidrSortKey/IPv6LitKey/DecimalConstText's role for their own types.

func DecimalConstText added in v0.18.1

func DecimalConstText(v any) (string, bool)

DecimalConstText renders a comparison constant as the decimal text a DECIMAL column's domain is reached through, and reports whether the constant IS a number.

The second result is not decoration. A constant nobody can read used to resolve to the value ZERO and match every stored zero (#463) — the worst shape a failure can take, because it neither errors nor returns nothing. PostgreSQL refuses the query instead ("invalid input syntax for type numeric"), and ADR-0012 makes PostgreSQL the authority on error-versus-not, so a false here is a query error at the caller, not a value.

Exponent form is passed through untouched: batch.DecimalTextAt folds the exponent into the scaling exactly, where expanding it through a float64 first is what lost 1e400 entirely.

func FiniteDecimalText added in v0.18.5

func FiniteDecimalText(s string) bool

FiniteDecimalText reports whether text names a FINITE number — isDecimalText without the comparison bounds.

It exists for the one caller whose question is not "can a DECIMAL column be compared against this": folding a unary minus into a quoted string literal (expr.compileWithCtx). `-'NaN'` is not a value in PostgreSQL either — an unknown-typed literal under unary minus is 42725 there, "operator is not unique" — and negating the text would produce '-NaN', which PostgreSQL's numeric input refuses outright. So that fold keeps the narrow reader and `-'NaN'` stays the refusal it already was.

func Float32LitUnrepresentable added in v0.18.5

func Float32LitUnrepresentable(v any) bool

Float32LitUnrepresentable reports whether a literal's boxed value is one PostgreSQL refuses to put in a `real`. It is the check exec.floatConstError turns into 22003, the same "nil kernel, caller raises" convention DateLiteralDays serves for DATE.

func FloatCompareOp added in v0.18.1

func FloatCompareOp[T FloatOrdered](a, b T, op CompareOp) bool

FloatCompareOp applies one of the six predicates to a pair. The row-at-a- time paths (exec's ColumnCompare fallback, expr's CmpFloat64) use this so they answer what the vectorized kernel answers; the kernels themselves resolve the operator ONCE and keep the per-row form above.

func FloatEq added in v0.18.1

func FloatEq[T FloatOrdered](a, b T) bool

FloatEq reports a = b under PostgreSQL's float order (NaN equals NaN).

func FloatGe added in v0.18.1

func FloatGe[T FloatOrdered](a, b T) bool

FloatGe reports a >= b under PostgreSQL's float order.

func FloatGt added in v0.18.1

func FloatGt[T FloatOrdered](a, b T) bool

FloatGt reports a > b under PostgreSQL's float order.

func FloatLe added in v0.18.1

func FloatLe[T FloatOrdered](a, b T) bool

FloatLe reports a <= b under PostgreSQL's float order.

func FloatLt added in v0.18.1

func FloatLt[T FloatOrdered](a, b T) bool

FloatLt reports a < b under PostgreSQL's float order (NaN is greatest).

func FloatNe added in v0.18.1

func FloatNe[T FloatOrdered](a, b T) bool

FloatNe reports a <> b under PostgreSQL's float order.

`a == a || b == b` rather than `!(a != a && b != b)`: the two are the same predicate, but this one short-circuits on the FIRST operand for every non-NaN row, which is the row the branch predictor sees.

func FloatSpecialText added in v0.18.5

func FloatSpecialText(text string) (float64, bool)

FloatSpecialText reads the NaN/±Infinity spellings PostgreSQL's FLOAT input accepts, and reports ok=false for everything else — including ordinary numbers, which are the caller's own business.

It is deliberately a second reader beside batch.DecimalSpecialText, because the two grammars differ in one place and that place is observable: float8 accepts a SIGNED NaN (`'+NaN'::float8` and `'-NaN'::float8` are both NaN) where numeric refuses it with 22P02. Verified live on postgres:17-alpine (17.11) for both float8 and float4; everything else matches — case insensitive, C whitespace stripped, `inf` and `infinity` with an optional adjacent sign, and no prefix matching (`Infin`, `infinit`, `infinityy` and `- inf` are all refused).

The VALUE returned is the ordinary IEEE one, so the caller compares it with CompareFloat64 and gets PostgreSQL's float order for free: a FLOAT column HOLDS all three, so its comparison against one of them is a plain float comparison — unlike DECIMAL, where the same literal is a BOUND because the carrier has no such value at all (ADR-0024 item 6, #534).

func IPv4LitKey added in v0.18.3

func IPv4LitKey(s string) (int64, bool)

IPv4LitKey exports parseIPv4ToInt64 for exec.networkConstError, which needs to know whether a filter literal named an address at all — not just its encoded value — the same way kernel.IPv6LitKey and kernel.CidrSortKey already do for their two types.

func IPv6LitKey added in v0.18.2

func IPv6LitKey(s string) (key string, ok bool)

IPv6LitKey re-keys an IPv6 filter literal into the form a TypeIPv6 column's rows compare against: the address's raw 16 bytes, which a byte comparison orders exactly as the address's own big-endian numeric value.

A v4-shaped literal is not that, and is not a v4-MAPPED v6 address either. PostgreSQL's inet compares the FAMILY first and puts every v4 address below every v6 one (`'255.255.255.255'::inet < '::'::inet` is true), including below a v4-mapped v6 address, which it still calls family 6 (`family('::ffff:10.0.0.2'::inet)` answers 6). The key for a v4 literal is therefore the EMPTY string: it is shorter than, and a prefix of, every 16-byte row value, so it compares strictly below all of them and equals none — PostgreSQL's family rule, with no per-row re-keying.

Reading a v4 literal as its v4-mapped 16 bytes instead — which is what the TypeIPv6 kernel arm used to do, through a plain net.ParseIP — placed it in the MIDDLE of the v6 range (below 2001:db8:: and above ::1), while the row-at-a-time path fell through to a lexical text comparison entirely: two paths, two orders, neither PostgreSQL's.

ok is false for a literal that is no address at all; the caller raises the query error, the same as CidrSortKey's.

func IPv6RowKey added in v0.18.3

func IPv6RowKey(s string) (string, bool)

IPv6RowKey re-keys a TypeIPv6 column's RENDERED text back into the raw 16 bytes the column actually stores, which is what the vectorized kernel compares (ResolveColColFilterKernel's TypeIPv6 arm reads BytesData directly) and what a byte comparison orders as the address's own big-endian value.

It exists because the two evaluation sites read the column through different doors. The kernel has the vector and reads the 16 bytes; the row-at-a-time evaluator has ColRef.Eval's BOX, which for TypeIPv6 is the address's TEXT (Vector.GetValue renders `net.IP(raw).String()`). Comparing that text lexically is not the address's order — "2001:db8::9" sorts ABOVE "2001:db8::10" as text and BELOW it as an address — so `WHERE a < z` answered one thing through the scan and the opposite through a projection or a later DAG stage's re-parsed filter (#565, #492's finding one type over).

The round trip is exact: Vector.SetValue stores `net.ParseIP(s).To16()` and GetValue renders that back, so parsing the rendering recovers the identical bytes — including for a v4-MAPPED address, which Go renders as a dotted quad and re-parses to the same v4-mapped 16 bytes, keeping the row on the v6 side of PostgreSQL's family split the way the stored bytes already put it. That is why this is NOT IPv6LitKey: a LITERAL dotted quad is a v4 address and keys BELOW every v6 row (PostgreSQL compares family first), while a STORED one is a v4-mapped v6 address and keys among them.

ok is false for a rendering that names no address, which a 16-byte column does not produce — GetValue answers "" only for a value that is not 16 bytes wide, which SetValue never writes.

func Int32FilterBound added in v0.18.11

func Int32FilterBound(v any, op CompareOp) (int32, CompareOp, IntBoundVerdict, IntConstStatus)

Int32FilterBound is IntFilterBound narrowed to the int32-backed integer types. A bound OUTSIDE int32 is the whole column's answer here rather than the IntConstRange refusal Int32FilterConst makes for an equality constant: `c_i32 > 3.5e9` names no int32 and no int32 satisfies it, which is a verdict and not an error.

func IntFilterBound added in v0.18.11

func IntFilterBound(v any, op CompareOp) (int64, CompareOp, IntBoundVerdict, IntConstStatus)

IntFilterBound resolves an integer column's filter constant AND its operator together, which is what a NON-INTEGRAL constant needs and Int64FilterConst alone cannot give (#704).

`int64(3.5)` is 3, so `c = 3.5` matched the row holding 3 and `c IN (3.5)` matched it too; Go truncates TOWARD ZERO, so `c = -0.5` matched the row holding 0. PostgreSQL compares `bigint = numeric` exactly and answers no rows for all three. The typemx measurement in the arc brief read 0 for the INT64 column only because no row of it holds 3 — `c_i64 = 1000003.5` matched one, which is the same defect one fixture row away.

For an integer column c and a constant f with a fraction, floor(f) = n:

c =  f  ->  no row          c <> f  ->  every non-NULL row
c >  f  ->  c >  n          c >= f  ->  c >  n
c <  f  ->  c <= n          c <= f  ->  c <= n

The same rewrite answers a constant OUTSIDE int64 entirely (±Infinity included, which is why the infinities need no arm of their own): there the verdict is the whole column's, one way or the other. A NaN constant declines — the caller raises rather than comparing against an implementation-defined conversion — and no SQL spelling reaches this with one, since a quoted 'NaN' is read by the integer grammar and refused there.

Every non-float box delegates to Int64FilterConst with the operator unchanged, so the ordinary path is exactly what it was.

func IsDateSyntaxError added in v0.18.4

func IsDateSyntaxError(err error) bool

IsDateSyntaxError reports whether err is a malformed-DATE-literal failure (PostgreSQL 22007, invalid_datetime_format) as opposed to a nonexistent or out-of-range calendar date (22008, datetime_field_overflow). exec. dateConstError reads it to pick the SQLSTATE, the same "nil kernel, caller raises" convention CidrSortKey/DecimalConstText use. The classification is owned by parquet.ParseDateDays, the one string->date conversion the filter path, the writers and the ingest boundary all share (#560).

func KeyFloat32Bits added in v0.18.1

func KeyFloat32Bits(f float32) uint32

KeyFloat32Bits is KeyFloat64Bits for float32.

func KeyFloat64Bits added in v0.18.1

func KeyFloat64Bits(f float64) uint64

KeyFloat64Bits / KeyFloat32Bits are Float64bits/Float32bits over the canonical value — the bits any KEY, hash or partition router should use for a float.

func MACLitKey added in v0.18.3

func MACLitKey(s string) (int64, bool)

MACLitKey exports parseMACToInt64 for exec.networkConstError; see IPv4LitKey.

func NumericTypeName added in v0.18.5

func NumericTypeName(typ batch.TypeID) (string, bool)

NumericTypeName is the PostgreSQL type name a numeric column's refusal message carries. ok=false for every type with no literal rule of this kind.

The wadjet-native PORT/PROTOCOL/DURATION have no PostgreSQL equivalent, so they name themselves; the rest use PostgreSQL's own spelling, which the pg-oracle's wire arm checks byte-for-byte.

func ParseBoolText added in v0.18.4

func ParseBoolText(s string) (val, ok bool)

ParseBoolText is `parse_bool_with_len` (src/backend/utils/adt/bool.c), which is what PostgreSQL's `text::boolean` runs — the boolean INPUT grammar, not a rendered-bool string match.

It accepts, case-insensitively and after trimming C `isspace` whitespace, any non-empty PREFIX of "true", "false", "yes" or "no", plus "on"/"off" and the single characters "1" and "0". The prefix rule is not decoration: `'tr'::boolean` and `'fals'::boolean` answer on live PostgreSQL 17, and a stricter reader would raise 22P02 for values PostgreSQL accepts. "o" alone is the one prefix REFUSED, because it cannot choose between "on" and "off".

This is the ONE binding for the grammar (#574): both comparison paths read a BOOL-column-versus-text-literal through it — the vectorized kernel here (ResolveFilterKernel's TypeBool arm and inFilterBool) and the row-at-a-time expr.compare — so they can no longer disagree with each other or with PostgreSQL. internal/engine/expr.parseBoolText delegates here rather than keeping a second copy. Before this, kernel.toBool read every string as false (so `bo = 't'` matched the FALSE rows) while expr.compare rendered the bool as "true"/"false" and matched only those exact spellings — two wrong answers in opposite directions, ADR-0012 item 8's two-path split one type over from the boxed-pair fixes.

func QuotedConstText added in v0.18.5

func QuotedConstText(v any) (string, bool)

QuotedConstText reports a filter constant's TEXT when the constant is a QUOTED literal (or a text-shaped parameter), and ok=false for a numeric box.

This is the whole of the quoted-versus-numeric distinction the rule turns on, and it is deliberately a test of the BOX rather than of a declaration: the planner boxes a quoted string literal as a Go string and an unquoted numeric literal as a float64/int64/int (see exec.decimalLitValue, which substitutes a numeric literal's source text ONLY for the DECIMAL and STRING column types), so "which spelling did the user write" is exactly what the box carries here and nothing else does.

func RealLitTextUnrepresentable added in v0.18.5

func RealLitTextUnrepresentable(text string) bool

RealLitTextUnrepresentable reports whether literal TEXT names a number a `real` cannot carry, in either direction — the condition that makes PostgreSQL's cast of an IN list to real[] fail with 22003. Both directions: 1e40 overflows to +Inf and 1e-46 underflows to 0.0, and each would match rows the predicate must not (see kernel.Float32Fit).

Text, not a float64 box: the box has already been through the compiler's numeric conversion, and a literal past float64's OWN range (1e400) arrives there as +Inf, which is a legal real value and would be waved through. Reading the digits keeps "the user wrote a number too big for a real" apart from "the user wrote infinity", which PostgreSQL also keeps apart (#549's Float32FitOf draws the same distinction for a boxed value).

func RealOverflowText added in v0.18.5

func RealOverflowText(text string) string

RealOverflowText renders a numeric literal's source text the way PostgreSQL's `numeric` output does — expanded, never in exponent form — for the SQLSTATE 22003 message raised when that literal will not fit a `real`.

The message is part of the answer, not decoration. PostgreSQL raises

ERROR:  "10000000000000000000000000000000000000000" is out of range for type real

for `real IN (1e40, 3.1)`, and it prints the same forty-one digits whether the query spelled the literal 1e40, 1e+40, 1.0e40 or in full: the cast that fails is numeric->real, and a numeric's text is its digits. Wadjet used to print whatever Go's %v gave the boxed float64 ("1e+40"), so the two evaluation paths could not even produce the same message for the same query, and neither matched PostgreSQL (ADR-0012 item 1 makes the SQLSTATE and its text PostgreSQL's to decide).

Text that is not a number at all is returned unchanged: this function renders, it does not validate — the caller has already established that the value overflows.

func StatsDomainValue added in v0.18.1

func StatsDomainValue(typ batch.TypeID, scale int, v any) (any, bool)

StatsDomainValue converts a SQL literal into the representation a column's parquet STATISTICS and DICTIONARY entries are in, and reports whether the conversion exists.

It is the producer half of the rule the prune layer cannot enforce for itself: `scan.CanPruneRowGroup` compares two `any` values by their Go kind and has no idea what either MEANS, so a raw file bound and an engine literal that both land in the same kind get compared as if they agreed. Three columns did exactly that (#442, and #438 which is the same defect seen through a DECIMAL):

DECIMAL(18,4)  stats hold the UNSCALED integer (1500.15 -> 15001500)
               and the literal arrives as float64(1500.15), so every row
               group whose unscaled bound exceeds the literal is pruned.
IPV6, UUID     stats hold the RAW 16 bytes and the literal arrives as
               text, and '2' (0x32) sorts above every byte of a
               2001:db8:: address, so every row group is pruned.

The engine's own order for those columns is the stored one — the filter kernel converts the LITERAL (IPv6LitKey, and decimalLiteralAt against the vector's scale) rather than rendering the column — so this function is that same conversion, hoisted to where the planner still knows the column's type and scale. Rendering the bounds the other way would be wrong for IPv6: text order is not address order ('2001:db8::10' sorts below '2001:db8::5').

A false second result means "no conversion" and the caller must WITHHOLD the predicate from the prune layer entirely. Every type is listed explicitly and there is no pass-through default, because a new type that silently inherited "compare it raw" is precisely how this class arrives.

func UUIDLiteralToRaw

func UUIDLiteralToRaw(s string) (string, bool)

UUIDLiteralToRaw is parseUUIDToRawString for the row-at-a-time predicate in package exec, so the two comparison paths convert the literal identically.

Types

type Accumulator

type Accumulator struct {
	SumI64    int64
	SumF64    float64
	SumDec    batch.Int128
	Count     int64
	MinI64    int64
	MaxI64    int64
	MinF64    float64
	MaxF64    float64
	MinDec    batch.Int128
	MaxDec    batch.Int128
	MinStr    string
	MaxStr    string
	HasMin    bool
	HasMax    bool
	IsFloat   bool // true when the source column is a float type (or AVG over int64, which accumulates in float64 to avoid int64 sum wraparound)
	IsDecimal bool // true when the source column is DECIMAL
	IsString  bool // true when the source column is byte-backed (MIN/MAX): STRING, BYTES, IPV6, CIDR, UUID
	IsBool    bool // true when the source column is BOOL (MIN/MAX); the value rides in MinI64/MaxI64 as 0/1
	DecScale  int  // scale for DECIMAL columns
	// IntOverflow marks an INT64 SUM that WRAPPED. It is DecOverflow's
	// integer sibling and is read at the same emit-time check: ADR-0012 item 9
	// says a wrapped sum is a different number wearing the right type, so the
	// query fails rather than showing it. Before #784's review round it was
	// silent — `SUM(-b)` over a column whose total is exactly 2^64 answered 0
	// while `SUM(b)` and `SUM(b + 0)` were exact, so three spellings of one
	// question were right and two were zero.
	IntOverflow bool
	// DecOverflow marks a DECIMAL SUM that left the 128-bit range. SumDec
	// then holds the WRAPPED value — a different number — so the emit path
	// turns this into a query error instead of writing it out (#455). It
	// rides the accumulator rather than a per-operator flag because every
	// merge, spill and clone path already carries the accumulator.
	DecOverflow bool
	// DecScaleConflict marks an accumulator handed two DECIMAL values at
	// DIFFERENT scales. The Int128s it carries are unscaled integers counted
	// in ONE scale, so 12.75 (1275 at scale 2) added to 0.1275 (1275 at scale
	// 4) is 2550 under whichever scale wins — 25.50 or 0.2550 depending on
	// arrival order, never the 12.8775 that is the answer. It rides the
	// accumulator beside DecOverflow, for the same reason and through the same
	// emit-time channel (exec.aggEmitErr), because there is no other way for a
	// kernel with no error return to refuse.
	//
	// It is one door of several, not the last one, and the difference matters
	// because the first draft of this comment claimed otherwise. The planner
	// reconciles a set operation's arms (#533), the shuffle writer refuses a
	// cross-scale chunk, and the shuffle reader refuses a cross-scale stage
	// input (#685) — those cover the producers that exist. This covers the
	// UNGROUPED accumulator; the GROUPED paths keep their state in the flat
	// SoA arrays, which hold one scale per aggregate and no per-group
	// accumulator to carry a flag on, so their latch is
	// exec.HashAggregate.decScaleConflict instead and reaches the same
	// aggEmitErr. What NEITHER covers is the SCAN: two base-table files whose
	// footers declare one column at two scales are read, mixed and answered
	// with no check anywhere, on every path including the fast one. That is a
	// scan-level schema-drift check and a pre-existing residual — recorded in
	// ADR-0010 rather than fixed here.
	DecScaleConflict bool
	// StrType is the SOURCE column type behind MinStr/MaxStr. The five
	// byte-backed types share one accumulator slot but not one boxed shape:
	// IPV6 and UUID store raw 16-byte values that only round-trip into their
	// own vector as []byte, while STRING and CIDR store their own text. Boxing
	// them all as a Go string handed the IPV6 output vector 16 arbitrary bytes
	// as an ADDRESS TO PARSE, which fails and writes NULL (#417).
	StrType batch.TypeID
}

Accumulator holds aggregate state with typed precision. Int64 sums stay int64 (no float64 precision loss); float sums use float64. Decimal sums use Int128 for exact fixed-point arithmetic.

func (*Accumulator) DecimalAvg added in v0.18.1

func (a *Accumulator) DecimalAvg() (batch.Int128, bool)

DecimalAvg is FinalAvg's exact half for a DECIMAL accumulator: the unscaled quotient at batch.AvgScale(DecScale). ok=false means the exact answer does not fit an Int128 — a query error, not a rounding.

func (*Accumulator) FinalAvg

func (a *Accumulator) FinalAvg() any

FinalAvg returns the accumulated average.

Over a DECIMAL it is exact numeric division at scale+AvgScaleIncrement (batch.AvgScale), rounded half away from zero — see that constant for why the increment is fixed rather than PostgreSQL's significant-digit rule.

nil for a quotient with no Int128 is NOT the answer to that case: callers run exec.aggEmitErr first, which fails the query the way a SUM overflow does. Returning nil here would be a NULL the client cannot tell from "no rows".

func (*Accumulator) FinalMax

func (a *Accumulator) FinalMax() any

FinalMax returns the accumulated maximum.

func (*Accumulator) FinalMin

func (a *Accumulator) FinalMin() any

FinalMin returns the accumulated minimum.

func (*Accumulator) FinalSum

func (a *Accumulator) FinalSum() any

FinalSum returns the accumulated sum as the appropriate type.

A DECIMAL sum is EXACT: Int128 at the column's own scale, so SUM over a DECIMAL is a DECIMAL and not the float64 that dropped every digit past the 16th (#455). An overflowed sum is not returned at all — the caller checks DecOverflow first and fails the query, since the wrapped value is a different number wearing the right type.

func (*Accumulator) Merge

func (a *Accumulator) Merge(other *Accumulator)

Merge combines another accumulator's state into this one. Used for parallel aggregation: each worker builds partial state, then merges.

type BatchAggKernel

type BatchAggKernel func(acc *Accumulator, vec *batch.Vector, sel []uint32, vecLen int)

BatchAggKernel processes an entire column (or selection) into an accumulator. Used for non-grouped aggregation or pre-aggregated groups.

func ResolveBatchAvg

func ResolveBatchAvg(typ batch.TypeID) BatchAggKernel

ResolveBatchAvg returns a batch-level kernel for AVG. Differs from ResolveBatchSum only for int64-class inputs (float64 accumulation).

func ResolveBatchCount

func ResolveBatchCount() BatchAggKernel

ResolveBatchCount returns a batch-level count kernel.

func ResolveBatchMax

func ResolveBatchMax(typ batch.TypeID) BatchAggKernel

ResolveBatchMax returns a batch-level max kernel for the given column type.

func ResolveBatchMin

func ResolveBatchMin(typ batch.TypeID) BatchAggKernel

ResolveBatchMin returns a batch-level min kernel for the given column type.

func ResolveBatchSum

func ResolveBatchSum(typ batch.TypeID) BatchAggKernel

ResolveBatchSum returns a batch-level sum kernel for the given column type.

func ResolveBatchSumIntExact added in v0.18.11

func ResolveBatchSumIntExact(typ batch.TypeID) BatchAggKernel

ResolveBatchSumIntExact is ResolveRowSumIntExact's whole-vector form, for the ungrouped scalar fast path.

type ColColFilterKernel

type ColColFilterKernel func(left, right *batch.Vector, sel []uint32, vecLen int, outSel []uint32) []uint32

ColColFilterKernel compares two columns element-wise, returning matching row indices.

func ResolveColColFilterKernel

func ResolveColColFilterKernel(typ batch.TypeID, op CompareOp) ColColFilterKernel

ResolveColColFilterKernel creates a ColColFilterKernel for comparing two columns of the given type. Returns nil if the type is not supported.

type CompareOp

type CompareOp int

CompareOp represents a comparison operation.

const (
	OpEq CompareOp = iota
	OpNe
	OpLt
	OpLe
	OpGt
	OpGe
)

type DecimalArithFault added in v0.18.5

type DecimalArithFault struct {
	Status batch.DecimalStatus
	Row    int
}

DecimalArithFault is where a vectorized fixed-point operation stopped and why. Status DecimalOK means it ran to the end and Row is meaningless.

The row travels with the status because the SQLSTATE alone does not locate the value: a caller reporting 22003 for `a * b` over 2048 rows wants to name the row whose product had no place in the declared type, and re-scanning to find it would run the multiply twice.

func DecimalArithVec added in v0.18.5

func DecimalArithVec(op DecimalOp, out []batch.Int128, l, r DecimalOperandVec, outP, outS, n int, nulls *batch.Bitmap) DecimalArithFault

DecimalArithVec applies op elementwise over rows [0, n), writing the exact unscaled result at the declared DECIMAL(outP, outS) into out.

nulls, when non-nil, is the OUTPUT's null mask, already carrying the combined nullity of the two operands. A null row is SKIPPED: its carriers are whatever the vector happened to hold, and running the operation over them would raise 22012 for a NULL divisor — an error where SQL says NULL. This kernel writes VALUES and never nullity; the caller owns the mask for exactly that reason.

It stops at the FIRST row with no answer and reports it. The output is partial then and the caller must not use it: the query is over at that point, because ADR-0024 item 4 makes a value with no carrier an error rather than a number.

func DecimalScalarVec added in v0.18.5

func DecimalScalarVec(op batch.DecimalScalarOp, out []batch.Int128, in []batch.Int128, inScale, digits, outP, outS, n int, nulls *batch.Bitmap) DecimalArithFault

DecimalScalarVec applies a one-argument scalar math function elementwise — abs/ceil/floor/round/trunc/sign over a DECIMAL column — writing the exact result at the declared DECIMAL(outP, outS) into out.

It is the execution half of batch.DecimalScalarType, and the two must agree: the type says how many digits the answer keeps and this produces exactly those digits. Rounding is half away from zero throughout (batch.Rescale), which is PostgreSQL's numeric rounding.

nulls has DecimalArithVec's meaning and the same reason. digits is round/trunc's second argument, ignored by the other ops.

func (DecimalArithFault) Fine added in v0.18.5

func (f DecimalArithFault) Fine() bool

Fine reports whether the operation produced every row it was asked for.

type DecimalLiteral added in v0.18.1

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

DecimalLiteral is a numeric literal held as the EXACT text it was written with, ready to be compared against a DECIMAL column in that column's own domain.

It exists because a literal is not a float64. `compileLit` used to turn every numeric literal that is not an int64 into one, and a float64 carries ~15-16 significant decimal digits where a DECIMAL(38,10) carries 38: the literal a user typed was silently replaced by the nearest double before it ever met the column, so `= 493827160549382.7160549350` matched nothing and `>` gained a row (#452). Text is the only lossless carrier the whole way from the parser to a kernel, which is why the filter kernels already take their DECIMAL constant that way (compareFilterDecimal).

The resolution — text, at the column's scale, plus the residual of any digits the scale cannot hold — is the SAME one compareFilterDecimal performs, through the same decimalLiteralAt: one comparison rule for one predicate, per #394. What this type adds is the cache, for the row-at-a-time paths that would otherwise re-parse per row.

Safe for concurrent use: a resolved literal is published whole, through an atomic pointer, and a losing racer merely re-resolves to the same value.

func NewDecimalLiteral added in v0.18.1

func NewDecimalLiteral(text string) *DecimalLiteral

NewDecimalLiteral binds literal text — plain or exponent form — for comparison against DECIMAL columns. The text is kept VERBATIM: the exponent is folded into the scaling exactly when the literal is resolved at a column's scale, never expanded through a float64 first (#463).

func (*DecimalLiteral) Compare added in v0.18.1

func (d *DecimalLiteral) Compare(vec *batch.Vector, row int, op CompareOp) bool

Compare answers `vec[row] <op> literal`.

func (*DecimalLiteral) Numeric added in v0.18.1

func (d *DecimalLiteral) Numeric() bool

Numeric reports whether the literal's text names a number at all. A false here is a query error at the comparison — PostgreSQL raises "invalid input syntax for type numeric" rather than reading the text as zero (#463).

func (*DecimalLiteral) Order added in v0.18.1

func (d *DecimalLiteral) Order(vec *batch.Vector, row int) int

Order returns -1, 0 or +1 as vec[row] is less than, equal to, or greater than the literal — exactly, including for a literal with more fractional digits than the column's scale (which equals no stored value but still has a place in the order) and for one wider than the carrier itself (which orders above or below every value the column can hold).

The caller owns the null check: a NULL row has no value to order.

func (*DecimalLiteral) OrderAt added in v0.18.1

func (d *DecimalLiteral) OrderAt(cell batch.Int128, scale int) int

OrderAt is Order against a value already read out of a column at `scale`.

func (*DecimalLiteral) Text added in v0.18.1

func (d *DecimalLiteral) Text() string

Text is the literal's source text, verbatim.

type DecimalOp added in v0.18.5

type DecimalOp uint8

DecimalOp names the fixed-point operator a kernel applies. It is an opcode rather than the operator's source text so the row loop never compares a string; the text is resolved once, where the shape is.

const (
	DecimalOpAdd DecimalOp = iota
	DecimalOpSub
	DecimalOpMul
	DecimalOpDiv
	DecimalOpMod
)

func DecimalOpOf added in v0.18.5

func DecimalOpOf(op string) (DecimalOp, bool)

DecimalOpOf maps an operator's SQL text to its opcode. ok=false for an operator with no fixed-point rule, which the caller must route elsewhere rather than default into one of these.

func (DecimalOp) String added in v0.18.5

func (o DecimalOp) String() string

String names the operator as SQL spells it, for the messages the wiring sites build.

type DecimalOperandVec added in v0.18.5

type DecimalOperandVec struct {
	Data  []batch.Int128
	Const batch.Int128
	Scale int
}

DecimalOperandVec is one side of a vectorized fixed-point operation: a COLUMN of unscaled carriers, or a single CONSTANT broadcast over the batch.

Data == nil is what makes it a constant, and it is the only discriminant — there is no second flag that could disagree with it. Scale is the operand's OWN declared scale either way: a constant carries the scale its literal text resolved at, which is the spelling the user wrote (ADR-0024 item 3), never the output's.

type FilterKernel

type FilterKernel func(vec *batch.Vector, sel []uint32, vecLen int, outSel []uint32) []uint32

FilterKernel evaluates a column against a pre-resolved constant for all rows, returning the indices of matching rows.

func ResolveFilterKernel

func ResolveFilterKernel(typ batch.TypeID, op CompareOp, value any) FilterKernel

ResolveFilterKernel creates a FilterKernel for comparing a column of the given type against a constant value. The type dispatch happens once here; the returned function has no type switches in its inner loop.

func ResolveInFilterKernel

func ResolveInFilterKernel(typ batch.TypeID, values []any, negate bool) FilterKernel

ResolveInFilterKernel creates a FilterKernel that checks set membership. The set is built once; the inner loop does a hash lookup per element.

It assumes the list's syntactic arity equals len(values), which holds whenever no NULL member was stripped before the call. The FLOAT32 width rule (see ResolveInFilterKernelArity) is the only place that distinction matters; a caller that strips NULLs must use the arity-aware variant.

func ResolveInFilterKernelArity added in v0.18.4

func ResolveInFilterKernelArity(typ batch.TypeID, values []any, negate bool, syntacticLen int) FilterKernel

ResolveInFilterKernelArity is ResolveInFilterKernel with the list's SYNTACTIC element count — the count BEFORE any NULL member was stripped for three-valued logic — passed explicitly. It matters only for FLOAT32: PostgreSQL decides `real IN (...)`'s comparison WIDTH from the syntactic arity (it casts the whole `{...}` array literal, NULLs included, to real[] when there is more than one element), so `real IN (0.1, NULL)` NARROWS and matches the 0.1 row even though one non-NULL literal reaches the kernel (#549). Every other type compares identically at either width, so they ignore the count.

func ResolveLikeFilterKernel

func ResolveLikeFilterKernel(typ batch.TypeID, pattern string, negate bool) FilterKernel

ResolveLikeFilterKernel creates a FilterKernel for SQL LIKE pattern matching against a column of the given type. Converts SQL LIKE patterns (% and _) to optimized matching functions.

The column's underlying storage is not always TEXT in BytesData: TypeIPv4/ TypeMAC/TypePort/TypeProtocol box as Int64Data/Int32Data, and TypeIPv6/ TypeUUID box as BytesData but hold the address's RAW binary form, not the human-readable text a LIKE pattern is written against. This used to be a single BytesData.UnsafeStringValue call with no type check at all — indexing an empty backing store for the Int64Data/Int32Data types (a process-killing panic, since it is not the one deliberate FatalEvalPanic shape recover() converts back into a query error) and matching nothing for IPv6/UUID (their raw bytes never contain the pattern's text) (#497). likeTextRenderer resolves the row->text function once per column, the same per-type-dispatch-once discipline ResolveFilterKernel already follows, so the inner loop has no per-row type switch.

nil for the four container types (#522): PostgreSQL has no `~~` operator for any composite or array type (verified live: `ARRAY[1,2,3] LIKE 'x'` raises "operator does not exist: integer[] ~~ unknown", SQLSTATE 42883), and there is no established text form for a ROW/ARRAY/MAP/VECTOR value this engine has committed to anywhere else — the old default arm's `fmt.Sprint(Vector.GetValue(i))` (`[1 2 3]`, `map[k0:0]`) was never a contract, just what happened to fall out of not refusing. The caller (exec.LikeFilter) turns a nil kernel into that same 42883, the way KernelFilter already turns decimalConstError/networkConstError into a query error for a different type family.

type Float32Fit added in v0.18.5

type Float32Fit int

Float32Fit says whether a float64 survives the conversion to float32, and which way it fails when it does not. PostgreSQL refuses BOTH directions with SQLSTATE 22003 and two different texts (utils/adt/float.c), so one primitive answers every site that has to make the distinction: the literal refusals here and in exec.floatConstError, and expr.Cast's REAL arm.

const (
	Float32Fits Float32Fit = iota
	// Float32Overflows: a finite value whose magnitude exceeds real's range,
	// so narrowing yields ±Inf — which would MATCH a genuine infinite row.
	Float32Overflows
	// Float32Underflows: a non-zero value that rounds to zero in real, which
	// would MATCH the rows holding 0.0. `real IN (1e-46, 3.1)` selected the
	// zero row for exactly this reason before the arm existed; PostgreSQL
	// refuses the list. The boundary is real's smallest DENORMAL: 1e-45 is
	// representable and must not be refused, 1e-46 is not.
	Float32Underflows
)

func Float32FitOf added in v0.18.5

func Float32FitOf(f float64) Float32Fit

Float32FitOf classifies one float64 against real's range. NaN and ±Inf are legal real values and always fit.

type FloatOrdered added in v0.18.1

type FloatOrdered interface{ ~float32 | ~float64 }

FloatOrdered is the float element type the predicates below are written for.

type IntBoundVerdict added in v0.18.11

type IntBoundVerdict uint8

IntBoundVerdict is how a comparison between an INTEGER column and a numeric constant that no row can equal is answered: by comparing against a rewritten bound, or by the constant answer the whole column has.

const (
	// IntBoundCompare: use the returned (value, operator) pair.
	IntBoundCompare IntBoundVerdict = iota
	// IntBoundNone: no row satisfies the predicate (NULLs excluded anyway).
	IntBoundNone
	// IntBoundAll: every non-NULL row satisfies it.
	IntBoundAll
)

type IntConstStatus added in v0.18.4

type IntConstStatus int

IntConstStatus classifies an integer-column filter constant. IntConstOK means it is a usable value; IntConstSyntax means the text names no integer at all (PostgreSQL raises 22P02, invalid_text_representation); IntConstRange means it names an integer that overflows the column type (PostgreSQL raises 22003, numeric_value_out_of_range, a DIFFERENT SQLSTATE with different wording). The two error paths carry this classification rather than a bare bool so each can raise the SQLSTATE PostgreSQL actually raises.

const (
	IntConstOK IntConstStatus = iota
	IntConstSyntax
	IntConstRange
)

func Int32FilterConst added in v0.18.4

func Int32FilterConst(v any) (int32, IntConstStatus)

Int32FilterConst is Int64FilterConst for the int32-backed integer types (TypeInt32 and the network-native TypePort/TypeProtocol): it parses the same grammar but reports IntConstRange for a value outside int32's range, so a numeric literal that would silently WRAP on narrowing (`int4col = '3000000000'`) becomes the 22003 refusal PostgreSQL gives rather than a comparison against the wrapped value — the same silent-wrong class #536 closes for the non-numeric literal.

func Int64FilterConst added in v0.18.4

func Int64FilterConst(v any) (int64, IntConstStatus)

Int64FilterConst resolves an integer-column filter constant to an int64, reporting a non-OK status for a text literal that is not a usable integer.

An integer box (int64/int32/int from a parameter or a folded literal) arrives already in the domain. A SQL text literal, though, is a STRING here — and the old toInt64 read a string through parseTimestampString, so `k = 'abc'` (and even `k = '42'`, which no timestamp layout matches) coerced to 0 and MATCHED every row holding zero (#536, the integer rung of #463's silent-sentinel ladder). It is read through Go's base-10 integer grammar now, so '42' compares as 42 and 'abc' names no integer (IntConstSyntax): the caller (ResolveFilterKernel's integer arms return a nil kernel; the row path panics) refuses the query the way PostgreSQL does, rather than answering the zero rows.

The grammar is PostgreSQL's own, not Go's: parseIntText reads the 0x/0o/0b radix prefixes, the underscore digit separators and the leading-zero decimals PostgreSQL 16+ accepts (`'0x1A'` = 26, `'1_000'` = 1000, `'007'` = 7), which Go's base-10 reader refused and Go's base-0 reader would have misread ('017' is decimal seven there, not octal fifteen). Refusing input PostgreSQL answers was a PG-superset regression (#634); it is closed.

TIMESTAMP is deliberately NOT routed here: its string literal IS a timestamp and must keep reading through parseTimestampString — a quoted numeric string against a TIMESTAMP column is #493's territory, not this fix's.

type NumConstStatus added in v0.18.5

type NumConstStatus = IntConstStatus

NumConstStatus classifies a numeric-column filter constant. It is IntConstStatus under the name the whole numeric family shares: #536 introduced the three-way split for the integer types and the float and DECIMAL arms need exactly the same three answers, so they are one type rather than two that must be kept in step.

NumConstOK: a usable value. NumConstSyntax: the text names no value of the type at all (PostgreSQL raises 22P02, invalid_text_representation). NumConstRange: it names a number the type cannot carry (22003, numeric_value_out_of_range) — a DIFFERENT SQLSTATE with different wording, which the WireProtocol oracle checks.

func Float32FilterConst added in v0.18.5

func Float32FilterConst(v any) (f float32, st NumConstStatus, quoted bool)

Float32FilterConst resolves a FLOAT32 (`real`) column's filter constant for the QUOTED spelling, which PostgreSQL coerces straight to real — so the value comes back NARROWED, and a literal outside real's range is 22003 rather than a saturating ±Inf or a silent 0.0.

ok=false as the third result means the constant is NOT a quoted literal: it is an unquoted numeric one, which takes the opposite rule (#631's widening to double, compareFilterFloat32Widen) and must not be narrowed here. The two spellings really are two predicates — `r = 3.1` selects no row over a column holding real(3.1) and `r = '3.1'` selects it.

func Float64FilterConst added in v0.18.5

func Float64FilterConst(v any) (float64, NumConstStatus)

Float64FilterConst resolves a FLOAT64 column's filter constant, reading a QUOTED literal through PostgreSQL's float8 input grammar rather than as the silent 0.0 toFloat64 answered for every string (#646).

func FloatLitText added in v0.18.5

func FloatLitText(text string, bits int) (float64, NumConstStatus)

FloatLitText reads PostgreSQL's FLOAT input grammar — float4in/float8in, which are `strtod` plus PostgreSQL's own special-value spellings — and classifies the failure the way PostgreSQL classifies it.

bits is 32 for `real` and 64 for `double precision`. The value comes back as a float64 in BOTH cases: the parse itself is always done at double width (Go's ParseFloat at bitSize 32 reports overflow but is SILENT about underflow, answering a plain 0 for '1e-46'), and real's range is then decided by Float32FitOf, whose boundary is real's smallest DENORMAL — the same boundary PostgreSQL draws, verified live: '1e-45'::real is a value, '7e-46'::real is 22003, '3.4e38'::real is a value, '3.5e38'::real is 22003.

Three differences from Go's own ParseFloat, each of them PostgreSQL's:

  • UNDERSCORES are refused. Go accepts '1_000' as 1000; PostgreSQL's float input does not (22P02, verified live) even though its INTEGER and NUMERIC inputs do since 16. Accepting it would answer where PostgreSQL errors.
  • HEX floats are accepted WITHOUT a binary exponent. glibc's strtod reads '0x10' as 16 and PostgreSQL inherits that ('0x10'::real is 16, '0x1p3'::real is 8, '0x.8p1'::float8 is 1 — all verified live); Go requires the 'p'. The exponent is supplied when the text omits it.
  • UNDERFLOW to zero is a RANGE error, not a value. Go answers 0 with no error for '1e-400'; PostgreSQL raises 22003 ("1e-400" is out of range for type double precision). A denormal is NOT underflow on either side ('1e-320'::float8 is a value).

The special spellings come from FloatSpecialText, which is PostgreSQL's float grammar for them and deliberately a second reader beside the DECIMAL one: float8 accepts a SIGNED NaN and numeric does not (#534).

func IntLitText added in v0.18.5

func IntLitText(text string) (int64, NumConstStatus)

IntLitText reads PostgreSQL's INTEGER input grammar (parseIntText) for callers outside this package — the boxed comparison sites in expr, which need the VALUE as well as the status QuotedLitStatus reports. One reader for the kernel, the row path and the plan-time refusal is the property that keeps them from disagreeing about which strings name an integer.

func QuotedLitStatus added in v0.18.5

func QuotedLitStatus(typ batch.TypeID, text string) (NumConstStatus, bool)

QuotedLitStatus is THE predicate: does text name a value of the column type typ? ok=false means the type has no rule of this kind and the caller must not refuse anything.

Every site that can refuse a quoted literal against a numeric column reads this one function — the plan-time refusal (physical.refuseLiteralForType via expr.RefuseNumericLiteral), the vectorized kernel's arms, the row-at-a-time ColumnCompareLit, the boxed sites' refusal masks, and the row-group prune's StatsDomainValue — so the accept-set cannot differ between them. A query refused at one site and answered at another is the two-path defect class the refusal exists to close.

type Numeric

type Numeric interface {
	~int32 | ~int64 | ~float32 | ~float64
}

Numeric constrains types that support arithmetic operations.

type Ordered

type Ordered interface {
	~int32 | ~int64 | ~float32 | ~float64 | ~string
}

Ordered constrains types that support comparison.

type RowAggUpdater

type RowAggUpdater func(acc *Accumulator, vec *batch.Vector, row int)

RowAggUpdater updates an accumulator for a single row (used in grouped aggregation). The type dispatch is resolved once; the function body has no type switches.

func ResolveRowAvg

func ResolveRowAvg(typ batch.TypeID) RowAggUpdater

ResolveRowAvg returns a row-level updater for AVG. Differs from ResolveRowSum only for int64-class inputs (float64 accumulation).

func ResolveRowAvgNoNulls

func ResolveRowAvgNoNulls(typ batch.TypeID) RowAggUpdater

ResolveRowAvgNoNulls is the no-null-check variant of ResolveRowAvg.

func ResolveRowCount

func ResolveRowCount(countStar bool) RowAggUpdater

ResolveRowCount returns a row-level count updater. If countStar is true, counts all rows (including nulls).

func ResolveRowMax

func ResolveRowMax(typ batch.TypeID) RowAggUpdater

ResolveRowMax returns a row-level max updater for the given column type.

func ResolveRowMaxNoNulls

func ResolveRowMaxNoNulls(typ batch.TypeID) RowAggUpdater

ResolveRowMaxNoNulls returns a no-null-check max updater.

func ResolveRowMin

func ResolveRowMin(typ batch.TypeID) RowAggUpdater

ResolveRowMin returns a row-level min updater for the given column type.

func ResolveRowMinNoNulls

func ResolveRowMinNoNulls(typ batch.TypeID) RowAggUpdater

ResolveRowMinNoNulls returns a no-null-check min updater.

func ResolveRowSum

func ResolveRowSum(typ batch.TypeID) RowAggUpdater

ResolveRowSum returns a row-level sum updater for the given column type.

func ResolveRowSumIntExact added in v0.18.11

func ResolveRowSumIntExact(typ batch.TypeID, noNulls bool) RowAggUpdater

ResolveRowSumIntExact returns the Int128 row updater for an INTEGER column, or nil for a type that has none — the caller then keeps its ordinary resolver, which is what makes this additive rather than a second dispatch nothing keeps in step with the first.

func ResolveRowSumNoNulls

func ResolveRowSumNoNulls(typ batch.TypeID) RowAggUpdater

ResolveRowSumNoNulls returns a no-null-check sum updater.

type SortCompareKernel

type SortCompareKernel func(a *batch.Vector, ai int, b *batch.Vector, bi int) int

SortCompareKernel compares one row from vector a against one row from vector b. Returns -1, 0, or 1. Null handling is included.

func ResolveSortCompare

func ResolveSortCompare(typ batch.TypeID) SortCompareKernel

ResolveSortCompare returns a comparison function for the given column type. The returned function has no type switch — the type is baked into the closure.

A nil return means "this resolver cannot order that type". Callers must treat nil as a refusal, not as a tie: SortMergeJoin uses these kernels for key EQUALITY, so a comparator that reports every pair equal is not a degraded sort, it is a cross product presented as an inner join. Until #415 the default arm returned exactly such a closure and ARRAY, ROW, MAP and VECTOR all fell into it — `ORDER BY arr_col` was a silent no-op and the `cmp == nil` guard in sort_merge_join.go was dead code. All 22 types are enumerated now; nil is reserved for a type the engine does not have.

func ResolveSortCompareNoNulls

func ResolveSortCompareNoNulls(typ batch.TypeID) SortCompareKernel

ResolveSortCompareNoNulls returns a sort compare function that skips null checks.

func ResolveSortCompareNullsLast

func ResolveSortCompareNullsLast(typ batch.TypeID) SortCompareKernel

ResolveSortCompareNullsLast returns a sort compare function with NULLS LAST ordering.

Jump to

Keyboard shortcuts

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