Skip to main content

Expr

Enum Expr 

Source
pub enum Expr {
Show 16 variants Raw(Cow<'static, str>), Literal(Cow<'static, str>), Ident(Vec<Cow<'static, str>>), Arg(Value), Args(Vec<Value>), NamedArg(Cow<'static, str>), Template { sql: Cow<'static, str>, args: Vec<RawArg>, }, Group(Vec<Expr>), Binary { lhs: Box<Expr>, op: &'static str, rhs: Box<Expr>, }, Prefix { op: &'static str, operand: Box<Expr>, }, Postfix { operand: Box<Expr>, op: &'static str, }, Join { exprs: Vec<Expr>, sep: &'static str, }, Func { name: Cow<'static, str>, args: Vec<Expr>, over: Option<Box<Expr>>, }, Case { whens: Vec<(Expr, Expr)>, else_: Option<Box<Expr>>, }, Cast { expr: Box<Expr>, type_name: Cow<'static, str>, }, Custom(Arc<dyn Expression>),
}
Expand description

A SQL expression, as data.

Where bob threads bob.Expression — a Go interface — through every slot and gives each shape its own unexported struct, keelson has one algebraic type. The reasons, in the order they matter:

  1. Expressions stay inspectable. Layer 4 rewrites a parsed query into clause-reconstruction code, and a rewriter needs to look at what it has. A Box<dyn Expression> can only be rendered.
  2. No dynamic dispatch on the hot path, and Clone is a memcpy plus a couple of refcount bumps.
  3. One match renders everything, so the spacing and separator decisions that bob spreads over a dozen files sit in one screen where they can be compared against the grammar.

The escape hatch is Expr::Custom, which holds an erased Expression. Dialect-specific shapes — PostgreSQL’s ROWS FROM, MySQL’s index hints — live in their own crate as an ordinary Expression and travel through core as Custom, so keelson-core never learns about them.

This enum is deliberately not #[non_exhaustive]: exhaustive matching is the point, and a downstream rewriter that stops compiling when a variant is added is being told something true.

§Strings

Identifiers, raw SQL and type names are Cow<'static, str>: a literal borrows and costs nothing, a computed name is owned, and no lifetime parameter escapes into any public type. Operators and separators are &'static str — they are always literals, in core and in a dialect crate alike.

Variants§

§

Raw(Cow<'static, str>)

SQL written out verbatim. ? is not rewritten — use Expr::Template for that.

This is bob’s “progressive enhancement” in the enum: a hand-written fragment is a first-class expression, and keyword fragments like AND or IS NULL are nothing more than this.

§

Literal(Cow<'static, str>)

A single-quoted SQL string literal — bob’s S(). Renders 'abc'.

Nothing is escaped, exactly as in bob. This is for keywords, enum labels and other SQL the program itself wrote; user input belongs in Expr::Arg, where it is bound rather than interpolated.

§

Ident(Vec<Cow<'static, str>>)

A dot-joined quoted identifier: ["users", "id"] renders "users"."id".

Empty parts are skipped, so an unset qualifier needs no branch at the call site, and an entirely empty list renders nothing at all.

§

Arg(Value)

One bound argument, rendered as the dialect’s placeholder.

§

Args(Vec<Value>)

Several bound arguments, comma-separated: $1, $2, $3.

Not parenthesised — it is usually written into a slot that brings its own parentheses, such as VALUES (..). Wrap it in Expr::Group when the parentheses are wanted; that is what super::arg_group does. An empty list renders NULL, matching bob.

§

NamedArg(Cow<'static, str>)

A named argument placeholder, for preparing a statement whose values arrive at bind time.

Binds nothing and consumes no positional slot. On a dialect with no named arguments this records Error::NoNamedArgs on the writer, which build then surfaces.

§

Template

Raw SQL whose ? placeholders are rewritten to the dialect’s own syntax, with args interleaved. See RawArg and super::template.

Fields

§sql: Cow<'static, str>

The SQL, using ? for every hole and \? for a literal question mark.

§args: Vec<RawArg>

One replacement per ?, in order.

§

Group(Vec<Expr>)

A parenthesised, comma-separated list: (a, b, c).

One element is how a plain parenthesised expression is written. Empty renders (NULL), matching bob — a row constructor with no columns is still a value.

§

Binary

An infix operator: lhs op rhs, one space either side.

Fields

§lhs: Box<Expr>

Left operand.

§op: &'static str

The operator, written verbatim between the operands.

§rhs: Box<Expr>

Right operand.

§

Prefix

A prefix operator: op operand. NOT x, -x.

Fields

§op: &'static str

The operator.

§operand: Box<Expr>

The operand.

§

Postfix

A postfix operator: operand op. x IS NULL, x DESC.

Fields

§operand: Box<Expr>

The operand.

§op: &'static str

The operator.

§

Join

A separator-joined sequence — bob’s Join. Renders nothing when empty.

This is the general-purpose “several fragments in a row” node: AND/OR chains, BETWEEN a AND b, a clause built out of keyword fragments.

Fields

§exprs: Vec<Expr>

The parts, in order.

§sep: &'static str

Written between consecutive parts, verbatim. Use " " for bob’s default; see Expr::join.

§

Func

A function call, optionally with an OVER window: avg(x) OVER (w).

Core keeps only what every dialect has. DISTINCT, FILTER (WHERE ..) and WITHIN GROUP are per-dialect and belong to a dialect’s own function builder, reaching core through Expr::Custom.

Fields

§name: Cow<'static, str>

The function name, written verbatim — not quoted.

§args: Vec<Expr>

The arguments, comma-separated.

§over: Option<Box<Expr>>

The window definition or window name, rendered inside OVER (..). An empty expression is meaningful: OVER ().

§

Case

CASE WHEN c THEN t .. [ELSE e] END.

At least one WHEN is required; with none this records Error::Incomplete and writes nothing, which is bob’s error turned into the recorded-failure form.

Fields

§whens: Vec<(Expr, Expr)>

The WHEN condition THEN result pairs, in order.

§else_: Option<Box<Expr>>

The ELSE branch.

§

Cast

CAST(expr AS type_name).

Fields

§expr: Box<Expr>

The expression being cast.

§type_name: Cow<'static, str>

The target type, written verbatim — int, numeric(10, 2).

§

Custom(Arc<dyn Expression>)

A dialect-specific expression core knows nothing about.

The one place dynamic dispatch survives, and the reason core never needs a variant for ROWS FROM, MATCH .. AGAINST or anything else that belongs to exactly one grammar.

Implementations§

Source§

impl Expr

Source

pub fn raw(sql: impl Into<Cow<'static, str>>) -> Expr

Raw SQL, verbatim. ? is left alone.

Source

pub fn template( sql: impl Into<Cow<'static, str>>, args: impl IntoIterator<Item = RawArg>, ) -> Expr

Raw SQL with ? placeholders and their replacements.

Source

pub fn literal(s: impl Into<Cow<'static, str>>) -> Expr

A single-quoted string literal — bob’s S().

Source

pub fn ident(parts: impl IntoIdent) -> Expr

A quoted identifier. ident("age") and ident(("users", "id")) both work; see IntoIdent.

Empty parts are dropped here rather than at render time, so the stored node is exactly what will be written.

Source

pub fn arg(v: impl ToValue) -> Expr

One bound argument.

Source

pub fn args<V>(vals: impl IntoIterator<Item = V>) -> Expr
where V: ToValue,

A comma-separated list of bound arguments.

Source

pub fn placeholders(n: usize) -> Expr

n unbound placeholders — bob’s Placeholder(n).

Each one binds NULL, so the shape of the statement is right and the values are supplied by whatever rebinds it.

Source

pub fn named_arg(name: impl Into<Cow<'static, str>>) -> Expr

A named argument placeholder.

Source

pub fn group(items: impl IntoExprList) -> Expr

A parenthesised list. A single expression gives plain parentheses.

Source

pub fn binary(lhs: impl IntoExpr, op: &'static str, rhs: impl IntoExpr) -> Expr

An infix operator applied to two operands.

Source

pub fn prefix(op: &'static str, operand: impl IntoExpr) -> Expr

A prefix operator.

Source

pub fn postfix(operand: impl IntoExpr, op: &'static str) -> Expr

A postfix operator.

Source

pub fn join(items: impl IntoExprList) -> Expr

Space-separated parts — bob’s Join with its default separator.

Source

pub fn join_with(sep: &'static str, items: impl IntoExprList) -> Expr

Parts joined by sep, written verbatim.

Unlike bob, an empty separator means an empty separator. bob silently substitutes a space for Sep: "", which is a trap in a language where the zero value is what you get by leaving a field out; here the separator is always passed explicitly, and Expr::join is the space-separated form.

Source

pub fn func(name: impl Into<Cow<'static, str>>, args: impl IntoExprList) -> Expr

A function call with no window.

Source

pub fn cast( expr: impl IntoExpr, type_name: impl Into<Cow<'static, str>>, ) -> Expr

CAST(expr AS type_name).

Source

pub fn custom(e: impl Expression + 'static) -> Expr

Wrap an arbitrary Expression so it can travel through core.

Source

pub fn is_atomic(&self) -> bool

Whether this expression renders as a self-delimiting fragment, so that parentheses around it would add nothing.

§The parenthesisation rule

This predicate and grouped are bob’s expr.X — the single most output-visible decision in the whole library. Every operator in bob’s chain wraps its result in parentheses unless the result is one of a small set of shapes, and that is precisely why bob emits ("id" = $1) for an equality but "users"."id" for a column.

Atomic, and so never wrapped:

  • Raw and Template — the author wrote the SQL and gets it back unedited.
  • Literal'abc' is one token.
  • Ident"users"."id" is one token.
  • Arg, Args, NamedArg — a placeholder list is normally written into a slot that supplies its own parentheses, such as VALUES (..).
  • Group — already parenthesised.

Everything else is wrapped, including Custom: core cannot see inside it, and bob’s fallback for an unrecognised expression is to wrap.

bob has a further arm — an expression that is already a built chain value is returned unchanged — which has no counterpart here and needs none. Every chain step applies this rule to its own result, so a chain value is always Group or atomic by construction, and re-applying the rule is a no-op. That invariant is what makes NOT ("a" = $1) come out with one set of parentheses rather than two.

Source

pub fn grouped(self) -> Expr

Parenthesise this expression unless it is_atomic.

This is bob’s expr.X, and every operator in Chain finishes with it.

Trait Implementations§

Source§

impl Chain for Expr

An Expr is its own chain, so operators are available without any wrapper type. A dialect that wants its own type implements Chain for that instead.

Source§

fn from_expr(e: Expr) -> Expr

Wrap a finished expression back into the chain type. Read more
Source§

fn step(self, f: impl FnOnce(Expr) -> Expr) -> Self

One chain step: build a node from this expression, apply the parenthesisation rule, and return a chain again. Read more
Source§

fn op(self, op: &'static str, rhs: impl IntoExpr) -> Self

An arbitrary infix operator: self op rhs. Read more
Source§

fn eq(self, rhs: impl IntoExpr) -> Self

self = rhs.
Source§

fn ne(self, rhs: impl IntoExpr) -> Self

self <> rhs. The standard spelling, which every dialect accepts.
Source§

fn lt(self, rhs: impl IntoExpr) -> Self

self < rhs.
Source§

fn lte(self, rhs: impl IntoExpr) -> Self

self <= rhs.
Source§

fn gt(self, rhs: impl IntoExpr) -> Self

self > rhs.
Source§

fn gte(self, rhs: impl IntoExpr) -> Self

self >= rhs.
Source§

fn in_(self, vals: impl IntoExprList) -> Self

self IN (a, b, c). Read more
Source§

fn not_in(self, vals: impl IntoExprList) -> Self

self NOT IN (a, b, c).
Source§

fn is_null(self) -> Self

self IS NULL.
Source§

fn is_not_null(self) -> Self

self IS NOT NULL.
Source§

fn is_distinct_from(self, rhs: impl IntoExpr) -> Self

self IS DISTINCT FROM rhs.
Source§

fn is_not_distinct_from(self, rhs: impl IntoExpr) -> Self

self IS NOT DISTINCT FROM rhs.
Source§

fn between(self, a: impl IntoExpr, b: impl IntoExpr) -> Self

self BETWEEN a AND b.
Source§

fn not_between(self, a: impl IntoExpr, b: impl IntoExpr) -> Self

self NOT BETWEEN a AND b.
Source§

fn like(self, rhs: impl IntoExpr) -> Self

self LIKE rhs.
Source§

fn concat(self, others: impl IntoExprList) -> Self

self || a || b — string concatenation.
Source§

fn and(self, others: impl IntoExprList) -> Self

self AND a AND b.
Source§

fn or(self, others: impl IntoExprList) -> Self

self OR a OR b.
Source§

fn plus(self, rhs: impl IntoExpr) -> Self

self + rhs.
Source§

fn minus(self, rhs: impl IntoExpr) -> Self

self - rhs.
Source§

fn as_(self, alias: impl IntoIdent) -> Expr

self AS "alias". Read more
Source§

impl Clone for Expr

Source§

fn clone(&self) -> Expr

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Expr

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Expression for Expr

Source§

fn write_sql(&self, w: &mut SqlWriter<'_>)

Append this fragment to w. Read more
Source§

impl IntoExpr for Expr

Source§

fn into_expr(self) -> Expr

Perform the conversion.
Source§

impl IntoExprList for Expr

Source§

fn into_expr_list(self) -> Vec<Expr>

Perform the conversion.

Auto Trait Implementations§

§

impl !RefUnwindSafe for Expr

§

impl !UnwindSafe for Expr

§

impl Freeze for Expr

§

impl Send for Expr

§

impl Sync for Expr

§

impl Unpin for Expr

§

impl UnsafeUnpin for Expr

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PsqlOps for T
where T: Chain,

Source§

fn ilike(self, rhs: impl IntoExpr) -> Self

self ILIKE rhsLIKE, case-insensitively.
Source§

fn not_ilike(self, rhs: impl IntoExpr) -> Self

self NOT ILIKE rhs.
Source§

fn not_like(self, rhs: impl IntoExpr) -> Self

self NOT LIKE rhs.
Source§

fn similar_to(self, rhs: impl IntoExpr) -> Self

self SIMILAR TO rhs — the SQL-standard regular-expression operator.
Source§

fn not_similar_to(self, rhs: impl IntoExpr) -> Self

self NOT SIMILAR TO rhs.
Source§

fn matches(self, rhs: impl IntoExpr) -> Self

self ~ rhs — POSIX regular-expression match.
Source§

fn imatches(self, rhs: impl IntoExpr) -> Self

self ~* rhs — POSIX match, case-insensitively.
Source§

fn not_matches(self, rhs: impl IntoExpr) -> Self

self !~ rhs.
Source§

fn not_imatches(self, rhs: impl IntoExpr) -> Self

self !~* rhs.
Source§

fn between_symmetric(self, a: impl IntoExpr, b: impl IntoExpr) -> Self

self BETWEEN SYMMETRIC a AND b — the bounds may be given either way round.
Source§

fn not_between_symmetric(self, a: impl IntoExpr, b: impl IntoExpr) -> Self

self NOT BETWEEN SYMMETRIC a AND b.
Source§

fn contains(self, rhs: impl IntoExpr) -> Self

self @> rhs — contains.
Source§

fn contained_by(self, rhs: impl IntoExpr) -> Self

self <@ rhs — is contained by.
Source§

fn overlaps(self, rhs: impl IntoExpr) -> Self

self && rhs — overlaps.
self @@ rhs — full-text search match.
Source§

fn json_get(self, rhs: impl IntoExpr) -> Self

self -> rhs — the field or element, as json/jsonb.
Source§

fn json_get_text(self, rhs: impl IntoExpr) -> Self

self ->> rhs — the field or element, as text.
Source§

fn json_get_path(self, rhs: impl IntoExpr) -> Self

self #> rhs — the value at a path, as json/jsonb.
Source§

fn json_get_path_text(self, rhs: impl IntoExpr) -> Self

self #>> rhs — the value at a path, as text.
Source§

fn json_has_key(self, rhs: impl IntoExpr) -> Self

self ? rhs — does the top level contain this key. Read more
Source§

fn json_has_any_key(self, rhs: impl IntoExpr) -> Self

self ?| rhs — any of these keys.
Source§

fn json_has_all_keys(self, rhs: impl IntoExpr) -> Self

self ?& rhs — all of these keys.
Source§

fn eq_any(self, vals: impl IntoExprList) -> Self

self = ANY (vals) — true for at least one element. Read more
Source§

fn ne_all(self, vals: impl IntoExprList) -> Self

self <> ALL (vals) — true for every element.
Source§

fn any(self, op: &'static str, vals: impl IntoExprList) -> Self

self <op> ANY (vals), for an operator this trait does not name.
Source§

fn all(self, op: &'static str, vals: impl IntoExprList) -> Self

self <op> ALL (vals), for an operator this trait does not name.
Source§

fn is_true(self) -> Self

self IS TRUE.
Source§

fn is_not_true(self) -> Self

self IS NOT TRUE.
Source§

fn is_false(self) -> Self

self IS FALSE.
Source§

fn is_not_false(self) -> Self

self IS NOT FALSE.
Source§

fn is_unknown(self) -> Self

self IS UNKNOWN.
Source§

fn is_not_unknown(self) -> Self

self IS NOT UNKNOWN.
Source§

fn cast_to(self, type_name: impl Into<Cow<'static, str>>) -> Self

self::type_name — PostgreSQL’s cast shorthand. Read more
Source§

fn collate(self, name: impl Into<Cow<'static, str>>) -> Self

self COLLATE "name".
Source§

fn at_time_zone(self, zone: impl IntoExpr) -> Self

self AT TIME ZONE zone.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.