Skip to content

THE INCQL BOOK

1. Read a typed relation

Create a Session-owned logical CSV source and carry its intended row shape in LazyFrame[Order].

Part I

Model the work

Chapter 1 of 2

Goal

By the end of this chapter, the included consumer project can open orders.csv as a deferred LazyFrame[Order]. You will know which part is a checked Incan type, which part is a logical source registration, and which compatibility check is not yet performed.

Start from a real consumer project

The book is not compiled as part of IncQL itself. It is a small downstream project whose manifest points at this checkout, which also verifies that the public pub::incql boundary works.

Project manifest

[project]
name = "incql_tutorial_book"
version = "0.1.0"

[dependencies]
incql = { path = "../.." }

[project.scripts]
main = "src/main.incn"

The path dependency is the truthful setup for this checkout. It should not be read as evidence that IncQL has been published to a package registry.

Define the intended row shape

The Order model gives the carrier an intended row type. Later functions can accept LazyFrame[Order] rather than an untyped table handle.

Shared domain model

"""Typed row contracts shared by every tutorial-book checkpoint."""


@derive(Clone)
pub model Order:
    """The intended row shape of the tutorial CSV source."""

    pub order_id: int
    pub customer_id: str
    pub status: str
    pub amount: float


@derive(Clone)
pub model PaidOrderReview:
    """Projected row shape for the query-block beginner route."""

    pub order_id: int
    pub customer_id: str
    pub amount: float


@derive(Clone)
pub model StatusSummary:
    """Grouped result shape for the query-block beginner route."""

    pub status: str
    pub order_count: int
    pub total_amount: float

That type parameter is useful, but its boundary must be stated precisely: current CSV ingress does not validate every physical CSV field and type against the annotated Incan model. LazyFrame[Order] carries the author's intended model while IncQL's planned schema comes from the registered source.

The included data is small enough to keep every later result understandable:

Tutorial CSV

order_id,customer_id,status,amount
1001,C-001,paid,120.50
1002,C-002,pending,64.00
1003,C-001,paid,89.25
1004,C-003,cancelled,42.75

Register the source

Session owns source registration, backend selection, execution, materialization, and writes. read_csv(...) registers a logical relation name and returns deferred work; it does not hand you materialized rows.

With this example, you are asking the Session to register the URI orders.csv under the logical name tutorial_orders. The assignment keeps the authored carrier contract as LazyFrame[Order], while ? propagates a registration failure instead of pretending that a carrier exists. The trace separates that Incan type, the Session registration, and the resulting named-table carrier so you can see which layer owns each fact.

Source registration · before backend execution

See what read_csv(...) actually establishes

The call joins an intended Incan carrier type to a Session-owned source registration. It reads the local CSV to establish a coarse planned schema, but it does not execute the relation through DataFusion.

  1. 01
    Intended row shapeState the carrier contract in checked Incan
    pub model Order:
        pub order_id: int
        pub customer_id: str
        pub status: str
        pub amount: float
    
    Owner
    Incan authoring surface
    Artifact
    Order and the intended LazyFrame[Order] type
    Knowable now
    The row shape the consumer intends to carry
  2. 02
    CSV registrationBind a logical name and establish a planned source schema
    orders: LazyFrame[Order] = session.read_csv( # (1)!
        "tutorial_orders", # (2)!
        "orders.csv", # (3)!
    )? # (4)!
    
    1. Carrier contract. LazyFrame[Order] is the declared result type: it carries the intended Order row shape with deferred relational work. It is not a materialized DataFrame[Order].

    2. Logical name. tutorial_orders becomes the Session-local name used by the ReadNamedTable plan root. The name must be non-empty and not already registered in this Session; changing it does not rename the CSV file.

    3. Source URI. orders.csv identifies the physical CSV source. Here it is a non-empty path relative to the tutorial project; the current read_csv(...) surface takes the logical name and URI as its two positional arguments.

    4. Failure propagation. read_csv(...) returns a Result. The trailing ? returns its SessionError from the caller when registration or source-schema discovery fails, instead of producing a carrier that cannot be used safely.

    Owner
    Session and its CSV source policy
    Artifact
    Logical source registration plus a coarse inferred CSV schema
    Knowable now
    Header names, primitive kinds, nullability, and source identity
  3. 03
    Deferred carrierReturn intent without materialising rows
    Logical root
    ReadNamedTable("tutorial_orders")
    Returned carrier
    LazyFrame[Order]
    Backend attempt
    None
    Session registration Deferred named-table plan
Open the complete Chapter 1 checkpoint
"""Chapter 1: bind a typed row contract to a CSV-backed lazy carrier."""

from domain import Order
from pub::incql import LazyFrame, Session, SessionError, report_session_error


def read_orders(mut session: Session) -> Result[LazyFrame[Order], SessionError]:
    """Register the CSV source and return its typed lazy carrier."""
    orders: LazyFrame[Order] = session.read_csv("tutorial_orders", "orders.csv")?
    return Ok(orders)


def main() -> None:
    mut session = Session.default()
    match read_orders(session):
        Ok(_) => println("Chapter 1: created a LazyFrame[Order] without executing it")
        Err(error) => report_session_error("tutorial.chapter_01", error)

Run the checkpoint from the tutorial project:

incan run src/chapter_01.incn

Expected output

Chapter 1: created a LazyFrame[Order] without executing it

The checkpoint deliberately prints no schema or rows. It proves that the public consumer can register the logical source and create its lazy carrier. Later materialization exposes resolved columns, row counts, and preview text rather than typed Order iteration.

Try it

Change only the logical table name passed to read_csv(...), then run the checkpoint again. The physical CSV stays the same, but the name attached to the logical read changes. Restore the checkpoint before continuing.

Chapter complete

You can explain why LazyFrame[Order] is deferred relational intent, why the Session owns the CSV binding, and why the Order annotation is not yet proof that the physical CSV conforms field-for-field.