Documentation
¶
Overview ¶
Package functions holds SQL helpers that need no connection or session state: SQL value comparison, identifier and table-path resolution over parse trees, string-literal unquoting, and the protoreflect <-> driver.Value marshaling shared by the planner, the executor and the embedded connection.
Index ¶
- Constants
- func CompareValues(a, b driver.Value) int
- func ConvertElementToProtoValue(fd protoreflect.FieldDescriptor, val any) (protoreflect.Value, error)
- func ConvertToProtoValue(fd protoreflect.FieldDescriptor, val any) (protoreflect.Value, error)
- func FlattenRecordWithOneField(v values.Value) values.Value
- func FullIdToName(fid antlrgen.IFullIdContext) string
- func NonArrayCorrelationError(typeText string) error
- func NormalizeIdentifier(s string) string
- func ProtoValueToDriver(fd protoreflect.FieldDescriptor, v protoreflect.Value) driver.Value
- func ResolveQualifiedTableName(dottedName, templateName string) (string, bool)
- func ResolveSourceTablePath(path []string, templateName string) (string, bool, error)
- func ResolveTargetTablePath(path []string, templateName string) (string, error)
- func StripStringLiteralQuotes(s string) string
- func UnknownSourceReferenceError(path []string) error
Constants ¶
const UUIDProtoMessageName = "com.apple.foundationdb.record.UUID"
UUIDProtoMessageName is the fully-qualified name of the tuple_fields.UUID proto message that fdb-relational uses to store UUID column values (matches Java's TupleFieldsProto.UUID).
Variables ¶
This section is empty.
Functions ¶
func CompareValues ¶
CompareValues returns -1/0/1 for a < b / a == b / a > b under SQL ordering semantics. NULL sorts before non-NULL (sort-site callers should honour NULLS FIRST / LAST before reaching here). Numeric promotion (int64 ↔ float64) mirrors ORDER BY rules; cross-type comparisons fall back to a stable type-name-based order so `=` correctly fails without a runtime panic.
func ConvertElementToProtoValue ¶
func ConvertElementToProtoValue(fd protoreflect.FieldDescriptor, val any) (protoreflect.Value, error)
ConvertElementToProtoValue converts one non-repeated value against fd, an array's element field included: the scalar lanes ConvertToProtoValue applies to every element of an array (UUID, enum by name, a STRUCT as its field map), without its array arm, which reads fd as the array itself.
func ConvertToProtoValue ¶
func ConvertToProtoValue(fd protoreflect.FieldDescriptor, val any) (protoreflect.Value, error)
ConvertToProtoValue converts a SQL-level driver.Value (int64, float64, string, bool, []byte) to a protoreflect.Value matching the field descriptor's kind. Range checks match Java's CastValue behaviour (LONG_TO_INT / DOUBLE_TO_LONG / DOUBLE_TO_FLOAT etc.).
A FLOAT/DOUBLE column carries every IEEE-754 value, NaN and ±Infinity included — Java's does, and so does the executor-side converter that UPDATE and INSERT … SELECT reach. The only float rejection left is the DOUBLE→FLOAT narrowing range check, and it is about what the NARROWING would CHANGE (a finite double past ±MaxFloat32 silently becomes Infinity), not about finiteness as such.
An ARRAY column takes the evaluated array literal — the []any an ArrayConstructorValue produces (Java's LightArrayConstructorValue evals to a List) — and converts each element through the same scalar lanes, mirroring Java's MessageHelpers.coerceArray element-wise coercion. A NON-nullable array is a flat repeated field; a NULLABLE array is stored through the NullableArrayWrapper (`message W { repeated E values = 1; }`), where a present wrapper with an empty list keeps [] distinct from NULL — Java's exact wire shape.
func FlattenRecordWithOneField ¶
FlattenRecordWithOneField is Java's SqlFunctionCatalog.flattenRecordWithOneField (SqlFunctionCatalog.java:98-108).
WHY IT EXISTS. SQL's grammar gives `(expr)` and a one-element record literal the SAME parse: there is no one-tuple literal syntax to tell them apart, so the parser cannot decide. Java resolves the ambiguity by POSITION instead of by parse, and the split is the whole design:
- the record constructor ALWAYS builds a record — ExpressionVisitor's visitRecordConstructor (ExpressionVisitor.java:918-925) goes straight to RecordConstructorValue.ofColumns with no one-element unwrap, which is why `SELECT (1 + 2)` is a one-field STRUCT and `SELECT ((1 + 2))` nests twice;
- every FUNCTION ARGUMENT is then flattened back down by this function, which is why `(3 + 4) * 5` multiplies by the scalar 7 and not by a record. Java states the precedence outright: when a value can be read as a single-item record or as the value itself, "the precedence is always given to the latter" (SqlFunctionCatalog.java:88-92).
So a parenthesised scalar survives as a record only where nothing consumes it as an argument — projection position. That is not a quirk to be normalised away; it is the observable difference between the two positions.
WHERE IT APPLIES. Java hangs it off resolveScalarFunction's argument mapping (SemanticAnalyzer.java:991-994, :1109, :1121, :1166), and BaseVisitor's resolveFunction (BaseVisitor.java:253-261) defaults flattenSingleItemRecords to true. Because ExpressionVisitor routes arithmetic (:731), comparison (:699), bitwise (:691), logical (:509), NOT (:501), IS NULL (:581), LIKE (:615), IN (:627) and BETWEEN (:716-722) all through resolveFunction, every one of those operand positions flattens.
THE ONE OPT-OUT is flattenSingleItemRecords=false, passed only to the `__internal_array` that builds the row list of the VALUES and inline-table paths (QueryVisitor.java:720 and :802). There a one-column row MUST stay a row: flattening it would turn `VALUES (1), (2)` into a bare element list and destroy the row structure the explode depends on. Callers on that path must therefore NOT call this function.
The recursion has two arms, exactly as Java does: a one-field record collapses to its single element and is re-examined (so nested parens peel completely), and any other node rebuilds itself with flattened children (so a record buried under an array or a cast is still reached).
func FullIdToName ¶
func FullIdToName(fid antlrgen.IFullIdContext) string
FullIdToName converts a FullId parse-tree node to a dot-separated, quote-stripped name. Used for table names in INSERT / UPDATE / DELETE and by the scalar function library when an argument is a qualified column reference.
func NonArrayCorrelationError ¶
NonArrayCorrelationError is Java's refusal of a correlated FROM item that is not an array (LogicalOperator.generateCorrelatedFieldAccess, INVALID_COLUMN_REFERENCE), in its wording; typeText is the column's type as Java's DataType.toString renders it (rowstruct.NonArrayCorrelationError).
func NormalizeIdentifier ¶
NormalizeIdentifier normalizes an identifier's raw parse text to its canonical lookup form: a quoted identifier is stripped of its surrounding `"` or backticks and otherwise preserved case-for-case; an unquoted one is folded to UPPER. Mirrors Java's SemanticAnalyzer.normalizeString (case-sensitive=false default).
IT IS NOT IDEMPOTENT, and must be applied exactly ONCE, at the parse boundary. Applying it twice silently destroys every quoted identifier while being invisible for every unquoted one, because folding twice is folding once.
It used to be called StripIdentifierQuotes, and that name is why this needs saying: all three CTE column-alias captures wrote `StripIdentifierQuotes(FullIdToName(fid))` — and FullIdToName already applies it per segment. So `"x"` became `x` became `X`, and `WITH c("x")` published a column no reference could name. Every site that CONSUMED the alias was faithful; the corruption was upstream of all of them, spelled as a function whose name promised a no-op. Renamed so the double application reads as obviously wrong at the call site rather than as a defensive strip.
func ProtoValueToDriver ¶
func ProtoValueToDriver(fd protoreflect.FieldDescriptor, v protoreflect.Value) driver.Value
ProtoValueToDriver maps a protoreflect.Value (read off a record) into a driver.Value for SQL-level consumption. Widens all integer kinds to int64 so the SQL evaluator doesn't need per-kind fan-out.
func ResolveQualifiedTableName ¶
ResolveQualifiedTableName is ResolveSourceTablePath over a dotted string, for the callers that still hold a joined name. It cannot tell a quoted dot from a qualifier, which is why parse-derived callers pass segments.
func ResolveSourceTablePath ¶
ResolveSourceTablePath is the qualifier half of Java's SemanticAnalyzer.tableExists, for a FROM source: it reports the table name a path names when the path is unqualified or qualified by templateName, and false otherwise, so the caller can go on to the source's other readings. A qualified source that is no table ends, in Java, in the correlated-field reading, whose refusal is UnknownSourceReferenceError. An empty path or segment is no user's spelling but a carrier that lost its name: an internal error.
func ResolveTargetTablePath ¶
ResolveTargetTablePath resolves a statement's target table as Java's SemanticAnalyzer.getTable does: more than one qualifier is an internal error, "Unknown table <path>"; a qualifier other than templateName is 42F00 "Unknown schema template <qualifier>". It returns the table's name, the last segment; whether that table exists is the caller's check (42F01).
func StripStringLiteralQuotes ¶
StripStringLiteralQuotes removes a single pair of surrounding single-quotes and unescapes doubled-quote ” sequences to a single quote. Used by every SQL string literal that reaches our code via the parser (the parser leaves the literal's raw source text, including quotes).
func UnknownSourceReferenceError ¶
UnknownSourceReferenceError is Java's answer for a qualified FROM source that names no table: SemanticAnalyzer.resolveCorrelatedIdentifier finds no column of an outer source by that path, UNDEFINED_COLUMN "Unknown reference <path>", the path printed as Java's Identifier prints it (segments joined by dots, without quotes).
Types ¶
This section is empty.