8000 docs: specify the type descriptor format by colinhacks · Pull Request #154 · colinhacks/reylo · GitHub
[go: up one dir, main page]

Skip to content

docs: specify the type descriptor format - #154

Merged
colinhacks merged 4 commits into
mainfrom
type-descriptor-spec
Nov 2, 2026
Merged

colinhacks merged 4 commits into
mainfrom
type-descriptor-spec

Conversation

@colinhacks
Copy link
Copy Markdown
Owner

Adds spec/type-descriptor.ts, the format for describing the types of a statement's parameters and columns across relational databases, and what introspecting a statement returns (StatementDescriptor). It is written as TypeScript types with the rules as comments, and the format itself is plain data (JSON, YAML or any serialization with objects, lists, strings, numbers and booleans).

  • The kinds: boolean, integer (bits, unsigned), float (exponent and mantissa widths), decimal (precision and a scale range), text in three shapes by well-formedness, bytes, uuid, array, record, json, date, time, datetime, instant, duration, union, set, map, range, multirange, geometry, xml, and the opaque, any and unknown escape hatches.
  • The rules: a consumer reads kind, its required fields and a closed list of widening fields, and may ignore the rest; a type states what the database guarantees; a value is what the type denotes, never its encoding; one spelling per meaning; values inside the format are strings; validity rules for what no shape can rule out.
  • Typed examples for Postgres, MySQL, SQL Server, SQLite and Oracle, checked by nub run typecheck, which now includes spec/.

AGENTS.md lists the file. Nothing is implemented yet; the engines' types are to follow it.

Copilot AI balanced review requested due to automatic review settings November 2, 2026 21:23
@vercel
vercel Bot commented Nov 2, 2026 •
Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
reylo Error Error Nov 2, 2026 9:48pm UTC

Request Review

@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

A few of the spec's stated bounds are wrong in ways that make them unsound under its own rule 2: a bound must hold for every value. Engines built to follow the spec would refuse values the database accepts. I checked the Postgres ones against a Postgres 16 server.

Reviewed changes

I reviewed the whole new spec: the kinds, the rules, the per-database facts and the typed examples. I also ran the type check with spec/ included, and it passes.

  • spec/type-descriptor.ts: adds the Type union and StatementDescriptor. The rules are written as comments, and the examples for Postgres, MySQL, SQL Server, SQLite and Oracle are checked by the type check.
  • tsconfig.json: adds spec to include, so nub run typecheck now checks the examples.
  • AGENTS.md: adds one row for the new file to the table of what is where.

ℹ️ Rule 4 (one spelling per meaning) still allows two spellings

Rule 4 says each meaning has one spelling, but the spec still allows two in at least two places:

  • Exclusive bounds. Rule 7 forbids exclusive bounds only on whole-number kinds, but every discrete kind has the same problem. On a numeric(10,2) column, CHECK (x > 0) can be written as exclusive_min: "0" or as min: "0.01". A date, or a time kind with a tick, can be written both ways too.
  • Union member order. Rule 4 orders a union's members by kind, but says nothing about members of the same kind. The inet example has two records, and so does the Oracle subtype example, and either order is allowed.

Two descriptors with the same meaning can therefore differ, which defeats comparing them for equality.

Technical details
# Canonical spelling gaps

## Affected sites
- spec/type-descriptor.ts:26 and :427 — the rule against exclusive bounds covers whole-number kinds only
- spec/type-descriptor.ts:421 — rule 4 orders union members by kind only
- spec/type-descriptor.ts:519-522 and :578-584 — unions whose members share a kind

## Required outcome
- Every discrete kind (a decimal with a fixed `max_scale`, `date`, and the time kinds with a `tick`) has one canonical bound spelling, or the rule states the exception explicitly.
- Union members of the same kind have a defined order, or the spec states that member order is not significant.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

Comment thread spec/type-descriptor.ts Resolved
Comment thread spec/type-descriptor.ts Resolved
Comment thread spec/type-descriptor.ts Resolved
Comment thread spec/type-descriptor.ts Resolved
Comment thread spec/type-descriptor.ts Resolved
Comment thread spec/type-descriptor.ts Resolved
Copilot AI left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Finite bounds incorrectly exclude valid Postgres special values, and restricting schema sources to objects rejects valid boolean JSON Schemas already supported by the runtime.

2 open issues
What changed in this PR

This PR specifies a shared type descriptor format for Reylo’s database engines to follow. It defines the contract without changing engine behavior.

Changes:

  • Defines statement descriptors, type kinds, validity rules, and database-specific semantics.
  • Adds typed examples for five databases and includes them in TypeScript checking.
  • Documents the specification’s location in the contributor guide.
File Description
tsconfig.json Includes spec/ in TypeScript checking.
spec/​type-descriptor.ts Defines the descriptor format, rules, and typed examples.
AGENTS.md Adds the specification to the repository guide.

Review effort: Balanced

Comment thread spec/type-descriptor.ts Outdated
comment?: string;
}

/** Bounds on an ordered kind's values, in its canonical text (`"-1.50"`, `"2026-10-23"`, and for a decimal an exponent where it is shorter, `"1e126"`), as JSON Schema's `minimum`, `maximum`, `exclusiveMinimum` and `exclusiveMaximum` are: each holds a value, so `CHECK (x > 0)` on a float is `exclusive_min: "0"`. A side takes one of its two fields, and a whole-number kind takes only `min` and `max` (`x > 0` is `min: "1"`). The infinities sort before and after every finite value, and NaN after infinity, as Postgres sorts them, so a `max` excludes NaN. */
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0b36bf7: bounds now constrain the finite values only, and whether infinity, -infinity and NaN occur is said by the infinity and nan flags alone, whatever the bounds. The timestamptz, day and total examples are right under that rule.

Comment thread spec/type-descriptor.ts
export interface JsonType extends Base {
kind: "json";
content?: Position;
schema?: { dialect: string; source: object };
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is deliberate, and nothing is lost. A true schema admits every JSON value, so it is written by leaving schema out (rule 4 omits defaults). A false schema admits no value, which is a union with no members (UnionType). Keeping source an object gives each meaning one spelling.

@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No new issues. Every bound corrected in e2a4540 is now right, and one thread from the first review is still open.

Reviewed changes

I reviewed e2a4540, which fixes five of the six threads from the first review. I resolved those five threads.

  • Fixed Postgres's earliest date: it is now -4713-11-24 in the date doc, in rule 5, and in the timestamptz and day examples.
  • Fixed the duration bounds' sign: each part's max is now the magnitude on the negative side (P2147483648M, P2147483648D, PT2562047788H54.775808S), and the DurationType doc explains why. The time bound checks out: 9,223,372,036,854.775808 s is 2,562,047,788 h plus 54.775808 s.
  • Fixed money's bounds: they are now -922,337,203,685,477.5808 and 922,337,203,685,477.5807.
  • Listed the duration parts as widening fields: rule 1 now names months, days and time.
  • Narrowed pgvector's element type: the vector(3) element is now a float that holds neither infinity nor NaN, and the unused float4 constant is gone. A grep finds no leftover use of the old values.

Still open from the first review: the StatementDescriptor.kind thread on line 347 (a statement that parses but is none of the five kinds, such as CALL, VALUES, PRAGMA or DDL, has no value for kind), and the ℹ️ note in the review body about rule 4's two spellings.

Pullfrog  | Fix it ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

@colinhacks
Copy link
Copy Markdown
Owner Author

On the first review's rule-4 points, both settled in 0b36bf7: only a whole-number kind must write its bounds inclusive; any other kind writes a bound as the constraint states it, so x > 0 on a numeric(10,2) is exclusive_min: "0", never min: "0.01". Union members of one kind follow the order the database declares them (an object type before its subtypes, an inet's IPv4 record before its IPv6 one), and the Oracle example now lists R.SHAPE_T first.

@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues. One minor suggestion inline: rule 2 still contradicts the new rule that bounds cover only finite values.

Reviewed changes

I reviewed 0b36bf7, which settles the open thread on kind, the bound spelling and union member order, and the Copilot thread on infinities beside bounds. I resolved the kind thread.

  • Limited bounds to finite values: Bounds now says only the infinity and nan flags decide whether the special values occur, whatever the bounds say. The date, datetime and instant flag docs no longer say a bound excludes the infinity beyond it. Under this rule the timestamptz, day and total examples are consistent.
  • Kept each bound as the constraint writes it: a kind that is not a whole number keeps an exclusive bound exclusive (x > 0 on numeric(10,2) is exclusive_min: "0", never min: "0.01"). Only whole-number kinds rewrite a bound as inclusive.
  • Added kind: "other": a statement that parses but is none of the five kinds is now other, and kind is absent only when the statement does not parse.
  • Ordered union members of one kind: members of the same kind now follow the order the database declares them in. The Oracle example now lists R.SHAPE_T before R.CIRCLE_T, and the inet example already lists IPv4 first.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

Comment thread spec/type-descriptor.ts Resolved
@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. Rule 2 and Bounds now agree that bounds cover only finite values.

Reviewed changes

I reviewed ff2f4f7, which fixes the one open thread from the last review. I resolved that thread.

  • Limited rule 2's bounds to finite values: rule 2 now says a bound must hold for every finite value, and the infinity and nan flags cover the rest. This matches Bounds, the date, datetime and instant flag docs, and the timestamptz, day and total examples, each of which has a finite bound beside infinity: true.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

@colinhacks
colinhacks merged commit fa64094 into main Nov 2, 2026
9 of 10 checks passed
@colinhacks
colinhacks deleted the type-descriptor-spec branch November 2, 2026 21:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0