Blueprints Blog Contact About

Save Module

Emit rows that become the model output

Turn script output into a table.

Every row you emit becomes part of the model’s result.

save is how scripts produce data in OndatraSQL. It is a predeclared global — no import needed.

Why This Exists

In SQL models, your SELECT defines the output.

In scripts, you generate rows manually.

save bridges that gap — it lets you emit rows that OndatraSQL materializes.

Mental Model

  • http.get() fetches data
  • You transform it in Starlark
  • save.row() emits rows
  • OndatraSQL materializes the result

Scripts behave like SELECT statements — but with full control.

Rows are collected incrementally during execution. You don’t need to build the full dataset in memory — just emit rows as you process them.

Methods

Emit rows one by one or in batches:

row

Emit a single row:

save.row({
    "id": 1,
    "name": "Alice",
    "email": "alice@example.com",
})

rows

Emit multiple rows:

save.rows([
    {"id": 1, "name": "Alice"},
    {"id": 2, "name": "Bob"},
])

count

Get the current number of collected rows:

n = save.count()

Column Types

Column types are inferred automatically from values:

Starlark TypeDuckDB Type
intBIGINT
floatDOUBLE
stringVARCHAR
boolBOOLEAN

Example

# @kind: merge
# @unique_key: product_id

resp = http.get("https://api.example.com/products")

for product in resp.json:
    save.row({
        "product_id": product["id"],
        "name": product["name"],
        "price": product["price"],
        "in_stock": product["stock"] > 0,
        "updated_at": product["updated_at"],
    })

print("Saved", save.count(), "products")

Each save.row() call adds one row to the output table. At the end of the script, OndatraSQL materializes the collected rows.

Integrated with the Pipeline

Rows collected via save are:

  • Validated with constraints and audits
  • Written using the model’s @kind (append, merge, etc.)
  • Tracked in lineage and metadata
  • Versioned in DuckLake

You don’t handle storage — only data.

Compared to Traditional Ingestion

Traditional approach:

  • Fetch data in Python
  • Store in memory or files
  • Load into a database separately

OndatraSQL:

  • Fetch, transform, emit rows
  • One step, one runtime