Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions src/content/docs/cypher/data-definition/alter.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,138 @@ The following query renames the age column to grade.
ALTER TABLE User RENAME age TO grade;
```

## Set sorted by

`SET SORTED BY` records sorted-by metadata on a table.

```cypher
ALTER TABLE User SET SORTED BY (age ASC);
```

Syntax (node tables):

```cypher
ALTER TABLE <node-table> SET SORTED BY (<property> ASC|DESC[, ...]) [CSR];
```

Each item names an existing column of the table followed by a required
direction (`ASC` or `DESC`). Single-column and composite lists are allowed, and
directions may be mixed (e.g. `(age DESC, name ASC)`). Declaring unknown columns
is rejected:

```cypher
ALTER TABLE User SET SORTED BY (height ASC);
-- Binder exception: Column height does not exist in table User.
```

A new declaration replaces any previous sorted-by metadata on the table, and a
plain declaration (without `CSR`) clears a previously declared `CSR` flag.
`RENAME COLUMN` follows renames inside the stored list. The declaration is
visible in `EXPLAIN` as `Set Table <table> Sorted By <col> ASC|DESC[, ...]
[CSR]`.

Without `CSR`, the list is metadata only: Ladybug neither verifies nor
establishes the ordering in storage, records no change-epoch watermark, and the
optimizer does not consume it. It is persisted across checkpoints and carried
over by subsequent `ALTER` operations. Only the `CSR` forms below have optimizer
or storage effects.

| Node-table form | Example | Result |
| --- | --- | --- |
| Single column, either direction | `(age ASC)`, `(age DESC)` | Recorded as metadata. |
| Composite, mixed directions | `(age DESC, name ASC)` | Recorded as metadata. |
| Single ascending primary key + `CSR` | `(id ASC) CSR` | CSR constraint declared (see below). |
| `CSR` with `DESC`, composite, or non-PK | `(id DESC) CSR`, `(id ASC, name ASC) CSR`, `(name ASC) CSR` | Rejected (see below). |

### Node-table CSR constraint

Appending the `CSR` keyword declares a CSR (compressed sparse row) constraint on a
node table:

```cypher
ALTER TABLE User SET SORTED BY (id ASC) CSR;
```

`CSR` asserts that the table's primary key is a `csr_index` interchangeable with
the rel table's `table_offset` (i.e. `primary_key == rowid`). This is a user
assertion: Ladybug does not verify it from the data, so only declare it when the
invariant actually holds.

Validation rules (node tables):

- Exactly one `SORTED BY` property.
- It must be ascending (`ASC`). `DESC` is rejected.
- It must be the table's primary key. Any other column is rejected.
- Composite `SORTED BY` lists with `CSR` are rejected.

```cypher
ALTER TABLE User SET SORTED BY (id DESC) CSR;
-- Binder exception: CSR requires exactly one SORTED BY property in ascending order.
ALTER TABLE User SET SORTED BY (id ASC, name ASC) CSR;
-- Binder exception: CSR requires exactly one SORTED BY property in ascending order.
ALTER TABLE User SET SORTED BY (name ASC) CSR;
-- Binder exception: CSR requires the SORTED BY property to be the primary key.
```

Invalidation: Ladybug records the node table's change epoch at declaration time.
Any subsequent mutation of the node table invalidates the invariant and the
optimizer silently disregards the `CSR` flag.

Optimizer behavior: when the `CSR` flag is set and the node table is unmutated
since declaration, the optimizer may use the offset-count degree rewrite
(`RelDegreeTableMode::OFFSET_COUNT`). It may also rewrite
`COUNT(DISTINCT nbr)` over a forward (`FWD`) `WALK` variable-length path
(`MATCH (a:User {id: ...})-[r:Friend*1..5]->(b:User) RETURN count(distinct b)`)
with a fixed source node, no node predicate, a single rel group, and single-labeled
bound/neighbor nodes into a bounded per-depth BFS that counts distinct reachable
offsets (`LogicalReachableCount`/`PhysicalReachableCount`). If any of these
conditions is not met, the original plan is used.

### Rel-table CSR sorted-by-dest

For relationship tables, `CSR` declares that the CSR adjacency lists are physically
stored with neighbors in non-decreasing order within each `FROM` row:

```cypher
ALTER TABLE Follows SET SORTED BY (FROM ASC, TO ASC) CSR;
```

`FROM` and `TO` are the user-facing names for the rel table's source and
destination endpoints. They are reserved structural names, not rel properties.
`bound` and `neighbor` remain internal concepts.

Validation rules (rel tables):

- The `CSR` keyword is required. Plain `SET SORTED BY` on a rel table is rejected.
- The property list must be exactly `(FROM ASC, TO ASC)`. Any other shape
(including `DESC`, single-column, or rel-property lists) is rejected.

```cypher
ALTER TABLE Follows SET SORTED BY (FROM ASC, TO ASC);
-- Binder exception: SORTED BY on rel table Follows requires the CSR clause.
ALTER TABLE Follows SET SORTED BY (FROM ASC, TO DESC) CSR;
-- Binder exception: CSR on rel table Follows requires SORTED BY (FROM ASC, TO ASC).
```

This is a user assertion: storage does not establish the ordering at checkpoint
time, so only declare it on data that is actually sorted (e.g. loaded from a
pre-sorted source). Declaring it on unsorted data produces wrong results on the
fast path.

Effect and invalidation: the flag lets `CSRArrowArrays::symmetrize()` (which
computes `A + A.T`, the symmetric/undirected view used for Arrow CSR results)
skip the `O(m log max-degree)` per-row sort and merge directly against the raw
indices. Ladybug records a `csrChangeEpoch` watermark at declaration time; if the
rel table is mutated afterwards, the fast path is invalidated and `symmetrize()`
falls back to the safe per-row sort.

### Node vs rel at a glance

| Table kind | Without `CSR` | With `CSR` |
| --- | --- | --- |
| Node | Any existing column(s), `ASC`/`DESC`, single or composite. Metadata only: no ordering enforced, no watermark, no optimizer effect. | Exactly one property, `ASC`, and it must be the primary key. Sets the `CSR` flag + change-epoch watermark; mutation invalidates it. |
| Rel | Rejected (`CSR` required). | Only `(FROM ASC, TO ASC)`. Declares sorted CSR adjacency; sets the watermark; mutation falls back to the safe path. |

## Comment on a table

`COMMENT ON` allows you to add comments to a table.
Expand Down
Loading