cake/fragment

Fragments are low level building blocks of queries which allow direct manipulation of the query string.

If you want to insert parameters, you are required to use prepared fragments, which will be validated against the number of parameters given and the parameters are automatically escaped by the RDBMS to prevent SQL injections.

Low-level building blocks for injecting raw SQL into queries while keeping parameter binding safe.

Aliases

import cake/fragment as f

When to use fragments

Use fragments when Cake’s typed builder functions do not cover your use case:

Never pass uncontrolled user input through f.literal(). Always use f.prepared() with typed params to bind user data safely.


Fragment constructors

prepared(string, params) -> Fragment

Creates a fragment where ? placeholders (the value of f.placeholder) are replaced with safely-bound parameters at query execution time.

f.prepared("?::uuid", [f.string("0000-0000-4000-a000-a00000000000")])

The placeholder character is exported as the constant f.placeholder (the ? grapheme). Use it when constructing fragment strings dynamically.

⛔ If the number of placeholders does not match the number of params, an error is printed to stderr and the fragment is created with best-effort fallback behaviour:

  • Too many placeholders → last param is repeated.
  • Too many params → excess params are ignored.

literal(string) -> Fragment

Creates a fragment from a static, developer-controlled SQL string. No parameter substitution occurs.

f.literal("NOW()")
f.literal("CURRENT_TIMESTAMP")
f.literal("COUNT(*)")

YOU ARE FORBIDDEN TO INSERT UNCONTROLLED USER INPUT THIS WAY.


Placeholder

f.placeholder  // the "?" grapheme used inside prepared fragment strings

Param constructors

Params are the typed values passed to f.prepared(). They are escaped by the database driver, preventing SQL injection.

FunctionGleam typeSQL type
bool(value)BoolBoolean
true()TRUE
false()FALSE
float(value)FloatFloat
int(value)IntInteger
string(value)StringString / Text
null()NULL
date(value)calendar.DateDate

Using fragments in queries

Fragments can appear in several positions depending on context:

flowchart TD
    A[Fragment] --> B[SelectValue\ns.fragment]
    A --> C[InsertValue\ni.fragment]
    A --> D[UpdateSet\nu.set_fragment]
    A --> E[WhereValue\nw.fragment_value]
    A --> F[Where condition\nw.fragment]

In SELECT projections

import cake/select as s
import cake/fragment as f

s.new()
|> s.from_table("orders")
|> s.select(s.fragment(f.literal("SUM(amount)")) |> s.alias("total"))

In INSERT values

import cake/insert as i
import cake/fragment as f

i.from_values("users", ["id", "name"], [
  i.row([
    i.fragment(f.prepared("?::uuid", [f.string("abc-123")])),
    i.string("Alice"),
  ]),
])

In UPDATE SET

import cake/update as u
import cake/fragment as f

u.new()
|> u.table("sessions")
|> u.set(u.set_fragment("expires_at", f.literal("NOW() + INTERVAL '1 hour'")))

In WHERE — as a value operand

import cake/where as w
import cake/fragment as f

w.eq(
  w.col("tags"),
  w.fragment_value(f.prepared("?::jsonb", [f.string("[\"gleam\"]")])),
)

In WHERE — as a full condition

w.fragment(f.prepared("? @> ?::jsonb", [
  f.string("tags"),
  f.string("[\"gleam\"]"),
]))

Safety reference

f.preparedf.literal
User input safe
Param substitution
Static SQL only

Full Example

import cake/select as s
import cake/where as w
import cake/fragment as f

// Select users whose tags JSON array contains "gleam"
s.new()
|> s.from_table("users")
|> s.select_cols(["id", "name"])
|> s.select(s.fragment(f.literal("tags::text")) |> s.alias("tags_text"))
|> s.where(w.fragment(
  f.prepared("tags @> ?::jsonb", [f.string("[\"gleam\"]")]),
))
|> s.order_by_asc("name")
|> s.to_query

Types

pub type Fragment =
  @internal Fragment

Values

pub fn bool(value: Bool) -> param.Param

Create a new Param with a Bool value.

pub fn date(value: calendar.Date) -> param.Param

Create a new Param with a calendar.Date value.

pub fn false() -> param.Param

Create a new Param with a False value.

pub fn float(value: Float) -> param.Param

Create a new Param with a Float value.

pub fn int(value: Int) -> param.Param

Create a new Param with an Int value.

pub fn literal(string: String) -> Fragment

Create a new fragment from a literal string.

⛔ ⛔ ⛔

WARNING: YOU ARE FORBIDDEN TO INSERT UNCONTROLLED USER INPUT THIS WAY!

⛔ ⛔ ⛔

pub fn null() -> param.Param

Create a new Param with an SQL NULL value.

pub const placeholder: String

This placeholder must be used when building fragments with parameters.

pub fn prepared(
  string: String,
  params: List(param.Param),
) -> Fragment

Create a new fragment from a string and a list of parameters.

⛔ ⛔ ⛔

If you mismatch the number of placeholders with the number of parameters, an error will be printed to stderr and the fragment will be created with the given parameters:

  • If there are too many placeholders, the fragment will be created with the given parameters and the last parameter will be repeated for the remaining placeholders.
  • If there are too many parameters, the fragment will be created with the given parameters and the excess parameters will be ignored.

⛔ ⛔ ⛔

pub fn string(value: String) -> param.Param

Create a new Param with a String value.

pub fn true() -> param.Param

Create a new Param with a True value.

Search Document