Inlining mockup data
When prototyping or testing, you often want to run a query without connecting to a real database. FeatureQL lets you inline data directly in the query using several approaches, from quick-and-dirty to production-ready patterns.
Binding inline values directly
The simplest approach binds mockup data directly to inputs with BIND_VALUES(). Every input gets its values in the FOR clause:
WITH
ID := INPUT(BIGINT),
NAME := INPUT(VARCHAR),
HELLO_MESSAGE := 'Hello, ' || NAME || '!',
SELECT
ID,
HELLO_MESSAGE
FOR
(ID, NAME) := BIND_VALUES(ARRAY[ROW(1, 'Alice'), ROW(2, 'Bob'), ROW(3, 'Charlie')]); This works well for small datasets where every column is an input. For larger datasets or when you need key-value separation, BIND_SQL() offers more flexibility with standard SQL VALUES():
WITH
ID := INPUT(BIGINT),
NAME := INPUT(VARCHAR),
HELLO_MESSAGE := 'Hello, ' || NAME || '!',
SELECT
ID,
HELLO_MESSAGE
FOR
(ID, NAME) := BIND_SQL(SELECT id, name FROM (VALUES(1, 'Alice'), (2, 'Bob'), (3, 'Charlie')) as t(id, name)); Separating keys from values with EXTERNAL_SQL
Both approaches above treat every column as an input. In practice, you usually want to bind only the key (e.g., a customer ID) and look up associated values from a data source. EXTERNAL_SQL() with inline VALUES() mimics this pattern without a real database:
WITH
ID := INPUT(BIGINT),
NAME := EXTERNAL_SQL(
`SELECT id, name FROM (VALUES(1, 'Alice'), (2, 'Bob'), (3, 'Charlie')) as t(id, name)`
ON `SELF.id = %ID`
AS ROW(ID BIGINT, NAME VARCHAR)
)[NAME],
HELLO_MESSAGE := 'Hello, ' || NAME || '!',
SELECT
ID,
HELLO_MESSAGE
FOR
ID := BIND_VALUES(ARRAY[1,2,3]); This is the recommended pattern for prototyping because it mirrors how production queries work: keys are bound, values are looked up. When you're ready to connect to a real table, you replace the inline VALUES() with an actual table reference.
The SQL syntax for inline data varies by backend. DuckDB and Trino support VALUES(), while BigQuery uses UNNEST() with arrays. See backend particularities for details.
When to use each approach
| Approach | Best for | Trade-off |
|---|---|---|
BIND_VALUES() | Quick tests with 2-3 rows | All columns must be inputs |
BIND_SQL() with VALUES() | Slightly larger inline datasets | All columns are still inputs |
EXTERNAL_SQL() with VALUES() | Prototyping production-like queries | More verbose, but mirrors real data source patterns |
See data source mapping for the full reference on connecting features to external data.