# gizdb:core/parquet

Apache Parquet files read as GizDB columns (db/source.giz reads them as a table): the footer's schema and row groups, and each row group's column chunks decoded into column.giz columns, a dictionary-encoded text chunk keeping its dictionary as the column's. Flat schemas, and a list of floats or doubles as a vector; the codecs UNCOMPRESSED, SNAPPY, GZIP, ZSTD and LZ4_RAW, every encoding the format defines for them (PLAIN, the dictionary ones, RLE booleans, the three DELTA ones, BYTE_STREAM_SPLIT), data pages v1 and v2, and the logical types GizDB has: strings, integers, floats, booleans, decimals (up to 38 digits), dates, times and timestamps (MILLIS, MICROS, NANOS, INT96). Malformed input is an error, never a fault: every read is bounded by the bytes it has. The writer at the end of the file writes the same format back (see "the writer").

```gizlang
import * as Parquet from "gizdb:core/parquet";
```

## Types

### `T`

```gizlang
type T = { k: i64; i: i64; d: f64; b: bytes; ids: Vec<i64>; kids: Vec<T>; }
```

A decoded Thrift value: `k` its type (compact protocol ids: 1 true, 2 false, 3-6 integers, 7
double, 8 binary, 9 list, 10 set, 11 map, 12 struct); a struct holds field `ids` and `kids`.

- `k`: `i64`
- `i`: `i64`
- `d`: `f64`
- `b`: `bytes`
- `ids`: `Vec<i64>`
- `kids`: `Vec<M90.T>`

### `CompactLayout`

```gizlang
type CompactLayout = { values: i64; fields: i64; binaryBytes: i64; depth: i64; }
```

Allocation geometry of the same compact grammar used by metadata/page readers.
Measuring retains no decoded tree and does not tighten the reader's wire semantics.

- `values`: `i64`
- `fields`: `i64`
- `binaryBytes`: `i64`
- `depth`: `i64`

### `Field`

```gizlang
type Field = { name: string; ty: Type; physical: i64; width: i64; optional: bool; scale: i64; unit: i64; list: i64; }
```

A column of the file: its name, the type GizDB reads it as, its physical type (0 BOOLEAN, 1
INT32, 2 INT64, 3 INT96, 4 FLOAT, 5 DOUBLE, 6 BYTE_ARRAY, 7 FIXED_LEN_BYTE_ARRAY) and width, whether
it may hold NULLs, the decimal scale (-1: no decimal) and the time unit it is stored in (1 ms, 2
µs, 3 ns, 0 none; values are read as µs, days for a DATE).

- `name`: `string`
- `ty`: `M80.Type`
- `physical`: `i64`
- `width`: `i64`
- `optional`: `bool`
- `scale`: `i64`
- `unit`: `i64`
- `list`: `i64`

### `Chunk`

```gizlang
type Chunk = { start: i64; size: i64; codec: i64; values: i64; min: bytes; max: bytes; nulls: i64; }
```

A column chunk of a row group: where its pages lie, their codec, value count and statistics
(`min`/`max` plain-encoded, empty when the writer gave none).

- `start`: `i64`
- `size`: `i64`
- `codec`: `i64`
- `values`: `i64`
- `min`: `bytes`
- `max`: `bytes`
- `nulls`: `i64`

### `RowGroup`

```gizlang
type RowGroup = { rows: i64; chunks: Vec<Chunk>; }
```

A row group: its row count and one chunk per leaf column.

- `rows`: `i64`
- `chunks`: `Vec<M90.Chunk>`

### `Meta`

```gizlang
type Meta = { fields: Vec<Field>; rows: i64; groups: Vec<RowGroup>; }
```

The file metadata: its leaf fields, its row count and its row groups.

- `fields`: `Vec<M90.Field>`
- `rows`: `i64`
- `groups`: `Vec<M90.RowGroup>`

### `PageHeader`

```gizlang
type PageHeader = { value: T; bytes: i64; compressed: i64; plain: i64; kind: i64; rows: i64; encoding: i64; }
```

A bounded header parsed by the same Compact Thrift owner as resident chunks.
The caller must admit the header window before parsing, then the body/decode credit.

- `value`: `M90.T`
- `bytes`: `i64`
- `compressed`: `i64`
- `plain`: `i64`
- `kind`: `i64`
- `rows`: `i64`
- `encoding`: `i64`

### `PageDecoder`

```gizlang
type PageDecoder = { dictionary: State; rows: i64; expected: i64; dictionaryBytes: i64; }
```

The dictionary alone survives page boundaries; plain data pages do not retain
all earlier output dictionaries/rows. Source owns encoded offsets and page leases.

- `dictionary`: `M90.State`
- `rows`: `i64`
- `expected`: `i64`
- `dictionaryBytes`: `i64`

### `Writer`

```gizlang
type Writer = { codec: i64; specs: Vec<Spec>; rows: Vec<Vec<Value>>; held: i64; maxRows: i64; maxBytes: i64; offset: i64; started: bool; groups: Vec<bytes>; total: i64; }
```

A Parquet file being written. `rows` are the rows of the open row group; `started` says that
the leading magic is out; `groups` hold each finished RowGroup struct; `codec` compresses the pages (0 none, the
default; 2 GZIP; 7 LZ4_RAW).

- `codec`: `i64`
- `specs`: `Vec<M90.Spec>`
- `rows`: `Vec<Vec<M80.Value>>`
- `held`: `i64`
- `maxRows`: `i64`
- `maxBytes`: `i64`
- `offset`: `i64`
- `started`: `bool`
- `groups`: `Vec<bytes>`
- `total`: `i64`

## Functions

### `compactLayout`

```gizlang
function compactLayout(foot: bytes): CompactLayout throws string
```

Measure one footer struct using the canonical parser, before allocating its tree.

Effects: fail.

### `footerLength`

```gizlang
function footerLength(tail: bytes, size: i64): i64 throws string
```

The length of the footer of a file of `size` bytes whose last 8 bytes are `tail`.

Effects: fail.

### `meta`

```gizlang
function meta(foot: bytes): Meta throws string
```

The metadata in a file's footer (the `footerLength` bytes before its last 8).

Effects: fail.

### `decodeChunk`

```gizlang
function decodeChunk(f: Field, ch: Chunk, data: bytes, rows: i64): Column throws string
```

Column `f`'s chunk of `rows` rows, from its bytes (`data`, the chunk's `size` bytes from its
start), as a column of `rows` values.

Effects: fail.

### `pageHeader`

```gizlang
function pageHeader(data: bytes, maxPageBytes: i64, maxPageRows: i64): PageHeader throws string
```

The page header at the start of DATA, refused when DATA is longer than MAXPAGEBYTES or the page has more than MAXPAGEROWS rows.

Effects: fail.

### `pageDecoder`

```gizlang
function pageDecoder(f: Field, rows: i64): PageDecoder throws string
```

A decoder of the pages of one chunk of F, which holds ROWS rows.

Effects: fail.

### `pageDecodeCredit`

```gizlang
function pageDecodeCredit(decoder: PageDecoder, h: PageHeader, allowance: i64): i64 throws string
```

Conservative logical overlap, including expanding delta prefixes and repeated
binary dictionary values. This is admission credit, not allocator/RSS bytes.

Effects: fail.

### `snappy`

```gizlang
function snappy(data: bytes, size: i64): bytes throws string
```

Snappy's raw format: the uncompressed length, then literals and back-references.

Effects: fail.

### `statInt`

```gizlang
function statInt(f: Field, b: bytes): Option<i64>
```

A chunk's minimum or maximum as an integer on the column's own scale (µs for times, days for
dates, coefficients for decimals), when its statistics give one in 8 bytes or fewer.

Effects: fail.

### `statFloat`

```gizlang
function statFloat(f: Field, b: bytes): Option<f64>
```

The statistic B of F as a float, when F is a float or double column.

Effects: fail.

### `writer`

```gizlang
function writer(names: Vec<string>, types: Vec<Type>, maxRows: i64, maxBytes: i64): Writer throws string
```

A writer for columns `names` of `types`: a row group closes at `maxRows` rows or about `maxBytes`
bytes of values, whichever comes first.

Effects: fail.

## Methods

### `T.at`

```gizlang
function T.at(self: T, id: i64): T
```

Field `id` of struct `t`, or an absent value (k 0).

Effects: fail.

### `T.has`

```gizlang
function T.has(self: T, id: i64): bool
```

Whether the struct has the field ID.

Effects: fail.

### `PageDecoder.decode`

```gizlang
function PageDecoder.decode(self: mut PageDecoder, ch: Chunk, h: PageHeader, body: bytes): Option<Column> throws string
```

The column of one page of the chunk CH with header H and body BODY; none for a dictionary or index page.

Effects: fail.

### `PageDecoder.finish`

```gizlang
function PageDecoder.finish(self: PageDecoder): bool throws string
```

True once the decoded pages hold as many rows as the chunk; an error otherwise.

### `Writer.add`

```gizlang
function Writer.add(self: mut Writer, batch: Vec<Vec<Value>>): bytes throws string
```

Takes `batch` rows (one value for each column); returns the bytes of the file that are ready (the
leading magic, then each row group that closed), possibly none.

Effects: fail.

### `Writer.finish`

```gizlang
function Writer.finish(self: mut Writer): bytes throws string
```

Closes the last row group and returns the rest of the file: the footer and the trailing magic.

Effects: fail.

## Constants

### `COMPACT_MAX_DEPTH`

```gizlang
const COMPACT_MAX_DEPTH = 64
```

The deepest nesting of compact-protocol structs and lists that a reader accepts.

