Argument Expression Examples#

These expressions are used in the args: list of a run query. Each entry in args: generates a value that is bound to a query parameter ($1, $2, etc.).

Looking for function signatures and return types? See the Function Reference.

Aggregation#

ExpressionDescription
avg('fetch_products', 'price')Average price across all products
count('fetch_products')Total number of rows in the dataset
distinct('fetch_products', 'category_id')Number of distinct category IDs across all products
max('fetch_products', 'price')Maximum price in the dataset
median('fetch_products', 'price')Median price, equivalent to percentile(..., 50)
median([3, 1, 2])Median of an array literal
min('fetch_products', 'price')Minimum price in the dataset
percentile('fetch_products', 'price', 95)95th percentile price, linearly interpolated between the two nearest ranks
percentile(prices, 95)95th percentile of an array; p is always the last argument
stddev('fetch_products', 'price')Population standard deviation of price (divides by N)
sum('fetch_products', 'price')Sum of the price field across all rows
sum([1, 2, 3])Sum of an array literal; every aggregate accepts this single-array form
variance('fetch_products', 'price')Population variance of price (divides by N)

Batch#

The recommended approach for batch inserts is exec_batch or query_batch with __values__:

seed {
  populate_users(count: 1000, size: 100)
    `INSERT INTO users (email)
    __values__` (
    gen('email')
  )
}

For Oracle, use the parameterized form __values__(table(cols)) to generate INSERT ALL ... SELECT 1 FROM DUAL.

The following expressions are used with the driver-specific batch expansion patterns (unnest, JSON_TABLE, etc.):

ExpressionDescription
batch(customers / batch_size)Drives batched execution: the parent query runs N times with $1 = 0..N-1
gen_batch(customers, batch_size, 'email')Generates unique emails via gofakeit, split into batches
string_to_array('$1', __sep__)Splits a batch-expanded placeholder back into rows using the driver-aware separator

When using driver-specific batch expansion, always use __sep__ instead of a literal comma delimiter. Generated values (names, addresses, etc.) can contain commas, which would silently split a single value into multiple rows and corrupt your data. The __values__ approach avoids this issue entirely.

Binary#

ExpressionDescription
bit(8)Random fixed-length bit string of 8 bits (e.g. 10110011)
blob(1024)Random 1KB blob as raw binary data (works across all databases)
bytes(16)Random 16 bytes as a hex-encoded CockroachDB/PostgreSQL BYTES literal
varbit(16)Random variable-length bit string of 1-16 bits

Conditionals#

ExpressionDescription
{'fra': 'eu-central-1', 'sin': 'ap-southeast-1'}[env(
'FLY_REGION')] ?? fail('bad region')
Map lookup with error on unknown value
arg('price') * float(arg('qty'))Same as above using named args
arg(0) * float(arg(1))Compute a total from previously generated price and quantity
arg(0) + " " + arg(1)Concatenate previously generated firstname and lastname
coalesce(ref('optional_data').value, 'default')First non-nil fallback value
cond(arg(0), gen('email'), nil)Email if coin flip is true, NULL if false
cond(gen('number:1,100') > 95, 'premium', 'standard')Conditional value based on a random roll
fail('unexpected value')Stop worker gracefully with an error message
fatal('missing required config')Terminate entire process immediately
first_name + " " + last_nameBare sibling reference - named args can be referenced directly without arg()
nullable(gen('email'), 0.3)30% chance of NULL, otherwise a random email
price * float(qty)Same as above using bare sibling names (named args only)

Constants & variables#

ExpressionDescription
const(42)Always passes the integer 42
const(null)Always passes NULL (alias for const(nil))
expr(warehouses * 10)Evaluates an arithmetic expression using globals
global('warehouses')Looks up a global by name (equivalent to using the variable directly)
int(coalesce(env_nil('CUSTOMERS'), 10000))Environment variable with default fallback, converted to int
warehouses * 10Direct global reference in an expression (equivalent to expr(...))

Dates & times#

ExpressionDescription
after(ref_same('parent').created_at,
'1s', '24h')
Random timestamp between 1 second and 24 hours after the parent’s created_at
before(ref_same('child').deleted_at,
'1s', '24h')
Random timestamp between 1 second and 24 hours before the reference timestamp
date_offset('-72h')Timestamp 72 hours in the past (e.g. for TTL or expiry columns)
date('2006-01-02', '2020-01-01T00:00:00Z',
'2025-01-01T00:00:00Z')
Random date formatted as YYYY-MM-DD
duration('1h', '24h')Random duration between 1 hour and 24 hours
time('08:00:00', '18:00:00')Random time of day between 08:00 and 18:00 (HH:MM:SS format)
timestamp_step()Next monotonic timestamp (requires timestamp_steps in count:)
timestamp_steps('2024-01-01T00:00:00Z',
'2025-01-01T00:00:00Z', 10000)
Count given directly: 10,000 evenly spaced timestamps between min and max; sets up timestamp_step()
timestamp_steps('2024-01-01T00:00:00Z',
'2025-01-01T00:00:00Z', '5m')
Count from interval: every 5 minutes between min and max; sets up timestamp_step()
timestamp('2020-01-01T00:00:00Z',
'2025-01-01T00:00:00Z')
Random timestamp between two dates (RFC3339 format)
timez('09:00:00', '17:00:00')Random time of day with timezone suffix (for TIMETZ columns)

Geographic#

ExpressionDescription
geo_bearing(51.5074, -0.1278, 48.8566, 2.3522)Initial compass bearing London -> Paris in degrees (~148.1); 0 is north, 90 east
geo_distance(51.5074, -0.1278, 48.8566, 2.3522)Great-circle distance London -> Paris in kilometres (~343.5)
point_wkt(51.5074, -0.1278, 10.0)Random geographic point as WKT for native geometry columns
point(51.5074, -0.1278, 10.0).latRandom geographic point within 10km of London, latitude
point(51.5074, -0.1278, 10.0).lonRandom geographic point within 10km of London, longitude

Hierarchical (ltree)#

ExpressionDescription
ltree('Top', 'Science', 'Astronomy')PostgreSQL/CockroachDB ltree path: Top.Science.Astronomy
ltree(arg('name'))Single-label root path from a previously generated name
ltree(gen('word'), gen('word'), gen('word'))Random 3-level path from generated words
ltree(ref('parent').path, arg('name'))Append a new label to a parent’s path for hierarchical data

Invalid ltree characters (hyphens, spaces, etc.) are automatically replaced with underscores. Nil and empty parts are skipped.

Identifiers#

ExpressionDescription
seq(1, 1)Auto-incrementing sequence: 1, 2, 3, … (per worker)
seq(100, 10)Auto-incrementing with custom start and step: 100, 110, 120, …
ulid()26-character Crockford base32 ULID; IDs from different milliseconds sort in creation order
uuid_v1()Random UUID v1 (timestamp + node ID)
uuid_v4()Random UUID v4 (random)
uuid_v6()Random UUID v6 (reordered timestamp)
uuid_v7()Random UUID v7 (time-ordered, sortable)

JSON & arrays#

ExpressionDescription
array(2, 5, 'email')PostgreSQL/CockroachDB array literal with 2-5 random email addresses
json_arr(1, 5, 'email')JSON array of 1-5 random email addresses
json_obj('source', 'web', 'version', 2, 'active', true)JSON metadata object for a JSONB column

Network#

ExpressionDescription
inet('192.168.1.0/24')Random IP address within a CIDR block

Correlated totals#

ExpressionDescription
distribute_sum(100.00, 3, 7, 2)3-7 random amounts that sum exactly to 100.00, each with 2 decimal places
distribute_sum(arg(1), 3, 7, 2)Partition a previously computed total across 3-7 child values
distribute_sum(ref_same('invoices').total, 3, 7, 2)Partition an invoice’s total into line item amounts
distribute_weighted(1000, [50, 30, 20], 0, 2)Exact 50/30/20 split: 500.00,300.00,200.00
distribute_weighted(1000, [50, 30, 20], 0.3, 2)Approximate 50/30/20 split with 30% noise
distribute_weighted(arg(1), [7, 2, 1], 0.1, 2)Split a parent value roughly 70/20/10

Numeric distributions#

ExpressionDescription
beta.float(2, 5, 0, 100, 2)Beta-distributed float scaled onto [0, 100] (min/max scale, they don’t clamp)
exp.float(0.5, 0, 100, 2)Exponentially-distributed float in [0, 100] with 2 decimal places
exp(0.5, 0, 100)Exponentially-distributed integer in [0, 100]
lognorm.float(1.0, 0.5, 1, 1000, 2)Log-normally-distributed float in [1, 1000] with 2 decimal places
lognorm(1.0, 0.5, 1, 1000)Log-normally-distributed integer in [1, 1000]
norm.float(50.0, 15.0, 1.0, 100.0, 2)Normally-distributed float price centred on 50.00, 2 decimal places
norm.n(50.0, 10.0, 1, 100, 5, 10)5-10 unique normally-distributed values as a comma-separated string
norm(4, 1, 1, 5)Normally-distributed integer review rating centred on 4, mostly 3-5
nurand_n(8191, 1, items, 5, 15)5-15 unique NURand values as a comma-separated string
nurand(1023, 1, customers / districts)Non-uniform random int using TPC-C NURand
pareto.float(2.0, 0, 1000, 2)Continuous Pareto float in [0, 1000] with 2 decimal places
pareto.int(2.0, 999)Pareto-distributed integer in [0, 999]; lower values dominate
uniform.float(0.01, 999.99, 2)Random float between 0.01 and 999.99 with 2 decimal places
uniform.int(0, 1)Uniform random float between 0 and 1 (e.g. for percentages)
uniform.n(1, 1000, 3, 5)3-5 unique uniform values as a comma-separated string
zipf.int(2.0, 1.0, 999)Zipfian distribution: hot-key pattern where value 0 is most frequent
zipf.n(1.1, 1.0, 100000, 5, 15)5-15 unique Zipfian item IDs, e.g. TPC-C New-Order lines

PII & locale#

ExpressionDescription
first_name + " " + last_nameComposed full name from previously generated named args. Bare sibling names are available directly - no arg() wrapper needed. arg('first_name') syntax still works.
gen_locale('address', 'de_DE')Full German address with street number, city, and zip
gen_locale('city', 'fr_FR')French city name (e.g. Paris, Lyon)
gen_locale('first_name', 'ja_JP')Japanese first name (e.g. 太郎, 花子)
gen_locale('last_name', 'de_DE')German last name (e.g. Müller, Schmidt)
gen_locale('name', 'ja_JP')Independent full name in locale order (東 = 佐藤太郎, 西 = Hans Müller). First and last are picked independently of first_name/last_name args.
gen_locale('phone', 'ko_KR')Korean phone number (e.g. 010-1234-5678)
gen_locale('street', 'es_ES')Spanish street name (e.g. Gran Vía)
gen_locale('zip', 'ja_JP')Japanese postal code (e.g. 123-4567)
mask('john@example.com', 'email', 4)Shorter local mask (e.g. ****@example.com)
mask('john@example.com', 'email')Masks local part, preserves domain (e.g. ****************@example.com)
mask('john@example.com')Deterministic 16-char hex token (e.g. a3f8c1d9e2b74f06). Same input -> same output within a session
mask('secret', 'asterisk', 4)4 asterisks (e.g. ****)
mask('secret', 'asterisk')16 asterisks (e.g. ****************)
mask('secret', 'base32')Base32-encoded token, 16 chars (e.g. UP4MDWPCR3YGQKH5)
mask('secret', 'base64', 8)Base64-encoded token, 8 chars (e.g. o/jB2eK3)
mask('secret', 'base64')Base64-encoded token, 16 chars (e.g. o/jB2eK3TwYKd1==)
mask('secret', 'redact')Fixed string [REDACTED], length ignored
mask(arg('email'), 8)8-char hex token of a previously generated email (e.g. a3f8c1d9)

Supported locales: en_US, ja_JP, de_DE, fr_FR, es_ES, pt_BR, zh_CN, ko_KR. Aliases like ja, de, fr also work.

Generation#

ExpressionDescription
bool()Random true or false
gen('number:1,10')Random integer between 1 and 10 using gofakeit
hash('user@example.com', 'sha256')Unkeyed 64-char lowercase hex digest; same input always gives the same output
hash(arg('email'), 'crc32')8-char hex digest for cheap dedup keys or shard selection
regex('[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}')Random IPv4 address
regex('[0-9a-f]{2}(:[0-9a-f]{2}){5}')Random MAC address
regex('[A-Z]{2}[0-9]{2} [A-Z]{3}')Random license plate (e.g. “AB12 CDE”)
regex('[A-Z]{3}-[0-9]{4}')Product code matching a regex pattern
regex('\\([0-9]{3}\\) [0-9]{3}-[0-9]{4}')Random US phone number
regex('#[0-9a-f]{6}')Random hex colour code

Uniqueness#

ExpressionDescription
uniq_across('emails', "gen('email')")Cross-query uniqueness pool - values are unique across all queries sharing the same pool name
uniq_across('ids', "gen('uuid')", 500)Cross-query pool with custom max retries (default: 100)
uniq_across('names', "gen('first')", "gen('last')")Cross-query composite uniqueness
uniq("gen('email')")Retry until a unique email is generated (per-query, reset between queries)
uniq("gen('first')", "gen('last')")Unique composite tuple across two expressions

uniq() state resets between queries, so two seed queries using uniq("gen('email')") can produce duplicates. Use uniq_across() when values must be unique across multiple queries (e.g. unique email across users and admins tables). The pool persists for the lifetime of the environment.

Reference data#

Random selection#

ExpressionDescription
ref_diff('fetch_warehouses').w_idUnique row on each call within a query (no repeats)
ref_perm('fetch_warehouses').w_idRandom row pinned to this worker for its lifetime
ref_same('fetch_warehouses').w_idSame random row for all ref_same calls within a single query execution
ref('fetch_warehouses').w_idRandom row from the dataset (uniform)

Distribution-based selection#

ExpressionDescription
beta.ref('products', 2, 5).nameBeta-distributed - alpha and beta shape which rows are favored
binomial.ref('products', 10, 0.3).nameBinomial-distributed - index centers around n*p
empirical.ref('products', [1, 2, 2, 3, 5]).nameEmpirical CDF from observed data - sample values define the distribution shape
exp.ref('products', 1.5).nameExponential - lower-indexed rows selected more frequently
gamma.ref('products', 2, 1).nameGamma-distributed - mean index is shape/rate
lognorm.ref('products', 0.0, 0.5).nameLog-normal - right-skewed, early rows favored
norm.ref('products', 0.5, 0.2).nameNormal - mean and stddev as fractions of dataset length
pareto.ref('fetch_warehouses', 2.0).w_idPareto - first rows are strongly favored
poisson.ref('products', 3.0).namePoisson - index centers around lambda
ref_weighted('data.cities', [556, 278, 139, 27]).nameWeighted; each integer weight controls relative probability
weibull.ref('products', 1.5, 100).nameWeibull - models failure-rate-based access patterns
zipf.ref('products', 2.0, 1.0).nameZipfian - first row is “hottest”, frequency drops off by skew

Iteration & pagination#

ExpressionDescription
ref_cursor('SELECT id FROM customer ORDER BY id', 1000, 'id', 3)Cursor with repeat: each row appears 3 times before advancing, constant memory
ref_cursor('SELECT id FROM customer ORDER BY id', 1000, 'id')Keyset-paginated cursor; pages through results 1000 at a time, constant memory
ref_each('SELECT id FROM t', 3)SQL variant with repeat: each SQL result row appears 3 times in the expansion
ref_each('SELECT id FROM warehouses ORDER BY id')Executes a SQL query; each row becomes a separate arg set
ref_each(customers, 3).idExact cardinality: each customer row is returned 3 times before advancing to the next
ref_each(product_catalog).nameIterates sequentially through a named reference dataset; same row cached within each iteration

Multi-value selection#

ExpressionDescription
ref_n('fetch_warehouses', 'id', 3, 8)Picks 3-8 unique random rows, returns comma-separated field values
weighted_sample_n('fetch_products', 'id', 'popularity', 3, 8)Pick 3-8 products weighted by their popularity column

Populating child tables from parent tables#

When seeding a child table (e.g. account) from every row in a parent table (e.g. customer), use ref_each or ref_cursor instead of manual LIMIT/OFFSET pagination.

ref_each - load all parent rows, iterate in batches#

ref_each('SQL') runs the SQL query, loads all returned rows into memory, then executes the parent query once per row. Combined with __values__, rows are collapsed into multi-row INSERTs controlled by size.

Best for small-to-medium parent tables (under ~1M rows) where loading all IDs into memory is acceptable.

seed {
  populate_customer(type: exec_batch, count: customers, size: batch_size, workers: 4)
    `INSERT INTO customer (email)
    __values__` (gen('email'))

  populate_account(size: batch_size, workers: 4)
    `INSERT INTO account (balance, customer_id)
    __values__` (
      initial_balance,
      ref_each('SELECT id FROM customer').id
  )
}

ref_each with exact cardinality#

ref_each(dataset, N) gives each parent row exactly N children. Pass the repeat count as the second argument and set count to count('parent') * N.

ref customers [
  {id: 1, name: "Alice"},
  {id: 2, name: "Bob"},
  {id: 3, name: "Carol"}
]

seed {
  populate_orders(type: exec_batch, count: count('customers') * 3, size: 100)
    `INSERT INTO orders (id, customer_id, amount)
    __values__` (
      uuid(),
      ref_each(customers, 3).id,
      rand_f(10.0, 500.0)
    )
}

Each customer gets exactly 3 orders (9 total). edg doctor warns if count doesn’t match dataset_size × repeat.

ref_each with N across two tables#

Combine two ref_each datasets to build a cross-product with controlled cardinality. Each warehouse gets exactly 2 inventory rows per product (6 rows per warehouse, 12 total).

ref warehouses [
  {id: 1, name: "East"},
  {id: 2, name: "West"}
]

ref products [
  {id: 10, sku: "BOLT"},
  {id: 20, sku: "NUT"},
  {id: 30, sku: "WASHER"}
]

seed {
  populate_inventory(type: exec_batch, count: count('warehouses') * count('products') * 2, size: 100)
    `INSERT INTO inventory (warehouse_id, product_id, quantity)
    __values__` (
      ref_each(warehouses, count('products') * 2).id,
      ref_each(products, 2).id,
      rand_i(0, 500)
    )
}

Output pattern (12 rows):

warehouse_idproduct_idquantity
110
110
120
120
130
130
210
210
220
220
230
230

ref_cursor - keyset-paginated streaming#

ref_cursor('SQL', page_size, 'cursor_column') pages through the parent table using keyset pagination (WHERE cursor_col > $last_value ORDER BY cursor_col LIMIT page_size). Only one page of rows is in memory at a time.

Best for large parent tables (1M+ rows) where loading all IDs into memory is impractical.

seed {
  populate_customer(type: exec_batch, count: customers, size: batch_size, workers: 4)
    `INSERT INTO customer (email)
    __values__` (gen('email'))

  populate_account(size: batch_size, workers: 4)
    `INSERT INTO account (balance, customer_id)
    __values__` (
      initial_balance,
      ref_cursor('SELECT id FROM customer ORDER BY id', batch_size, 'id')
  )
}

ref_cursor with exact cardinality#

ref_cursor('SQL', page_size, 'cursor_col', N) combines keyset pagination with repeat count. Each parent row is returned N times before advancing. Memory stays constant - only one page in memory at a time, same as without repeat.

Best for large parent tables (1M+ rows) where ref_each(query, N) would exhaust memory.

seed {
  populate_customer(type: exec_batch, count: 1000000, size: batch_size, workers: 4)
    `INSERT INTO customer (email)
    __values__` (gen('email'))

  populate_account(size: batch_size, workers: 4)
    `INSERT INTO account (balance, customer_id)
    __values__` (
      initial_balance,
      ref_cursor('SELECT id FROM customer ORDER BY id', batch_size, 'id', 3)
  )
}

Each customer gets exactly 3 accounts (3M total). Pages stream 1 batch at a time - no matter how many customers exist.

Set selection#

ExpressionDescription
each(['admin', 'user', 'guest'])Deterministic cycling; row 0 -> admin, row 1 -> user, row 2 -> guest, then wraps
exp.set(['low', 'medium', 'high', 'critical'], 0.5)Exponential distribution; concentrates picks toward first item
lognorm.set(['free', 'basic', 'pro', 'enterprise'], 0.5, 0.5)Log-normal distribution (right-skewed toward early indices)
norm.set([1, 2, 3, 4, 5], 2, 0.8)Normal distribution; index 2 is most common
pareto.set(['electronics', 'clothing', 'books', 'food', 'toys'], 2.0)Pareto distribution; strong power-law skew toward first items
set(['1', '2', '3', '4', '5'], [5, 10, 20, 35, 30])Weighted random; skewed toward 4 and 5 stars
set(['credit_card', 'debit_card', 'paypal'], [])Uniform random payment method selection
zipf.set(['electronics', 'clothing', 'books', 'food', 'toys'], 2.0, 1.0)Zipfian distribution; strong skew toward first items

Strings & formatting#

ExpressionDescription
template('ORD-%05d', seq(1, 1))Formatted order number: “ORD-00001”, “ORD-00002”, …

Vectors#

Synthetic (no API required)#

ExpressionDescription
exp.vector(384, 10, 0.1, 0.5)Exponential centroid selection: cluster 0 is the “hottest”, frequency decays per rate
lognorm.vector(384, 5, 0.1, 1.0, 0.5)Log-normal centroid selection: right-skewed toward early clusters
norm.vector(384, 5, 0.1, 2.0, 0.8)Normal centroid selection: cluster 2 is most common, bell curve falloff
pareto.vector(384, 10, 0.1, 2.0)Pareto centroid selection: cluster 0 is the “hottest”, continuous power-law skew
vector(32, 3, 0.3)32-dimensional vector for testing; higher spread = more cluster overlap
vector(384, 5, 0.1)pgvector-compatible 384-dimensional vector with 5 clusters and tight spread
zipf.vector(384, 10, 0.1, 2.0, 1.0)Zipfian centroid selection: cluster 0 is the “hottest”, realistic skew

Real embeddings (requires --embed-api-key)#

ExpressionDescription
embed('fixed search query')Embed a literal string for similarity search queries
embed(field('name'), field('description'))Embed concatenated object fields (joined with space)
embed(gen('sentence:3'))Embed a generated sentence via external API

Non-batched (exec/query)#

Each embed() call makes a separate API request:

insert_product(type: exec)
  `INSERT INTO product (name, description, embedding)
  VALUES ($1, $2, $3::VECTOR)` (
  ref_same('product_catalog').name,
  ref_same('product_catalog').description,
  embed(ref_same('product_catalog').name, ref_same('product_catalog').description)
)

With 100 iterations, this makes 100 API calls (one per row).

Batched (exec_batch/query_batch)#

In batch queries, embed() calls are deferred - placeholders are inserted during arg evaluation, then all pending texts are resolved together at the end of each batch:

populate_product(count: 100, size: 50)
  `INSERT INTO product (name, description, embedding)
  SELECT n, d, e::VECTOR
  FROM unnest(ARRAY[$1], ARRAY[$2], ARRAY[$3]) AS t(n, d, e)` (
  ref_each(product_catalog).name,
  ref_each(product_catalog).description,
  embed(ref_each(product_catalog).name, ref_each(product_catalog).description)
)

With count: 100 and size: 50, there are 2 batches of 50. Each batch collects 50 texts, then resolves them in a single API call - 2 API calls instead of 100.

Use --embed-max-batch to cap texts per API call. For example, --embed-max-batch 30 on a 50-row batch produces 2 API calls (30+20) per batch, or 4 total (30+20+30+20).

User-defined expressions#

The expr declaration defines named functions from expr-lang expression strings. Each expression becomes a callable function available in any query arg. Expressions can reference globals, built-in functions, and other expressions. Arguments are available via the args slice.

let total_rows = 10000
let num_buckets = 10

expr rows_per_bucket = total_rows / num_buckets
expr ten_percent = int(ceil(total_rows * 0.1))

expr clamp = max(min(args[0], args[1]), 0)
expr pct_of = int(ceil(args[0] * args[1] / 100))
expr like_prefix = string(args[0]) + '%'
expr pick_label = args[0] > 1000 ? 'large' : 'small'
expr wrapped_offset = abs(args[0] - args[1]) % args[2]
expr power_scale = int(floor(float(args[0]) ** 2))
expr is_active = not (args[0] == 0) and (args[0] != args[1] or args[0] < args[2])
expr safe_val = args[0] ?? args[1]
expr round_ratio = round(float(args[0]) / float(args[1]))
expr normalize = lower(trim(replace(args[0], ' ', '_')))
expr shout = upper(split(args[0], ',')[0])
expr add_piped = (args[0] + args[1]) | int

run {
  example_query `SELECT * FROM t WHERE bucket = $1 AND label = $2 AND name LIKE $3 AND score >= $4 AND active = $5 AND tag = $6 AND rank >= $7 AND tier = $8 AND fallback = $9 AND weight = $10 AND grp = $11 AND max_id <= $12 LIMIT $13 OFFSET $14` (
    gen('number:1,' + num_buckets),
    pick_label(total_rows),
    like_prefix('foo'),
    pct_of(total_rows, 5),
    is_active(1, 2, 100),
    normalize(' Foo Bar '),
    power_scale(3),
    shout('hello,world'),
    safe_val('premium', 'basic'),
    round_ratio(total_rows, num_buckets),
    wrapped_offset(7, 20, num_buckets),
    add_piped(rows_per_bucket, ten_percent),
    clamp(rows_per_bucket, 500),
    ten_percent,
  )
}