Skip to content

Parameterized Queries

Whenever an external value (user input, program variable) needs to appear in a query, use positional parameters instead of string concatenation. Each ? in the SQL is replaced, in order, by the corresponding element of the bindings array passed as the second argument to execute or query.

This has two benefits: it protects against SQL injection and lets the driver reuse the query's execution plan across repeated calls.

Inserts users with ? and filters by minimum age — the values never touch the SQL text.

04-parameterized.zolo
// Feature: Database — parameterized queries (`?` + bindings array)

// Syntax: `db.query(sql, [v1, v2, ...])` and `db.execute(sql, [...])`

// When to use: ALWAYS when SQL receives external values. Prevents SQL

// injection and lets the driver cache the query plan.


use std::Database

let db = Database.open("sqlite://:memory:").unwrap()
defer db.close()

db.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)").unwrap()

// `?` in order; the bind array fills it in.

db.execute("INSERT INTO users (id, name, age) VALUES (?, ?, ?)", [1, "Ana", 30]).unwrap()
db.execute("INSERT INTO users (id, name, age) VALUES (?, ?, ?)", [2, "Bruno", 25]).unwrap()
db.execute("INSERT INTO users (id, name, age) VALUES (?, ?, ?)", [3, "Carla", 40]).unwrap()

const min_age = 28
let rows = db.query("SELECT name, age FROM users WHERE age >= ? ORDER BY age", [min_age]).unwrap()

print("users aged {min_age}+:")
for row in rows {
  print("  {row.name} ({row.age})")
}
// expected:

//   users aged 28+:

//     Ana (30)

//     Carla (40)

Requires the Zolo CLI/host — open in the playground or run locally.

Challenge

Add a fourth user via parameters and filter by name using WHERE name = ?. What happens if you pass nil as a value?

enespt-br