Skip to content

Incremental Models (R1)

This guide explains how to configure incremental models, use is_incremental() in SQL, engine compatibility, and schema change policies.

Quick Start

A minimal incremental model:

-- examples/r1_demo/models/fct_events_inc.ff.sql
{{ config(
  materialized='incremental',
  unique_key=['event_id'],
  on_schema_change='append_new_columns'  -- or 'sync_all_columns'
) }}
with src as (
  select * from {{ source('app', 'events') }}
  {% if is_incremental() %}
    where ingested_at > (select coalesce(max(ingested_at), timestamp '1970-01-01') from {{ this.name }})
  {% endif %}
)
select
  event_id,
  user_id,
  event_type,
  ingested_at,
  -- evolving column: will appear later
  meta_json
from src;
````

### `is_incremental()`

* Available in SQL templates during rendering.
* Returns `true` when the model exists and the current `materialized='incremental'` run chooses an incremental path (insert/merge) instead of full rebuild.
* Typical usage: filter the source to new rows only.

### Engine Matrix (MVP)

| Engine             | Incremental Insert | Merge/Upsert | Schema Change Policy |
| ------------------ | ------------------ | ------------ | -------------------- |
| DuckDB             |  insert           | 🚧 fallback* |  append new cols    |
| Postgres           |  insert           | 🚧 fallback* |  append new cols    |
| BigQuery (classic) |  insert           | 🚧 fallback* | 🚧 best-effort       |
| BigQuery BigFrames |  insert           | 🚧 fallback* | 🚧 best-effort       |
| Databricks Spark   |  insert           | 🚧 fallback* | 🚧 best-effort       |
| Snowflake Snowpark |  insert           | 🚧 fallback* | 🚧 best-effort       |

* Fallback strategy merges by delete-on-keys + insert (best effort) if native merge isnt wired.

### Schema Change Policies

* `append_new_columns` (default): new columns appear in target if they show up in the select.
* `sync_all_columns` (planned): attempt to keep type/nullable alignment. Currently not enforced; prefer append in R1.

### End-to-End

```bash
# Seeds  initial incremental build  run again with filter
fft seed examples/r1_demo --env dev
fft run  examples/r1_demo --env dev --select fct_events_inc.ff
# simulate new data (re-seed or append), then:
fft run  examples/r1_demo --env dev --select fct_events_inc.ff

Artifacts: see .fastflowtransform/target/{manifest.json, run_results.json, catalog.json}.